@woosh/meep-engine 3.1.0 → 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 (1822) hide show
  1. package/LICENSE +188 -188
  2. package/README.md +152 -152
  3. package/editor/Editor.js +598 -598
  4. package/editor/EditorCameraNavigation.js +205 -205
  5. package/editor/EditorKeyMap.js +182 -182
  6. package/editor/SelectionVisualizer.js +127 -127
  7. package/editor/actions/concrete/ActionInvalidateSampler.js +27 -27
  8. package/editor/actions/concrete/PatchTerrainHeightAction.js +85 -85
  9. package/editor/camera/camera_framing.js +104 -104
  10. package/editor/clipboard.js +184 -184
  11. package/editor/ecs/component/editors/ObservedIntegerEditor.js +22 -22
  12. package/editor/ecs/component/editors/ObservedStringEditor.js +34 -34
  13. package/editor/ecs/component/editors/common/two_way_sync.js +50 -50
  14. package/editor/ecs/component/editors/ecs/GridObstacleEditor.js +19 -19
  15. package/editor/ecs/component/editors/ecs/ParameterLookupTableEditor.js +35 -35
  16. package/editor/ecs/component/editors/ecs/ParameterTrackEditor.js +17 -17
  17. package/editor/ecs/component/editors/ecs/SGMeshEditor.js +24 -24
  18. package/editor/ecs/component/editors/ecs/ShadedGeometryEditor.js +33 -33
  19. package/editor/ecs/component/editors/ecs/Trail3DEditor.js +45 -45
  20. package/editor/ecs/component/editors/ecs/buildGridObstaclePreview.js +19 -19
  21. package/editor/ecs/component/editors/ecs/terrain/TerrainEditor.js +72 -72
  22. package/editor/ecs/component/editors/shade/MeshletGeometryEditor.js +30 -30
  23. package/editor/ecs/component/editors/shade/ShadeImageEditor.js +70 -70
  24. package/editor/ecs/component/editors/shade/ShadeMaterialEditor.js +34 -34
  25. package/editor/ecs/component/editors/shade/ShadeTextureEditor.js +46 -46
  26. package/editor/ecs/component/editors/shade/StandardShadeMaterialEditor.js +24 -24
  27. package/editor/ecs/component/prototypeObjectEditor.js +265 -265
  28. package/editor/ecs/component/registerBasicTypeEditors.js +100 -100
  29. package/editor/ecs/component/registerEngineComponentEditors.js +95 -95
  30. package/editor/entity_world_bounds.js +27 -27
  31. package/editor/particles/lut_ops.js +148 -148
  32. package/editor/particles/particleEditEvents.js +12 -12
  33. package/editor/particles/rebuildParticleEmitter.js +31 -31
  34. package/editor/persistence/EditorLayoutStore.js +71 -71
  35. package/editor/persistence/SceneMetadata.js +52 -52
  36. package/editor/persistence/SceneSlotManager.js +144 -144
  37. package/editor/persistence/buildNewSceneDataset.js +67 -67
  38. package/editor/persistence/files.js +68 -68
  39. package/editor/persistence/scene_import.js +143 -143
  40. package/editor/persistence/scene_serialization.js +79 -79
  41. package/editor/persistence/string_buffer_codec.js +41 -41
  42. package/editor/process/GridDisplayProcess.js +125 -125
  43. package/editor/process/SymbolicDisplayProcess.js +106 -108
  44. package/editor/process/symbolic/CameraSymbolicDisplay.js +146 -146
  45. package/editor/process/symbolic/LightSymbolicDisplay.js +172 -172
  46. package/editor/process/symbolic/PathSymbolicDisplay.js +118 -118
  47. package/editor/process/symbolic/PositionedMarkerSymbolicDisplay.js +79 -79
  48. package/editor/process/symbolic/SoundEmitterSymbolicDisplay.js +57 -57
  49. package/editor/process/symbolic/SymbolicDisplay.js +62 -62
  50. package/editor/process/symbolic/gizmo_draw_wire_box_oriented.js +85 -85
  51. package/editor/process/symbolic/gizmo_draw_wire_circle.js +53 -53
  52. package/editor/prototypeEditorShell.js +156 -156
  53. package/editor/selection/editor_pick.js +130 -130
  54. package/editor/selection/screen_rect_frustum.js +119 -119
  55. package/editor/style/controls.scss +168 -168
  56. package/editor/style/inspector.scss +223 -223
  57. package/editor/templates/entity_templates.js +167 -167
  58. package/editor/tools/GridPaintTool.js +273 -273
  59. package/editor/tools/SelectionTool.js +307 -307
  60. package/editor/tools/TransformToolV2.js +378 -378
  61. package/editor/tools/engine/ToolEngine.js +188 -188
  62. package/editor/tools/paint/TerrainTexturePaintTool.js +250 -250
  63. package/editor/tools/v2/BlenderCameraOrientationGizmo.js +501 -501
  64. package/editor/tools/v2/TransformControls.js +971 -971
  65. package/editor/tools/v2/handle/gizmo_drag_plane.js +141 -141
  66. package/editor/tools/v2/handle/gizmo_draw.js +399 -399
  67. package/editor/tools/v2/handle/gizmo_handles.js +364 -364
  68. package/editor/tools/v2/handle/gizmo_ray_shapes.js +239 -239
  69. package/editor/view/ecs/HierarchicalEntityListView.js +339 -339
  70. package/editor/view/ecs/components/DatGuiController.js +2 -2
  71. package/editor/view/ecs/components/common/AutoCanvasView.js +100 -100
  72. package/editor/view/ecs/components/common/TextController.js +59 -59
  73. package/editor/view/library/MeshLibraryView.js +175 -175
  74. package/editor/view/library/mesh_library_drag.js +63 -63
  75. package/editor/view/library/model_thumbnail_source.js +86 -86
  76. package/editor/view/makeEntityDecorators.js +125 -125
  77. package/editor/view/node-graph/NodeGraphSelection.js +89 -89
  78. package/editor/view/node-graph/NodeView.js +211 -211
  79. package/editor/view/node-graph/actions/ConnectionDeleteAction.js +36 -36
  80. package/editor/view/node-graph/actions/NodesMoveAction.js +41 -41
  81. package/editor/view/node-graph/connection_wire_geometry.js +107 -107
  82. package/editor/view/particles/ColorLutGradientView.js +291 -291
  83. package/editor/view/particles/ScalarLutCurveView.js +279 -279
  84. package/editor/view/prepareMeshLibrary.js +188 -188
  85. package/editor/view/shell/SceneTreePanelView.js +119 -119
  86. package/editor/view/v2/SplitView.js +2 -2
  87. package/package.json +94 -94
  88. package/samples/generation/generators/interactive/mir_generator_place_buff_objects.js +362 -362
  89. package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +738 -738
  90. package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +79 -79
  91. package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +93 -93
  92. package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +80 -80
  93. package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +164 -164
  94. package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +69 -69
  95. package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +102 -102
  96. package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +129 -129
  97. package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +72 -72
  98. package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +74 -74
  99. package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +169 -169
  100. package/src/DUPLICATION_AUDIT_2026_08_07.md +254 -254
  101. package/src/DUPLICATION_AUDIT_REMEDIATION.md +250 -250
  102. package/src/REVIEW_2026_08_06/adv-core.md +511 -511
  103. package/src/REVIEW_2026_08_06/adv-gfx.md +480 -480
  104. package/src/REVIEW_2026_08_06/adv-view.md +446 -446
  105. package/src/REVIEW_2026_08_06/api-core.md +296 -296
  106. package/src/REVIEW_2026_08_06/api-engine.md +195 -195
  107. package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +507 -507
  108. package/src/REVIEW_2026_08_06/core-binary.md +348 -348
  109. package/src/REVIEW_2026_08_06/core-collection.md +376 -376
  110. package/src/REVIEW_2026_08_06/core-math.md +221 -221
  111. package/src/REVIEW_2026_08_06/core-process.md +266 -266
  112. package/src/REVIEW_2026_08_06/core-science.md +223 -223
  113. package/src/REVIEW_2026_08_06/engine-ai-nav.md +279 -279
  114. package/src/REVIEW_2026_08_06/engine-animation.md +167 -167
  115. package/src/REVIEW_2026_08_06/engine-asset.md +166 -166
  116. package/src/REVIEW_2026_08_06/engine-ecs.md +920 -920
  117. package/src/REVIEW_2026_08_06/engine-input.md +197 -197
  118. package/src/REVIEW_2026_08_06/engine-misc.md +248 -248
  119. package/src/REVIEW_2026_08_06/engine-terrain.md +52 -52
  120. package/src/REVIEW_2026_08_06/generation.md +176 -176
  121. package/src/REVIEW_2026_08_06/geom-2d.md +434 -434
  122. package/src/REVIEW_2026_08_06/geom-3d.md +117 -117
  123. package/src/REVIEW_2026_08_06/gfx-gi.md +143 -143
  124. package/src/REVIEW_2026_08_06/gfx-render.md +740 -740
  125. package/src/REVIEW_2026_08_06/gfx-texture.md +690 -690
  126. package/src/REVIEW_2026_08_06/prior-criticals.md +49 -49
  127. package/src/REVIEW_2026_08_06/prior-highs.md +138 -138
  128. package/src/REVIEW_2026_08_06/test-gaps.md +364 -364
  129. package/src/REVIEW_2026_08_06/view.md +667 -667
  130. package/src/REVIEW_2026_08_06.md +610 -610
  131. package/src/avif/codec/dec/avif_dec.cpp +233 -233
  132. package/src/avif/codec/dec/avif_dec.js +16 -16
  133. package/src/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/input/GAMEPAD_API_PROPOSAL.md +717 -717
  1072. package/src/engine/input/analog/apply_radial_dead_zone.js +38 -38
  1073. package/src/engine/input/devices/GamepadDevice.js +466 -466
  1074. package/src/engine/input/devices/ManualInputDevice.js +152 -152
  1075. package/src/engine/input/devices/PointerCoordinates.js +26 -26
  1076. package/src/engine/input/devices/events/GamepadEvents.js +10 -10
  1077. package/src/engine/input/devices/gamepad/GamepadAxes.js +19 -19
  1078. package/src/engine/input/devices/gamepad/GamepadButtons.js +29 -29
  1079. package/src/engine/input/devices/gamepad/GamepadCoordinates.js +17 -17
  1080. package/src/engine/input/devices/gamepad/GamepadDeviceLayout.js +323 -323
  1081. package/src/engine/input/devices/gamepad/GamepadHandle.js +286 -286
  1082. package/src/engine/input/devices/gamepad/GamepadLayout.js +160 -160
  1083. package/src/engine/input/devices/gamepad/GamepadStandardMapping.js +31 -31
  1084. package/src/engine/input/devices/gamepad/GamepadStick.js +169 -169
  1085. package/src/engine/input/devices/gamepad/GamepadStickCalibration.js +119 -119
  1086. package/src/engine/input/devices/gamepad/GamepadStickCalibrator.js +250 -250
  1087. package/src/engine/input/devices/gamepad/gamepad_layout_database.js +144 -144
  1088. package/src/engine/input/devices/gamepad/gamepad_read_hat_axis.js +61 -61
  1089. package/src/engine/input/devices/gamepad/gamepad_write_dpad_vector.js +30 -30
  1090. package/src/engine/input/devices/gamepad/prototypeGamepadTester.js +2124 -2124
  1091. package/src/engine/input/devices/mouse/MouseButtons.js +22 -22
  1092. package/src/engine/input/ecs/components/Input.js +118 -118
  1093. package/src/engine/input/ecs/components/InputController.js +78 -78
  1094. package/src/engine/input/ecs/ism/ISM_DESIGN.md +1523 -1523
  1095. package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +627 -627
  1096. package/src/engine/input/ecs/ism/InputActionEvent.js +40 -40
  1097. package/src/engine/input/ecs/ism/InputBinding.js +71 -71
  1098. package/src/engine/input/ecs/ism/InputCoordinateKind.js +25 -25
  1099. package/src/engine/input/ecs/ism/InputCoordinateSource.js +86 -86
  1100. package/src/engine/input/ecs/ism/InputDirection.js +33 -33
  1101. package/src/engine/input/ecs/ism/InputMapProgram.js +236 -236
  1102. package/src/engine/input/ecs/ism/InputMapSystem.js +976 -976
  1103. package/src/engine/input/ecs/ism/InputOcclusion.js +31 -31
  1104. package/src/engine/input/ecs/ism/InputSource.js +67 -67
  1105. package/src/engine/input/ecs/ism/InputSourceTable.js +107 -107
  1106. package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannel.js +93 -93
  1107. package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannelType.js +11 -11
  1108. package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.js +153 -153
  1109. package/src/engine/input/ecs/ism/coordinate/InputCoordinateSwitches.js +160 -160
  1110. package/src/engine/input/ecs/ism/device/GAMEPAD_INPUT_DEVICE_ID.js +9 -9
  1111. package/src/engine/input/ecs/ism/device/GamepadInputDeviceAdapter.js +88 -88
  1112. package/src/engine/input/ecs/ism/device/InputDeviceAdapter.js +72 -72
  1113. package/src/engine/input/ecs/ism/device/KEYBOARD_INPUT_DEVICE_ID.js +9 -9
  1114. package/src/engine/input/ecs/ism/device/KeyboardInputDeviceAdapter.js +63 -63
  1115. package/src/engine/input/ecs/ism/device/ManualInputDeviceAdapter.js +55 -55
  1116. package/src/engine/input/ecs/ism/device/POINTER_INPUT_DEVICE_ID.js +9 -9
  1117. package/src/engine/input/ecs/ism/device/PointerInputDeviceAdapter.js +160 -160
  1118. package/src/engine/input/ecs/ism/map/InputBinding.js +88 -88
  1119. package/src/engine/input/ecs/ism/map/InputCoordinateBinding.js +102 -102
  1120. package/src/engine/input/ecs/ism/map/InputLayer.js +220 -220
  1121. package/src/engine/input/ecs/ism/map/InputMap.js +331 -331
  1122. package/src/engine/input/ecs/ism/prototypeInputMap.js +309 -309
  1123. package/src/engine/input/ecs/ism/trigger/InputTrigger.js +148 -148
  1124. package/src/engine/input/ecs/ism/trigger/InputTriggerChord.js +173 -173
  1125. package/src/engine/input/ecs/ism/trigger/InputTriggerGamepadButton.js +37 -37
  1126. package/src/engine/input/ecs/ism/trigger/InputTriggerHold.js +176 -176
  1127. package/src/engine/input/ecs/ism/trigger/InputTriggerKey.js +39 -39
  1128. package/src/engine/input/ecs/ism/trigger/InputTriggerMouseButton.js +38 -38
  1129. package/src/engine/input/ecs/ism/trigger/InputTriggerRelease.js +120 -120
  1130. package/src/engine/input/ecs/ism/trigger/InputTriggerSequence.js +315 -315
  1131. package/src/engine/input/ecs/ism/trigger/InputTriggerSwitch.js +105 -105
  1132. package/src/engine/input/ecs/ism/trigger/InputTriggerType.js +17 -17
  1133. package/src/engine/input/ecs/systems/InputControllerSystem.js +253 -253
  1134. package/src/engine/input/ecs/systems/InputSystem.js +162 -162
  1135. package/src/engine/intelligence/behavior/primitive/FailingBehaviorSerializationAdapter.js +25 -25
  1136. package/src/engine/intelligence/behavior/primitive/SucceedingBehaviorSerializationAdapter.js +26 -26
  1137. package/src/engine/intelligence/behavior/util/AsynchronousDelayAction.js +55 -55
  1138. package/src/engine/interpolation/BinaryInterpolationAdapter.js +56 -56
  1139. package/src/engine/interpolation/Interpoland.js +49 -49
  1140. package/src/engine/interpolation/Interpolated.js +149 -149
  1141. package/src/engine/interpolation/PoseInterpolationAdapter.js +61 -61
  1142. package/src/engine/interpolation/TransformPoseSerializationAdapter.js +57 -57
  1143. package/src/engine/interpolation/pose_interpoland.js +27 -27
  1144. package/src/engine/navigation/ecs/path_following/PathFollower.js +222 -222
  1145. package/src/engine/navigation/mesh/NavigationMesh.js +145 -145
  1146. package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +661 -0
  1147. package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +229 -229
  1148. package/src/engine/navigation/mesh/build/clip_soup_against_overhangs.d.ts.map +1 -1
  1149. package/src/engine/navigation/mesh/build/clip_soup_against_overhangs.js +628 -520
  1150. package/src/engine/network/CONGESTION_CONTROL.md +87 -87
  1151. package/src/engine/network/README.md +52 -52
  1152. package/src/engine/network/adapters/QuaternionInterpolationAdapter.js +43 -43
  1153. package/src/engine/network/adapters/TransformInterpolationAdapter.js +84 -84
  1154. package/src/engine/network/adapters/TransformReplicationAdapter.js +87 -87
  1155. package/src/engine/network/adapters/Vector3InterpolationAdapter.js +46 -46
  1156. package/src/engine/network/convertPathToURL.js +2 -2
  1157. package/src/engine/network/core/MalformedPacketError.js +26 -26
  1158. package/src/engine/network/core/quantize/quantize_float.js +73 -73
  1159. package/src/engine/network/core/quantize/quantize_position.js +65 -65
  1160. package/src/engine/network/core/sequence/ack_bitfield.js +77 -77
  1161. package/src/engine/network/core/sequence/seq16.js +69 -69
  1162. package/src/engine/network/core/sequence/seq32.js +73 -73
  1163. package/src/engine/network/ecs/NetworkSystem.js +84 -84
  1164. package/src/engine/network/ecs/components/NetworkIdentity.js +73 -73
  1165. package/src/engine/network/ecs/owner_authorization.js +55 -55
  1166. package/src/engine/network/ecs/serialization/NetworkIdentitySerializationAdapter.js +64 -64
  1167. package/src/engine/network/replication/ScopeFilter.js +71 -71
  1168. package/src/engine/network/sim/ActionLog.js +152 -152
  1169. package/src/engine/network/sim/SimAction.js +273 -273
  1170. package/src/engine/network/sim/Snapshotter.js +222 -222
  1171. package/src/engine/network/state/FrameBufferRing.js +168 -168
  1172. package/src/engine/network/state/InputRing.js +134 -134
  1173. package/src/engine/network/state/PriorityAccumulator.js +180 -180
  1174. package/src/engine/network/time/TimeDilation.js +62 -62
  1175. package/src/engine/network/transport/Channel.js +336 -336
  1176. package/src/engine/network/transport/Transport.js +203 -203
  1177. package/src/engine/network/transport/adapters/NodeUDPTransport.js +186 -186
  1178. package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.js +105 -105
  1179. package/src/engine/network/transport/fragments/fragment_send.js +147 -147
  1180. package/src/engine/network/transport/fragments/packet_size.js +101 -101
  1181. package/src/engine/network/xhr.js +23 -23
  1182. package/src/engine/physics/BULLET_REVIEW.md +945 -945
  1183. package/src/engine/physics/CANNON_REVIEW.md +1300 -1300
  1184. package/src/engine/physics/CONSTRAINT_SOLVER_BENCH_LOG.md +208 -208
  1185. package/src/engine/physics/CONSTRAINT_SOLVER_IMPROVEMENTS_PLAN.md +364 -364
  1186. package/src/engine/physics/INTEPOLATION_SYSTEM_PLAN.md +287 -287
  1187. package/src/engine/physics/JOLT_REVIEW.md +913 -913
  1188. package/src/engine/physics/PLAN.md +1120 -1094
  1189. package/src/engine/physics/RAPIER_REVIEW.md +934 -934
  1190. package/src/engine/physics/REVIEW_001_ACTION_PLAN.md +642 -642
  1191. package/src/engine/physics/REVIEW_002.md +151 -151
  1192. package/src/engine/physics/REVIEW_003.md +166 -166
  1193. package/src/engine/physics/broadphase/compute_fat_world_aabb.js +97 -97
  1194. package/src/engine/physics/constraint/DofMode.js +35 -35
  1195. package/src/engine/physics/constraint/solve_constraints.js +1 -1
  1196. package/src/engine/physics/contact/combine_material.js +35 -35
  1197. package/src/engine/physics/ecs/BodyKind.js +29 -29
  1198. package/src/engine/physics/ecs/Collider.js +177 -177
  1199. package/src/engine/physics/ecs/ColliderFlags.js +15 -15
  1200. package/src/engine/physics/ecs/ColliderSerializationAdapter.js +37 -37
  1201. package/src/engine/physics/ecs/Joint.js +475 -475
  1202. package/src/engine/physics/ecs/JointSerializationAdapter.js +72 -72
  1203. package/src/engine/physics/ecs/PhysicsSystem.d.ts +3 -0
  1204. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  1205. package/src/engine/physics/ecs/PhysicsSystem.js +7 -2
  1206. package/src/engine/physics/ecs/RigidBodySerializationUpgrader_0_1.js +37 -37
  1207. package/src/engine/physics/ecs/SleepState.js +12 -12
  1208. package/src/engine/physics/ecs/find_non_finite_physics_state.js +76 -76
  1209. package/src/engine/physics/fluid/FluidField.js +867 -867
  1210. package/src/engine/physics/fluid/FluidSimulator.js +832 -832
  1211. package/src/engine/physics/fluid/REVIEW_02_PLAN.md +243 -243
  1212. package/src/engine/physics/fluid/SliceVisualiser.js +190 -190
  1213. package/src/engine/physics/fluid/ecs/FluidComponent.js +239 -239
  1214. package/src/engine/physics/fluid/ecs/FluidEffectorsComponent.js +89 -89
  1215. package/src/engine/physics/fluid/ecs/FluidObstacle.js +97 -97
  1216. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  1217. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.js +348 -348
  1218. package/src/engine/physics/fluid/ecs/FluidSystem.js +142 -142
  1219. package/src/engine/physics/fluid/ecs/FluidWorkerCore.js +123 -123
  1220. package/src/engine/physics/fluid/ecs/WorkerFluidSystem.js +395 -395
  1221. package/src/engine/physics/fluid/ecs/fluid.worker.js +12 -12
  1222. package/src/engine/physics/fluid/ecs/fluid_build_world_to_grid.js +32 -32
  1223. package/src/engine/physics/fluid/ecs/fluid_gather_visible_effectors.js +47 -47
  1224. package/src/engine/physics/fluid/ecs/fluid_reanchor_field.js +85 -85
  1225. package/src/engine/physics/fluid/ecs/fluid_sync_effectors_from_transform.js +16 -16
  1226. package/src/engine/physics/fluid/ecs/fluid_worker_protocol.js +111 -111
  1227. package/src/engine/physics/fluid/ecs/makeFluidWorker.js +18 -18
  1228. package/src/engine/physics/fluid/effector/AbstractFluidEffector.js +81 -81
  1229. package/src/engine/physics/fluid/effector/GlobalFluidEffector.js +202 -202
  1230. package/src/engine/physics/fluid/effector/ImpulseFluidEffector.js +257 -257
  1231. package/src/engine/physics/fluid/effector/WakeFluidEffector.js +405 -405
  1232. package/src/engine/physics/fluid/prototype.js +126 -126
  1233. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_scalar.js +143 -143
  1234. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.js +162 -162
  1235. package/src/engine/physics/fluid/solver/v3_grid_advect_sl_velocity.js +107 -107
  1236. package/src/engine/physics/fluid/solver/v3_grid_apply_vorticity_confinement.js +131 -131
  1237. package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js +90 -90
  1238. package/src/engine/physics/fluid/solver/v3_grid_limit8_at_indices.js +38 -38
  1239. package/src/engine/physics/fluid/solver/v3_grid_patch_edges_constant.js +77 -77
  1240. package/src/engine/physics/fluid/solver/v3_grid_patch_edges_uniform.js +113 -113
  1241. package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.js +92 -92
  1242. package/src/engine/physics/fluid/solver/v3_grid_shift_in_place.js +107 -107
  1243. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.js +135 -135
  1244. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js +511 -511
  1245. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +108 -108
  1246. package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.js +317 -317
  1247. package/src/engine/physics/fluid/solver/v3_mac_advect_scalar.js +236 -236
  1248. package/src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.js +217 -217
  1249. package/src/engine/physics/fluid/solver/v3_mac_apply_vorticity_confinement.js +174 -174
  1250. package/src/engine/physics/fluid/solver/v3_mac_clip_trace.js +95 -95
  1251. package/src/engine/physics/fluid/solver/v3_mac_compute_divergence.js +77 -77
  1252. package/src/engine/physics/fluid/solver/v3_mac_compute_face_solid.js +131 -131
  1253. package/src/engine/physics/fluid/solver/v3_mac_subtract_pressure_gradient.js +104 -104
  1254. package/src/engine/physics/inertia/world_inverse_inertia.js +160 -160
  1255. package/src/engine/physics/integration/integrate_position.js +97 -97
  1256. package/src/engine/physics/narrowphase/box_box_manifold.js +681 -681
  1257. package/src/engine/physics/narrowphase/box_triangle_contact.js +906 -906
  1258. package/src/engine/physics/narrowphase/capsule_triangle_contact.js +395 -395
  1259. package/src/engine/physics/narrowphase/compute_penetration.js +369 -369
  1260. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.js +55 -55
  1261. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.js +37 -37
  1262. package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.js +117 -117
  1263. package/src/engine/physics/narrowphase/narrowphase_step.js +1738 -1738
  1264. package/src/engine/physics/narrowphase/reduce_manifold_contacts.js +74 -74
  1265. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +12 -11
  1266. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -1
  1267. package/src/engine/physics/narrowphase/refine_ray_hit.js +228 -36
  1268. package/src/engine/physics/narrowphase/sphere_sphere_contact.js +52 -52
  1269. package/src/engine/physics/narrowphase/sphere_triangle_contact.js +138 -138
  1270. package/src/engine/physics/persistence/solver_caches.js +309 -309
  1271. package/src/engine/physics/queries/overlap_shape.js +187 -187
  1272. package/src/engine/physics/queries/raycast.d.ts +7 -4
  1273. package/src/engine/physics/queries/raycast.d.ts.map +1 -1
  1274. package/src/engine/physics/queries/raycast.js +11 -5
  1275. package/src/engine/physics/queries/shape_cast.js +417 -417
  1276. package/src/engine/physics/solver/apply_impulse_to_body.js +86 -86
  1277. package/src/engine/physics/solver/friction_cone.js +37 -37
  1278. package/src/engine/physics/solver/solve_contacts.js +1 -1
  1279. package/src/engine/platform/WebEnginePlatform.d.ts.map +1 -1
  1280. package/src/engine/platform/WebEnginePlatform.js +1 -2
  1281. package/src/engine/scene/SerializedScene.d.ts +2 -2
  1282. package/src/engine/scene/SerializedScene.d.ts.map +1 -1
  1283. package/src/engine/scene/transitionToScene.js +121 -121
  1284. package/src/engine/simulation/Ticker.d.ts.map +1 -1
  1285. package/src/engine/simulation/Ticker.js +33 -0
  1286. package/src/engine/sound/ecs/audio/LiveEmitterSet.js +345 -345
  1287. package/src/engine/sound/ecs/audio/SpatialAudioIndex.js +140 -140
  1288. package/src/engine/sound/simulation/AcousticSimulator.js +646 -646
  1289. package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +432 -432
  1290. package/src/engine/sound/simulation/PROBE_PLACEMENT.md +250 -250
  1291. package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +322 -322
  1292. package/src/engine/sound/simulation/README.md +277 -277
  1293. package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +261 -261
  1294. package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +121 -121
  1295. package/src/engine/sound/simulation/configureAcousticSimulation.js +48 -48
  1296. package/src/engine/sound/simulation/core/AcousticMedium.js +113 -113
  1297. package/src/engine/sound/simulation/core/AcousticMediumSerializationAdapter.js +27 -27
  1298. package/src/engine/sound/simulation/core/AcousticOccluderIndex.js +312 -312
  1299. package/src/engine/sound/simulation/core/AcousticRegionIndex.js +159 -159
  1300. package/src/engine/sound/simulation/core/AcousticSolution.js +90 -90
  1301. package/src/engine/sound/simulation/core/AcousticSourceState.js +109 -109
  1302. package/src/engine/sound/simulation/core/AcousticVolumeRegion.js +73 -73
  1303. package/src/engine/sound/simulation/core/OcclusionSolver.js +112 -112
  1304. package/src/engine/sound/simulation/core/RayHit.js +50 -50
  1305. package/src/engine/sound/simulation/core/VolumeField.js +149 -149
  1306. package/src/engine/sound/simulation/definition/AcousticDirectivity.js +114 -114
  1307. package/src/engine/sound/simulation/definition/AcousticDirectivitySerializationAdapter.js +30 -30
  1308. package/src/engine/sound/simulation/definition/AcousticMaterial.js +122 -122
  1309. package/src/engine/sound/simulation/definition/AcousticMaterialSerializationAdapter.js +32 -32
  1310. package/src/engine/sound/simulation/ecs/AcousticBody.js +48 -48
  1311. package/src/engine/sound/simulation/ecs/AcousticBodySerializationAdapter.js +34 -34
  1312. package/src/engine/sound/simulation/ecs/AcousticProbeFieldSystem.js +64 -64
  1313. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.js +75 -75
  1314. package/src/engine/sound/simulation/ecs/AcousticVolume.js +74 -74
  1315. package/src/engine/sound/simulation/ecs/AcousticVolumeSerializationAdapter.js +37 -37
  1316. package/src/engine/sound/simulation/ecs/AcousticVolumeSystem.js +66 -66
  1317. package/src/engine/sound/simulation/probe/AcousticProbeField.js +578 -578
  1318. package/src/engine/sound/simulation/probe/AcousticProbeFieldSerializationAdapter.js +60 -60
  1319. package/src/engine/sound/simulation/probe/ProbeFieldPathingSolution.js +64 -64
  1320. package/src/engine/sound/simulation/probe/ProbeVisibilityRecheck.js +132 -132
  1321. package/src/engine/sound/simulation/probe/acoustic_probe_transfer.js +103 -103
  1322. package/src/engine/sound/simulation/probe/acoustic_trace_energy.js +136 -136
  1323. package/src/engine/sound/simulation/probe/bakeProbeField.js +169 -169
  1324. package/src/engine/sound/simulation/probe/bakeProbeReflectors.js +110 -110
  1325. package/src/engine/sound/simulation/probe/bakeReverbBands.js +110 -110
  1326. package/src/engine/sound/simulation/probe/bakeVisibility.js +148 -148
  1327. package/src/engine/sound/simulation/probe/echogram_band_rt60.js +83 -83
  1328. package/src/engine/sound/simulation/probe/probe_delaunay_edges.js +215 -215
  1329. package/src/engine/sound/simulation/probe/probe_densify_portals.js +140 -140
  1330. package/src/engine/sound/simulation/probe/probe_detect_portals.js +196 -196
  1331. package/src/engine/sound/simulation/probe/probe_edge_key.js +38 -38
  1332. package/src/engine/sound/simulation/probe/probe_edge_visible.js +41 -41
  1333. package/src/engine/sound/simulation/probe/probe_place_sdf_cover.js +82 -82
  1334. package/src/engine/sound/simulation/probe/probe_refine_connectivity.js +181 -181
  1335. package/src/engine/sound/simulation/probe/probe_sdf_grid.js +171 -171
  1336. package/src/engine/sound/simulation/probe/probe_visibility_augment.js +146 -146
  1337. package/src/engine/sound/simulation/probe/probe_visibility_bridge_components.js +167 -167
  1338. package/src/engine/sound/simulation/render/FoaProbeReverbRenderer.js +354 -354
  1339. package/src/engine/sound/simulation/render/ProbeReverbRenderer.js +231 -231
  1340. package/src/engine/sound/simulation/render/foaReverbImpulseResponse.js +105 -105
  1341. package/src/engine/sound/simulation/render/reverbImpulseResponse.js +66 -66
  1342. package/src/engine/sound/simulation/render/reverb_band_tails.js +93 -93
  1343. package/src/engine/sound/simulation/serialization/populateAcousticSerializationRegistry.js +25 -25
  1344. package/src/engine/sound/sopra/README.md +643 -643
  1345. package/src/engine/sound/sopra/SopraEngine.js +432 -432
  1346. package/src/engine/sound/sopra/asset/AssetManagerBufferProvider.js +71 -71
  1347. package/src/engine/sound/sopra/asset/BufferProvider.js +29 -29
  1348. package/src/engine/sound/sopra/asset/StubBufferProvider.js +58 -58
  1349. package/src/engine/sound/sopra/definition/BusDefinition.js +142 -142
  1350. package/src/engine/sound/sopra/definition/BusDefinitionSerializationAdapter.js +54 -54
  1351. package/src/engine/sound/sopra/definition/DuckingRule.js +106 -106
  1352. package/src/engine/sound/sopra/definition/DuckingRuleSerializationAdapter.js +31 -31
  1353. package/src/engine/sound/sopra/definition/EventDescription.js +262 -262
  1354. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.js +55 -55
  1355. package/src/engine/sound/sopra/definition/MixerSnapshot.js +83 -83
  1356. package/src/engine/sound/sopra/definition/MixerSnapshotSerializationAdapter.js +39 -39
  1357. package/src/engine/sound/sopra/definition/ParameterDefinition.js +117 -117
  1358. package/src/engine/sound/sopra/definition/ParameterDefinitionSerializationAdapter.js +31 -31
  1359. package/src/engine/sound/sopra/definition/VoiceStealMode.js +18 -18
  1360. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +109 -109
  1361. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.js +185 -185
  1362. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClipSerializationAdapter.js +59 -59
  1363. package/src/engine/sound/sopra/definition/clip/ContainerAudioClip.js +128 -128
  1364. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.js +226 -226
  1365. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClipSerializationAdapter.js +54 -54
  1366. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +191 -191
  1367. package/src/engine/sound/sopra/definition/clip/SampleAudioClipSerializationAdapter.js +39 -39
  1368. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.js +79 -79
  1369. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClipSerializationAdapter.js +42 -42
  1370. package/src/engine/sound/sopra/definition/clip/SilenceAudioClip.js +77 -77
  1371. package/src/engine/sound/sopra/definition/clip/SilenceAudioClipSerializationAdapter.js +27 -27
  1372. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +5 -0
  1373. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -1
  1374. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.js +125 -125
  1375. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClipSerializationAdapter.js +41 -41
  1376. package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.js +24 -24
  1377. package/src/engine/sound/sopra/definition/effect/CompressorEffect.js +120 -120
  1378. package/src/engine/sound/sopra/definition/effect/CompressorEffectSerializationAdapter.js +31 -31
  1379. package/src/engine/sound/sopra/definition/effect/EqEffect.js +128 -128
  1380. package/src/engine/sound/sopra/definition/effect/EqEffectSerializationAdapter.js +29 -29
  1381. package/src/engine/sound/sopra/definition/effect/ReverbEffect.js +113 -113
  1382. package/src/engine/sound/sopra/definition/effect/ReverbEffectSerializationAdapter.js +25 -25
  1383. package/src/engine/sound/sopra/runtime/BusGraph.js +231 -231
  1384. package/src/engine/sound/sopra/runtime/ParameterStore.js +98 -98
  1385. package/src/engine/sound/sopra/runtime/SopraPlaybackContext.js +68 -68
  1386. package/src/engine/sound/sopra/runtime/Voice.js +145 -145
  1387. package/src/engine/sound/sopra/runtime/VoiceManager.js +136 -136
  1388. package/src/engine/sound/sopra/runtime/VoicePool.js +17 -17
  1389. package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.js +42 -42
  1390. package/src/engine/sound/sopra/serialization/sopraJSON.js +99 -99
  1391. package/src/engine/sound/sopra/serialization/sopraSerializationHarness.js +49 -49
  1392. package/src/engine/sound/sopra/util/MockAudioContext.js +224 -224
  1393. package/src/engine/sound/sopra/util/fadeOutAndStop.js +60 -60
  1394. package/src/engine/ui/DraggableAspect.js +115 -115
  1395. package/src/engine/ui/apply_pointer_tilt_3d.js +50 -50
  1396. package/src/generation/COORDINATES.md +54 -54
  1397. package/src/generation/GridGenerationResource.js +24 -24
  1398. package/src/generation/GridTaskGroup.js +214 -214
  1399. package/src/generation/REVIEW_01_ACTION_PLAN.md +628 -628
  1400. package/src/generation/automata/CaveGeneratorCellularAutomata.js +79 -79
  1401. package/src/generation/automata/CellularAutomata.js +22 -22
  1402. package/src/generation/compute_writes_union.js +25 -25
  1403. package/src/generation/filtering/CellFilter.js +117 -117
  1404. package/src/generation/filtering/core/CellFilterBinaryOperation.js +37 -37
  1405. package/src/generation/filtering/core/CellFilterOperationTertiary.js +43 -43
  1406. package/src/generation/filtering/core/CellFilterUnaryOperation.js +29 -29
  1407. package/src/generation/filtering/numeric/process/computeCellFilterSobel.js +36 -36
  1408. package/src/generation/filtering/numeric/process/computeFilterSurfaceNormal.js +42 -42
  1409. package/src/generation/filtering/numeric/util/populateSampler2DFromCellFilter.js +18 -18
  1410. package/src/generation/filtering/numeric/util/write_cell_filter_to_sampler2d.js +47 -47
  1411. package/src/generation/filtering/util/cell_filter_weights_pick.js +54 -54
  1412. package/src/generation/grid/GridData.js +233 -233
  1413. package/src/generation/grid/coords/grid_to_texel.js +10 -10
  1414. package/src/generation/grid/coords/texel_to_grid.js +10 -10
  1415. package/src/generation/grid/generation/GridTaskApplyActionToCells.js +90 -90
  1416. package/src/generation/grid/generation/GridTaskDensityMarkerDistribution.js +388 -388
  1417. package/src/generation/grid/generation/GridTaskExecuteRuleTimes.js +127 -127
  1418. package/src/generation/grid/generation/GridTaskSequence.js +58 -58
  1419. package/src/generation/grid/generation/NoopGridTaskGenerator.js +12 -12
  1420. package/src/generation/grid/generation/discrete/GridTaskActionRuleSet.js +48 -48
  1421. package/src/generation/grid/generation/discrete/GridTaskCellularAutomata.js +146 -146
  1422. package/src/generation/grid/generation/discrete/GridTaskConnectRooms.js +540 -540
  1423. package/src/generation/grid/generation/discrete/layer/GridTaskBuildSourceDistanceMap.js +222 -222
  1424. package/src/generation/grid/generation/grid/GridTaskAddNodesFixed.js +44 -44
  1425. package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +614 -614
  1426. package/src/generation/markers/GridCellActionPlaceMarker.js +244 -244
  1427. package/src/generation/markers/GridCellActionPlaceMarkerGroup.js +86 -86
  1428. package/src/generation/markers/actions/terrain/MarkerNodeActionPaintTerrain.js +266 -266
  1429. package/src/generation/markers/emitter/MarkerNodeEmitterGridCellAction.js +49 -49
  1430. package/src/generation/placement/GridCellActionTransformNearbyMarkers.js +120 -120
  1431. package/src/generation/placement/action/GridCellAction.js +45 -45
  1432. package/src/generation/placement/action/GridCellActionPlaceTags.js +140 -140
  1433. package/src/generation/placement/action/GridCellActionWriteFilterToLayer.js +89 -89
  1434. package/src/generation/placement/action/random/CellActionSelectRandom.js +69 -69
  1435. package/src/generation/placement/action/random/weighted/CellActionSelectWeightedRandom.js +91 -91
  1436. package/src/generation/placement/action/util/GridCellActionSequence.js +61 -61
  1437. package/src/generation/placement/action/util/GridCellDisplacedAction.js +61 -61
  1438. package/src/generation/rules/cell/CellMatcherRemovalPreservesConnectivity.js +183 -183
  1439. package/src/generation/theme/ThemeEngine.js +666 -666
  1440. package/src/shade/RENDERER_CONTRACT.md +568 -568
  1441. package/src/shade/descriptor/texture/format/gpu_texture_format_info.js +267 -267
  1442. package/src/shade/device/ShadeDeviceFailure.js +104 -104
  1443. package/src/shade/device/ShadeDeviceFailureReason.js +41 -41
  1444. package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +255 -255
  1445. package/src/shade/device/mock/REVIEW_2026_08_22.md +527 -527
  1446. package/src/shade/device/mock/SoftwareGPUBindGroup.js +23 -23
  1447. package/src/shade/device/mock/SoftwareGPUBindGroupLayout.js +22 -22
  1448. package/src/shade/device/mock/SoftwareGPUError.js +63 -63
  1449. package/src/shade/device/mock/SoftwareGPUPipeline.js +75 -75
  1450. package/src/shade/device/mock/SoftwareGPUQuerySet.js +113 -113
  1451. package/src/shade/device/mock/SoftwareGPUSampler.js +30 -30
  1452. package/src/shade/device/mock/SoftwareGPUShaderModule.js +32 -32
  1453. package/src/shade/device/mock/SoftwareGPUTextureView.js +22 -22
  1454. package/src/shade/device/mock/SoftwareGPUUncapturedErrorEvent.js +23 -23
  1455. package/src/shade/device/mock/normalize_dynamic_offsets.js +26 -26
  1456. package/src/shade/device/mock/texture_copy.js +287 -287
  1457. package/src/shade/device/timing/GPUTimer.js +285 -285
  1458. package/src/shade/device/timing/GPUTimerArray.js +205 -205
  1459. package/src/shade/playground/add_random_lights.js +49 -49
  1460. package/src/shade/playground/add_random_meshes.js +83 -83
  1461. package/src/shade/playground/animation_prototype.js +508 -508
  1462. package/src/shade/playground/animation_prototype_skinning.js +647 -647
  1463. package/src/shade/playground/animation_prototype_stress.js +765 -765
  1464. package/src/shade/playground/enable_picker.js +59 -59
  1465. package/src/shade/playground/gltf_skinned_prototype.js +560 -560
  1466. package/src/shade/playground/gltf_skinned_stress_prototype.js +387 -387
  1467. package/src/shade/playground/gltf_viewer.js +282 -282
  1468. package/src/shade/playground/load_default_environment.js +19 -19
  1469. package/src/shade/playground/main.js +2727 -2727
  1470. package/src/shade/playground/make_animated_instances.js +117 -117
  1471. package/src/shade/playground/prototype_virtual_texture.js +274 -274
  1472. package/src/shade/playground/prototype_volumetrics_01.js +206 -206
  1473. package/src/shade/playground/sample.js +234 -234
  1474. package/src/shade/playground/sample_load_gltf.js +102 -102
  1475. package/src/shade/playground/spread_node3d_grid.js +19 -19
  1476. package/src/shade/renderer/Renderer.js +2436 -2436
  1477. package/src/shade/renderer/ShadeIndirectLightingMode.js +14 -14
  1478. package/src/shade/renderer/animation/ShadeAnimationChannel.js +414 -414
  1479. package/src/shade/renderer/animation/compute_skinned_mesh_clip_bounding_sphere.js +531 -531
  1480. package/src/shade/renderer/animation/pose/PosePlayback.js +55 -55
  1481. package/src/shade/renderer/animation/pose/pose_evaluate_local.js +179 -179
  1482. package/src/shade/renderer/animation/pose/pose_evaluate_world.js +76 -76
  1483. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +1024 -1024
  1484. package/src/shade/renderer/animation/skinning/SKINNING_BINDING_STRUCT.js +79 -79
  1485. package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.js +56 -56
  1486. package/src/shade/renderer/binding/BindGroupManager.js +282 -282
  1487. package/src/shade/renderer/binding/adapt_cache_capacity.js +25 -25
  1488. package/src/shade/renderer/buffer/GPUBufferAllocator.js +116 -116
  1489. package/src/shade/renderer/buffer/buffer_pool_ops.js +123 -123
  1490. package/src/shade/renderer/buffer/table/GPU_DATABASE_PAGE_SIZE.js +9 -9
  1491. package/src/shade/renderer/buffer/table/GPU_DATABASE_UPLOAD_BUFFER_LIMIT.js +8 -8
  1492. package/src/shade/renderer/buffer/table/PageBufferPool.js +71 -71
  1493. package/src/shade/renderer/buffer/table/PageSlotAllocator.js +95 -95
  1494. package/src/shade/renderer/camera/Camera.js +281 -281
  1495. package/src/shade/renderer/camera/GPUCameraContext.js +233 -233
  1496. package/src/shade/renderer/camera/orbital/README.md +84 -84
  1497. package/src/shade/renderer/deferred/shader_composite_volumetrics.js +75 -75
  1498. package/src/shade/renderer/dynamic/DYNAMIC_MESH_VERTEX.js +25 -25
  1499. package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.js +342 -342
  1500. package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.js +167 -167
  1501. package/src/shade/renderer/dynamic/shader_dynamic_mesh.js +170 -170
  1502. package/src/shade/renderer/extension/FrameContext.js +263 -263
  1503. package/src/shade/renderer/extension/FramePhase.js +104 -104
  1504. package/src/shade/renderer/extension/FrameRecord.js +126 -126
  1505. package/src/shade/renderer/extension/GBufferTextures.js +83 -83
  1506. package/src/shade/renderer/extension/PresentTarget.js +34 -34
  1507. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +918 -918
  1508. package/src/shade/renderer/extension/RenderExtension.js +69 -69
  1509. package/src/shade/renderer/extension/RenderExtensionRegistry.js +319 -319
  1510. package/src/shade/renderer/extension/SceneColor.js +40 -40
  1511. package/src/shade/renderer/extension/ViewTextures.js +78 -78
  1512. package/src/shade/renderer/fow/GPUFogOfWarRenderer.js +182 -182
  1513. package/src/shade/renderer/geometry/Geometry.js +608 -608
  1514. package/src/shade/renderer/geometry/geometry_build_from_meshlet_geometry.js +342 -342
  1515. package/src/shade/renderer/geometry/meshlet_geometry_transfer.js +41 -41
  1516. package/src/shade/renderer/geometry/primitives/make_box_geometry.js +153 -153
  1517. package/src/shade/renderer/geometry/primitives/make_cylinder_geometry.js +240 -240
  1518. package/src/shade/renderer/geometry/primitives/make_plane_geometry.js +145 -145
  1519. package/src/shade/renderer/geometry/primitives/make_polyhedron_geometry.js +327 -327
  1520. package/src/shade/renderer/geometry/primitives/make_torus_geometry.js +107 -107
  1521. package/src/shade/renderer/geometry/primitives/make_torus_knot_geometry.js +165 -165
  1522. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +607 -607
  1523. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +66 -66
  1524. package/src/shade/renderer/geometry/virtual/build/VGeoBuildResult.js +32 -32
  1525. package/src/shade/renderer/geometry/virtual/build/VGeoFaceGraph.js +103 -103
  1526. package/src/shade/renderer/geometry/virtual/build/VGeoGatheredFaces.js +25 -25
  1527. package/src/shade/renderer/geometry/virtual/build/VGeoLevel.js +33 -33
  1528. package/src/shade/renderer/geometry/virtual/build/VGeoLevelOptions.js +62 -62
  1529. package/src/shade/renderer/geometry/virtual/build/VGeoLevelSummary.js +49 -49
  1530. package/src/shade/renderer/geometry/virtual/build/VGeoSubmesh.js +20 -20
  1531. package/src/shade/renderer/geometry/virtual/build/VirtualGeometryMesh.js +214 -214
  1532. package/src/shade/renderer/geometry/virtual/build/gltf/GltfByteSource.js +122 -122
  1533. package/src/shade/renderer/geometry/virtual/build/gltf/GltfCollectReport.js +29 -29
  1534. package/src/shade/renderer/geometry/virtual/build/gltf/GltfDocument.js +210 -210
  1535. package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometry.js +48 -48
  1536. package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometryInstance.js +25 -25
  1537. package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometrySelection.js +28 -28
  1538. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_collect_geometries.js +296 -296
  1539. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.js +166 -166
  1540. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_read_geometry.js +199 -199
  1541. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.js +173 -173
  1542. package/src/shade/renderer/geometry/virtual/build/vgeo_bounding_sphere.js +28 -28
  1543. package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +200 -200
  1544. package/src/shade/renderer/geometry/virtual/build/vgeo_build_levels.js +1786 -1786
  1545. package/src/shade/renderer/geometry/virtual/build/vgeo_build_page_nodes.js +157 -157
  1546. package/src/shade/renderer/geometry/virtual/build/vgeo_build_root_group.js +26 -26
  1547. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_normals.js +75 -75
  1548. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_tangents.js +143 -143
  1549. package/src/shade/renderer/geometry/virtual/build/vgeo_containing_sphere.js +44 -44
  1550. package/src/shade/renderer/geometry/virtual/build/vgeo_extract_submesh.js +41 -41
  1551. package/src/shade/renderer/geometry/virtual/build/vgeo_gather_faces.js +71 -71
  1552. package/src/shade/renderer/geometry/virtual/build/vgeo_graph_components.js +123 -123
  1553. package/src/shade/renderer/geometry/virtual/build/vgeo_narrow_indices.js +26 -26
  1554. package/src/shade/renderer/geometry/virtual/build/vgeo_normalize_source.js +112 -112
  1555. package/src/shade/renderer/geometry/virtual/build/vgeo_partition_graph.js +56 -56
  1556. package/src/shade/renderer/geometry/virtual/build/vgeo_sphere_inflate_to_contain.js +70 -70
  1557. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set.js +187 -187
  1558. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set_packed.js +207 -207
  1559. package/src/shade/renderer/geometry/virtual/format/VGEO_DEFECT.js +18 -18
  1560. package/src/shade/renderer/geometry/virtual/format/VGeoDefect.js +38 -38
  1561. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +64 -64
  1562. package/src/shade/renderer/geometry/virtual/format/attribute/VGEO_ATTRIBUTE.js +13 -13
  1563. package/src/shade/renderer/geometry/virtual/format/attribute/VGEO_ATTRIBUTES.js +50 -50
  1564. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attribute_allocate.js +28 -28
  1565. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attribute_mask.js +23 -23
  1566. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.js +31 -31
  1567. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_vertex.js +32 -32
  1568. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.js +46 -46
  1569. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_like.js +19 -19
  1570. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.js +27 -27
  1571. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_asset_id_preimage.js +62 -62
  1572. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_asset_id.js +13 -13
  1573. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_directory_block_checksum.js +41 -41
  1574. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_frame_checksum.js +41 -41
  1575. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_header_checksum.js +25 -25
  1576. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.js +20 -20
  1577. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_RECORD_SIZE.js +5 -5
  1578. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CHILD_REF.js +5 -5
  1579. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CLUSTER_TRIANGLE_COUNT.js +5 -5
  1580. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CLUSTER_VERTEX_COUNT.js +5 -5
  1581. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_NO_CHILD.js +5 -5
  1582. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_NO_GROUP.js +5 -5
  1583. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_SAME_PAGE.js +6 -6
  1584. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.js +81 -81
  1585. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryGroup.js +37 -37
  1586. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryPageNode.js +55 -55
  1587. package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_BLOCK_RECORDS.js +5 -5
  1588. package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_FLAG_ROOT_PAGE.js +5 -5
  1589. package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_RECORD_SIZE.js +5 -5
  1590. package/src/shade/renderer/geometry/virtual/format/directory/vgeo_directory_block_count.js +12 -12
  1591. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CHILD_OFFSET.js +11 -11
  1592. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CHILD_TABLE_ENTRY_SIZE.js +5 -5
  1593. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.js +11 -11
  1594. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_RAW.js +5 -5
  1595. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_ALIGNMENT.js +5 -5
  1596. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_HEADER_SIZE.js +5 -5
  1597. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_OFFSET.js +13 -13
  1598. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_PAGE_MAGIC.js +5 -5
  1599. package/src/shade/renderer/geometry/virtual/format/frame/VGeoFrameBlob.js +21 -21
  1600. package/src/shade/renderer/geometry/virtual/format/frame/VGeoFrameExtent.js +59 -59
  1601. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_codec_name.js +27 -27
  1602. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.js +84 -84
  1603. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js +56 -56
  1604. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.js +35 -35
  1605. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_scan_frames.js +74 -74
  1606. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.js +6 -6
  1607. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HASH_ALGO_XXH3.js +5 -5
  1608. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_OFFSET.js +31 -31
  1609. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_SIZE.js +5 -5
  1610. package/src/shade/renderer/geometry/virtual/format/header/VGEO_LEVEL_OFFSET.js +20 -20
  1611. package/src/shade/renderer/geometry/virtual/format/header/VGEO_LEVEL_RECORD_SIZE.js +9 -9
  1612. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAGIC.js +5 -5
  1613. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.js +5 -5
  1614. package/src/shade/renderer/geometry/virtual/format/header/VGEO_PAYLOAD_ENCODING_SHADE_MESHLET_V1.js +5 -5
  1615. package/src/shade/renderer/geometry/virtual/format/header/VGeoLevelRecord.js +41 -41
  1616. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_RECORD_SIZE.js +5 -5
  1617. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_GROUP_CLUSTER_COUNT.js +6 -6
  1618. package/src/shade/renderer/geometry/virtual/format/page/VGEO_METRIC_OFFSET.js +21 -21
  1619. package/src/shade/renderer/geometry/virtual/format/page/VGEO_METRIC_SIZE.js +5 -5
  1620. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_FANOUT.js +9 -9
  1621. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_FLAG_LEAF.js +5 -5
  1622. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_OFFSET.js +10 -10
  1623. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_RECORD_SIZE.js +5 -5
  1624. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_HEADER_SIZE.js +5 -5
  1625. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.js +16 -16
  1626. package/src/shade/renderer/geometry/virtual/format/payload/VGEO_ENCODING_FLAGS_MASK.js +6 -6
  1627. package/src/shade/renderer/geometry/virtual/format/payload/VGeoEncodedPayload.js +18 -18
  1628. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js +200 -200
  1629. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_payload_attribute_offset.js +39 -39
  1630. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_payload_size.js +20 -20
  1631. package/src/shade/renderer/geometry/virtual/format/prototypeVGEOFormatViewer.js +2063 -2063
  1632. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +1622 -1622
  1633. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +485 -485
  1634. package/src/shade/renderer/gpu_primitive/sort/ffx/graph_radix_sort.js +115 -115
  1635. package/src/shade/renderer/gpu_primitive/sort/one_sweep/verify_radix_sort_one_sweep.js +110 -110
  1636. package/src/shade/renderer/light/environment/octahedral_resample_kernel.js +50 -50
  1637. package/src/shade/renderer/light/environment/octahedral_uv_to_direction.js +30 -30
  1638. package/src/shade/renderer/light/environment/resample_cube_to_octahedral.js +157 -157
  1639. package/src/shade/renderer/light/make_sunlight.js +35 -35
  1640. package/src/shade/renderer/light/model/io/light_record_point.js +39 -39
  1641. package/src/shade/renderer/loader/gltf/GLTF_PRIMITIVE_MODE.js +23 -23
  1642. package/src/shade/renderer/loader/gltf/MESHOPT_COMPRESSION_PLAN.md +793 -793
  1643. package/src/shade/renderer/loader/gltf/ext/GltfBufferViewExtension.js +54 -54
  1644. package/src/shade/renderer/loader/gltf/ext/GltfBufferViewExtensionSet.js +109 -109
  1645. package/src/shade/renderer/loader/gltf/ext/gltf_apply_buffer_view_extensions.js +69 -69
  1646. package/src/shade/renderer/loader/gltf/ext/gltf_buffer_view_extensions.js +22 -22
  1647. package/src/shade/renderer/loader/gltf/ext/gltf_geometry_extensions.js +39 -39
  1648. package/src/shade/renderer/loader/gltf/ext/meshopt/MeshoptBufferViewExtension.js +301 -301
  1649. package/src/shade/renderer/loader/gltf/gltf_component_type.js +223 -223
  1650. package/src/shade/renderer/loader/gltf/gltf_node_world_matrices.js +137 -137
  1651. package/src/shade/renderer/loader/gltf/gltf_parse_container.js +241 -241
  1652. package/src/shade/renderer/loader/gltf/gltf_read_accessor.js +294 -294
  1653. package/src/shade/renderer/loader/gltf/gltf_used_image_indices.js +49 -49
  1654. package/src/shade/renderer/loader/gltf/load_gltf.js +1253 -1253
  1655. package/src/shade/renderer/loader/gltf/tiny-gltf.js +646 -646
  1656. package/src/shade/renderer/loader/usd/UsdError.js +33 -33
  1657. package/src/shade/renderer/loader/usd/UsdExtensionRegistry.js +69 -69
  1658. package/src/shade/renderer/loader/usd/load_usd.js +226 -226
  1659. package/src/shade/renderer/loader/usd/parse_usda.js +931 -931
  1660. package/src/shade/renderer/loader/usd/unpack_usdz.js +106 -106
  1661. package/src/shade/renderer/loader/usd/usd_build_scene.js +658 -658
  1662. package/src/shade/renderer/loader/usd/usd_compose_transform.js +258 -258
  1663. package/src/shade/renderer/loader/usd/usd_decode_image.js +384 -384
  1664. package/src/shade/renderer/loader/usd/usd_lz4.js +182 -182
  1665. package/src/shade/renderer/loader/usd/usd_triangulate.js +159 -159
  1666. package/src/shade/renderer/material/ShadeMaterial.d.ts +2 -2
  1667. package/src/shade/renderer/material/ShadeMaterial.js +2 -2
  1668. package/src/shade/renderer/material/standard/fragment_gbuffer.js +334 -334
  1669. package/src/shade/renderer/material/standard/fragment_gbuffer_vt.js +169 -169
  1670. package/src/shade/renderer/material/standard/material_gbuffer_vertex.js +45 -45
  1671. package/src/shade/renderer/material/standard_material_to_struct.js +23 -23
  1672. package/src/shade/renderer/particles/prototypeParticleSystem.js +450 -450
  1673. package/src/shade/renderer/particles/runtime/spawn_request_encoding.js +52 -52
  1674. package/src/shade/renderer/pool/TransientResourcePool.js +392 -392
  1675. package/src/shade/renderer/postprocess/bloom/bloom_chain_resolution.js +29 -29
  1676. package/src/shade/renderer/postprocess/bloom/graph_bloom_downsample.js +109 -109
  1677. package/src/shade/renderer/postprocess/bloom/graph_postprocess_bloom.js +155 -155
  1678. package/src/shade/renderer/postprocess/cas/shader_ffx_rcas.js +114 -114
  1679. package/src/shade/renderer/postprocess/dof/DOF_AUTOFOCUS_PARAMS_STRUCT.js +25 -25
  1680. package/src/shade/renderer/postprocess/dof/DOF_FOCUS_STATE_STRUCT.js +23 -23
  1681. package/src/shade/renderer/postprocess/dof/DOF_PRESETS.js +27 -27
  1682. package/src/shade/renderer/postprocess/dof/chunk_dof_coc_band_diameter.js +41 -41
  1683. package/src/shade/renderer/postprocess/dof/chunk_dof_coc_band_radius.js +32 -32
  1684. package/src/shade/renderer/postprocess/dof/chunk_dof_coc_coefficient.js +26 -26
  1685. package/src/shade/renderer/postprocess/dof/compute_dof_autofocus_params.js +64 -64
  1686. package/src/shade/renderer/postprocess/dof/compute_dof_uniform.js +54 -54
  1687. package/src/shade/renderer/postprocess/dof/gather/DOF2_CONFIG.js +91 -91
  1688. package/src/shade/renderer/postprocess/dof/gather/DOF2_PRESETS.js +9 -9
  1689. package/src/shade/renderer/postprocess/dof/gather/DOF2_SETTINGS_STRUCT.js +66 -66
  1690. package/src/shade/renderer/postprocess/dof/gather/DOF2_TEXTURE_FORMAT.js +22 -22
  1691. package/src/shade/renderer/postprocess/dof/gather/DepthOfFieldU.js +292 -292
  1692. package/src/shade/renderer/postprocess/dof/gather/chunk_dof2_coc_to_near_far.js +25 -25
  1693. package/src/shade/renderer/postprocess/dof/gather/chunk_dof2_gather_weight.js +30 -30
  1694. package/src/shade/renderer/postprocess/dof/gather/compute_dof2_uniform.js +54 -54
  1695. package/src/shade/renderer/postprocess/dof/gather/graph_postprocess_dof2.js +194 -194
  1696. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_fill.js +68 -68
  1697. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_far.js +140 -140
  1698. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_near.js +142 -142
  1699. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_recombine.js +99 -99
  1700. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_setup.js +102 -102
  1701. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_tile_dilate.js +75 -75
  1702. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_tile_max.js +73 -73
  1703. package/src/shade/renderer/postprocess/dof/raymarch/DOF_SETTINGS_STRUCT.js +49 -49
  1704. package/src/shade/renderer/postprocess/dof/raymarch/DepthOfField.js +298 -298
  1705. package/src/shade/renderer/postprocess/dof/raymarch/graph_postprocess_dof.js +244 -244
  1706. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_composite.js +91 -91
  1707. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_firefly_cleanup.js +61 -61
  1708. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_prefilter.js +90 -90
  1709. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch_compute.js +267 -267
  1710. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_slice_compute.js +271 -271
  1711. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_tile_dilate.js +70 -70
  1712. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_tile_max.js +72 -72
  1713. package/src/shade/renderer/postprocess/dof/shader_dof_autofocus.js +99 -99
  1714. package/src/shade/renderer/postprocess/gtao/generate_magic_square_noise.js +51 -51
  1715. package/src/shade/renderer/postprocess/motion_blur/MotionBlur.js +56 -56
  1716. package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +78 -78
  1717. package/src/shade/renderer/postprocess/upscale-dither/UpscaleDither.js +95 -95
  1718. package/src/shade/renderer/postprocess/upscale-dither/fragment_shader_upscale_dither.js +204 -204
  1719. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.js +58 -58
  1720. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +230 -230
  1721. package/src/shade/renderer/rasterize/native/graph_rasterize_meshes_alpha_tested.js +278 -278
  1722. package/src/shade/renderer/rasterize/native/oit/chunk_material_side_should_draw.js +26 -26
  1723. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments_ibl.js +178 -178
  1724. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +235 -235
  1725. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +208 -208
  1726. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +206 -206
  1727. package/src/shade/renderer/scene/DynamicMesh.js +70 -70
  1728. package/src/shade/renderer/scene/DynamicMeshBatch.js +90 -90
  1729. package/src/shade/renderer/scene/GPUSceneContext.js +901 -901
  1730. package/src/shade/renderer/scene/SceneNodeBinding.js +56 -56
  1731. package/src/shade/renderer/scene/SkinnedMesh.js +55 -55
  1732. package/src/shade/renderer/scene/TransformAuthority.js +33 -33
  1733. package/src/shade/renderer/scene/optimization/deduplicate_geometries.js +38 -38
  1734. package/src/shade/renderer/scene/optimization/deduplicate_images.js +93 -93
  1735. package/src/shade/renderer/scene/optimization/geometry/internal/match_refit_validate.js +127 -127
  1736. package/src/shade/renderer/scene/optimization/geometry/internal/match_solve_transform.js +492 -492
  1737. package/src/shade/renderer/scene/optimization/optimize_texture_set.js +81 -81
  1738. package/src/shade/renderer/scene/optimization/source_to_sampler2d.js +21 -21
  1739. package/src/shade/renderer/scene/serialization/STANDARD_MATERIAL_SERIALIZED_BYTE_SIZE.js +12 -12
  1740. package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_transform_point.js +26 -26
  1741. package/src/shade/renderer/shader/chunk/noise/chunk_interleaved_gradient_noise.js +20 -20
  1742. package/src/shade/renderer/shader/chunk/spherical_harmonics/dering/chunk_sh2_dering_optimize_positive.js +61 -61
  1743. package/src/shade/renderer/shader/chunk/texture/chunk_texture_filter_fxaa_lite.js +225 -225
  1744. package/src/shade/renderer/shader/chunk/texture/dither/chunk_dither_color_8bit_gradient_noise.js +25 -25
  1745. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.js +384 -384
  1746. package/src/shade/renderer/shadow/map/PointLightShadowmap.js +105 -105
  1747. package/src/shade/renderer/shadow/map/SpotLightShadowmap.js +63 -63
  1748. package/src/shade/renderer/shadow/map/TetrahedronPointLightShadowmap.js +89 -89
  1749. package/src/shade/renderer/shadow/map/shadowmap_update_score.js +232 -232
  1750. package/src/shade/renderer/texture/GPUTextureAllocator.js +85 -85
  1751. package/src/shade/renderer/texture/texture_pool_ops.js +171 -171
  1752. package/src/shade/renderer/texture/virtual/PageTableQuadAllocator.js +253 -253
  1753. package/src/shade/renderer/texture/virtual/README.md +276 -276
  1754. package/src/shade/renderer/texture/virtual/VTPageTable.js +330 -330
  1755. package/src/shade/renderer/texture/virtual/VTPhysicalCache.js +349 -349
  1756. package/src/shade/renderer/texture/virtual/VT_CONSTANTS.js +154 -154
  1757. package/src/shade/renderer/texture/virtual/VT_STACK_INFO_STRUCT.js +20 -20
  1758. package/src/shade/renderer/texture/virtual/VirtualTextureManager.js +1276 -1276
  1759. package/src/shade/renderer/texture/virtual/VirtualTextureStack.js +106 -106
  1760. package/src/shade/renderer/texture/virtual/build_chunk_vt_lookup.js +150 -150
  1761. package/src/shade/renderer/texture/virtual/build_shader_vt_feedback.js +232 -232
  1762. package/src/shade/renderer/texture/virtual/chunk_vt_request_key.js +28 -28
  1763. package/src/shade/renderer/texture/virtual/source/VTSourceImage.js +207 -207
  1764. package/src/shade/renderer/texture/virtual/source/VTSourceProcedural.js +135 -135
  1765. package/src/shade/renderer/texture/virtual/source/VTSourceTiled.js +132 -132
  1766. package/src/shade/renderer/water/GPUWaterRenderer.js +167 -167
  1767. package/src/shade/renderer/water/shader_water.js +246 -246
  1768. package/src/shade/renderer/water/water_height_texture_from_sampler.js +56 -56
  1769. package/src/shade/vitest.setup.mjs +89 -89
  1770. package/src/shade/wgsl/validate_wgsl_source.js +316 -316
  1771. package/src/view/controller/controls/NumericIntervalControl.js +90 -90
  1772. package/src/view/controller/controls/Vector2Control.js +83 -83
  1773. package/src/view/controller/controls/Vector3Control.js +90 -90
  1774. package/src/view/elements/Group.js +56 -56
  1775. package/src/view/elements/drawCheckers.js +33 -33
  1776. package/src/view/elements/windrose/WindRoseDiagram.js +369 -369
  1777. package/src/view/graphics/WebGLContextFailureView.js +76 -76
  1778. package/src/view/layout/DockPanel.js +51 -51
  1779. package/src/view/layout/SplitView.js +365 -365
  1780. package/src/view/layout/show_popup_menu.js +81 -81
  1781. package/src/view/minimap/MinimapCanvasView.js +411 -411
  1782. package/src/view/minimap/MinimapImageCache.js +88 -88
  1783. package/src/view/minimap/MinimapMarkerCollection.d.ts +0 -1
  1784. package/src/view/minimap/MinimapMarkerCollection.d.ts.map +1 -1
  1785. package/src/view/minimap/MinimapMarkerCollection.js +180 -180
  1786. package/src/view/minimap/dom/MinimapCameraView.js +206 -206
  1787. package/src/view/minimap/minimap_focus_area.js +97 -97
  1788. package/editor/process/symbolic/SocketsSymbolicDisplay.js +0 -192
  1789. package/src/core/geom/3d/topology/struct/binary/io/moved_tests.txt +0 -37
  1790. package/src/engine/ecs/attachment/Attachment.d.ts +0 -40
  1791. package/src/engine/ecs/attachment/Attachment.d.ts.map +0 -1
  1792. package/src/engine/ecs/attachment/Attachment.js +0 -57
  1793. package/src/engine/ecs/attachment/AttachmentBinding.d.ts +0 -28
  1794. package/src/engine/ecs/attachment/AttachmentBinding.d.ts.map +0 -1
  1795. package/src/engine/ecs/attachment/AttachmentBinding.js +0 -41
  1796. package/src/engine/ecs/attachment/AttachmentSystem.d.ts +0 -61
  1797. package/src/engine/ecs/attachment/AttachmentSystem.d.ts.map +0 -1
  1798. package/src/engine/ecs/attachment/AttachmentSystem.js +0 -307
  1799. package/src/engine/ecs/attachment/BoneAttachmentBinding.d.ts +0 -27
  1800. package/src/engine/ecs/attachment/BoneAttachmentBinding.d.ts.map +0 -1
  1801. package/src/engine/ecs/attachment/BoneAttachmentBinding.js +0 -69
  1802. package/src/engine/ecs/attachment/TransformAttachmentBinding.d.ts +0 -4
  1803. package/src/engine/ecs/attachment/TransformAttachmentBinding.d.ts.map +0 -1
  1804. package/src/engine/ecs/attachment/TransformAttachmentBinding.js +0 -51
  1805. package/src/engine/ecs/sockets/AttachmentSocket.d.ts +0 -32
  1806. package/src/engine/ecs/sockets/AttachmentSocket.d.ts.map +0 -1
  1807. package/src/engine/ecs/sockets/AttachmentSocket.js +0 -60
  1808. package/src/engine/ecs/sockets/AttachmentSocketType.d.ts +0 -6
  1809. package/src/engine/ecs/sockets/AttachmentSocketType.d.ts.map +0 -1
  1810. package/src/engine/ecs/sockets/AttachmentSocketType.js +0 -8
  1811. package/src/engine/ecs/sockets/AttachmentSockets.d.ts +0 -57
  1812. package/src/engine/ecs/sockets/AttachmentSockets.d.ts.map +0 -1
  1813. package/src/engine/ecs/sockets/AttachmentSockets.js +0 -130
  1814. package/src/engine/ecs/sockets/BoneAttachmentSocket.d.ts +0 -29
  1815. package/src/engine/ecs/sockets/BoneAttachmentSocket.d.ts.map +0 -1
  1816. package/src/engine/ecs/sockets/BoneAttachmentSocket.js +0 -56
  1817. package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.d.ts +0 -6
  1818. package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.d.ts.map +0 -1
  1819. package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.js +0 -19
  1820. package/src/shade/renderer/animation/_dump_resolve.d.mts +0 -2
  1821. package/src/shade/renderer/animation/_dump_resolve.d.mts.map +0 -1
  1822. 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)