@woosh/meep-engine 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1869) hide show
  1. package/LICENSE +188 -188
  2. package/README.md +152 -152
  3. package/editor/Editor.js +598 -598
  4. package/editor/EditorCameraNavigation.js +205 -205
  5. package/editor/EditorKeyMap.js +182 -182
  6. package/editor/SelectionVisualizer.js +127 -127
  7. package/editor/actions/concrete/ActionInvalidateSampler.js +27 -27
  8. package/editor/actions/concrete/PatchTerrainHeightAction.js +85 -85
  9. package/editor/camera/camera_framing.js +104 -104
  10. package/editor/clipboard.js +184 -184
  11. package/editor/ecs/component/editors/ObservedIntegerEditor.js +22 -22
  12. package/editor/ecs/component/editors/ObservedStringEditor.js +34 -34
  13. package/editor/ecs/component/editors/common/two_way_sync.js +50 -50
  14. package/editor/ecs/component/editors/ecs/GridObstacleEditor.js +19 -19
  15. package/editor/ecs/component/editors/ecs/ParameterLookupTableEditor.js +35 -35
  16. package/editor/ecs/component/editors/ecs/ParameterTrackEditor.js +17 -17
  17. package/editor/ecs/component/editors/ecs/SGMeshEditor.js +24 -24
  18. package/editor/ecs/component/editors/ecs/ShadedGeometryEditor.js +33 -33
  19. package/editor/ecs/component/editors/ecs/Trail3DEditor.js +45 -45
  20. package/editor/ecs/component/editors/ecs/buildGridObstaclePreview.js +19 -19
  21. package/editor/ecs/component/editors/ecs/terrain/TerrainEditor.js +72 -72
  22. package/editor/ecs/component/editors/shade/MeshletGeometryEditor.js +30 -30
  23. package/editor/ecs/component/editors/shade/ShadeImageEditor.js +70 -70
  24. package/editor/ecs/component/editors/shade/ShadeMaterialEditor.js +34 -34
  25. package/editor/ecs/component/editors/shade/ShadeTextureEditor.js +46 -46
  26. package/editor/ecs/component/editors/shade/StandardShadeMaterialEditor.js +24 -24
  27. package/editor/ecs/component/prototypeObjectEditor.js +265 -265
  28. package/editor/ecs/component/registerBasicTypeEditors.js +100 -100
  29. package/editor/ecs/component/registerEngineComponentEditors.js +95 -95
  30. package/editor/entity_world_bounds.js +27 -27
  31. package/editor/particles/lut_ops.js +148 -148
  32. package/editor/particles/particleEditEvents.js +12 -12
  33. package/editor/particles/rebuildParticleEmitter.js +31 -31
  34. package/editor/persistence/EditorLayoutStore.js +71 -71
  35. package/editor/persistence/SceneMetadata.js +52 -52
  36. package/editor/persistence/SceneSlotManager.js +144 -144
  37. package/editor/persistence/buildNewSceneDataset.js +67 -67
  38. package/editor/persistence/files.js +68 -68
  39. package/editor/persistence/scene_import.js +143 -143
  40. package/editor/persistence/scene_serialization.js +79 -79
  41. package/editor/persistence/string_buffer_codec.js +41 -41
  42. package/editor/process/GridDisplayProcess.js +125 -125
  43. package/editor/process/SymbolicDisplayProcess.js +106 -108
  44. package/editor/process/symbolic/CameraSymbolicDisplay.js +146 -146
  45. package/editor/process/symbolic/LightSymbolicDisplay.js +172 -172
  46. package/editor/process/symbolic/PathSymbolicDisplay.js +118 -118
  47. package/editor/process/symbolic/PositionedMarkerSymbolicDisplay.js +79 -79
  48. package/editor/process/symbolic/SoundEmitterSymbolicDisplay.js +57 -57
  49. package/editor/process/symbolic/SymbolicDisplay.js +62 -62
  50. package/editor/process/symbolic/gizmo_draw_wire_box_oriented.js +85 -85
  51. package/editor/process/symbolic/gizmo_draw_wire_circle.js +53 -53
  52. package/editor/prototypeEditorShell.js +156 -156
  53. package/editor/selection/editor_pick.js +130 -130
  54. package/editor/selection/screen_rect_frustum.js +119 -119
  55. package/editor/style/controls.scss +168 -168
  56. package/editor/style/inspector.scss +223 -223
  57. package/editor/templates/entity_templates.js +167 -167
  58. package/editor/tools/GridPaintTool.js +273 -273
  59. package/editor/tools/SelectionTool.js +307 -307
  60. package/editor/tools/TransformToolV2.js +378 -378
  61. package/editor/tools/engine/ToolEngine.js +188 -188
  62. package/editor/tools/paint/TerrainTexturePaintTool.js +250 -250
  63. package/editor/tools/v2/BlenderCameraOrientationGizmo.js +501 -501
  64. package/editor/tools/v2/TransformControls.js +971 -971
  65. package/editor/tools/v2/handle/gizmo_drag_plane.js +141 -141
  66. package/editor/tools/v2/handle/gizmo_draw.js +399 -399
  67. package/editor/tools/v2/handle/gizmo_handles.js +364 -364
  68. package/editor/tools/v2/handle/gizmo_ray_shapes.js +239 -239
  69. package/editor/view/ecs/HierarchicalEntityListView.js +339 -339
  70. package/editor/view/ecs/components/DatGuiController.js +2 -2
  71. package/editor/view/ecs/components/common/AutoCanvasView.js +100 -100
  72. package/editor/view/ecs/components/common/TextController.js +59 -59
  73. package/editor/view/library/MeshLibraryView.js +175 -175
  74. package/editor/view/library/mesh_library_drag.js +63 -63
  75. package/editor/view/library/model_thumbnail_source.js +86 -86
  76. package/editor/view/makeEntityDecorators.js +125 -125
  77. package/editor/view/node-graph/NodeGraphSelection.js +89 -89
  78. package/editor/view/node-graph/NodeView.js +211 -211
  79. package/editor/view/node-graph/actions/ConnectionDeleteAction.js +36 -36
  80. package/editor/view/node-graph/actions/NodesMoveAction.js +41 -41
  81. package/editor/view/node-graph/connection_wire_geometry.js +107 -107
  82. package/editor/view/particles/ColorLutGradientView.js +291 -291
  83. package/editor/view/particles/ScalarLutCurveView.js +279 -279
  84. package/editor/view/prepareMeshLibrary.js +188 -188
  85. package/editor/view/shell/SceneTreePanelView.js +119 -119
  86. package/editor/view/v2/SplitView.js +2 -2
  87. package/package.json +94 -94
  88. package/samples/generation/generators/interactive/mir_generator_place_buff_objects.js +362 -362
  89. package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +738 -738
  90. package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +79 -79
  91. package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +93 -93
  92. package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +80 -80
  93. package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +164 -164
  94. package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +69 -69
  95. package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +102 -102
  96. package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +129 -129
  97. package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +72 -72
  98. package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +74 -74
  99. package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +169 -169
  100. package/src/DUPLICATION_AUDIT_2026_08_07.md +254 -254
  101. package/src/DUPLICATION_AUDIT_REMEDIATION.md +250 -250
  102. package/src/REVIEW_2026_08_06/adv-core.md +511 -511
  103. package/src/REVIEW_2026_08_06/adv-gfx.md +480 -480
  104. package/src/REVIEW_2026_08_06/adv-view.md +446 -446
  105. package/src/REVIEW_2026_08_06/api-core.md +296 -296
  106. package/src/REVIEW_2026_08_06/api-engine.md +195 -195
  107. package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +507 -507
  108. package/src/REVIEW_2026_08_06/core-binary.md +348 -348
  109. package/src/REVIEW_2026_08_06/core-collection.md +376 -376
  110. package/src/REVIEW_2026_08_06/core-math.md +221 -221
  111. package/src/REVIEW_2026_08_06/core-process.md +266 -266
  112. package/src/REVIEW_2026_08_06/core-science.md +223 -223
  113. package/src/REVIEW_2026_08_06/engine-ai-nav.md +279 -279
  114. package/src/REVIEW_2026_08_06/engine-animation.md +167 -167
  115. package/src/REVIEW_2026_08_06/engine-asset.md +166 -166
  116. package/src/REVIEW_2026_08_06/engine-ecs.md +920 -920
  117. package/src/REVIEW_2026_08_06/engine-input.md +197 -197
  118. package/src/REVIEW_2026_08_06/engine-misc.md +248 -248
  119. package/src/REVIEW_2026_08_06/engine-terrain.md +52 -52
  120. package/src/REVIEW_2026_08_06/generation.md +176 -176
  121. package/src/REVIEW_2026_08_06/geom-2d.md +434 -434
  122. package/src/REVIEW_2026_08_06/geom-3d.md +117 -117
  123. package/src/REVIEW_2026_08_06/gfx-gi.md +143 -143
  124. package/src/REVIEW_2026_08_06/gfx-render.md +740 -740
  125. package/src/REVIEW_2026_08_06/gfx-texture.md +690 -690
  126. package/src/REVIEW_2026_08_06/prior-criticals.md +49 -49
  127. package/src/REVIEW_2026_08_06/prior-highs.md +138 -138
  128. package/src/REVIEW_2026_08_06/test-gaps.md +364 -364
  129. package/src/REVIEW_2026_08_06/view.md +667 -667
  130. package/src/REVIEW_2026_08_06.md +610 -610
  131. package/src/avif/codec/dec/avif_dec.cpp +233 -233
  132. package/src/avif/codec/dec/avif_dec.js +16 -16
  133. package/src/avif/encoder_worker.js +5 -18
  134. package/src/core/binary/BitImage2.js +246 -246
  135. package/src/core/binary/align_32.js +21 -21
  136. package/src/core/binary/data_view/DataType2DataViewReaders.js +23 -23
  137. package/src/core/binary/data_view/DataType2DataViewWriters.js +25 -25
  138. package/src/core/binary/hash/XXH128Hash.js +55 -55
  139. package/src/core/binary/hash/XXH3_SECRET.js +25 -25
  140. package/src/core/binary/hash/xxh3_128.js +341 -341
  141. package/src/core/binary/hash/xxh3_64.js +241 -241
  142. package/src/core/binary/hash/xxh3_common.js +476 -476
  143. package/src/core/binary/lz4/LZ4_MAX_INPUT_SIZE.js +9 -9
  144. package/src/core/binary/lz4/lz4_compress_block.js +385 -385
  145. package/src/core/binary/lz4/lz4_compress_bound.js +25 -25
  146. package/src/core/binary/lz4/lz4_decompress_block.js +139 -139
  147. package/src/core/binary/lz4/lz4_decompress_bound.js +30 -30
  148. package/src/core/binary/meshopt/MESHOPT_GROUP_SIZE.js +18 -18
  149. package/src/core/binary/meshopt/MeshoptCompressionFilter.js +30 -30
  150. package/src/core/binary/meshopt/MeshoptCompressionMode.js +21 -21
  151. package/src/core/binary/meshopt/__meshopt_test_streams.js +237 -237
  152. package/src/core/binary/meshopt/meshopt_decode.js +73 -73
  153. package/src/core/binary/meshopt/meshopt_decode_byte_group.js +67 -67
  154. package/src/core/binary/meshopt/meshopt_decode_data_block.js +59 -59
  155. package/src/core/binary/meshopt/meshopt_decode_filter_color.js +69 -69
  156. package/src/core/binary/meshopt/meshopt_decode_filter_exponential.js +43 -43
  157. package/src/core/binary/meshopt/meshopt_decode_filter_octahedral.js +60 -60
  158. package/src/core/binary/meshopt/meshopt_decode_filter_quaternion.js +71 -71
  159. package/src/core/binary/meshopt/meshopt_decode_index_buffer.js +294 -294
  160. package/src/core/binary/meshopt/meshopt_decode_index_sequence.js +115 -115
  161. package/src/core/binary/meshopt/meshopt_decode_vertex_buffer.js +381 -381
  162. package/src/core/binary/meshopt/meshopt_read_uint_var.js +59 -59
  163. package/src/core/binary/reinterpret_float32_as_uint32.js +13 -13
  164. package/src/core/binary/reinterpret_uint32_as_float32.js +14 -14
  165. package/src/core/binary/type/DataType2TypedArrayConstructorMapping.js +35 -35
  166. package/src/core/bvh2/bvh3/ebvh_geometry_query_any_triangle_ray.js +149 -149
  167. package/src/core/bvh2/bvh3/ebvh_sort_for_traversal_depth_first.js +96 -96
  168. package/src/core/bvh2/bvh3/query/bvh_query_depth_range_in_frustum.js +101 -101
  169. package/src/core/bvh2/bvh3/query/bvh_query_leaves_generic.js +77 -77
  170. package/src/core/bvh2/bvh3/query/bvh_query_leaves_ray.js +90 -90
  171. package/src/core/bvh2/bvh3/query/bvh_query_leaves_ray_segment.js +94 -94
  172. package/src/core/bvh2/bvh3/query/bvh_query_user_data_generic.js +86 -86
  173. package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_capsule.js +101 -101
  174. package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_sphere.js +92 -92
  175. package/src/core/bvh2/bvh3/query/bvh_query_user_data_ray.js +96 -96
  176. package/src/core/bvh2/bvh3/query/bvh_query_user_data_ray_segment.js +100 -100
  177. package/src/core/bvh2/visual/BVHGeometry.js +131 -131
  178. package/src/core/bvh8/BVH8.js +436 -436
  179. package/src/core/bvh8/aabb3_round_to_float32.js +22 -22
  180. package/src/core/bvh8/build/BVH8Converter.js +588 -588
  181. package/src/core/bvh8/build/TriangleCluster.js +122 -122
  182. package/src/core/bvh8/build/aabb3_compute_merge_cost.js +28 -28
  183. package/src/core/bvh8/build/aabb3_from_triangle_by_index.js +17 -17
  184. package/src/core/bvh8/build/bvh8_build_for_geometry.js +303 -303
  185. package/src/core/bvh8/build/bvh8_from_proxy.js +256 -256
  186. package/src/core/bvh8/build/byte.js +9 -9
  187. package/src/core/bvh8/build/encode_bounds_e.js +11 -11
  188. package/src/core/bvh8/bvh8_convert_to_dot.js +132 -132
  189. package/src/core/bvh8/bvh8_count_primitives.js +98 -98
  190. package/src/core/bvh8/bvh8_geometry_validate.js +138 -138
  191. package/src/core/bvh8/bvh8_geometry_validate_indirect.js +166 -166
  192. package/src/core/bvh8/bvh8_get_node_bounds.js +34 -34
  193. package/src/core/bvh8/bvh8_get_node_child_bounds.js +52 -52
  194. package/src/core/bvh8/bvh8_node_child_surface_area.js +18 -18
  195. package/src/core/bvh8/bvh8_node_count_triangles.js +27 -27
  196. package/src/core/bvh8/bvh8_quality.js +72 -72
  197. package/src/core/bvh8/bvh8_validate_structure.js +87 -87
  198. package/src/core/clipboard/obtainClipBoard.js +29 -29
  199. package/src/core/clipboard/safeClipboardReadText.js +55 -55
  200. package/src/core/clipboard/safeClipboardWriteText.js +23 -23
  201. package/src/core/collection/array/typed/typed_array_value_denormalize.js +52 -52
  202. package/src/core/collection/array/typed/typed_array_value_normalize.js +51 -51
  203. package/src/core/collection/union-find/union_find.js +79 -79
  204. package/src/core/color/PQ/PQ_constants.d.ts +43 -0
  205. package/src/core/color/PQ/PQ_constants.d.ts.map +1 -0
  206. package/src/core/color/PQ/PQ_constants.js +50 -0
  207. package/src/core/color/PQ/PQ_to_linear.d.ts +24 -0
  208. package/src/core/color/PQ/PQ_to_linear.d.ts.map +1 -0
  209. package/src/core/color/PQ/PQ_to_linear.js +35 -0
  210. package/src/core/color/PQ/linear_to_PQ.d.ts +27 -0
  211. package/src/core/color/PQ/linear_to_PQ.d.ts.map +1 -0
  212. package/src/core/color/PQ/linear_to_PQ.js +32 -0
  213. package/src/core/color/YCoCg/rgb_to_ycocg.js +20 -20
  214. package/src/core/color/YCoCg/ycocg_to_rgb.js +17 -17
  215. package/src/core/color/colormap/MAGMA_LUT.js +26 -26
  216. package/src/core/color/illuminant/D65_TRISTIMULUS_XYZ.js +19 -19
  217. package/src/core/color/lab/lab_apply_hunt_adjustment.js +27 -27
  218. package/src/core/color/lab/lab_distance_hyab.js +23 -23
  219. package/src/core/color/lab/xyz_to_lab.js +62 -62
  220. package/src/core/color/operations/color_darken.js +24 -24
  221. package/src/core/color/operations/color_desaturate.js +24 -24
  222. package/src/core/color/operations/color_lighten.js +24 -24
  223. package/src/core/color/operations/color_saturate.js +24 -24
  224. package/src/core/color/operations/color_scale_okhsv_channel.js +45 -45
  225. package/src/core/color/tonemap/tonemap_aces.js +41 -41
  226. package/src/core/color/ycxcz/xyz_to_ycxcz.js +28 -28
  227. package/src/core/color/ycxcz/ycxcz_to_xyz.js +19 -19
  228. package/src/core/dom/isImageBitmap.js +12 -12
  229. package/src/core/function/frameThrottle.js +23 -23
  230. package/src/core/geom/2d/aabb/aabb2_array_combine.js +7 -7
  231. package/src/core/geom/2d/aabb/aabb2_compute_area.js +7 -7
  232. package/src/core/geom/2d/aabb/aabb2_compute_center_from_multiple.js +7 -7
  233. package/src/core/geom/2d/aabb/aabb2_compute_overlap.js +7 -7
  234. package/src/core/geom/2d/aabb/aabb2_from_v2_array.js +7 -7
  235. package/src/core/geom/2d/aabb/aabb2_intersects_point.js +7 -7
  236. package/src/core/geom/2d/aabb/aabb2_overlap_exists.js +7 -7
  237. package/src/core/geom/2d/circle/circle_compute_circle_intersection.js +7 -7
  238. package/src/core/geom/2d/circle/circle_compute_circle_penetration.js +7 -7
  239. package/src/core/geom/2d/circle/circle_intersects_point.js +7 -7
  240. package/src/core/geom/2d/compute_polygon_area_2d.js +7 -7
  241. package/src/core/geom/2d/convex-hull/fixed_convex_hull_humus.js +7 -7
  242. package/src/core/geom/2d/convex-hull/fixed_convex_hull_relaxation.js +7 -7
  243. package/src/core/geom/2d/convex-hull/orientation3_array.js +7 -7
  244. package/src/core/geom/2d/intersect_ray_2d.js +7 -7
  245. package/src/core/geom/2d/line/line2_distance_to_point_sqr.js +7 -7
  246. package/src/core/geom/2d/line/line2_segment_intersection_fraction_2d.js +44 -44
  247. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_2d.js +7 -7
  248. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_array_2d.js +7 -7
  249. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_vectors_2d.js +7 -7
  250. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_vectors_array_2d.js +7 -7
  251. package/src/core/geom/2d/line/line_segment_intersection_fraction_2d.js +7 -7
  252. package/src/core/geom/2d/line/line_segment_line_segment_intersection_exists_2d.js +7 -7
  253. package/src/core/geom/2d/line/segment2_intersects_segment2_2d.js +25 -25
  254. package/src/core/geom/2d/line/segment2_segment_compute_intersection_array_2d.js +46 -46
  255. package/src/core/geom/2d/line/segment2_segment_intersection_fractions.js +52 -52
  256. package/src/core/geom/2d/oriented-box/min_area_rect_2d.js +69 -69
  257. package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +433 -433
  258. package/src/core/geom/2d/polygon/polygon2_clip_axis_halfplane.js +51 -51
  259. package/src/core/geom/2d/polygon/polygon2_clip_halfplane.js +50 -50
  260. package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.js +84 -84
  261. package/src/core/geom/2d/polygon/polygon2_signed_area.js +52 -52
  262. package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.js +86 -86
  263. package/src/core/geom/2d/polygon/polygon2_triangulate.js +1280 -1280
  264. package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.js +77 -77
  265. package/src/core/geom/2d/quad-tree/qt_collect_by_circle.js +39 -39
  266. package/src/core/geom/2d/quad-tree/qt_match_data_by_circle.js +101 -101
  267. package/src/core/geom/2d/quad-tree/qt_query_data_nearest_to_point.js +7 -7
  268. package/src/core/geom/2d/rectangle_to_aabb.js +7 -7
  269. package/src/core/geom/2d/spline_bezier2_2d.js +7 -7
  270. package/src/core/geom/2d/triangle/tri2_rasterize_conservative.js +99 -99
  271. package/src/core/geom/2d/triangle/tri2_signed_area.js +35 -35
  272. package/src/core/geom/2d/triangle2d_compute_area.js +7 -7
  273. package/src/core/geom/2d/triangle2d_contains_point.js +7 -7
  274. package/src/core/geom/2d/triangle2d_get_barycentric.js +7 -7
  275. package/src/core/geom/2d/v2_morton_encode.js +23 -23
  276. package/src/core/geom/3d/aabb/aabb3_array_intersects_frustum_degree.js +7 -7
  277. package/src/core/geom/3d/aabb/aabb3_box_surface_area_2.js +7 -7
  278. package/src/core/geom/3d/aabb/aabb3_build_frustum.js +5 -5
  279. package/src/core/geom/3d/aabb/aabb3_closest_point_on_surface.js +57 -57
  280. package/src/core/geom/3d/aabb/aabb3_combined_surface_area.js +7 -7
  281. package/src/core/geom/3d/aabb/aabb3_compute_half_surface_area.js +7 -7
  282. package/src/core/geom/3d/aabb/aabb3_compute_projected_area.js +7 -7
  283. package/src/core/geom/3d/aabb/aabb3_compute_surface_area.js +7 -7
  284. package/src/core/geom/3d/aabb/aabb3_detailed_volume_intersection.js +7 -7
  285. package/src/core/geom/3d/aabb/aabb3_estimate_projected_area2.js +7 -7
  286. package/src/core/geom/3d/aabb/aabb3_from_threejs_geometry.js +7 -7
  287. package/src/core/geom/3d/aabb/aabb3_from_triangle_group.js +46 -46
  288. package/src/core/geom/3d/aabb/aabb3_intersects_frustum_degree.js +7 -7
  289. package/src/core/geom/3d/aabb/aabb3_intersects_line_segment.js +7 -7
  290. package/src/core/geom/3d/aabb/aabb3_near_distance_to_intersection_ray_segment.js +7 -7
  291. package/src/core/geom/3d/aabb/aabb3_nearest_point_on_surface.js +7 -7
  292. package/src/core/geom/3d/aabb/aabb3_ray_segment_entry_distance.js +95 -95
  293. package/src/core/geom/3d/aabb/aabb3_raycast.js +7 -7
  294. package/src/core/geom/3d/aabb/aabb3_transform_oriented.js +93 -93
  295. package/src/core/geom/3d/aabb/aabb3_transform_oriented_inverse.js +82 -82
  296. package/src/core/geom/3d/aabb/compute_aabb_from_points.js +7 -7
  297. package/src/core/geom/3d/aabb/compute_triangle_group_aabb3.js +7 -7
  298. package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +486 -486
  299. package/src/core/geom/3d/atlas/BFF_PLAN.md +174 -174
  300. package/src/core/geom/3d/atlas/Chart.js +77 -77
  301. package/src/core/geom/3d/atlas/REVIEW_02.md +392 -392
  302. package/src/core/geom/3d/atlas/atlas_bench_lib.js +315 -315
  303. package/src/core/geom/3d/atlas/atlas_chart_type.js +16 -16
  304. package/src/core/geom/3d/atlas/atlas_compute_charts.js +188 -188
  305. package/src/core/geom/3d/atlas/atlas_extract_chart.js +107 -107
  306. package/src/core/geom/3d/atlas/atlas_generate.js +50 -50
  307. package/src/core/geom/3d/atlas/atlas_test_fixtures.js +266 -266
  308. package/src/core/geom/3d/atlas/atlas_write_chart_uvs.js +36 -36
  309. package/src/core/geom/3d/atlas/data/atlas_build_edge_dihedrals.js +62 -62
  310. package/src/core/geom/3d/atlas/data/atlas_build_edge_lengths.js +33 -33
  311. package/src/core/geom/3d/atlas/data/atlas_build_face_areas.js +50 -50
  312. package/src/core/geom/3d/atlas/io/atlas_mesh_weld_colocals.js +39 -39
  313. package/src/core/geom/3d/atlas/io/bt_loop_uv.js +35 -35
  314. package/src/core/geom/3d/atlas/pack/ChartPlacement.js +46 -46
  315. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.js +269 -269
  316. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.js +209 -209
  317. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_ortho.js +38 -38
  318. package/src/core/geom/3d/atlas/param/atlas_chart_stretch_metric.js +86 -86
  319. package/src/core/geom/3d/atlas/param/atlas_piecewise_resegment.js +183 -183
  320. package/src/core/geom/3d/atlas/param/fit_plane_basis.js +66 -66
  321. package/src/core/geom/3d/atlas/segment/RegionGraph.js +40 -40
  322. package/src/core/geom/3d/atlas/segment/atlas_build_region_graph.js +136 -136
  323. package/src/core/geom/3d/atlas/segment/atlas_chart_roundness_delta.js +30 -30
  324. package/src/core/geom/3d/atlas/segment/atlas_compute_planar_regions.js +77 -77
  325. package/src/core/geom/3d/atlas/segment/atlas_merge_charts.js +116 -116
  326. package/src/core/geom/3d/atlas/segment/atlas_segment_charts.js +271 -271
  327. package/src/core/geom/3d/atlas/segment/atlas_segment_charts_atomic.js +191 -191
  328. package/src/core/geom/3d/box/box3_projected_half_extent.js +35 -35
  329. package/src/core/geom/3d/capsule/capsule_intersects_aabb3_closed.js +67 -67
  330. package/src/core/geom/3d/capsule/capsule_intersects_aabb3_iterative.js +137 -137
  331. package/src/core/geom/3d/compute_circle_bounding_box.js +7 -7
  332. package/src/core/geom/3d/cone/computeConeBoundingBox.js +7 -7
  333. package/src/core/geom/3d/cone/computeConePlaneSide.js +7 -7
  334. package/src/core/geom/3d/cone/compute_bounding_cone_of_2_cones.js +7 -7
  335. package/src/core/geom/3d/cylinder/cylinder3_raycast.d.ts +38 -0
  336. package/src/core/geom/3d/cylinder/cylinder3_raycast.d.ts.map +1 -0
  337. package/src/core/geom/3d/cylinder/cylinder3_raycast.js +105 -0
  338. package/src/core/geom/3d/equirectangular/equirectangular_direction_to_uv.js +18 -18
  339. package/src/core/geom/3d/equirectangular/equirectangular_uv_to_direction.js +24 -24
  340. package/src/core/geom/3d/frustum/clipping_volume_matrix4_project.js +7 -7
  341. package/src/core/geom/3d/frustum/frustum3_computeNearestPointToPoint.js +7 -7
  342. package/src/core/geom/3d/frustum/frustum3_compute_closest_point.js +117 -117
  343. package/src/core/geom/3d/frustum/frustum_matrix4_project.js +7 -7
  344. package/src/core/geom/3d/frustum/hexahedron_from_aabb.js +7 -7
  345. package/src/core/geom/3d/frustum/read_frustum_corner.js +17 -17
  346. package/src/core/geom/3d/frustum/read_three_planes_to_array.js +7 -7
  347. package/src/core/geom/3d/frustum/slice_frustum_linear_to_points.js +7 -7
  348. package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +146 -146
  349. package/src/core/geom/3d/gjk/gjk.js +430 -430
  350. package/src/core/geom/3d/gjk/gjk_epa_penetration.js +520 -520
  351. package/src/core/geom/3d/gjk/minkowski_support.js +71 -71
  352. package/src/core/geom/3d/gjk/mpr.js +368 -368
  353. package/src/core/geom/3d/line/line3_closest_points_segment_segment.js +7 -7
  354. package/src/core/geom/3d/line/line3_compute_nearest_point_to_point.js +7 -7
  355. package/src/core/geom/3d/line/line3_compute_point_segment_point_distance_unfolded.js +64 -64
  356. package/src/core/geom/3d/line/line3_compute_segment_closest_point_to_aabb3_t.js +153 -153
  357. package/src/core/geom/3d/line/line3_compute_segment_nearest_point_to_aabb3_t.js +7 -7
  358. package/src/core/geom/3d/line/line3_compute_segment_nearest_point_to_point_t.js +7 -7
  359. package/src/core/geom/3d/line/line3_compute_segment_segment_closest_points.js +88 -88
  360. package/src/core/geom/3d/line/line3_segment_segment_closest_point_pair.js +71 -71
  361. package/src/core/geom/3d/line/segment3_point_distance_to_segments.js +25 -25
  362. package/src/core/geom/3d/mat4/allocate_m4.js +7 -7
  363. package/src/core/geom/3d/mat4/apply_mat4_transform_to_direction_v3_array.js +7 -7
  364. package/src/core/geom/3d/mat4/apply_mat4_transform_to_v3_array.js +7 -7
  365. package/src/core/geom/3d/mat4/compose_matrix4_array.js +7 -7
  366. package/src/core/geom/3d/mat4/decompose_matrix_4_array.js +7 -7
  367. package/src/core/geom/3d/mat4/eulerAnglesFromMatrix.js +7 -7
  368. package/src/core/geom/3d/mat4/m4_compose.js +22 -22
  369. package/src/core/geom/3d/mat4/m4_compute_normal_matrix3.js +7 -7
  370. package/src/core/geom/3d/mat4/m4_decompose.js +64 -64
  371. package/src/core/geom/3d/mat4/m4_decompose_array.js +157 -157
  372. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.js +32 -32
  373. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_scalar.js +67 -67
  374. package/src/core/geom/3d/mat4/m4_linear_matrix3.js +29 -29
  375. package/src/core/geom/3d/mat4/m4_look_at.js +96 -96
  376. package/src/core/geom/3d/mat4/m4_make_rotation_x.js +47 -47
  377. package/src/core/geom/3d/mat4/m4_make_rotation_y.js +47 -47
  378. package/src/core/geom/3d/mat4/m4_make_rotation_z.js +47 -47
  379. package/src/core/geom/3d/mat4/m4_normal_matrix3.js +39 -39
  380. package/src/core/geom/3d/mat4/m4_perspective.js +66 -66
  381. package/src/core/geom/3d/mat4/m4_transpose.js +48 -48
  382. package/src/core/geom/3d/morton/mortonEncode_LUT.js +7 -7
  383. package/src/core/geom/3d/morton/mortonEncode_magicbits.js +7 -7
  384. package/src/core/geom/3d/normal/hemioct/encode_unit3_hemioct.js +5 -5
  385. package/src/core/geom/3d/normal/hemioct/encode_unit_to_hemioct.js +28 -28
  386. package/src/core/geom/3d/normal/lambert_azimuth/decode_azimuth_to_unit.js +30 -30
  387. package/src/core/geom/3d/normal/lambert_azimuth/encode_unit_to_azimuth.js +19 -19
  388. package/src/core/geom/3d/normal/spherical/decode_spheremap_to_unit.js +38 -38
  389. package/src/core/geom/3d/normal/spherical/encode_unit_to_spheremap.js +30 -30
  390. package/src/core/geom/3d/plane/is_point_within_planes.js +7 -7
  391. package/src/core/geom/3d/plane/orient3d_fast_point.js +47 -47
  392. package/src/core/geom/3d/plane/plane3_compute_convex_3_plane_intersection.js +7 -7
  393. package/src/core/geom/3d/plane/plane3_compute_plane_intersection.js +7 -7
  394. package/src/core/geom/3d/plane/plane3_intersect_plane.js +7 -7
  395. package/src/core/geom/3d/plane/plane3_three_compute_convex_3_plane_intersection.js +7 -7
  396. package/src/core/geom/3d/point/point3_raycast.d.ts +33 -0
  397. package/src/core/geom/3d/point/point3_raycast.d.ts.map +1 -0
  398. package/src/core/geom/3d/point/point3_raycast.js +47 -0
  399. package/src/core/geom/3d/polygon/polygon3_compute_normal.js +60 -60
  400. package/src/core/geom/3d/polygon/polygon3_triangulate.js +94 -94
  401. package/src/core/geom/3d/polyhedron/convex_polyhedron3_raycast.d.ts +40 -0
  402. package/src/core/geom/3d/polyhedron/convex_polyhedron3_raycast.d.ts.map +1 -0
  403. package/src/core/geom/3d/polyhedron/convex_polyhedron3_raycast.js +94 -0
  404. package/src/core/geom/3d/quadric/QUADRIC3_SIZE.js +14 -14
  405. package/src/core/geom/3d/quadric/Quadric3.js +282 -282
  406. package/src/core/geom/3d/quadric/quadric3_add.js +33 -33
  407. package/src/core/geom/3d/quadric/quadric3_clear.js +25 -25
  408. package/src/core/geom/3d/quadric/quadric3_copy.js +27 -27
  409. package/src/core/geom/3d/quadric/quadric3_distance.js +36 -36
  410. package/src/core/geom/3d/quadric/quadric3_evaluate.js +25 -25
  411. package/src/core/geom/3d/quadric/quadric3_optimize.js +45 -45
  412. package/src/core/geom/3d/quadric/quadric3_scale.js +32 -32
  413. package/src/core/geom/3d/quadric/quadric3_set_from_plane_through_point.js +30 -30
  414. package/src/core/geom/3d/quadric/quadric3_set_from_vector4.js +35 -35
  415. package/src/core/geom/3d/quadric/quadric3_sub.js +36 -36
  416. package/src/core/geom/3d/quadric/quadric3_to_tensor_m3.js +28 -28
  417. package/src/core/geom/3d/quadric/quadric3_to_tensor_m3_inverse.js +56 -56
  418. package/src/core/geom/3d/quadric/quadric3_to_vector3.js +19 -19
  419. package/src/core/geom/3d/quaternion/quat3_createFromAxisAngle.js +7 -7
  420. package/src/core/geom/3d/quaternion/quat3_integrate.js +62 -62
  421. package/src/core/geom/3d/quaternion/quat3_multiply.js +31 -31
  422. package/src/core/geom/3d/quaternion/quat3_nlerp.js +72 -72
  423. package/src/core/geom/3d/quaternion/quat3_to_matrix3.js +69 -69
  424. package/src/core/geom/3d/quaternion/quat_decode_from_uint32.js +7 -7
  425. package/src/core/geom/3d/quaternion/quat_encode_to_uint32.js +7 -7
  426. package/src/core/geom/3d/ray/ray3_array_compose.js +7 -7
  427. package/src/core/geom/3d/ray/ray3_compute_nearest_point_to_point.js +7 -7
  428. package/src/core/geom/3d/ray/ray3_distance_to_point.js +7 -7
  429. package/src/core/geom/3d/ray/ray3_shift_origin_along_direction.js +7 -7
  430. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts +17 -0
  431. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
  432. package/src/core/geom/3d/shape/ConvexHullShape3D.js +43 -0
  433. package/src/core/geom/3d/shape/CylinderShape3D.d.ts +1 -0
  434. package/src/core/geom/3d/shape/CylinderShape3D.d.ts.map +1 -1
  435. package/src/core/geom/3d/shape/CylinderShape3D.js +21 -0
  436. package/src/core/geom/3d/shape/PointShape3D.d.ts +7 -0
  437. package/src/core/geom/3d/shape/PointShape3D.d.ts.map +1 -1
  438. package/src/core/geom/3d/shape/PointShape3D.js +88 -63
  439. package/src/core/geom/3d/shape/PosedShape3D.d.ts +6 -0
  440. package/src/core/geom/3d/shape/PosedShape3D.d.ts.map +1 -1
  441. package/src/core/geom/3d/shape/PosedShape3D.js +27 -0
  442. package/src/core/geom/3d/shape/TransformedShape3D.d.ts +10 -0
  443. package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
  444. package/src/core/geom/3d/shape/TransformedShape3D.js +13 -0
  445. package/src/core/geom/3d/shape/Triangle3D.d.ts +1 -0
  446. package/src/core/geom/3d/shape/Triangle3D.d.ts.map +1 -1
  447. package/src/core/geom/3d/shape/Triangle3D.js +26 -0
  448. package/src/core/geom/3d/shape/UnionShape3D.d.ts +6 -0
  449. package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
  450. package/src/core/geom/3d/shape/UnionShape3D.js +7 -0
  451. package/src/core/geom/3d/shape/UnitCubeShape3D.js +45 -45
  452. package/src/core/geom/3d/shape/UnitSphereShape3D.js +44 -44
  453. package/src/core/geom/3d/shape/shape3_mesh_from_geometry.js +64 -64
  454. package/src/core/geom/3d/shape/shape_mesh_from_geometry.js +7 -7
  455. package/src/core/geom/3d/shape/util/compute_signed_distance_gradient_by_sampling.js +51 -51
  456. package/src/core/geom/3d/shape/util/shape3d_voxelize_to_grid.js +7 -7
  457. package/src/core/geom/3d/sphere/compute_bounding_sphere_of_2_spheres.js +7 -7
  458. package/src/core/geom/3d/sphere/harmonics/sh3_convolve_with_zh.js +53 -53
  459. package/src/core/geom/3d/sphere/harmonics/sh3_ggx_zh.js +30 -30
  460. package/src/core/geom/3d/sphere/harmonics/sh3_rotate_bl.js +7 -7
  461. package/src/core/geom/3d/sphere/harmonics/sh_compute_K.js +7 -7
  462. package/src/core/geom/3d/sphere/sphere_array_intersects_point.js +7 -7
  463. package/src/core/geom/3d/sphere/sphere_array_intersects_ray.js +7 -7
  464. package/src/core/geom/3d/sphere/sphere_intersects_point.js +7 -7
  465. package/src/core/geom/3d/sphere/sphere_matrix4_transform.js +5 -5
  466. package/src/core/geom/3d/sphere/sphere_projected_sphere_radius_sqr.js +7 -7
  467. package/src/core/geom/3d/sphere/sphere_radius_sqr_from_v3_array_transformed.js +7 -7
  468. package/src/core/geom/3d/sphere/spherical_from_cartesian.js +7 -7
  469. package/src/core/geom/3d/tetrahedra/compute_tetrahedral_mesh_from_surface.js +174 -174
  470. package/src/core/geom/3d/tetrahedra/compute_tetrahedron_quality.js +7 -7
  471. package/src/core/geom/3d/tetrahedra/compute_tetrahedron_volume.js +7 -7
  472. package/src/core/geom/3d/tetrahedra/delaunay/fill_in_a_cavity.js +7 -7
  473. package/src/core/geom/3d/tetrahedra/graphics/build_tetrahedral_mesh_geometry.js +39 -39
  474. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.js +48 -48
  475. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.js +173 -173
  476. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_compute_tet_quality.js +22 -22
  477. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_find_tets_around_edge.js +146 -146
  478. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_23.js +232 -232
  479. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_32.js +255 -255
  480. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.js +387 -387
  481. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.js +140 -140
  482. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_tet_get_neighbours.js +7 -7
  483. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.js +97 -97
  484. package/src/core/geom/3d/tetrahedra/tetrahedron_compute_quality.js +66 -66
  485. package/src/core/geom/3d/tetrahedra/triangle/triangle_decode_depth.js +59 -59
  486. package/src/core/geom/3d/tetrahedra/triangle/triangle_rasterized_depth_sample_index.js +54 -54
  487. package/src/core/geom/3d/tetrahedra/validate_neighbour.js +70 -70
  488. package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.js +314 -314
  489. package/src/core/geom/3d/topology/struct/binary/BinaryDataLayer.js +364 -364
  490. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.js +567 -567
  491. package/src/core/geom/3d/topology/struct/binary/BinaryTopology.js +1085 -1085
  492. package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.js +73 -73
  493. package/src/core/geom/3d/topology/struct/binary/io/bt_loop_kill.js +35 -35
  494. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_append.js +119 -119
  495. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.js +84 -84
  496. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compact.js +172 -172
  497. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.js +56 -56
  498. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadratics.js +7 -7
  499. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.js +227 -227
  500. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_decouple.js +161 -161
  501. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_island_erode.js +490 -490
  502. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.js +120 -120
  503. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_from_indexed_geometry.js +120 -120
  504. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_collapse.js +52 -52
  505. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_create.js +115 -115
  506. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_flip.js +142 -142
  507. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_get_or_create.js +21 -21
  508. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.js +92 -92
  509. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_split.js +179 -179
  510. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_swap_vertex_slots.js +28 -28
  511. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_kill_only_edge.js +8 -8
  512. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_fuse_duplicate_edges.js +83 -83
  513. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_kill_short_edges.js +89 -89
  514. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_kill.js +25 -25
  515. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_poke.js +127 -127
  516. package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.js +268 -268
  517. package/src/core/geom/3d/topology/struct/binary/io/face/bt_kill_only_face.js +8 -8
  518. package/src/core/geom/3d/topology/struct/binary/io/face/bt_mesh_kill_degenerate_faces.js +72 -72
  519. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_kill_only_vert.js +7 -7
  520. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_merge_verts_by_distance.js +7 -7
  521. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_mesh_relative_merge_distance.js +48 -48
  522. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_mesh_split_pinched_vertices.js +256 -256
  523. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_mesh_vertex_merge_distance.js +77 -77
  524. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_fuse_duplicate_edges.js +7 -7
  525. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_kill.js +7 -7
  526. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_fuse_duplicate_edges.js +148 -148
  527. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_kill_only.js +7 -7
  528. package/src/core/geom/3d/topology/struct/binary/query/bt_collect_boundary_segments.js +29 -29
  529. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_breaks_link_condition.js +107 -107
  530. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_flip_is_legal.js +41 -41
  531. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_flip_would_fold.js +120 -120
  532. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_get.js +42 -42
  533. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_is_boundary.js +20 -20
  534. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.d.ts +32 -0
  535. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.d.ts.map +1 -0
  536. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_other_face.js +59 -0
  537. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_read_diamond.js +96 -96
  538. package/src/core/geom/3d/topology/struct/binary/query/bt_face_compute_newell_normal.js +72 -72
  539. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_attached_faces.js +7 -7
  540. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_edge_adjacent_faces.js +39 -39
  541. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_edge_adjacent_faces_weighted.js +93 -93
  542. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_neighbour_faces.js +7 -7
  543. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_shared_loop.js +48 -48
  544. package/src/core/geom/3d/topology/struct/binary/query/bt_face_island_flood_fill.js +45 -45
  545. package/src/core/geom/3d/topology/struct/binary/query/bt_face_read_triangle.js +66 -66
  546. package/src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.d.ts +26 -0
  547. package/src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.d.ts.map +1 -0
  548. package/src/core/geom/3d/topology/struct/binary/query/bt_face_third_vertex.js +55 -0
  549. package/src/core/geom/3d/topology/struct/binary/query/bt_faces_shared_loop.js +7 -7
  550. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.js +44 -44
  551. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_boundary_euclidean_distance_field.js +32 -32
  552. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_compute_face_islands.js +53 -53
  553. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path.js +703 -703
  554. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.js +181 -181
  555. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_segment_penetrates_surface.js +133 -133
  556. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_surface_ray_parity.js +82 -82
  557. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.js +599 -599
  558. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_walk_boundary_loops.js +108 -108
  559. package/src/core/geom/3d/topology/struct/binary/query/bt_query_edge_is_boundary.js +7 -7
  560. package/src/core/geom/3d/triangle/computeTriangleClosestPointToPointBarycentric.js +7 -7
  561. package/src/core/geom/3d/triangle/computeTriangleRayIntersection.js +7 -7
  562. package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentric.js +7 -7
  563. package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentricEdge.js +7 -7
  564. package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentricGeometry.js +7 -7
  565. package/src/core/geom/3d/triangle/compute_triangle_area_3d.js +7 -7
  566. package/src/core/geom/3d/triangle/compute_triangle_normal.js +7 -7
  567. package/src/core/geom/3d/triangle/rayTriangleIntersection.js +7 -7
  568. package/src/core/geom/3d/triangle/tri3_closest_point.js +65 -65
  569. package/src/core/geom/3d/triangle/tri3_closest_point_barycentric.js +171 -171
  570. package/src/core/geom/3d/triangle/tri3_flatten_isometric.js +79 -79
  571. package/src/core/geom/3d/triangle/tri3_ray_intersection.js +160 -160
  572. package/src/core/geom/3d/triangle/tri3_ray_intersection_barycentric.js +96 -96
  573. package/src/core/geom/3d/triangle/tri3_raycast.d.ts +42 -0
  574. package/src/core/geom/3d/triangle/tri3_raycast.d.ts.map +1 -0
  575. package/src/core/geom/3d/triangle/tri3_raycast.js +94 -0
  576. package/src/core/geom/3d/triangle/triangle3_incenter.js +7 -7
  577. package/src/core/geom/3d/triangle/triangle_compute_plane_side.js +7 -7
  578. package/src/core/geom/3d/triangle/triangle_compute_signed_volume.js +7 -7
  579. package/src/core/geom/3d/triangle/triangle_intersects_clipping_volume.js +7 -7
  580. package/src/core/geom/3d/triangle/triangle_mesh_compute_signed_volume.js +7 -7
  581. package/src/core/geom/3d/triangle/v3_compute_triangle_normal.js +7 -7
  582. package/src/core/geom/3d/util/make_justified_point_grid.js +7 -7
  583. package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +381 -381
  584. package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +684 -684
  585. package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +554 -554
  586. package/src/core/geom/mat3/m3_cm_from_translation.js +7 -7
  587. package/src/core/geom/mat3/m3_cm_invert.js +7 -7
  588. package/src/core/geom/mat3/m3_make_rotation.js +5 -5
  589. package/src/core/geom/mat3/m3_make_rotation_TBN.js +31 -31
  590. package/src/core/geom/mat3/m3_multiply_vec3.js +7 -7
  591. package/src/core/geom/packing/computeBoundingSphereOfSpheres.js +7 -7
  592. package/src/core/geom/packing/max-rect/cost/costByBestShortSide.js +7 -7
  593. package/src/core/geom/packing/max-rect/cost/costByRemainingArea.js +7 -7
  594. package/src/core/geom/packing/max-rect/cutArea.js +7 -7
  595. package/src/core/geom/packing/max-rect/findBestContainer.js +7 -7
  596. package/src/core/geom/packing/max-rect/packMaxRectangles.js +7 -7
  597. package/src/core/geom/packing/max-rect/packOneBox.js +7 -7
  598. package/src/core/geom/packing/max-rect/removeRedundantBoxes.js +7 -7
  599. package/src/core/geom/packing/max-rect/removeRedundantBoxesArray.js +7 -7
  600. package/src/core/geom/random/randomPointInBox.js +7 -7
  601. package/src/core/geom/random/randomPointInCircle.js +7 -7
  602. package/src/core/geom/random/randomPointInPoint.js +7 -7
  603. package/src/core/geom/random/randomPointInSphere.js +7 -7
  604. package/src/core/geom/random/randomPointOnBox.js +7 -7
  605. package/src/core/geom/random/randomPointOnSphere.js +7 -7
  606. package/src/core/geom/random/random_orthonormal_basis.js +31 -31
  607. package/src/core/geom/vec/vector_axpy.js +15 -15
  608. package/src/core/geom/vec/vector_axpy_offset.js +20 -20
  609. package/src/core/geom/vec/vector_copy.js +7 -7
  610. package/src/core/geom/vec/vector_dot_offset.js +25 -25
  611. package/src/core/geom/vec/vector_scale_array.js +7 -7
  612. package/src/core/geom/vec2/v2_rotate.js +21 -21
  613. package/src/core/geom/vec2/v2_rotate_cos_sin.js +21 -21
  614. package/src/core/geom/vec3/v3_angle_atan2_between.js +7 -7
  615. package/src/core/geom/vec3/v3_angle_between.js +7 -7
  616. package/src/core/geom/vec3/v3_angle_cos_between.js +7 -7
  617. package/src/core/geom/vec3/v3_array_add_in_place.js +7 -7
  618. package/src/core/geom/vec3/v3_array_centroid.js +7 -7
  619. package/src/core/geom/vec3/v3_array_copy.js +7 -7
  620. package/src/core/geom/vec3/v3_array_displace_in_direction.js +7 -7
  621. package/src/core/geom/vec3/v3_array_equals.js +7 -7
  622. package/src/core/geom/vec3/v3_array_immediate_add.js +7 -7
  623. package/src/core/geom/vec3/v3_array_matrix4_rotate.js +7 -7
  624. package/src/core/geom/vec3/v3_array_normalize.js +7 -7
  625. package/src/core/geom/vec3/v3_array_scale.js +7 -7
  626. package/src/core/geom/vec3/v3_compute_interior_angle.js +7 -7
  627. package/src/core/geom/vec3/v3_cotangent.js +33 -33
  628. package/src/core/geom/vec3/v3_dot_array_array.js +7 -7
  629. package/src/core/geom/vec3/v3_matrix3_multiply.js +30 -30
  630. package/src/core/geom/vec3/v3_matrix3_rotate.js +49 -49
  631. package/src/core/geom/vec3/v3_matrix4_rotate.js +7 -7
  632. package/src/core/geom/vec3/v3_multiply.js +23 -23
  633. package/src/core/geom/vec3/v3_multiply_scalar.js +7 -7
  634. package/src/core/geom/vec3/v3_orthonormal_matrix_from_normal.js +49 -49
  635. package/src/core/geom/vec3/v3_quat3_apply.js +7 -7
  636. package/src/core/geom/vec3/v3_quat3_apply_inverse.js +7 -7
  637. package/src/core/geom/vec3/v3_quaternion_apply.js +39 -39
  638. package/src/core/geom/vec3/v3_quaternion_apply_inverse.js +41 -41
  639. package/src/core/geom/vec3/v3_reflect.js +24 -24
  640. package/src/core/geom/vec3/v3_scale_array.js +18 -18
  641. package/src/core/geom/vec3/v3_triple_cross.js +45 -45
  642. package/src/core/geom/vec3/v3_triple_cross_product.js +7 -7
  643. package/src/core/geom/vec3/v3_uniform_sample_cone.js +21 -21
  644. package/src/core/geom/vec4/v4_multiply_mat4.js +7 -7
  645. package/src/core/graph/csr/csr_graph_build_distance_field.js +112 -112
  646. package/src/core/graph/csr/csr_graph_find_edge.js +25 -25
  647. package/src/core/graph/csr/csr_graph_find_shortest_path.js +105 -105
  648. package/src/core/graph/csr/csr_graph_from_undirected_edge_list.js +94 -94
  649. package/src/core/graph/graph_k_means_cluster.js +250 -250
  650. package/src/core/graph/layout/CircleLayout.js +1 -1
  651. package/src/core/graph/layout/box/BoxLayouter.js +1 -1
  652. package/src/core/graph/layout/graph_peel_topological_layer.js +45 -45
  653. package/src/core/graph/metis/BinaryTopologyFaceGraph.js +49 -49
  654. package/src/core/graph/metis/bt_mesh_build_face_graph.js +111 -111
  655. package/src/core/graph/metis/cluster_mesh_metis.js +137 -137
  656. package/src/core/graph/metis/native/bisection/bisect_graph.js +100 -100
  657. package/src/core/graph/metis/native/bisection/compute_2way_params.js +86 -86
  658. package/src/core/graph/metis/native/bisection/fm_2way.js +297 -297
  659. package/src/core/graph/metis/native/bisection/grow_bisection.js +137 -137
  660. package/src/core/graph/metis/native/bisection/split_graph_two_way.js +119 -119
  661. package/src/core/graph/metis/native/coarsen/coarsen_graph.js +94 -94
  662. package/src/core/graph/metis/native/coarsen/create_coarse_graph.js +158 -158
  663. package/src/core/graph/metis/native/coarsen/match_shem.js +175 -175
  664. package/src/core/graph/metis/native/initial/initial_kway_bfs.js +122 -122
  665. package/src/core/graph/metis/native/initial/initial_kway_recursive_bisection.js +170 -170
  666. package/src/core/graph/metis/native/metis_partition_kway.js +126 -126
  667. package/src/core/graph/metis/native/refine/RefinementScratch.js +53 -53
  668. package/src/core/graph/metis/native/refine/fm_kway.js +487 -487
  669. package/src/core/graph/metis/native/refine/project_kway.js +43 -43
  670. package/src/core/graph/metis/native/refine/refine_kway.js +43 -43
  671. package/src/core/lang/reactive/pegjs/parser.js +3458 -3458
  672. package/src/core/math/complex/complex_horner_eval.js +31 -31
  673. package/src/core/math/computeWholeDivisorLow.js +33 -33
  674. package/src/core/math/f32_next_down.js +24 -24
  675. package/src/core/math/f32_next_up.js +27 -27
  676. package/src/core/math/hash/hash_mix2.js +22 -22
  677. package/src/core/math/hash/hash_mix3.js +19 -19
  678. package/src/core/math/linalg/cg/cg_solve.js +106 -106
  679. package/src/core/math/linalg/cg/cg_solve_normal_equations.js +59 -59
  680. package/src/core/math/linalg/cubic_residual_times_derivative_accumulate.js +43 -43
  681. package/src/core/math/linalg/eigen/matrix_householder_in_place.js +122 -122
  682. package/src/core/math/linalg/eigen/matrix_qr_in_place.js +190 -190
  683. package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.js +117 -117
  684. package/src/core/math/linalg/givens/givens_apply_rows.js +28 -28
  685. package/src/core/math/linalg/givens/givens_apply_rows_offset.js +30 -30
  686. package/src/core/math/linalg/givens/givens_rotation_coefficients.js +39 -39
  687. package/src/core/math/linalg/lu_factor_linear_system.js +108 -108
  688. package/src/core/math/linalg/lu_solve_linear_system.js +53 -53
  689. package/src/core/math/linalg/polynomial_add_into.js +29 -29
  690. package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.js +159 -159
  691. package/src/core/math/linalg/polynomial_cubic_derivative_eval.js +16 -16
  692. package/src/core/math/linalg/polynomial_cubic_horner_eval.js +19 -19
  693. package/src/core/math/linalg/polynomial_cubic_second_derivative_eval.js +14 -14
  694. package/src/core/math/linalg/polynomial_multiply.js +41 -41
  695. package/src/core/math/linalg/polynomial_real_roots_in_interval.js +211 -211
  696. package/src/core/math/linalg/polynomial_root_bound_cauchy.js +30 -30
  697. package/src/core/math/linalg/polynomial_scale_into.js +17 -17
  698. package/src/core/math/linalg/polynomial_sub_into.js +29 -29
  699. package/src/core/math/linalg/solve_linear_system_GEPP_2x2.js +96 -96
  700. package/src/core/math/linalg/sor_optimal_omega.js +42 -42
  701. package/src/core/math/linalg/sparse/SparseMatrixCSR.js +88 -88
  702. package/src/core/math/linalg/sparse/spm_matvec.js +22 -22
  703. package/src/core/math/linalg/sparse/spm_matvec_transpose.js +29 -29
  704. package/src/core/math/lookup/LUT_HEATMAP.js +30 -30
  705. package/src/core/math/lookup/ParameterLookupTable.js +495 -495
  706. package/src/core/math/lookup/ParameterLookupTableFlags.js +6 -6
  707. package/src/core/math/noise/curl_noise_3d.js +18 -18
  708. package/src/core/math/physics/brdf/cone_cosine_from_roughness.js +28 -28
  709. package/src/core/math/physics/brdf/reflection_sample_weight.js +48 -48
  710. package/src/core/math/physics/kinematics/computeInterceptPoint.js +79 -79
  711. package/src/core/math/random/randomSeed.js +16 -16
  712. package/src/core/math/random/random_pick_weighted_index.js +26 -26
  713. package/src/core/math/round_half_away_from_zero.js +13 -13
  714. package/src/core/math/spline/spline3_hermite_apply_transform.js +107 -107
  715. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.js +125 -125
  716. package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.js +50 -50
  717. package/src/core/math/spline/spline3_hermite_reverse.js +49 -49
  718. package/src/core/math/spline/spline3_hermite_to_monomial.js +37 -37
  719. package/src/core/math/statistics/hammersley_sequence.js +53 -53
  720. package/src/core/model/reactive/model/util/createRandomReactiveExpression.js +185 -185
  721. package/src/core/path/convertPathToURL.js +107 -107
  722. package/src/core/process/delay.js +16 -16
  723. package/src/core/science/units/MEASUREMENT_UNITS_SCIENTIFIC.js +87 -87
  724. package/src/core/science/units/MEASUREMENT_UNITS_SI.js +17 -17
  725. package/src/core/science/units/NamedUnit.js +48 -48
  726. package/src/core/science/units/UNIT_DIMENSION_MAPPING.js +29 -29
  727. package/src/core/science/units/UnitDimension.js +28 -28
  728. package/src/core/science/units/format_quantity.js +74 -74
  729. package/src/core/science/units/magnitude_prefix.js +127 -127
  730. package/src/core/science/units/si/AMPERE.js +13 -13
  731. package/src/core/science/units/si/CANDELA.js +13 -13
  732. package/src/core/science/units/si/DIMENSIONLESS.js +14 -14
  733. package/src/core/science/units/si/KELVIN.js +13 -13
  734. package/src/core/science/units/si/KILOGRAM.js +13 -13
  735. package/src/core/science/units/si/METER.js +13 -13
  736. package/src/core/science/units/si/MOLE.js +13 -13
  737. package/src/core/science/units/si/SECOND.js +13 -13
  738. package/src/core/science/units/si/derived/BECQUEREL.js +16 -16
  739. package/src/core/science/units/si/derived/COULOMB.js +13 -13
  740. package/src/core/science/units/si/derived/FARAD.js +13 -13
  741. package/src/core/science/units/si/derived/GRAY.js +16 -16
  742. package/src/core/science/units/si/derived/HENRY.js +13 -13
  743. package/src/core/science/units/si/derived/HERTZ.js +16 -16
  744. package/src/core/science/units/si/derived/JOULE.js +13 -13
  745. package/src/core/science/units/si/derived/KATAL.js +13 -13
  746. package/src/core/science/units/si/derived/LUMEN.js +14 -14
  747. package/src/core/science/units/si/derived/LUX.js +13 -13
  748. package/src/core/science/units/si/derived/NEWTON.js +13 -13
  749. package/src/core/science/units/si/derived/OHM.js +13 -13
  750. package/src/core/science/units/si/derived/PASCAL.js +13 -13
  751. package/src/core/science/units/si/derived/SIEMENS.js +13 -13
  752. package/src/core/science/units/si/derived/SIEVERT.js +16 -16
  753. package/src/core/science/units/si/derived/TESLA.js +13 -13
  754. package/src/core/science/units/si/derived/VOLT.js +13 -13
  755. package/src/core/science/units/si/derived/WATT.js +13 -13
  756. package/src/core/science/units/si/derived/WEBER.js +13 -13
  757. package/src/core/science/units/unit_matrix_to_string.js +403 -403
  758. package/src/engine/Engine.js +726 -726
  759. package/src/engine/EngineHarness.js +590 -590
  760. package/src/engine/REVIEW_2026_07_03.md +211 -211
  761. package/src/engine/animation/AnimatedActions.js +81 -81
  762. package/src/engine/animation/AnimationUtils.js +252 -252
  763. package/src/engine/animation/async/TimeSeries.js +300 -300
  764. package/src/engine/animation/curve/animation_curve_binary_codec.js +60 -60
  765. package/src/engine/animation/curve/draw/canvas_point_to_curve.js +22 -22
  766. package/src/engine/animation/curve/draw/curve_point_to_canvas.js +38 -38
  767. package/src/engine/animation/curve/draw/position_canvas_to_curve.js +22 -22
  768. package/src/engine/animation/curve/draw/position_curve_to_canvas.js +22 -22
  769. package/src/engine/asset/GameAssetType.d.ts +0 -1
  770. package/src/engine/asset/GameAssetType.js +0 -1
  771. package/src/engine/asset/load_model_scene_bundle.js +27 -27
  772. package/src/engine/asset/loaders/font/FontAsset.js +21 -21
  773. package/src/engine/asset/loaders/font/FontAssetLoader.js +20 -20
  774. package/src/engine/asset/loaders/gltf_test_fixtures.js +358 -358
  775. package/src/engine/asset/loaders/image/ImageBitmapAssetLoader.js +52 -52
  776. package/src/engine/asset/loaders/image/ImageRGBADataLoader.js +94 -94
  777. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterAverage.js +59 -59
  778. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.js +55 -55
  779. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterPaeth.js +74 -74
  780. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.js +34 -34
  781. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.js +46 -46
  782. package/src/engine/control/first-person/DESIGN.md +640 -640
  783. package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +563 -563
  784. package/src/engine/control/first-person/FirstPersonMotionPhase.js +134 -134
  785. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +2045 -2045
  786. package/src/engine/control/first-person/mastery/DecisionPoint.js +30 -30
  787. package/src/engine/control/first-person/math/Spring.js +71 -71
  788. package/src/engine/control/first-person/math/computeJumpFromApex.js +23 -23
  789. package/src/engine/control/first-person/math/computeLRCBreathRate.js +41 -41
  790. package/src/engine/control/first-person/math/computeMassRatios.js +44 -44
  791. package/src/engine/control/first-person/math/criticallyDampedSpring.js +34 -34
  792. package/src/engine/control/first-person/math/dampedSpringStep.js +72 -72
  793. package/src/engine/control/first-person/math/jumpDynamics.js +108 -108
  794. package/src/engine/control/first-person/math/stepTowards.js +20 -20
  795. package/src/engine/control/first-person/pose/FirstPersonPose.js +137 -137
  796. package/src/engine/control/first-person/pose/FirstPersonPosture.js +27 -27
  797. package/src/engine/control/first-person/test/buildTestPlayer.js +36 -36
  798. package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +531 -531
  799. package/src/engine/ecs/EntityManager.d.ts +7 -3
  800. package/src/engine/ecs/EntityManager.d.ts.map +1 -1
  801. package/src/engine/ecs/EntityManager.js +10 -5
  802. package/src/engine/ecs/HIERARCHY_REVIEW.md +717 -717
  803. package/src/engine/ecs/async/SystemWorkerHost.js +329 -329
  804. package/src/engine/ecs/async/SystemWorkerLoopback.js +143 -143
  805. package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +246 -246
  806. package/src/engine/ecs/async/WorkerSystem.js +702 -702
  807. package/src/engine/ecs/async/WorkerSystemProtocol.js +150 -150
  808. package/src/engine/ecs/fow/FogOfWar.js +707 -707
  809. package/src/engine/ecs/fow/FogOfWarEdgeMode.js +40 -40
  810. package/src/engine/ecs/fow/fog_of_war_mask_world_bounds.js +24 -24
  811. package/src/engine/ecs/fow/fog_of_war_uv_transform.js +30 -30
  812. package/src/engine/ecs/gui/hud/testHudEdgeStick.js +132 -134
  813. package/src/engine/ecs/hierarchy/EntityChildIndex.js +140 -140
  814. package/src/engine/ecs/ik/IKProblem.js +42 -42
  815. package/src/engine/ecs/ik/InverseKinematicsSystem.js +193 -193
  816. package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +134 -134
  817. package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.js +97 -97
  818. package/src/engine/ecs/ik/ik_bone_chain.js +85 -85
  819. package/src/engine/ecs/ik/ik_probe_terrain_contact.js +119 -119
  820. package/src/engine/ecs/parent/EntityNode.d.ts +1 -1
  821. package/src/engine/ecs/parent/EntityNode.js +2 -2
  822. package/src/engine/ecs/parent/ParentEntitySystem.js +255 -255
  823. package/src/engine/ecs/speaker/VoiceSystem.d.ts +2 -2
  824. package/src/engine/ecs/speaker/VoiceSystem.d.ts.map +1 -1
  825. package/src/engine/ecs/speaker/VoiceSystem.js +13 -7
  826. package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +269 -269
  827. package/src/engine/ecs/storage/binary/object-v2/BinaryObjectDeSerializer.js +315 -315
  828. package/src/engine/ecs/storage/binary/object-v2/BinaryObjectSerializer.js +331 -331
  829. package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +199 -199
  830. package/src/engine/ecs/storage/binary/object-v2/ObjectBinaryFormat.js +57 -57
  831. package/src/engine/ecs/storage/binary/object-v2/ObjectSerializationAdapter.js +56 -56
  832. package/src/engine/ecs/storage/binary/object-v2/README.md +110 -110
  833. package/src/engine/ecs/storage/binary/object-v2/objectStringCodec.js +48 -48
  834. package/src/engine/ecs/storage/binary/object-v2/serializerTestHarness.js +44 -44
  835. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  836. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +5 -1
  837. package/src/engine/ecs/terrain/TerrainClouds.js +97 -97
  838. package/src/engine/ecs/terrain/ecs/StubTerrainBuildWorker.js +65 -65
  839. package/src/engine/ecs/terrain/ecs/Terrain.d.ts +4 -0
  840. package/src/engine/ecs/terrain/ecs/Terrain.d.ts.map +1 -1
  841. package/src/engine/ecs/terrain/ecs/Terrain.js +790 -790
  842. package/src/engine/ecs/terrain/ecs/TerrainSystem.js +209 -209
  843. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +316 -316
  844. package/src/engine/ecs/terrain/ecs/layers/TerrainLayers.js +416 -416
  845. package/src/engine/ecs/terrain/ecs/splat/SplatMapping.js +525 -525
  846. package/src/engine/ecs/terrain/overlay/TerrainOverlay.js +302 -302
  847. package/src/engine/ecs/terrain/tiles/TerrainTile.js +561 -561
  848. package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +896 -896
  849. package/src/engine/ecs/terrain/tiles/tile_geometry_to_meshlets.js +36 -36
  850. package/src/engine/ecs/terrain/util/loadVisibleTerrainTiles.js +129 -129
  851. package/src/engine/ecs/transform/Transform64.js +814 -814
  852. package/src/engine/ecs/transform/t64_copy_from_transform.js +35 -35
  853. package/src/engine/ecs/transform/t64_equals_transform.js +55 -55
  854. package/src/engine/ecs/transform/t64_look_rotation.js +36 -36
  855. package/src/engine/ecs/transform/t64_transform_direction.js +25 -25
  856. package/src/engine/ecs/transform/transform_copy_from_t64.js +28 -28
  857. package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.js +345 -345
  858. package/src/engine/ecs/transform-attachment/transform_attachment_find_descendant_by_name.js +65 -65
  859. package/src/engine/ecs/transform-attachment/transform_attachment_parent_of.js +24 -24
  860. package/src/engine/ecs/util/hideEntityGracefully.js +208 -208
  861. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +446 -446
  862. package/src/engine/graphics/FULL_SCREEN_QUAD_VERTEX_SHADER.js +14 -14
  863. package/src/engine/graphics/FrameThrottle.js +2 -2
  864. package/src/engine/graphics/ecs/animation/animator/graph/AnimationState.js +207 -207
  865. package/src/engine/graphics/ecs/animation/animator/graph/AnimationTransition.js +151 -151
  866. package/src/engine/graphics/ecs/animation/animator/graph/definition/serialization/AnimationGraphDefinitionAssetLoader.js +22 -22
  867. package/src/engine/graphics/ecs/camera/Camera.js +165 -165
  868. package/src/engine/graphics/ecs/camera/camera_active_frustum.js +25 -25
  869. package/src/engine/graphics/ecs/camera/camera_find_active.js +28 -28
  870. package/src/engine/graphics/ecs/camera/camera_traverse_active.js +18 -18
  871. package/src/engine/graphics/ecs/camera/pp/PerfectPanner.js +170 -170
  872. package/src/engine/graphics/ecs/camera/serialization/CameraSerializationAdapter.js +68 -68
  873. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +296 -296
  874. package/src/engine/graphics/ecs/decal/v2/Decal.js +220 -220
  875. package/src/engine/graphics/ecs/decal/v2/DecalSerializationAdapter.js +56 -56
  876. package/src/engine/graphics/ecs/decal/v2/DecalSerializationUpgrader_0_1.js +38 -38
  877. package/src/engine/graphics/ecs/light/shadow/compute_view_frustum_aabb_in_space.js +62 -62
  878. package/src/engine/graphics/ecs/light/shadow/extend_shadow_camera_near_for_casters.js +77 -77
  879. package/src/engine/graphics/ecs/make_bvh_depth_range_computer.js +33 -33
  880. package/src/engine/graphics/ecs/mesh-v2/DeferredBoundsQueue.js +88 -88
  881. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +428 -428
  882. package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMesh.js +303 -303
  883. package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMeshSystem.d.ts +1 -1
  884. package/src/engine/graphics/ecs/mesh-v2/aggregate/serialization/SGMeshSerializationAdapter.js +39 -39
  885. package/src/engine/graphics/ecs/mesh-v2/sg_hierarchy_compute_bounding_box_via_parent_entity.js +36 -36
  886. package/src/engine/graphics/ecs/path/tube/build/GeometryStream.js +94 -94
  887. package/src/engine/graphics/ecs/path/tube/build/build_stream_catmullrom.js +310 -310
  888. package/src/engine/graphics/ecs/path/tube/build/build_stream_linear.js +73 -73
  889. package/src/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +199 -199
  890. package/src/engine/graphics/ecs/path/tube/build/makeTubeStream.js +217 -217
  891. package/src/engine/graphics/ecs/path/tube/build/make_cap.js +340 -340
  892. package/src/engine/graphics/ecs/path/tube/build/make_ring_faces.js +40 -40
  893. package/src/engine/graphics/ecs/path/tube/build/make_ring_vertices.js +153 -153
  894. package/src/engine/graphics/ecs/render_layers_compute_depth_range.js +64 -64
  895. package/src/engine/graphics/ecs/trail3d/Trail3D.js +483 -483
  896. package/src/engine/graphics/ecs/trail3d/Trail3DFlags.js +26 -26
  897. package/src/engine/graphics/ecs/trail3d/advance_trail.js +42 -42
  898. package/src/engine/graphics/ecs/trail3d/make_gradient_stroke.js +58 -58
  899. package/src/engine/graphics/ecs/trail3d/seed_trail_stroke.js +90 -90
  900. package/src/engine/graphics/ecs/trail3d/seed_trail_tube.js +75 -75
  901. package/src/engine/graphics/ecs/trail3d/trail_head_position.js +19 -19
  902. package/src/engine/graphics/ecs/water/Water.js +104 -104
  903. package/src/engine/graphics/geometry/AttributeData.js +84 -84
  904. package/src/engine/graphics/geometry/buffered/geometry_compute_vertex_normals_indexed.js +49 -49
  905. package/src/engine/graphics/geometry/buffered/query/GeometrySpatialQueryAccelerator.js +288 -288
  906. package/src/engine/graphics/geometry/buffered/query/nearest_point_on_triangle_record.js +87 -87
  907. package/src/engine/graphics/geometry/buffered/read_triangle_vertices.js +102 -102
  908. package/src/engine/graphics/geometry/bvh/buffered/bvh32_from_buffered_geometry.js +52 -52
  909. package/src/engine/graphics/geometry/bvh/buffered/bvh32_from_indexed_geometry.js +20 -20
  910. package/src/engine/graphics/geometry/bvh/buffered/bvh32_from_unindexed_geometry.js +19 -19
  911. package/src/engine/graphics/geometry/optimization/geometry_optimize_uv_tension.js +135 -135
  912. package/src/engine/graphics/impostors/voxel/README.md +149 -149
  913. package/src/engine/graphics/impostors/voxel/VoxelImpostorDescription.js +141 -141
  914. package/src/engine/graphics/particles/node-based/codegen/glsl/getTypeByteSize.js +20 -20
  915. package/src/engine/graphics/particles/node-based/nodes/FloatConstant.d.ts +2 -0
  916. package/src/engine/graphics/particles/node-based/nodes/FloatConstant.d.ts.map +1 -1
  917. package/src/engine/graphics/particles/node-based/nodes/ReadFloatUniform.d.ts +2 -0
  918. package/src/engine/graphics/particles/node-based/nodes/ReadFloatUniform.d.ts.map +1 -1
  919. package/src/engine/graphics/particles/node-based/nodes/ReadVelocity.d.ts +2 -0
  920. package/src/engine/graphics/particles/node-based/nodes/ReadVelocity.d.ts.map +1 -1
  921. package/src/engine/graphics/particles/node-based/nodes/ShaderNode.d.ts +1 -2
  922. package/src/engine/graphics/particles/node-based/nodes/ShaderNode.d.ts.map +1 -1
  923. package/src/engine/graphics/particles/node-based/nodes/Vector3Constant.d.ts +2 -0
  924. package/src/engine/graphics/particles/node-based/nodes/Vector3Constant.d.ts.map +1 -1
  925. package/src/engine/graphics/particles/node-based/nodes/Vector3Merge.d.ts +2 -0
  926. package/src/engine/graphics/particles/node-based/nodes/Vector3Merge.d.ts.map +1 -1
  927. package/src/engine/graphics/particles/node-based/nodes/Vector3Split.d.ts +2 -0
  928. package/src/engine/graphics/particles/node-based/nodes/Vector3Split.d.ts.map +1 -1
  929. package/src/engine/graphics/particles/node-based/nodes/WriteVelocity.d.ts +2 -0
  930. package/src/engine/graphics/particles/node-based/nodes/WriteVelocity.d.ts.map +1 -1
  931. package/src/engine/graphics/particles/node-based/nodes/attribute/ReadVector3Attribute.d.ts +2 -0
  932. package/src/engine/graphics/particles/node-based/nodes/attribute/ReadVector3Attribute.d.ts.map +1 -1
  933. package/src/engine/graphics/particles/node-based/nodes/attribute/WriteVector3Attribute.d.ts +2 -0
  934. package/src/engine/graphics/particles/node-based/nodes/attribute/WriteVector3Attribute.d.ts.map +1 -1
  935. package/src/engine/graphics/particles/node-based/nodes/math/AddFloatNode.d.ts +2 -0
  936. package/src/engine/graphics/particles/node-based/nodes/math/AddFloatNode.d.ts.map +1 -1
  937. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Add.d.ts +2 -0
  938. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Add.d.ts.map +1 -1
  939. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Divide.d.ts +2 -0
  940. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Divide.d.ts.map +1 -1
  941. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Multiply.d.ts +2 -0
  942. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Multiply.d.ts.map +1 -1
  943. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Subtract.d.ts +2 -0
  944. package/src/engine/graphics/particles/node-based/nodes/math/Vector3Subtract.d.ts.map +1 -1
  945. package/src/engine/graphics/particles/node-based/nodes/noise/CurlNoiseNode.d.ts +2 -0
  946. package/src/engine/graphics/particles/node-based/nodes/noise/CurlNoiseNode.d.ts.map +1 -1
  947. package/src/engine/graphics/particles/node-based/nodes/particle_data_type_component_count.js +45 -45
  948. package/src/engine/graphics/particles/particular/engine/ParticularEngine.js +229 -229
  949. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts +2 -10
  950. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts.map +1 -1
  951. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +1149 -1149
  952. package/src/engine/graphics/particles/particular/engine/emitter/ParticlePool.js +472 -472
  953. package/src/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.js +2 -2
  954. package/src/engine/graphics/particles/particular/engine/parameter/ParameterLookupTableFlags.js +2 -2
  955. package/src/engine/graphics/particles/particular/engine/parameter/ParameterSet.d.ts.map +1 -1
  956. package/src/engine/graphics/particles/particular/engine/parameter/ParameterSet.js +3 -5
  957. package/src/engine/graphics/particles/particular/engine/shader/ShaderManager.js +135 -135
  958. package/src/engine/graphics/particles/particular/engine/utils/distribute_points_on_indexed_triangles.js +168 -168
  959. package/src/engine/graphics/render/forward_plus/plugin/forwardPlusMaterialMarker.js +18 -18
  960. package/src/engine/graphics/render/forward_plus/read_frustum_corner.js +2 -2
  961. package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +219 -219
  962. package/src/engine/graphics/render/frame_graph/FrameGraph.js +900 -900
  963. package/src/engine/graphics/render/frame_graph/FrameGraphScope.js +72 -72
  964. package/src/engine/graphics/render/frame_graph/FrameGraphState.js +39 -39
  965. package/src/engine/graphics/render/frame_graph/ResourceEntry.js +96 -96
  966. package/src/engine/graphics/render/gizmo/GizmoShapeBuffer.js +105 -105
  967. package/src/engine/graphics/render/gizmo/GizmoShapeRenderingInterface.js +162 -162
  968. package/src/engine/graphics/render/layers/RenderLayer.js +95 -95
  969. package/src/engine/graphics/sh3/build_injected_shader.js +56 -56
  970. package/src/engine/graphics/sh3/grid_dilate_average.js +175 -175
  971. package/src/engine/graphics/sh3/path_tracer/BufferedGeometryBVH.js +506 -506
  972. package/src/engine/graphics/sh3/path_tracer/PathTracedMesh.js +161 -161
  973. package/src/engine/graphics/sh3/path_tracer/PathTracedRenderer.js +205 -205
  974. package/src/engine/graphics/sh3/path_tracer/PathTracedScene.js +698 -698
  975. package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +490 -490
  976. package/src/engine/graphics/sh3/path_tracer/geometry/compute_triangle_group_aabb3.js +2 -2
  977. package/src/engine/graphics/sh3/path_tracer/make_sky_hosek.js +45 -45
  978. package/src/engine/graphics/sh3/path_tracer/populate_path_traced_scene_from_ecd.js +73 -73
  979. package/src/engine/graphics/sh3/path_tracer/ray_hit_apply_transform.js +32 -32
  980. package/src/engine/graphics/sh3/path_tracer/texture/apply_texture_clamping_to_coordinate.js +28 -28
  981. package/src/engine/graphics/sh3/path_tracer/texture/sample_material.js +142 -142
  982. package/src/engine/graphics/sh3/sky/hosek/render_hosek_sky_to_equirectangular.js +70 -70
  983. package/src/engine/graphics/sh3/sky/hosek/sky_hosek_compute_irradiance_by_direction.js +388 -388
  984. package/src/engine/graphics/shaders/chunks/glsl_octahedral_impostor.js +277 -277
  985. package/src/engine/graphics/shaders/chunks/glsl_voxel_impostor_splat.js +57 -57
  986. package/src/engine/graphics/shadows/vsm.glsl.js +43 -43
  987. package/src/engine/graphics/texture/3d/scs3d_sample_linear3.js +81 -81
  988. package/src/engine/graphics/texture/EnvironmentTextureProjection.js +15 -15
  989. package/src/engine/graphics/texture/channel_count_to_format_prefix.js +26 -26
  990. package/src/engine/graphics/texture/isImageBitmap.js +2 -2
  991. package/src/engine/graphics/texture/reflection/convolve_equirectangular_reflection.js +189 -189
  992. package/src/engine/graphics/texture/reflection/equirectangular_reflection_roughness.js +51 -51
  993. package/src/engine/graphics/texture/sampler/HarmonicDiffusionGrid.js +145 -145
  994. package/src/engine/graphics/texture/sampler/Sampler2DArray.js +172 -172
  995. package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +466 -466
  996. package/src/engine/graphics/texture/sampler/compare/flip/FlipParameters.js +18 -18
  997. package/src/engine/graphics/texture/sampler/compare/flip/FlipPooling.js +33 -33
  998. package/src/engine/graphics/texture/sampler/compare/flip/flip_build_feature_kernel.js +147 -147
  999. package/src/engine/graphics/texture/sampler/compare/flip/flip_build_spatial_kernel.js +177 -177
  1000. package/src/engine/graphics/texture/sampler/compare/flip/flip_compute_color_difference.js +104 -104
  1001. package/src/engine/graphics/texture/sampler/compare/flip/flip_compute_feature_difference.js +76 -76
  1002. package/src/engine/graphics/texture/sampler/compare/flip/flip_compute_pixels_per_degree.js +34 -34
  1003. package/src/engine/graphics/texture/sampler/compare/flip/flip_constants.js +77 -77
  1004. package/src/engine/graphics/texture/sampler/compare/flip/flip_filter_feature.js +48 -48
  1005. package/src/engine/graphics/texture/sampler/compare/flip/flip_filter_spatial.js +86 -86
  1006. package/src/engine/graphics/texture/sampler/compare/flip/flip_pool.js +71 -71
  1007. package/src/engine/graphics/texture/sampler/compare/flip/flip_srgb_to_ycxcz.js +55 -55
  1008. package/src/engine/graphics/texture/sampler/compare/flip/sampler2d_flip_ldr.js +80 -80
  1009. package/src/engine/graphics/texture/sampler/filter/SeparableKernel.js +82 -82
  1010. package/src/engine/graphics/texture/sampler/filter/sampler2d_convolve_separable.js +122 -122
  1011. package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.js +148 -148
  1012. package/src/engine/graphics/texture/sampler/sampler2d_channel_apply_lut.js +82 -82
  1013. package/src/engine/graphics/texture/sampler/sampler2d_combine.js +67 -67
  1014. package/src/engine/graphics/texture/sampler/sampler2d_linear_f16_to_srgb_u8.js +54 -54
  1015. package/src/engine/graphics/texture/sampler/sampler2d_sample_equirectangular_direction.js +63 -63
  1016. package/src/engine/graphics/texture/sampler/sampler2d_srgb_to_linear.js +33 -33
  1017. package/src/engine/graphics/texture/sampler/sampler2d_srgb_u8_to_linear_f16.js +68 -68
  1018. package/src/engine/graphics/texture/shade_image_preview.js +103 -103
  1019. package/src/engine/graphics/trail/TrailSpawnMode.js +20 -20
  1020. package/src/engine/graphics/trail/tube/TubeX.js +573 -573
  1021. package/src/engine/graphics/trail/tube/TubeXMaterialSpec.js +58 -58
  1022. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js +34 -34
  1023. package/src/engine/graphics/trail/tube/tube_attributes_spec.js +69 -69
  1024. package/src/engine/graphics/trail/tube/tube_frame.js +83 -83
  1025. package/src/engine/graphics3/AnimationGraphSystem3.js +329 -329
  1026. package/src/engine/graphics3/AnimationSystem3.js +352 -352
  1027. package/src/engine/graphics3/CameraSystem3.js +170 -170
  1028. package/src/engine/graphics3/DecalSystem3.js +441 -441
  1029. package/src/engine/graphics3/FogOfWarSystem3.js +265 -265
  1030. package/src/engine/graphics3/GraphicsEngine3.js +776 -776
  1031. package/src/engine/graphics3/HighlightSystem3.js +241 -241
  1032. package/src/engine/graphics3/LightSystem3.js +211 -211
  1033. package/src/engine/graphics3/MeshSystem3.js +646 -646
  1034. package/src/engine/graphics3/ParticleEmitterSystem3.js +289 -289
  1035. package/src/engine/graphics3/PathDisplaySystem3.js +309 -309
  1036. package/src/engine/graphics3/PickingQuery.js +48 -48
  1037. package/src/engine/graphics3/PickingResult.js +47 -47
  1038. package/src/engine/graphics3/PickingSystem3.js +273 -273
  1039. package/src/engine/graphics3/ShadeCameraAdapter.js +55 -55
  1040. package/src/engine/graphics3/ShadedGeometrySystem3.js +169 -169
  1041. package/src/engine/graphics3/TerrainSystem3.js +566 -566
  1042. package/src/engine/graphics3/TooltipComponentSystem3.js +328 -328
  1043. package/src/engine/graphics3/Trail3DSystem3.js +221 -221
  1044. package/src/engine/graphics3/VolumetricLightMap.d.ts +90 -0
  1045. package/src/engine/graphics3/VolumetricLightMap.d.ts.map +1 -0
  1046. package/src/engine/graphics3/VolumetricLightMap.js +160 -0
  1047. package/src/engine/graphics3/VolumetricLightMapSerializationAdapter.d.ts +32 -0
  1048. package/src/engine/graphics3/VolumetricLightMapSerializationAdapter.d.ts.map +1 -0
  1049. package/src/engine/graphics3/VolumetricLightMapSerializationAdapter.js +67 -0
  1050. package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts +98 -0
  1051. package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts.map +1 -0
  1052. package/src/engine/graphics3/VolumetricLightMapSystem3.js +261 -0
  1053. package/src/engine/graphics3/WaterSystem3.js +293 -293
  1054. package/src/engine/graphics3/animation/ClipPlayback.js +84 -84
  1055. package/src/engine/graphics3/animation/wrap_clip_time.js +23 -23
  1056. package/src/engine/graphics3/animation/write_graph_playback.js +48 -48
  1057. package/src/engine/graphics3/camera_sync_from_transform.js +29 -29
  1058. package/src/engine/graphics3/debug/gizmo_view_from_camera.js +48 -48
  1059. package/src/engine/graphics3/decal/DECAL_CLUSTER.js +61 -61
  1060. package/src/engine/graphics3/decal/DECAL_GPU_RECORD.js +117 -117
  1061. package/src/engine/graphics3/decal/DecalAtlas.js +194 -194
  1062. package/src/engine/graphics3/decal/DecalAtlasFill.js +32 -32
  1063. package/src/engine/graphics3/decal/GPUDecalRenderer.js +360 -360
  1064. package/src/engine/graphics3/decal/chunk_decal_intersects_frustum.js +59 -59
  1065. package/src/engine/graphics3/decal/chunk_decal_sampling_info.js +57 -57
  1066. package/src/engine/graphics3/decal/chunk_decal_surface_frame.js +54 -54
  1067. package/src/engine/graphics3/decal/decal_atlas_border.js +69 -69
  1068. package/src/engine/graphics3/decal/decal_gpu_records.js +120 -120
  1069. package/src/engine/graphics3/decal/decal_material_samplers.js +109 -109
  1070. package/src/engine/graphics3/decal/graph_build_decal_clusters.js +212 -212
  1071. package/src/engine/graphics3/decal/shader_cluster_assign_decals.js +207 -207
  1072. package/src/engine/graphics3/decal/shader_cull_decals_frustum.js +108 -108
  1073. package/src/engine/graphics3/highlight/GPUHighlightRenderer.js +151 -151
  1074. package/src/engine/graphics3/highlight/HIGHLIGHT_OUTLINE.js +23 -23
  1075. package/src/engine/graphics3/highlight/chunk_highlight_lookup.js +53 -53
  1076. package/src/engine/graphics3/highlight/pack_highlight_color.js +44 -44
  1077. package/src/engine/graphics3/highlight/pack_highlight_table.js +74 -74
  1078. package/src/engine/graphics3/highlight/shader_highlight_outline.js +150 -150
  1079. package/src/engine/graphics3/load_cube_environment.js +68 -68
  1080. package/src/engine/graphics3/make_default_environment.js +107 -107
  1081. package/src/engine/graphics3/particles/particle_gpu_records.js +221 -221
  1082. package/src/engine/graphics3/path/build_tube_path_streams.js +98 -98
  1083. package/src/engine/graphics3/path/tube_stream_write_geometry.js +90 -90
  1084. package/src/engine/graphics3/preview/ModelPreviewFraming.js +35 -35
  1085. package/src/engine/graphics3/preview/make_model_preview_scene.js +154 -154
  1086. package/src/engine/graphics3/preview/make_model_thumbnail.js +48 -48
  1087. package/src/engine/graphics3/preview/model_preview_framing.js +70 -70
  1088. package/src/engine/graphics3/shade_bundle_bounds.js +55 -55
  1089. package/src/engine/graphics3/shade_camera_projection_ray.js +77 -77
  1090. package/src/engine/graphics3/shade_node_to_entity_composition.js +84 -84
  1091. package/src/engine/graphics3/terrain/GPUTerrainSplatRenderer.js +481 -481
  1092. package/src/engine/graphics3/terrain/chunk_terrain_grid_overlay.js +61 -61
  1093. package/src/engine/graphics3/terrain/chunk_terrain_splat_mix.js +66 -66
  1094. package/src/engine/graphics3/terrain/pack_terrain_row_table.js +53 -53
  1095. package/src/engine/graphics3/terrain/shader_terrain_splat.js +215 -215
  1096. package/src/engine/graphics3/trail/trail_tube_write_geometry.js +158 -158
  1097. package/src/engine/input/GAMEPAD_API_PROPOSAL.md +717 -717
  1098. package/src/engine/input/analog/apply_radial_dead_zone.js +38 -38
  1099. package/src/engine/input/devices/GamepadDevice.js +466 -466
  1100. package/src/engine/input/devices/ManualInputDevice.js +152 -152
  1101. package/src/engine/input/devices/PointerCoordinates.js +26 -26
  1102. package/src/engine/input/devices/events/GamepadEvents.js +10 -10
  1103. package/src/engine/input/devices/gamepad/GamepadAxes.js +19 -19
  1104. package/src/engine/input/devices/gamepad/GamepadButtons.js +29 -29
  1105. package/src/engine/input/devices/gamepad/GamepadCoordinates.js +17 -17
  1106. package/src/engine/input/devices/gamepad/GamepadDeviceLayout.js +323 -323
  1107. package/src/engine/input/devices/gamepad/GamepadHandle.js +286 -286
  1108. package/src/engine/input/devices/gamepad/GamepadLayout.js +160 -160
  1109. package/src/engine/input/devices/gamepad/GamepadStandardMapping.js +31 -31
  1110. package/src/engine/input/devices/gamepad/GamepadStick.js +169 -169
  1111. package/src/engine/input/devices/gamepad/GamepadStickCalibration.js +119 -119
  1112. package/src/engine/input/devices/gamepad/GamepadStickCalibrator.js +250 -250
  1113. package/src/engine/input/devices/gamepad/gamepad_layout_database.js +144 -144
  1114. package/src/engine/input/devices/gamepad/gamepad_read_hat_axis.js +61 -61
  1115. package/src/engine/input/devices/gamepad/gamepad_write_dpad_vector.js +30 -30
  1116. package/src/engine/input/devices/gamepad/prototypeGamepadTester.js +2124 -2124
  1117. package/src/engine/input/devices/mouse/MouseButtons.js +22 -22
  1118. package/src/engine/input/ecs/components/Input.js +118 -118
  1119. package/src/engine/input/ecs/components/InputController.js +78 -78
  1120. package/src/engine/input/ecs/ism/ISM_DESIGN.md +1523 -1523
  1121. package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +627 -627
  1122. package/src/engine/input/ecs/ism/InputActionEvent.js +40 -40
  1123. package/src/engine/input/ecs/ism/InputBinding.js +71 -71
  1124. package/src/engine/input/ecs/ism/InputCoordinateKind.js +25 -25
  1125. package/src/engine/input/ecs/ism/InputCoordinateSource.js +86 -86
  1126. package/src/engine/input/ecs/ism/InputDirection.js +33 -33
  1127. package/src/engine/input/ecs/ism/InputMapProgram.js +236 -236
  1128. package/src/engine/input/ecs/ism/InputMapSystem.js +976 -976
  1129. package/src/engine/input/ecs/ism/InputOcclusion.js +31 -31
  1130. package/src/engine/input/ecs/ism/InputSource.js +67 -67
  1131. package/src/engine/input/ecs/ism/InputSourceTable.js +107 -107
  1132. package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannel.js +93 -93
  1133. package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannelType.js +11 -11
  1134. package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.js +153 -153
  1135. package/src/engine/input/ecs/ism/coordinate/InputCoordinateSwitches.js +160 -160
  1136. package/src/engine/input/ecs/ism/device/GAMEPAD_INPUT_DEVICE_ID.js +9 -9
  1137. package/src/engine/input/ecs/ism/device/GamepadInputDeviceAdapter.js +88 -88
  1138. package/src/engine/input/ecs/ism/device/InputDeviceAdapter.js +72 -72
  1139. package/src/engine/input/ecs/ism/device/KEYBOARD_INPUT_DEVICE_ID.js +9 -9
  1140. package/src/engine/input/ecs/ism/device/KeyboardInputDeviceAdapter.js +63 -63
  1141. package/src/engine/input/ecs/ism/device/ManualInputDeviceAdapter.js +55 -55
  1142. package/src/engine/input/ecs/ism/device/POINTER_INPUT_DEVICE_ID.js +9 -9
  1143. package/src/engine/input/ecs/ism/device/PointerInputDeviceAdapter.js +160 -160
  1144. package/src/engine/input/ecs/ism/map/InputBinding.js +88 -88
  1145. package/src/engine/input/ecs/ism/map/InputCoordinateBinding.js +102 -102
  1146. package/src/engine/input/ecs/ism/map/InputLayer.js +220 -220
  1147. package/src/engine/input/ecs/ism/map/InputMap.js +331 -331
  1148. package/src/engine/input/ecs/ism/prototypeInputMap.js +309 -309
  1149. package/src/engine/input/ecs/ism/trigger/InputTrigger.js +148 -148
  1150. package/src/engine/input/ecs/ism/trigger/InputTriggerChord.js +173 -173
  1151. package/src/engine/input/ecs/ism/trigger/InputTriggerGamepadButton.js +37 -37
  1152. package/src/engine/input/ecs/ism/trigger/InputTriggerHold.js +176 -176
  1153. package/src/engine/input/ecs/ism/trigger/InputTriggerKey.js +39 -39
  1154. package/src/engine/input/ecs/ism/trigger/InputTriggerMouseButton.js +38 -38
  1155. package/src/engine/input/ecs/ism/trigger/InputTriggerRelease.js +120 -120
  1156. package/src/engine/input/ecs/ism/trigger/InputTriggerSequence.js +315 -315
  1157. package/src/engine/input/ecs/ism/trigger/InputTriggerSwitch.js +105 -105
  1158. package/src/engine/input/ecs/ism/trigger/InputTriggerType.js +17 -17
  1159. package/src/engine/input/ecs/systems/InputControllerSystem.js +253 -253
  1160. package/src/engine/input/ecs/systems/InputSystem.js +162 -162
  1161. package/src/engine/intelligence/behavior/primitive/FailingBehaviorSerializationAdapter.js +25 -25
  1162. package/src/engine/intelligence/behavior/primitive/SucceedingBehaviorSerializationAdapter.js +26 -26
  1163. package/src/engine/intelligence/behavior/util/AsynchronousDelayAction.js +55 -55
  1164. package/src/engine/interpolation/BinaryInterpolationAdapter.js +56 -56
  1165. package/src/engine/interpolation/Interpoland.js +49 -49
  1166. package/src/engine/interpolation/Interpolated.js +149 -149
  1167. package/src/engine/interpolation/PoseInterpolationAdapter.js +61 -61
  1168. package/src/engine/interpolation/TransformPoseSerializationAdapter.js +57 -57
  1169. package/src/engine/interpolation/pose_interpoland.js +27 -27
  1170. package/src/engine/navigation/ecs/path_following/PathFollower.js +222 -222
  1171. package/src/engine/navigation/mesh/NavigationMesh.js +145 -145
  1172. package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +661 -0
  1173. package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +229 -229
  1174. package/src/engine/navigation/mesh/build/clip_soup_against_overhangs.d.ts.map +1 -1
  1175. package/src/engine/navigation/mesh/build/clip_soup_against_overhangs.js +628 -520
  1176. package/src/engine/network/CONGESTION_CONTROL.md +87 -87
  1177. package/src/engine/network/README.md +52 -52
  1178. package/src/engine/network/adapters/QuaternionInterpolationAdapter.js +43 -43
  1179. package/src/engine/network/adapters/TransformInterpolationAdapter.js +84 -84
  1180. package/src/engine/network/adapters/TransformReplicationAdapter.js +87 -87
  1181. package/src/engine/network/adapters/Vector3InterpolationAdapter.js +46 -46
  1182. package/src/engine/network/convertPathToURL.js +2 -2
  1183. package/src/engine/network/core/MalformedPacketError.js +26 -26
  1184. package/src/engine/network/core/quantize/quantize_float.js +73 -73
  1185. package/src/engine/network/core/quantize/quantize_position.js +65 -65
  1186. package/src/engine/network/core/sequence/ack_bitfield.js +77 -77
  1187. package/src/engine/network/core/sequence/seq16.js +69 -69
  1188. package/src/engine/network/core/sequence/seq32.js +73 -73
  1189. package/src/engine/network/ecs/NetworkSystem.js +84 -84
  1190. package/src/engine/network/ecs/components/NetworkIdentity.js +73 -73
  1191. package/src/engine/network/ecs/owner_authorization.js +55 -55
  1192. package/src/engine/network/ecs/serialization/NetworkIdentitySerializationAdapter.js +64 -64
  1193. package/src/engine/network/replication/ScopeFilter.js +71 -71
  1194. package/src/engine/network/sim/ActionLog.js +152 -152
  1195. package/src/engine/network/sim/SimAction.js +273 -273
  1196. package/src/engine/network/sim/Snapshotter.js +222 -222
  1197. package/src/engine/network/state/FrameBufferRing.js +168 -168
  1198. package/src/engine/network/state/InputRing.js +134 -134
  1199. package/src/engine/network/state/PriorityAccumulator.js +180 -180
  1200. package/src/engine/network/time/TimeDilation.js +62 -62
  1201. package/src/engine/network/transport/Channel.js +336 -336
  1202. package/src/engine/network/transport/Transport.js +203 -203
  1203. package/src/engine/network/transport/adapters/NodeUDPTransport.js +186 -186
  1204. package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.js +105 -105
  1205. package/src/engine/network/transport/fragments/fragment_send.js +147 -147
  1206. package/src/engine/network/transport/fragments/packet_size.js +101 -101
  1207. package/src/engine/network/xhr.js +23 -23
  1208. package/src/engine/physics/BULLET_REVIEW.md +945 -945
  1209. package/src/engine/physics/CANNON_REVIEW.md +1300 -1300
  1210. package/src/engine/physics/CONSTRAINT_SOLVER_BENCH_LOG.md +208 -208
  1211. package/src/engine/physics/CONSTRAINT_SOLVER_IMPROVEMENTS_PLAN.md +364 -364
  1212. package/src/engine/physics/INTEPOLATION_SYSTEM_PLAN.md +287 -287
  1213. package/src/engine/physics/JOLT_REVIEW.md +913 -913
  1214. package/src/engine/physics/PLAN.md +1120 -1094
  1215. package/src/engine/physics/RAPIER_REVIEW.md +934 -934
  1216. package/src/engine/physics/REVIEW_001_ACTION_PLAN.md +642 -642
  1217. package/src/engine/physics/REVIEW_002.md +151 -151
  1218. package/src/engine/physics/REVIEW_003.md +166 -166
  1219. package/src/engine/physics/broadphase/compute_fat_world_aabb.js +97 -97
  1220. package/src/engine/physics/constraint/DofMode.js +35 -35
  1221. package/src/engine/physics/constraint/solve_constraints.js +1 -1
  1222. package/src/engine/physics/contact/combine_material.js +35 -35
  1223. package/src/engine/physics/ecs/BodyKind.js +29 -29
  1224. package/src/engine/physics/ecs/Collider.js +177 -177
  1225. package/src/engine/physics/ecs/ColliderFlags.js +15 -15
  1226. package/src/engine/physics/ecs/ColliderSerializationAdapter.js +37 -37
  1227. package/src/engine/physics/ecs/Joint.js +475 -475
  1228. package/src/engine/physics/ecs/JointSerializationAdapter.js +72 -72
  1229. package/src/engine/physics/ecs/PhysicsSystem.d.ts +3 -0
  1230. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  1231. package/src/engine/physics/ecs/PhysicsSystem.js +7 -2
  1232. package/src/engine/physics/ecs/RigidBodySerializationUpgrader_0_1.js +37 -37
  1233. package/src/engine/physics/ecs/SleepState.js +12 -12
  1234. package/src/engine/physics/ecs/find_non_finite_physics_state.js +76 -76
  1235. package/src/engine/physics/fluid/FluidField.js +867 -867
  1236. package/src/engine/physics/fluid/FluidSimulator.js +832 -832
  1237. package/src/engine/physics/fluid/REVIEW_02_PLAN.md +243 -243
  1238. package/src/engine/physics/fluid/SliceVisualiser.js +190 -190
  1239. package/src/engine/physics/fluid/ecs/FluidComponent.js +239 -239
  1240. package/src/engine/physics/fluid/ecs/FluidEffectorsComponent.js +89 -89
  1241. package/src/engine/physics/fluid/ecs/FluidObstacle.js +97 -97
  1242. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  1243. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.js +348 -348
  1244. package/src/engine/physics/fluid/ecs/FluidSystem.js +142 -142
  1245. package/src/engine/physics/fluid/ecs/FluidWorkerCore.js +123 -123
  1246. package/src/engine/physics/fluid/ecs/WorkerFluidSystem.js +395 -395
  1247. package/src/engine/physics/fluid/ecs/fluid.worker.js +12 -12
  1248. package/src/engine/physics/fluid/ecs/fluid_build_world_to_grid.js +32 -32
  1249. package/src/engine/physics/fluid/ecs/fluid_gather_visible_effectors.js +47 -47
  1250. package/src/engine/physics/fluid/ecs/fluid_reanchor_field.js +85 -85
  1251. package/src/engine/physics/fluid/ecs/fluid_sync_effectors_from_transform.js +16 -16
  1252. package/src/engine/physics/fluid/ecs/fluid_worker_protocol.js +111 -111
  1253. package/src/engine/physics/fluid/ecs/makeFluidWorker.js +18 -18
  1254. package/src/engine/physics/fluid/effector/AbstractFluidEffector.js +81 -81
  1255. package/src/engine/physics/fluid/effector/GlobalFluidEffector.js +202 -202
  1256. package/src/engine/physics/fluid/effector/ImpulseFluidEffector.js +257 -257
  1257. package/src/engine/physics/fluid/effector/WakeFluidEffector.js +405 -405
  1258. package/src/engine/physics/fluid/prototype.js +126 -126
  1259. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_scalar.js +143 -143
  1260. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.js +162 -162
  1261. package/src/engine/physics/fluid/solver/v3_grid_advect_sl_velocity.js +107 -107
  1262. package/src/engine/physics/fluid/solver/v3_grid_apply_vorticity_confinement.js +131 -131
  1263. package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js +90 -90
  1264. package/src/engine/physics/fluid/solver/v3_grid_limit8_at_indices.js +38 -38
  1265. package/src/engine/physics/fluid/solver/v3_grid_patch_edges_constant.js +77 -77
  1266. package/src/engine/physics/fluid/solver/v3_grid_patch_edges_uniform.js +113 -113
  1267. package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.js +92 -92
  1268. package/src/engine/physics/fluid/solver/v3_grid_shift_in_place.js +107 -107
  1269. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.js +135 -135
  1270. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js +511 -511
  1271. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +108 -108
  1272. package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.js +317 -317
  1273. package/src/engine/physics/fluid/solver/v3_mac_advect_scalar.js +236 -236
  1274. package/src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.js +217 -217
  1275. package/src/engine/physics/fluid/solver/v3_mac_apply_vorticity_confinement.js +174 -174
  1276. package/src/engine/physics/fluid/solver/v3_mac_clip_trace.js +95 -95
  1277. package/src/engine/physics/fluid/solver/v3_mac_compute_divergence.js +77 -77
  1278. package/src/engine/physics/fluid/solver/v3_mac_compute_face_solid.js +131 -131
  1279. package/src/engine/physics/fluid/solver/v3_mac_subtract_pressure_gradient.js +104 -104
  1280. package/src/engine/physics/inertia/world_inverse_inertia.js +160 -160
  1281. package/src/engine/physics/integration/integrate_position.js +97 -97
  1282. package/src/engine/physics/narrowphase/box_box_manifold.js +681 -681
  1283. package/src/engine/physics/narrowphase/box_triangle_contact.js +906 -906
  1284. package/src/engine/physics/narrowphase/capsule_triangle_contact.js +395 -395
  1285. package/src/engine/physics/narrowphase/compute_penetration.js +369 -369
  1286. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.js +55 -55
  1287. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.js +37 -37
  1288. package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.js +117 -117
  1289. package/src/engine/physics/narrowphase/narrowphase_step.js +1738 -1738
  1290. package/src/engine/physics/narrowphase/reduce_manifold_contacts.js +74 -74
  1291. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +12 -11
  1292. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -1
  1293. package/src/engine/physics/narrowphase/refine_ray_hit.js +228 -36
  1294. package/src/engine/physics/narrowphase/sphere_sphere_contact.js +52 -52
  1295. package/src/engine/physics/narrowphase/sphere_triangle_contact.js +138 -138
  1296. package/src/engine/physics/persistence/solver_caches.js +309 -309
  1297. package/src/engine/physics/queries/overlap_shape.js +187 -187
  1298. package/src/engine/physics/queries/raycast.d.ts +7 -4
  1299. package/src/engine/physics/queries/raycast.d.ts.map +1 -1
  1300. package/src/engine/physics/queries/raycast.js +11 -5
  1301. package/src/engine/physics/queries/shape_cast.js +417 -417
  1302. package/src/engine/physics/solver/apply_impulse_to_body.js +86 -86
  1303. package/src/engine/physics/solver/friction_cone.js +37 -37
  1304. package/src/engine/physics/solver/solve_contacts.js +1 -1
  1305. package/src/engine/platform/WebEnginePlatform.d.ts.map +1 -1
  1306. package/src/engine/platform/WebEnginePlatform.js +1 -2
  1307. package/src/engine/scene/SerializedScene.d.ts +2 -2
  1308. package/src/engine/scene/SerializedScene.d.ts.map +1 -1
  1309. package/src/engine/scene/transitionToScene.js +121 -121
  1310. package/src/engine/simulation/Ticker.d.ts.map +1 -1
  1311. package/src/engine/simulation/Ticker.js +33 -0
  1312. package/src/engine/sound/ecs/audio/LiveEmitterSet.js +345 -345
  1313. package/src/engine/sound/ecs/audio/SpatialAudioIndex.js +140 -140
  1314. package/src/engine/sound/simulation/AcousticSimulator.js +646 -646
  1315. package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +432 -432
  1316. package/src/engine/sound/simulation/PROBE_PLACEMENT.md +250 -250
  1317. package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +322 -322
  1318. package/src/engine/sound/simulation/README.md +277 -277
  1319. package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +261 -261
  1320. package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +121 -121
  1321. package/src/engine/sound/simulation/configureAcousticSimulation.js +48 -48
  1322. package/src/engine/sound/simulation/core/AcousticMedium.js +113 -113
  1323. package/src/engine/sound/simulation/core/AcousticMediumSerializationAdapter.js +27 -27
  1324. package/src/engine/sound/simulation/core/AcousticOccluderIndex.js +312 -312
  1325. package/src/engine/sound/simulation/core/AcousticRegionIndex.js +159 -159
  1326. package/src/engine/sound/simulation/core/AcousticSolution.js +90 -90
  1327. package/src/engine/sound/simulation/core/AcousticSourceState.js +109 -109
  1328. package/src/engine/sound/simulation/core/AcousticVolumeRegion.js +73 -73
  1329. package/src/engine/sound/simulation/core/OcclusionSolver.js +112 -112
  1330. package/src/engine/sound/simulation/core/RayHit.js +50 -50
  1331. package/src/engine/sound/simulation/core/VolumeField.js +149 -149
  1332. package/src/engine/sound/simulation/definition/AcousticDirectivity.js +114 -114
  1333. package/src/engine/sound/simulation/definition/AcousticDirectivitySerializationAdapter.js +30 -30
  1334. package/src/engine/sound/simulation/definition/AcousticMaterial.js +122 -122
  1335. package/src/engine/sound/simulation/definition/AcousticMaterialSerializationAdapter.js +32 -32
  1336. package/src/engine/sound/simulation/ecs/AcousticBody.js +48 -48
  1337. package/src/engine/sound/simulation/ecs/AcousticBodySerializationAdapter.js +34 -34
  1338. package/src/engine/sound/simulation/ecs/AcousticProbeFieldSystem.js +64 -64
  1339. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.js +75 -75
  1340. package/src/engine/sound/simulation/ecs/AcousticVolume.js +74 -74
  1341. package/src/engine/sound/simulation/ecs/AcousticVolumeSerializationAdapter.js +37 -37
  1342. package/src/engine/sound/simulation/ecs/AcousticVolumeSystem.js +66 -66
  1343. package/src/engine/sound/simulation/probe/AcousticProbeField.js +578 -578
  1344. package/src/engine/sound/simulation/probe/AcousticProbeFieldSerializationAdapter.js +60 -60
  1345. package/src/engine/sound/simulation/probe/ProbeFieldPathingSolution.js +64 -64
  1346. package/src/engine/sound/simulation/probe/ProbeVisibilityRecheck.js +132 -132
  1347. package/src/engine/sound/simulation/probe/acoustic_probe_transfer.js +103 -103
  1348. package/src/engine/sound/simulation/probe/acoustic_trace_energy.js +136 -136
  1349. package/src/engine/sound/simulation/probe/bakeProbeField.js +169 -169
  1350. package/src/engine/sound/simulation/probe/bakeProbeReflectors.js +110 -110
  1351. package/src/engine/sound/simulation/probe/bakeReverbBands.js +110 -110
  1352. package/src/engine/sound/simulation/probe/bakeVisibility.js +148 -148
  1353. package/src/engine/sound/simulation/probe/echogram_band_rt60.js +83 -83
  1354. package/src/engine/sound/simulation/probe/probe_delaunay_edges.js +215 -215
  1355. package/src/engine/sound/simulation/probe/probe_densify_portals.js +140 -140
  1356. package/src/engine/sound/simulation/probe/probe_detect_portals.js +196 -196
  1357. package/src/engine/sound/simulation/probe/probe_edge_key.js +38 -38
  1358. package/src/engine/sound/simulation/probe/probe_edge_visible.js +41 -41
  1359. package/src/engine/sound/simulation/probe/probe_place_sdf_cover.js +82 -82
  1360. package/src/engine/sound/simulation/probe/probe_refine_connectivity.js +181 -181
  1361. package/src/engine/sound/simulation/probe/probe_sdf_grid.js +171 -171
  1362. package/src/engine/sound/simulation/probe/probe_visibility_augment.js +146 -146
  1363. package/src/engine/sound/simulation/probe/probe_visibility_bridge_components.js +167 -167
  1364. package/src/engine/sound/simulation/render/FoaProbeReverbRenderer.js +354 -354
  1365. package/src/engine/sound/simulation/render/ProbeReverbRenderer.js +231 -231
  1366. package/src/engine/sound/simulation/render/foaReverbImpulseResponse.js +105 -105
  1367. package/src/engine/sound/simulation/render/reverbImpulseResponse.js +66 -66
  1368. package/src/engine/sound/simulation/render/reverb_band_tails.js +93 -93
  1369. package/src/engine/sound/simulation/serialization/populateAcousticSerializationRegistry.js +25 -25
  1370. package/src/engine/sound/sopra/README.md +643 -643
  1371. package/src/engine/sound/sopra/SopraEngine.js +432 -432
  1372. package/src/engine/sound/sopra/asset/AssetManagerBufferProvider.js +71 -71
  1373. package/src/engine/sound/sopra/asset/BufferProvider.js +29 -29
  1374. package/src/engine/sound/sopra/asset/StubBufferProvider.js +58 -58
  1375. package/src/engine/sound/sopra/definition/BusDefinition.js +142 -142
  1376. package/src/engine/sound/sopra/definition/BusDefinitionSerializationAdapter.js +54 -54
  1377. package/src/engine/sound/sopra/definition/DuckingRule.js +106 -106
  1378. package/src/engine/sound/sopra/definition/DuckingRuleSerializationAdapter.js +31 -31
  1379. package/src/engine/sound/sopra/definition/EventDescription.js +262 -262
  1380. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.js +55 -55
  1381. package/src/engine/sound/sopra/definition/MixerSnapshot.js +83 -83
  1382. package/src/engine/sound/sopra/definition/MixerSnapshotSerializationAdapter.js +39 -39
  1383. package/src/engine/sound/sopra/definition/ParameterDefinition.js +117 -117
  1384. package/src/engine/sound/sopra/definition/ParameterDefinitionSerializationAdapter.js +31 -31
  1385. package/src/engine/sound/sopra/definition/VoiceStealMode.js +18 -18
  1386. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +109 -109
  1387. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.js +185 -185
  1388. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClipSerializationAdapter.js +59 -59
  1389. package/src/engine/sound/sopra/definition/clip/ContainerAudioClip.js +128 -128
  1390. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.js +226 -226
  1391. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClipSerializationAdapter.js +54 -54
  1392. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +191 -191
  1393. package/src/engine/sound/sopra/definition/clip/SampleAudioClipSerializationAdapter.js +39 -39
  1394. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.js +79 -79
  1395. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClipSerializationAdapter.js +42 -42
  1396. package/src/engine/sound/sopra/definition/clip/SilenceAudioClip.js +77 -77
  1397. package/src/engine/sound/sopra/definition/clip/SilenceAudioClipSerializationAdapter.js +27 -27
  1398. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +5 -0
  1399. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -1
  1400. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.js +125 -125
  1401. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClipSerializationAdapter.js +41 -41
  1402. package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.js +24 -24
  1403. package/src/engine/sound/sopra/definition/effect/CompressorEffect.js +120 -120
  1404. package/src/engine/sound/sopra/definition/effect/CompressorEffectSerializationAdapter.js +31 -31
  1405. package/src/engine/sound/sopra/definition/effect/EqEffect.js +128 -128
  1406. package/src/engine/sound/sopra/definition/effect/EqEffectSerializationAdapter.js +29 -29
  1407. package/src/engine/sound/sopra/definition/effect/ReverbEffect.js +113 -113
  1408. package/src/engine/sound/sopra/definition/effect/ReverbEffectSerializationAdapter.js +25 -25
  1409. package/src/engine/sound/sopra/runtime/BusGraph.js +231 -231
  1410. package/src/engine/sound/sopra/runtime/ParameterStore.js +98 -98
  1411. package/src/engine/sound/sopra/runtime/SopraPlaybackContext.js +68 -68
  1412. package/src/engine/sound/sopra/runtime/Voice.js +145 -145
  1413. package/src/engine/sound/sopra/runtime/VoiceManager.js +136 -136
  1414. package/src/engine/sound/sopra/runtime/VoicePool.js +17 -17
  1415. package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.js +42 -42
  1416. package/src/engine/sound/sopra/serialization/sopraJSON.js +99 -99
  1417. package/src/engine/sound/sopra/serialization/sopraSerializationHarness.js +49 -49
  1418. package/src/engine/sound/sopra/util/MockAudioContext.js +224 -224
  1419. package/src/engine/sound/sopra/util/fadeOutAndStop.js +60 -60
  1420. package/src/engine/ui/DraggableAspect.js +115 -115
  1421. package/src/engine/ui/apply_pointer_tilt_3d.js +50 -50
  1422. package/src/generation/COORDINATES.md +54 -54
  1423. package/src/generation/GridGenerationResource.js +24 -24
  1424. package/src/generation/GridTaskGroup.js +214 -214
  1425. package/src/generation/REVIEW_01_ACTION_PLAN.md +628 -628
  1426. package/src/generation/automata/CaveGeneratorCellularAutomata.js +79 -79
  1427. package/src/generation/automata/CellularAutomata.js +22 -22
  1428. package/src/generation/compute_writes_union.js +25 -25
  1429. package/src/generation/filtering/CellFilter.js +117 -117
  1430. package/src/generation/filtering/core/CellFilterBinaryOperation.js +37 -37
  1431. package/src/generation/filtering/core/CellFilterOperationTertiary.js +43 -43
  1432. package/src/generation/filtering/core/CellFilterUnaryOperation.js +29 -29
  1433. package/src/generation/filtering/numeric/process/computeCellFilterSobel.js +36 -36
  1434. package/src/generation/filtering/numeric/process/computeFilterSurfaceNormal.js +42 -42
  1435. package/src/generation/filtering/numeric/util/populateSampler2DFromCellFilter.js +18 -18
  1436. package/src/generation/filtering/numeric/util/write_cell_filter_to_sampler2d.js +47 -47
  1437. package/src/generation/filtering/util/cell_filter_weights_pick.js +54 -54
  1438. package/src/generation/grid/GridData.js +233 -233
  1439. package/src/generation/grid/coords/grid_to_texel.js +10 -10
  1440. package/src/generation/grid/coords/texel_to_grid.js +10 -10
  1441. package/src/generation/grid/generation/GridTaskApplyActionToCells.js +90 -90
  1442. package/src/generation/grid/generation/GridTaskDensityMarkerDistribution.js +388 -388
  1443. package/src/generation/grid/generation/GridTaskExecuteRuleTimes.js +127 -127
  1444. package/src/generation/grid/generation/GridTaskSequence.js +58 -58
  1445. package/src/generation/grid/generation/NoopGridTaskGenerator.js +12 -12
  1446. package/src/generation/grid/generation/discrete/GridTaskActionRuleSet.js +48 -48
  1447. package/src/generation/grid/generation/discrete/GridTaskCellularAutomata.js +146 -146
  1448. package/src/generation/grid/generation/discrete/GridTaskConnectRooms.js +540 -540
  1449. package/src/generation/grid/generation/discrete/layer/GridTaskBuildSourceDistanceMap.js +222 -222
  1450. package/src/generation/grid/generation/grid/GridTaskAddNodesFixed.js +44 -44
  1451. package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +614 -614
  1452. package/src/generation/markers/GridCellActionPlaceMarker.js +244 -244
  1453. package/src/generation/markers/GridCellActionPlaceMarkerGroup.js +86 -86
  1454. package/src/generation/markers/actions/terrain/MarkerNodeActionPaintTerrain.js +266 -266
  1455. package/src/generation/markers/emitter/MarkerNodeEmitterGridCellAction.js +49 -49
  1456. package/src/generation/placement/GridCellActionTransformNearbyMarkers.js +120 -120
  1457. package/src/generation/placement/action/GridCellAction.js +45 -45
  1458. package/src/generation/placement/action/GridCellActionPlaceTags.js +140 -140
  1459. package/src/generation/placement/action/GridCellActionWriteFilterToLayer.js +89 -89
  1460. package/src/generation/placement/action/random/CellActionSelectRandom.js +69 -69
  1461. package/src/generation/placement/action/random/weighted/CellActionSelectWeightedRandom.js +91 -91
  1462. package/src/generation/placement/action/util/GridCellActionSequence.js +61 -61
  1463. package/src/generation/placement/action/util/GridCellDisplacedAction.js +61 -61
  1464. package/src/generation/rules/cell/CellMatcherRemovalPreservesConnectivity.js +183 -183
  1465. package/src/generation/theme/ThemeEngine.js +666 -666
  1466. package/src/shade/RENDERER_CONTRACT.md +568 -568
  1467. package/src/shade/descriptor/texture/format/gpu_texture_format_info.js +267 -267
  1468. package/src/shade/device/ShadeDeviceFailure.js +104 -104
  1469. package/src/shade/device/ShadeDeviceFailureReason.js +41 -41
  1470. package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +255 -255
  1471. package/src/shade/device/mock/REVIEW_2026_08_22.md +527 -527
  1472. package/src/shade/device/mock/SoftwareGPUBindGroup.js +23 -23
  1473. package/src/shade/device/mock/SoftwareGPUBindGroupLayout.js +22 -22
  1474. package/src/shade/device/mock/SoftwareGPUError.js +63 -63
  1475. package/src/shade/device/mock/SoftwareGPUPipeline.js +75 -75
  1476. package/src/shade/device/mock/SoftwareGPUQuerySet.js +113 -113
  1477. package/src/shade/device/mock/SoftwareGPUSampler.js +30 -30
  1478. package/src/shade/device/mock/SoftwareGPUShaderModule.js +32 -32
  1479. package/src/shade/device/mock/SoftwareGPUTextureView.js +22 -22
  1480. package/src/shade/device/mock/SoftwareGPUUncapturedErrorEvent.js +23 -23
  1481. package/src/shade/device/mock/normalize_dynamic_offsets.js +26 -26
  1482. package/src/shade/device/mock/texture_copy.js +287 -287
  1483. package/src/shade/device/timing/GPUTimer.js +285 -285
  1484. package/src/shade/device/timing/GPUTimerArray.js +205 -205
  1485. package/src/shade/playground/add_random_lights.js +49 -49
  1486. package/src/shade/playground/add_random_meshes.js +83 -83
  1487. package/src/shade/playground/animation_prototype.js +508 -508
  1488. package/src/shade/playground/animation_prototype_skinning.js +647 -647
  1489. package/src/shade/playground/animation_prototype_stress.js +765 -765
  1490. package/src/shade/playground/enable_picker.js +59 -59
  1491. package/src/shade/playground/gltf_skinned_prototype.js +560 -560
  1492. package/src/shade/playground/gltf_skinned_stress_prototype.js +387 -387
  1493. package/src/shade/playground/gltf_viewer.js +282 -282
  1494. package/src/shade/playground/load_default_environment.js +19 -19
  1495. package/src/shade/playground/main.js +2727 -2727
  1496. package/src/shade/playground/make_animated_instances.js +117 -117
  1497. package/src/shade/playground/prototype_virtual_texture.js +274 -274
  1498. package/src/shade/playground/prototype_volumetrics_01.js +206 -206
  1499. package/src/shade/playground/sample.js +234 -234
  1500. package/src/shade/playground/sample_load_gltf.js +102 -102
  1501. package/src/shade/playground/spread_node3d_grid.js +19 -19
  1502. package/src/shade/renderer/Renderer.js +2436 -2436
  1503. package/src/shade/renderer/ShadeIndirectLightingMode.js +14 -14
  1504. package/src/shade/renderer/animation/ShadeAnimationChannel.js +414 -414
  1505. package/src/shade/renderer/animation/compute_skinned_mesh_clip_bounding_sphere.js +531 -531
  1506. package/src/shade/renderer/animation/pose/PosePlayback.js +55 -55
  1507. package/src/shade/renderer/animation/pose/pose_evaluate_local.js +179 -179
  1508. package/src/shade/renderer/animation/pose/pose_evaluate_world.js +76 -76
  1509. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +1024 -1024
  1510. package/src/shade/renderer/animation/skinning/SKINNING_BINDING_STRUCT.js +79 -79
  1511. package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.js +56 -56
  1512. package/src/shade/renderer/binding/BindGroupManager.js +282 -282
  1513. package/src/shade/renderer/binding/adapt_cache_capacity.js +25 -25
  1514. package/src/shade/renderer/buffer/GPUBufferAllocator.js +116 -116
  1515. package/src/shade/renderer/buffer/buffer_pool_ops.js +123 -123
  1516. package/src/shade/renderer/buffer/table/GPU_DATABASE_PAGE_SIZE.js +9 -9
  1517. package/src/shade/renderer/buffer/table/GPU_DATABASE_UPLOAD_BUFFER_LIMIT.js +8 -8
  1518. package/src/shade/renderer/buffer/table/PageBufferPool.js +71 -71
  1519. package/src/shade/renderer/buffer/table/PageSlotAllocator.js +95 -95
  1520. package/src/shade/renderer/camera/Camera.js +281 -281
  1521. package/src/shade/renderer/camera/GPUCameraContext.js +233 -233
  1522. package/src/shade/renderer/camera/orbital/README.md +84 -84
  1523. package/src/shade/renderer/deferred/shader_composite_volumetrics.js +75 -75
  1524. package/src/shade/renderer/dynamic/DYNAMIC_MESH_VERTEX.js +25 -25
  1525. package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.js +342 -342
  1526. package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.js +167 -167
  1527. package/src/shade/renderer/dynamic/shader_dynamic_mesh.js +170 -170
  1528. package/src/shade/renderer/extension/FrameContext.js +263 -263
  1529. package/src/shade/renderer/extension/FramePhase.js +104 -104
  1530. package/src/shade/renderer/extension/FrameRecord.js +126 -126
  1531. package/src/shade/renderer/extension/GBufferTextures.js +83 -83
  1532. package/src/shade/renderer/extension/PresentTarget.js +34 -34
  1533. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +918 -918
  1534. package/src/shade/renderer/extension/RenderExtension.js +69 -69
  1535. package/src/shade/renderer/extension/RenderExtensionRegistry.js +319 -319
  1536. package/src/shade/renderer/extension/SceneColor.js +40 -40
  1537. package/src/shade/renderer/extension/ViewTextures.js +78 -78
  1538. package/src/shade/renderer/fow/GPUFogOfWarRenderer.js +182 -182
  1539. package/src/shade/renderer/geometry/Geometry.js +608 -608
  1540. package/src/shade/renderer/geometry/geometry_build_from_meshlet_geometry.js +342 -342
  1541. package/src/shade/renderer/geometry/meshlet_geometry_transfer.js +41 -41
  1542. package/src/shade/renderer/geometry/primitives/make_box_geometry.js +153 -153
  1543. package/src/shade/renderer/geometry/primitives/make_cylinder_geometry.js +240 -240
  1544. package/src/shade/renderer/geometry/primitives/make_plane_geometry.js +145 -145
  1545. package/src/shade/renderer/geometry/primitives/make_polyhedron_geometry.js +327 -327
  1546. package/src/shade/renderer/geometry/primitives/make_torus_geometry.js +107 -107
  1547. package/src/shade/renderer/geometry/primitives/make_torus_knot_geometry.js +165 -165
  1548. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +607 -607
  1549. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +66 -66
  1550. package/src/shade/renderer/geometry/virtual/build/VGeoBuildResult.js +32 -32
  1551. package/src/shade/renderer/geometry/virtual/build/VGeoFaceGraph.js +103 -103
  1552. package/src/shade/renderer/geometry/virtual/build/VGeoGatheredFaces.js +25 -25
  1553. package/src/shade/renderer/geometry/virtual/build/VGeoLevel.js +33 -33
  1554. package/src/shade/renderer/geometry/virtual/build/VGeoLevelOptions.js +62 -62
  1555. package/src/shade/renderer/geometry/virtual/build/VGeoLevelSummary.js +49 -49
  1556. package/src/shade/renderer/geometry/virtual/build/VGeoSubmesh.js +20 -20
  1557. package/src/shade/renderer/geometry/virtual/build/VirtualGeometryMesh.js +214 -214
  1558. package/src/shade/renderer/geometry/virtual/build/gltf/GltfByteSource.js +122 -122
  1559. package/src/shade/renderer/geometry/virtual/build/gltf/GltfCollectReport.js +29 -29
  1560. package/src/shade/renderer/geometry/virtual/build/gltf/GltfDocument.js +210 -210
  1561. package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometry.js +48 -48
  1562. package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometryInstance.js +25 -25
  1563. package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometrySelection.js +28 -28
  1564. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_collect_geometries.js +296 -296
  1565. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.js +166 -166
  1566. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_read_geometry.js +199 -199
  1567. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.js +173 -173
  1568. package/src/shade/renderer/geometry/virtual/build/vgeo_bounding_sphere.js +28 -28
  1569. package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +200 -200
  1570. package/src/shade/renderer/geometry/virtual/build/vgeo_build_levels.js +1786 -1786
  1571. package/src/shade/renderer/geometry/virtual/build/vgeo_build_page_nodes.js +157 -157
  1572. package/src/shade/renderer/geometry/virtual/build/vgeo_build_root_group.js +26 -26
  1573. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_normals.js +75 -75
  1574. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_tangents.js +143 -143
  1575. package/src/shade/renderer/geometry/virtual/build/vgeo_containing_sphere.js +44 -44
  1576. package/src/shade/renderer/geometry/virtual/build/vgeo_extract_submesh.js +41 -41
  1577. package/src/shade/renderer/geometry/virtual/build/vgeo_gather_faces.js +71 -71
  1578. package/src/shade/renderer/geometry/virtual/build/vgeo_graph_components.js +123 -123
  1579. package/src/shade/renderer/geometry/virtual/build/vgeo_narrow_indices.js +26 -26
  1580. package/src/shade/renderer/geometry/virtual/build/vgeo_normalize_source.js +112 -112
  1581. package/src/shade/renderer/geometry/virtual/build/vgeo_partition_graph.js +56 -56
  1582. package/src/shade/renderer/geometry/virtual/build/vgeo_sphere_inflate_to_contain.js +70 -70
  1583. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set.js +187 -187
  1584. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set_packed.js +207 -207
  1585. package/src/shade/renderer/geometry/virtual/format/VGEO_DEFECT.js +18 -18
  1586. package/src/shade/renderer/geometry/virtual/format/VGeoDefect.js +38 -38
  1587. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +64 -64
  1588. package/src/shade/renderer/geometry/virtual/format/attribute/VGEO_ATTRIBUTE.js +13 -13
  1589. package/src/shade/renderer/geometry/virtual/format/attribute/VGEO_ATTRIBUTES.js +50 -50
  1590. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attribute_allocate.js +28 -28
  1591. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attribute_mask.js +23 -23
  1592. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.js +31 -31
  1593. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_vertex.js +32 -32
  1594. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.js +46 -46
  1595. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_like.js +19 -19
  1596. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.js +27 -27
  1597. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_asset_id_preimage.js +62 -62
  1598. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_asset_id.js +13 -13
  1599. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_directory_block_checksum.js +41 -41
  1600. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_frame_checksum.js +41 -41
  1601. package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_header_checksum.js +25 -25
  1602. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.js +20 -20
  1603. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_RECORD_SIZE.js +5 -5
  1604. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CHILD_REF.js +5 -5
  1605. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CLUSTER_TRIANGLE_COUNT.js +5 -5
  1606. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CLUSTER_VERTEX_COUNT.js +5 -5
  1607. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_NO_CHILD.js +5 -5
  1608. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_NO_GROUP.js +5 -5
  1609. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_SAME_PAGE.js +6 -6
  1610. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.js +81 -81
  1611. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryGroup.js +37 -37
  1612. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryPageNode.js +55 -55
  1613. package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_BLOCK_RECORDS.js +5 -5
  1614. package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_FLAG_ROOT_PAGE.js +5 -5
  1615. package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_RECORD_SIZE.js +5 -5
  1616. package/src/shade/renderer/geometry/virtual/format/directory/vgeo_directory_block_count.js +12 -12
  1617. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CHILD_OFFSET.js +11 -11
  1618. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CHILD_TABLE_ENTRY_SIZE.js +5 -5
  1619. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.js +11 -11
  1620. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_RAW.js +5 -5
  1621. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_ALIGNMENT.js +5 -5
  1622. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_HEADER_SIZE.js +5 -5
  1623. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_OFFSET.js +13 -13
  1624. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_PAGE_MAGIC.js +5 -5
  1625. package/src/shade/renderer/geometry/virtual/format/frame/VGeoFrameBlob.js +21 -21
  1626. package/src/shade/renderer/geometry/virtual/format/frame/VGeoFrameExtent.js +59 -59
  1627. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_codec_name.js +27 -27
  1628. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.js +84 -84
  1629. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js +56 -56
  1630. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.js +35 -35
  1631. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_scan_frames.js +74 -74
  1632. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.js +6 -6
  1633. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HASH_ALGO_XXH3.js +5 -5
  1634. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_OFFSET.js +31 -31
  1635. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_SIZE.js +5 -5
  1636. package/src/shade/renderer/geometry/virtual/format/header/VGEO_LEVEL_OFFSET.js +20 -20
  1637. package/src/shade/renderer/geometry/virtual/format/header/VGEO_LEVEL_RECORD_SIZE.js +9 -9
  1638. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAGIC.js +5 -5
  1639. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.js +5 -5
  1640. package/src/shade/renderer/geometry/virtual/format/header/VGEO_PAYLOAD_ENCODING_SHADE_MESHLET_V1.js +5 -5
  1641. package/src/shade/renderer/geometry/virtual/format/header/VGeoLevelRecord.js +41 -41
  1642. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_RECORD_SIZE.js +5 -5
  1643. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_GROUP_CLUSTER_COUNT.js +6 -6
  1644. package/src/shade/renderer/geometry/virtual/format/page/VGEO_METRIC_OFFSET.js +21 -21
  1645. package/src/shade/renderer/geometry/virtual/format/page/VGEO_METRIC_SIZE.js +5 -5
  1646. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_FANOUT.js +9 -9
  1647. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_FLAG_LEAF.js +5 -5
  1648. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_OFFSET.js +10 -10
  1649. package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_RECORD_SIZE.js +5 -5
  1650. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_HEADER_SIZE.js +5 -5
  1651. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.js +16 -16
  1652. package/src/shade/renderer/geometry/virtual/format/payload/VGEO_ENCODING_FLAGS_MASK.js +6 -6
  1653. package/src/shade/renderer/geometry/virtual/format/payload/VGeoEncodedPayload.js +18 -18
  1654. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js +200 -200
  1655. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_payload_attribute_offset.js +39 -39
  1656. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_payload_size.js +20 -20
  1657. package/src/shade/renderer/geometry/virtual/format/prototypeVGEOFormatViewer.js +2063 -2063
  1658. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +1622 -1622
  1659. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +485 -485
  1660. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_empty_gpu_structure.d.ts +23 -0
  1661. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_empty_gpu_structure.d.ts.map +1 -0
  1662. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_empty_gpu_structure.js +34 -0
  1663. package/src/shade/renderer/gpu_primitive/sort/ffx/graph_radix_sort.js +115 -115
  1664. package/src/shade/renderer/gpu_primitive/sort/one_sweep/verify_radix_sort_one_sweep.js +110 -110
  1665. package/src/shade/renderer/light/environment/octahedral_resample_kernel.js +50 -50
  1666. package/src/shade/renderer/light/environment/octahedral_uv_to_direction.js +30 -30
  1667. package/src/shade/renderer/light/environment/resample_cube_to_octahedral.js +157 -157
  1668. package/src/shade/renderer/light/make_sunlight.js +35 -35
  1669. package/src/shade/renderer/light/model/io/light_record_point.js +39 -39
  1670. package/src/shade/renderer/loader/gltf/GLTF_PRIMITIVE_MODE.js +23 -23
  1671. package/src/shade/renderer/loader/gltf/MESHOPT_COMPRESSION_PLAN.md +793 -793
  1672. package/src/shade/renderer/loader/gltf/ext/GltfBufferViewExtension.js +54 -54
  1673. package/src/shade/renderer/loader/gltf/ext/GltfBufferViewExtensionSet.js +109 -109
  1674. package/src/shade/renderer/loader/gltf/ext/gltf_apply_buffer_view_extensions.js +69 -69
  1675. package/src/shade/renderer/loader/gltf/ext/gltf_buffer_view_extensions.js +22 -22
  1676. package/src/shade/renderer/loader/gltf/ext/gltf_geometry_extensions.js +39 -39
  1677. package/src/shade/renderer/loader/gltf/ext/meshopt/MeshoptBufferViewExtension.js +301 -301
  1678. package/src/shade/renderer/loader/gltf/gltf_component_type.js +223 -223
  1679. package/src/shade/renderer/loader/gltf/gltf_node_world_matrices.js +137 -137
  1680. package/src/shade/renderer/loader/gltf/gltf_parse_container.js +241 -241
  1681. package/src/shade/renderer/loader/gltf/gltf_read_accessor.js +294 -294
  1682. package/src/shade/renderer/loader/gltf/gltf_used_image_indices.js +49 -49
  1683. package/src/shade/renderer/loader/gltf/load_gltf.js +1253 -1253
  1684. package/src/shade/renderer/loader/gltf/tiny-gltf.js +646 -646
  1685. package/src/shade/renderer/loader/usd/UsdError.js +33 -33
  1686. package/src/shade/renderer/loader/usd/UsdExtensionRegistry.js +69 -69
  1687. package/src/shade/renderer/loader/usd/load_usd.js +226 -226
  1688. package/src/shade/renderer/loader/usd/parse_usda.js +931 -931
  1689. package/src/shade/renderer/loader/usd/unpack_usdz.js +106 -106
  1690. package/src/shade/renderer/loader/usd/usd_build_scene.js +658 -658
  1691. package/src/shade/renderer/loader/usd/usd_compose_transform.js +258 -258
  1692. package/src/shade/renderer/loader/usd/usd_decode_image.js +384 -384
  1693. package/src/shade/renderer/loader/usd/usd_lz4.js +182 -182
  1694. package/src/shade/renderer/loader/usd/usd_triangulate.js +159 -159
  1695. package/src/shade/renderer/material/ShadeMaterial.d.ts +2 -2
  1696. package/src/shade/renderer/material/ShadeMaterial.js +2 -2
  1697. package/src/shade/renderer/material/standard/fragment_gbuffer.js +334 -334
  1698. package/src/shade/renderer/material/standard/fragment_gbuffer_vt.js +169 -169
  1699. package/src/shade/renderer/material/standard/material_gbuffer_vertex.js +45 -45
  1700. package/src/shade/renderer/material/standard_material_to_struct.js +23 -23
  1701. package/src/shade/renderer/particles/prototypeParticleSystem.js +450 -450
  1702. package/src/shade/renderer/particles/runtime/spawn_request_encoding.js +52 -52
  1703. package/src/shade/renderer/pool/TransientResourcePool.js +392 -392
  1704. package/src/shade/renderer/postprocess/bloom/bloom_chain_resolution.js +29 -29
  1705. package/src/shade/renderer/postprocess/bloom/graph_bloom_downsample.js +109 -109
  1706. package/src/shade/renderer/postprocess/bloom/graph_postprocess_bloom.js +155 -155
  1707. package/src/shade/renderer/postprocess/cas/shader_ffx_rcas.js +114 -114
  1708. package/src/shade/renderer/postprocess/dof/DOF_AUTOFOCUS_PARAMS_STRUCT.js +25 -25
  1709. package/src/shade/renderer/postprocess/dof/DOF_FOCUS_STATE_STRUCT.js +23 -23
  1710. package/src/shade/renderer/postprocess/dof/DOF_PRESETS.js +27 -27
  1711. package/src/shade/renderer/postprocess/dof/chunk_dof_coc_band_diameter.js +41 -41
  1712. package/src/shade/renderer/postprocess/dof/chunk_dof_coc_band_radius.js +32 -32
  1713. package/src/shade/renderer/postprocess/dof/chunk_dof_coc_coefficient.js +26 -26
  1714. package/src/shade/renderer/postprocess/dof/compute_dof_autofocus_params.js +64 -64
  1715. package/src/shade/renderer/postprocess/dof/compute_dof_uniform.js +54 -54
  1716. package/src/shade/renderer/postprocess/dof/gather/DOF2_CONFIG.js +91 -91
  1717. package/src/shade/renderer/postprocess/dof/gather/DOF2_PRESETS.js +9 -9
  1718. package/src/shade/renderer/postprocess/dof/gather/DOF2_SETTINGS_STRUCT.js +66 -66
  1719. package/src/shade/renderer/postprocess/dof/gather/DOF2_TEXTURE_FORMAT.js +22 -22
  1720. package/src/shade/renderer/postprocess/dof/gather/DepthOfFieldU.js +292 -292
  1721. package/src/shade/renderer/postprocess/dof/gather/chunk_dof2_coc_to_near_far.js +25 -25
  1722. package/src/shade/renderer/postprocess/dof/gather/chunk_dof2_gather_weight.js +30 -30
  1723. package/src/shade/renderer/postprocess/dof/gather/compute_dof2_uniform.js +54 -54
  1724. package/src/shade/renderer/postprocess/dof/gather/graph_postprocess_dof2.js +194 -194
  1725. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_fill.js +68 -68
  1726. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_far.js +140 -140
  1727. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_near.js +142 -142
  1728. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_recombine.js +99 -99
  1729. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_setup.js +102 -102
  1730. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_tile_dilate.js +75 -75
  1731. package/src/shade/renderer/postprocess/dof/gather/shader_dof2_tile_max.js +73 -73
  1732. package/src/shade/renderer/postprocess/dof/raymarch/DOF_SETTINGS_STRUCT.js +49 -49
  1733. package/src/shade/renderer/postprocess/dof/raymarch/DepthOfField.js +298 -298
  1734. package/src/shade/renderer/postprocess/dof/raymarch/graph_postprocess_dof.js +244 -244
  1735. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_composite.js +91 -91
  1736. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_firefly_cleanup.js +61 -61
  1737. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_prefilter.js +90 -90
  1738. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch_compute.js +267 -267
  1739. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_slice_compute.js +271 -271
  1740. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_tile_dilate.js +70 -70
  1741. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_tile_max.js +72 -72
  1742. package/src/shade/renderer/postprocess/dof/shader_dof_autofocus.js +99 -99
  1743. package/src/shade/renderer/postprocess/gtao/generate_magic_square_noise.js +51 -51
  1744. package/src/shade/renderer/postprocess/motion_blur/MotionBlur.js +56 -56
  1745. package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +78 -78
  1746. package/src/shade/renderer/postprocess/upscale-dither/UpscaleDither.js +95 -95
  1747. package/src/shade/renderer/postprocess/upscale-dither/fragment_shader_upscale_dither.js +204 -204
  1748. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.js +58 -58
  1749. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +230 -230
  1750. package/src/shade/renderer/rasterize/native/graph_rasterize_meshes_alpha_tested.js +278 -278
  1751. package/src/shade/renderer/rasterize/native/oit/chunk_material_side_should_draw.js +26 -26
  1752. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments_ibl.js +178 -178
  1753. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +235 -235
  1754. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +208 -208
  1755. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +206 -206
  1756. package/src/shade/renderer/scene/DynamicMesh.js +70 -70
  1757. package/src/shade/renderer/scene/DynamicMeshBatch.js +90 -90
  1758. package/src/shade/renderer/scene/GPUSceneContext.js +901 -901
  1759. package/src/shade/renderer/scene/SceneNodeBinding.js +56 -56
  1760. package/src/shade/renderer/scene/SkinnedMesh.js +55 -55
  1761. package/src/shade/renderer/scene/TransformAuthority.js +33 -33
  1762. package/src/shade/renderer/scene/optimization/deduplicate_geometries.js +38 -38
  1763. package/src/shade/renderer/scene/optimization/deduplicate_images.js +93 -93
  1764. package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.d.ts.map +1 -1
  1765. package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.js +12 -3
  1766. package/src/shade/renderer/scene/optimization/geometry/internal/match_refit_validate.js +127 -127
  1767. package/src/shade/renderer/scene/optimization/geometry/internal/match_solve_transform.js +492 -492
  1768. package/src/shade/renderer/scene/optimization/geometry/internal/match_validate_topology.js +3 -3
  1769. package/src/shade/renderer/scene/optimization/geometry/internal/seed_correspondence_from_window.js +3 -3
  1770. package/src/shade/renderer/scene/optimization/optimize_texture_set.js +81 -81
  1771. package/src/shade/renderer/scene/optimization/source_to_sampler2d.js +21 -21
  1772. package/src/shade/renderer/scene/serialization/STANDARD_MATERIAL_SERIALIZED_BYTE_SIZE.js +12 -12
  1773. package/src/shade/renderer/scene/serialization/deserialize_scene.d.ts.map +1 -1
  1774. package/src/shade/renderer/scene/serialization/deserialize_scene.js +0 -2
  1775. package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_transform_point.js +26 -26
  1776. package/src/shade/renderer/shader/chunk/noise/chunk_interleaved_gradient_noise.js +20 -20
  1777. package/src/shade/renderer/shader/chunk/spherical_harmonics/dering/chunk_sh2_dering_optimize_positive.js +61 -61
  1778. package/src/shade/renderer/shader/chunk/texture/chunk_texture_filter_fxaa_lite.js +225 -225
  1779. package/src/shade/renderer/shader/chunk/texture/dither/chunk_dither_color_8bit_gradient_noise.js +25 -25
  1780. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.js +384 -384
  1781. package/src/shade/renderer/shadow/map/PointLightShadowmap.js +105 -105
  1782. package/src/shade/renderer/shadow/map/SpotLightShadowmap.js +63 -63
  1783. package/src/shade/renderer/shadow/map/TetrahedronPointLightShadowmap.js +89 -89
  1784. package/src/shade/renderer/shadow/map/shadowmap_update_score.js +232 -232
  1785. package/src/shade/renderer/texture/GPUTextureAllocator.js +85 -85
  1786. package/src/shade/renderer/texture/texture_pool_ops.js +171 -171
  1787. package/src/shade/renderer/texture/virtual/PageTableQuadAllocator.js +253 -253
  1788. package/src/shade/renderer/texture/virtual/README.md +276 -276
  1789. package/src/shade/renderer/texture/virtual/VTPageTable.js +330 -330
  1790. package/src/shade/renderer/texture/virtual/VTPhysicalCache.js +349 -349
  1791. package/src/shade/renderer/texture/virtual/VT_CONSTANTS.js +154 -154
  1792. package/src/shade/renderer/texture/virtual/VT_STACK_INFO_STRUCT.js +20 -20
  1793. package/src/shade/renderer/texture/virtual/VirtualTextureManager.js +1276 -1276
  1794. package/src/shade/renderer/texture/virtual/VirtualTextureStack.js +106 -106
  1795. package/src/shade/renderer/texture/virtual/build_chunk_vt_lookup.js +150 -150
  1796. package/src/shade/renderer/texture/virtual/build_shader_vt_feedback.js +232 -232
  1797. package/src/shade/renderer/texture/virtual/chunk_vt_request_key.js +28 -28
  1798. package/src/shade/renderer/texture/virtual/source/VTSourceImage.js +207 -207
  1799. package/src/shade/renderer/texture/virtual/source/VTSourceProcedural.js +135 -135
  1800. package/src/shade/renderer/texture/virtual/source/VTSourceTiled.js +132 -132
  1801. package/src/shade/renderer/water/GPUWaterRenderer.js +167 -167
  1802. package/src/shade/renderer/water/shader_water.js +246 -246
  1803. package/src/shade/renderer/water/water_height_texture_from_sampler.js +56 -56
  1804. package/src/shade/vitest.setup.mjs +89 -89
  1805. package/src/shade/wgsl/validate_wgsl_source.js +316 -316
  1806. package/src/view/controller/controls/NumericIntervalControl.js +90 -90
  1807. package/src/view/controller/controls/Vector2Control.js +83 -83
  1808. package/src/view/controller/controls/Vector3Control.js +90 -90
  1809. package/src/view/elements/Group.js +56 -56
  1810. package/src/view/elements/drawCheckers.js +33 -33
  1811. package/src/view/elements/windrose/WindRoseDiagram.js +369 -369
  1812. package/src/view/graphics/WebGLContextFailureView.js +76 -76
  1813. package/src/view/layout/DockPanel.js +51 -51
  1814. package/src/view/layout/SplitView.js +365 -365
  1815. package/src/view/layout/show_popup_menu.js +81 -81
  1816. package/src/view/minimap/MinimapCanvasView.js +411 -411
  1817. package/src/view/minimap/MinimapImageCache.js +88 -88
  1818. package/src/view/minimap/MinimapMarkerCollection.d.ts +0 -1
  1819. package/src/view/minimap/MinimapMarkerCollection.d.ts.map +1 -1
  1820. package/src/view/minimap/MinimapMarkerCollection.js +180 -180
  1821. package/src/view/minimap/dom/MinimapCameraView.js +206 -206
  1822. package/src/view/minimap/minimap_focus_area.js +97 -97
  1823. package/editor/process/symbolic/SocketsSymbolicDisplay.js +0 -192
  1824. package/src/core/geom/3d/topology/struct/binary/io/moved_tests.txt +0 -37
  1825. package/src/engine/ecs/attachment/Attachment.d.ts +0 -40
  1826. package/src/engine/ecs/attachment/Attachment.d.ts.map +0 -1
  1827. package/src/engine/ecs/attachment/Attachment.js +0 -57
  1828. package/src/engine/ecs/attachment/AttachmentBinding.d.ts +0 -28
  1829. package/src/engine/ecs/attachment/AttachmentBinding.d.ts.map +0 -1
  1830. package/src/engine/ecs/attachment/AttachmentBinding.js +0 -41
  1831. package/src/engine/ecs/attachment/AttachmentSystem.d.ts +0 -61
  1832. package/src/engine/ecs/attachment/AttachmentSystem.d.ts.map +0 -1
  1833. package/src/engine/ecs/attachment/AttachmentSystem.js +0 -307
  1834. package/src/engine/ecs/attachment/BoneAttachmentBinding.d.ts +0 -27
  1835. package/src/engine/ecs/attachment/BoneAttachmentBinding.d.ts.map +0 -1
  1836. package/src/engine/ecs/attachment/BoneAttachmentBinding.js +0 -69
  1837. package/src/engine/ecs/attachment/TransformAttachmentBinding.d.ts +0 -4
  1838. package/src/engine/ecs/attachment/TransformAttachmentBinding.d.ts.map +0 -1
  1839. package/src/engine/ecs/attachment/TransformAttachmentBinding.js +0 -51
  1840. package/src/engine/ecs/sockets/AttachmentSocket.d.ts +0 -32
  1841. package/src/engine/ecs/sockets/AttachmentSocket.d.ts.map +0 -1
  1842. package/src/engine/ecs/sockets/AttachmentSocket.js +0 -60
  1843. package/src/engine/ecs/sockets/AttachmentSocketType.d.ts +0 -6
  1844. package/src/engine/ecs/sockets/AttachmentSocketType.d.ts.map +0 -1
  1845. package/src/engine/ecs/sockets/AttachmentSocketType.js +0 -8
  1846. package/src/engine/ecs/sockets/AttachmentSockets.d.ts +0 -57
  1847. package/src/engine/ecs/sockets/AttachmentSockets.d.ts.map +0 -1
  1848. package/src/engine/ecs/sockets/AttachmentSockets.js +0 -130
  1849. package/src/engine/ecs/sockets/BoneAttachmentSocket.d.ts +0 -29
  1850. package/src/engine/ecs/sockets/BoneAttachmentSocket.d.ts.map +0 -1
  1851. package/src/engine/ecs/sockets/BoneAttachmentSocket.js +0 -56
  1852. package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.d.ts +0 -6
  1853. package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.d.ts.map +0 -1
  1854. package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.js +0 -19
  1855. package/src/shade/renderer/animation/_dump_resolve.d.mts +0 -2
  1856. package/src/shade/renderer/animation/_dump_resolve.d.mts.map +0 -1
  1857. package/src/shade/renderer/animation/_dump_resolve.mjs +0 -5
  1858. package/src/shade/renderer/scene/optimization/geometry/internal/bt_face_loop_helpers.d.ts +0 -32
  1859. package/src/shade/renderer/scene/optimization/geometry/internal/bt_face_loop_helpers.d.ts.map +0 -1
  1860. package/src/shade/renderer/scene/optimization/geometry/internal/bt_face_loop_helpers.js +0 -77
  1861. package/src/shade/renderer/scene/optimization/geometry/internal/bt_find_edge_between.d.ts +0 -12
  1862. package/src/shade/renderer/scene/optimization/geometry/internal/bt_find_edge_between.d.ts.map +0 -1
  1863. package/src/shade/renderer/scene/optimization/geometry/internal/bt_find_edge_between.js +0 -35
  1864. package/src/shade/renderer/scene/serialization/linear_to_pq.d.ts +0 -2
  1865. package/src/shade/renderer/scene/serialization/linear_to_pq.d.ts.map +0 -1
  1866. package/src/shade/renderer/scene/serialization/linear_to_pq.js +0 -14
  1867. package/src/shade/renderer/scene/serialization/pq_to_linear.d.ts +0 -9
  1868. package/src/shade/renderer/scene/serialization/pq_to_linear.d.ts.map +0 -1
  1869. package/src/shade/renderer/scene/serialization/pq_to_linear.js +0 -30
@@ -1,920 +1,920 @@
1
- # Correctness review — `engine/ecs`
2
-
3
- Scope: every non-spec source file under `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/`
4
- (208 source files). Reviewed 2026-08-06.
5
-
6
- Findings marked **Confirmed** were either reproduced by executing the code or read directly
7
- off the source with an unambiguous trace. `assert.*` is treated as absent (stripped in prod).
8
-
9
- ---
10
-
11
- ## Core (Entity / EntityComponentDataset / EntityManager / System)
12
-
13
- ### ECS-1 `Entity.readFromDataset` never watches destruction, so a stale `Built` flag corrupts recycled entity ids
14
- - **Severity**: CRITICAL
15
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/Entity.js:508-525`
16
- - **Defect**: `build()` registers `#handleEntityDestroyed` on `EventType.EntityRemoved` when the
17
- `WatchDestruction` flag is set (line 487-489), which is on by default. `readFromDataset` sets
18
- `EntityFlags.Built` and binds the reference but registers no such listener, so the returned
19
- `Entity` keeps reporting `isBuilt === true` forever after the underlying entity is removed.
20
- Since `EntityComponentDataset.createEntity` recycles ids (`entityOccupancy.nextClearBit(0)`),
21
- every subsequent operation on that `Entity` lands on a *different* entity.
22
- - **Failure scenario** (reproduced):
23
- ```js
24
- const ds = new EntityComponentDataset();
25
- ds.setComponentTypeMap([A, B]);
26
- const id = ds.createEntity(); // id = 0
27
- ds.addComponentToEntity(id, new A());
28
- const e = Entity.readFromDataset(id, ds);
29
- ds.removeEntity(id);
30
- const recycled = ds.createEntity(); // id = 0 again, generation 1
31
- e.isBuilt // => true (should be false)
32
- e.add(new B()); // => writes B onto `recycled`, an unrelated entity
33
- e.destroy(); // => destroys `recycled`
34
- ```
35
- Output observed: `recycled entity now has B: true`, `after e.destroy(), recycled entity exists: false`.
36
- The `EntityReference` already carries the generation needed to detect this (`e.generation === 0`
37
- vs `ds.getEntityGeneration(recycled) === 1`) but nothing consults it.
38
- - **Fix**: in `readFromDataset`, mirror `build()` — after setting the `Built` flag, do
39
- `if (r.getFlag(EntityFlags.WatchDestruction)) dataset.addEntityEventListener(entity, EventType.EntityRemoved, r.#handleEntityDestroyed, r);`
40
- (needs a small private accessor since `#handleEntityDestroyed` is instance-private).
41
- - **Confidence**: Confirmed
42
-
43
- ### ECS-2 `setComponentTypeMap` leaves the dataset permanently corrupted when observer rebuild throws
44
- - **Severity**: MAJOR
45
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/EntityComponentDataset.js:674-731`
46
- - **Defect**: `setComponentTypeMap` mutates state in four steps — `updateComponentOccupancy` (716),
47
- `updateComponentStores` (718), `updateObservers` (720), then `componentTypeMap` / `componentTypeCount`
48
- (722, 730). `updateObservers` calls `observer.build(map)` for every observer sitting in a *surviving*
49
- component's store; `EntityObserver.build` **throws** if any of that observer's component types is
50
- absent from the new map (`EntityObserver.js:102-104`). Because occupancy and the component stores
51
- have already been re-strided at that point, the throw leaves the dataset with the new stride but the
52
- old `componentTypeMap` / `componentTypeCount` / `__type_to_index_map`.
53
- - **Failure scenario** (reproduced):
54
- ```js
55
- ds.setComponentTypeMap([A, B]);
56
- ds.addObserver(new EntityObserver([A, B], onComplete, onBroken), false);
57
- ds.unregisterComponentType(A); // A has no live instances, so the removal check passes
58
- ```
59
- Observed: throws `Component type[0] was not found in the supplied map`, then
60
- `componentTypeMap = ['A','B']`, `componentTypeCount = 2`, but `components.length = 1`.
61
- The next `ds.addComponentToEntity(e, new B())` throws
62
- `TypeError: Cannot read properties of undefined (reading '0')` — `components[1]` no longer exists.
63
- The dataset cannot be used again.
64
- - **Fix**: validate first, mutate second. Before touching occupancy/stores, iterate every registered
65
- observer and check `map` contains all of `observer.componentTypes`; throw there (dataset untouched).
66
- Observers that watch only removed types should be disconnected explicitly (`observer.dataset = null`)
67
- rather than silently dropped from the stores.
68
- - **Confidence**: Confirmed
69
-
70
- ### ECS-3 `traverseEntitiesCompactedFiltered` uses `-1` as a component index for unregistered classes
71
- - **Severity**: MAJOR
72
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/EntityComponentDataset.js:1990-2020`
73
- - **Defect**: `componentIndices[i] = this.computeComponentTypeIndex(klass)` stores `-1` for a class
74
- that is not registered in this dataset, and nothing filters it out. The loop then does
75
- `componentOccupancy.get(entityOccupancyAddress + (-1))` — reading the *last* component bit of the
76
- *previous* entity — and, when that bit happens to be set, `this.components[-1][i]`.
77
- `this.components[-1]` is `undefined`. (In production `BitSet.get`'s `assert.isNonNegativeInteger`
78
- is stripped, so it silently returns the neighbouring entity's bit instead of throwing.)
79
- - **Failure scenario** (reproduced): dataset registered with `[A, B]`; entity 0 has both A and B;
80
- entity 1 has A. `ds.traverseEntitiesCompactedFiltered([C, A], visitor)` where `C` was never
81
- registered. With asserts on it throws `bitIndex must be >= 0, instead was -1`. With asserts stripped,
82
- entity 1 reads bit `2 - 1 = 1` (entity 0's B bit, which is set) and then dereferences
83
- `this.components[-1][1]` → `TypeError: Cannot read properties of undefined`.
84
- - **Fix**: skip unregistered classes when building `componentIndices` (build a compacted array of
85
- valid indices only), the same way `traverseEntities` returns early on `-1`.
86
- - **Confidence**: Confirmed
87
-
88
- ### ECS-4 Bare `.sort()` on component indices is lexicographic
89
- - **Severity**: MINOR
90
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/EntityComponentDataset.js:1996`
91
- - **Defect**: `componentIndices.sort()` with no comparator sorts numbers as strings. The comment
92
- says "sort component indices array for faster traversal"; with 10 or more registered component
93
- types the array is not sorted at all, and the `components` array handed to the visitor comes out
94
- in a scrambled, size-dependent order.
95
- - **Failure scenario** (reproduced): 12 registered component types; an entity holds instances of
96
- type index 2 and type index 10. The visitor receives `[K10, K2]` instead of `[K2, K10]`. Any
97
- consumer of this "main utility … to facilitate serialization" method that depends on a stable
98
- component ordering (e.g. a byte-for-byte save comparison or a diff) sees the order flip as soon
99
- as the dataset crosses 10 component types.
100
- - **Fix**: `componentIndices.sort((a, b) => a - b);`
101
- - **Confidence**: Confirmed
102
-
103
- ### ECS-5 `Entity.getComponentSafe` throws `ReferenceError` — its helper is neither imported nor exported
104
- - **Severity**: MINOR
105
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/Entity.js:256`
106
- - **Defect**: The error path calls `computeComponentClassName(klass)`, but `Entity.js` imports only
107
- `assert`, `Signal`, `EntityFlags`, `EntityReference`, `EventType`. Worse,
108
- `computeComponentClassName.js` declares the function without any `export` statement at all, so
109
- there is nothing to import.
110
- - **Failure scenario** (reproduced): `new Entity().getComponentSafe(SomeClass)` throws
111
- `ReferenceError: computeComponentClassName is not defined` instead of the documented
112
- `Error: Component of given class 'SomeClass' not found`. The diagnostic naming the missing
113
- component — the entire point of the method over `getComponent` — is lost.
114
- - **Fix**: add `export` to `computeComponentClassName.js` and import it in `Entity.js`.
115
- - **Confidence**: Confirmed
116
-
117
- ### ECS-6 Re-`build()`ing an `Entity` whose components drifted orphans the old entity and mis-aims its destruction watcher
118
- - **Severity**: MINOR
119
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/Entity.js:442-493`
120
- - **Defect**: `build()` returns early only when `checkExistingComponents` passes. Otherwise it falls
121
- through and calls `dataset.createEntity()` **without** removing the entity it was previously bound
122
- to and without unregistering the `EntityRemoved` listener registered on the old id at line 488.
123
- - **Failure scenario** (reproduced):
124
- ```js
125
- const e = new Entity(); e.add(new A());
126
- const id1 = e.build(ds); // 0
127
- ds.removeComponentFromEntity(id1, A); // a system strips the component directly
128
- const id2 = e.build(ds); // 1 -- entity 0 is now orphaned, entityCount = 2
129
- ds.removeEntity(id1); // fires the stale watcher
130
- e.isBuilt // => false, although entity 1 is alive
131
- e.destroy() // => false; entity 1 leaks permanently
132
- ```
133
- - **Fix**: when the early-return check fails but `EntityFlags.Built` is set, call `this.destroy()`
134
- first so the previous entity and its listener are released before rebinding.
135
- - **Confidence**: Confirmed
136
-
137
- ### ECS-7 `EntityManager.removeSystem` never clears `system.entityManager`
138
- - **Severity**: MINOR
139
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/EntityManager.js:590-631`
140
- - **Defect**: `startSystem` refuses to start a system whose `entityManager` is set to a different
141
- manager (`EntityManager.js:739-741`). Neither `removeSystem` nor `stopSystem` resets the field.
142
- - **Failure scenario**: `em1.removeSystem(s)` then `em2.addSystem(s); em2.startup()` →
143
- `startSystem` throws `System is bound to another EntityManager`, caught at line 763, so
144
- `errorCallback` fires and `em2.state` becomes `EntityManagerState.Failed`; the whole manager
145
- never reaches `Running`. Reusing a system instance across manager instances (level reload, test
146
- fixtures) is impossible.
147
- - **Fix**: set `system.entityManager = null` in `removeSystem` after `stopSystem` resolves.
148
- - **Confidence**: Confirmed
149
-
150
- ### ECS-8 `system_validate_class` duplicate-component backtrace is off by one
151
- - **Severity**: MINOR
152
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/system_validate_class.js:49`
153
- - **Defect**: `for (let j = 0; j < i - 1; j++)` never compares `components_used[i]` against
154
- `components_used[i - 1]`, so adjacent duplicate resource specifications slip through.
155
- - **Failure scenario**: `components_used = [Spec(Transform, Read), Spec(Transform, Write)]` passes
156
- validation. `System.getAccessForComponent` (`System.js:110-117`) `break`s at the first match, so
157
- the `Write` access is silently discarded, and `EntityManager.updateExecutionOrder`'s `scoreSystem`
158
- scores the system as a reader (`attached_edge_count` instead of `× 2`) — the system is scheduled
159
- after the systems that should run after it.
160
- - **Fix**: `for (let j = 0; j < i; j++)`.
161
- - **Confidence**: Confirmed
162
-
163
- ### ECS-48 `EntityManager.startup` reports `Running` while a system added during startup is still `STARTING`
164
- - **Severity**: MINOR
165
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/EntityManager.js:795,814,567-574`
166
- - **Defect**: `startup()` captures `expectedReadyCount = systems.length` before the start loop, then
167
- `finalizeStartup()` fires when `readyCount === expectedReadyCount`. A system's own `startup()` may
168
- call `entityManager.addSystem(...)`, which pushes onto the same `systems` array the `for…of` loop is
169
- walking, so the loop does start the newcomer — but the target count is stale by one. Separately,
170
- `addSystem` only auto-starts when `this.state === EntityManagerState.Running`; during
171
- `EntityManagerState.Starting` it returns `Promise.resolve()` and never calls `startSystem`, so a
172
- system added after the loop has drained is never started at all.
173
- - **Failure scenario**: `SGMeshSystem.startup(em)` does
174
- `if (!em.hasSystem(TransformAttachmentSystem)) await em.addSystem(new TransformAttachmentSystem());`
175
- (`engine/graphics/ecs/mesh-v2/aggregate/SGMeshSystem.js:94-96`). With N systems registered,
176
- `expectedReadyCount = N`; once the original N have reported ready, `finalizeStartup()` sets
177
- `state = Running` and invokes `readyCallback()` while `TransformAttachmentSystem` is still in
178
- `STARTING`. The caller begins `simulate()`; `updateExecutionOrder` skips non-`RUNNING` systems and its
179
- observer has not been added to the dataset, so attachments silently do not update for those frames.
180
- If the same `addSystem` were made from after an `await` (once the start loop has drained) the system
181
- would never start at all, while its promise still resolves.
182
- - **Fix**: read `systems.length` live at each `systemReady` (`readyCount === systems.length`) instead of
183
- a captured count, and treat `EntityManagerState.Starting` like `Running` in `addSystem`'s auto-start
184
- branch.
185
- - **Confidence**: Confirmed
186
-
187
- ---
188
-
189
- ## Fog of war
190
-
191
- ### ECS-9 `FogOfWarSystem.shutdown` removes none of the three render-loop subscriptions it made in `startup`
192
- - **Severity**: MAJOR
193
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/fow/FogOfWarSystem.js:162-219`
194
- - **Defect**: `startup` registers three anonymous closures on `graphics.on.preRender`,
195
- `graphics.on.buffersRendered` and `graphics.on.postRender`. `shutdown` only calls
196
- `this.graphics.layerComposer.remove(this.compositLayer)`. Because the handlers are inline
197
- functions with no retained reference, they can never be removed even by a caller.
198
- - **Failure scenario**: `entityManager.shutdown()` (or `removeSystem(fogOfWarSystem)`) removes the
199
- composit layer, but on the very next frame the `preRender` handler still runs
200
- `compositerLayer.enable()` on the removed layer and `depthFrameBuffer.referenceCount++`, and
201
- `buffersRendered` still runs `fogOfWarRenderer.render(renderer, camera, scene, compositerLayer.renderTarget)`
202
- into a render target that is no longer composited — burning a full-screen pass every frame for the
203
- lifetime of the `GraphicsEngine`. The closures also pin `entityManager`, `graphics` and the
204
- `FogOfWarRenderer` (its `ShaderMaterial`, `Mesh` and `Scene` are never disposed). Starting a second
205
- `FogOfWarSystem` — e.g. a level reload on a shared `GraphicsEngine` — stacks a second set of handlers,
206
- so each frame renders the fog twice.
207
- - **Fix**: hoist the three handlers into named fields (or wrap them in `SignalBinding`s) in `startup`,
208
- `remove` them in `shutdown`, and dispose `fogOfWarRenderer.material` / `fogOfWarRenderer.quad`.
209
- - **Confidence**: Confirmed
210
-
211
- ### ECS-10 A revealer on the *wrong* team still reveals fog whenever its radius changes
212
- - **Severity**: MAJOR
213
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/fow/FogOfWarRevealerSystem.js:52-97`
214
- - **Defect**: The team check lives only in `attemptToWatch`, which gates the *position* watcher.
215
- `update()` itself contains no team test, and `radiusWatcher` (line 93-97) is linked
216
- unconditionally for every linked entity regardless of team.
217
- - **Failure scenario**: `new FogOfWarRevealerSystem(/* team */ 1)`. An enemy unit with
218
- `Team = 2`, `Transform`, `FogOfWarRevealer` links: `attemptToWatch(2)` correctly leaves
219
- `positionWatcher` unlinked, but `radiusWatcher.link()` runs anyway. The enemy then receives a
220
- vision buff (`revealer.radius.set(8)`) — `revealer.radius.onChanged` fires `update()`, which maps
221
- the *enemy's* position to the grid and calls `fow.reveal(...)` on the player's fog. The player's
222
- map is permanently uncovered at the enemy's location.
223
- - **Fix**: put the team check inside `update()` (`if (team.getValue() !== visibilityTeam) return;`),
224
- or link/unlink `radiusWatcher` from `attemptToWatch` alongside `positionWatcher`.
225
- - **Confidence**: Confirmed
226
-
227
- ### ECS-11 `computeRevealedGridBoundingRectangle` returns an inverted AABB when nothing is revealed
228
- - **Severity**: MINOR
229
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/fow/FogOfWar.js:532-563`
230
- - **Defect**: `x0`/`y0` seed to `width - 1` / `height - 1` and `x1`/`y1` to `0`. If no cell differs
231
- from 255 the loop body never runs and those seeds are written out unchanged, producing
232
- `x0 > x1` and `y0 > y1`.
233
- - **Failure scenario**: a freshly resized 64×64 fog (sampler 66×66, all 255) →
234
- `result.set(65, 65, 0, 0)`. `MinimapFogOfWar.updateFocusArea`
235
- (`view/minimap/gl/MinimapFogOfWar.js:87`) then does `bounds.grow(2)`, `bounds.move(-0.5, 0.5)`
236
- and multiplies by the scale, feeding a negative-extent rectangle into `enforceAspectRatio` — the
237
- minimap focus area is garbage on the first frame of any map before the first reveal.
238
- - **Fix**: track a `found` flag and, when nothing is revealed, emit an explicitly empty rectangle
239
- (`result.set(0, 0, 0, 0)`) or leave `result` untouched and return a boolean.
240
- - **Confidence**: Confirmed
241
-
242
- ### ECS-12 `getWorldClearance` reads the distance sampler unclamped
243
- - **Severity**: MINOR
244
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/fow/FogOfWar.js:677-684`
245
- - **Defect**: Its sibling `computeAABBVisibility` clamps the sample position before
246
- `distanceSampler.readChannel` (line 628-629); `getWorldClearance` does not.
247
- `Sampler2D.readChannel` computes `(y * width + x) * itemSize + channel` with no bounds check
248
- (the `assert`s are stripped), so an x outside `[0, width)` silently indexes into an adjacent row.
249
- - **Failure scenario**: `FogOfWarVisibilityPredicate.test(x, y, z)` (line 41) is called with an
250
- object's world position. For a 64×64 fog with `scale = 1`, an object one cell east of the map at
251
- grid row 20 produces `sampleX = 65`, `sampleY = 21`, giving index `21 * 66 + 65 = 1451` — which is
252
- cell `(1, 22)`, the *west* border of the row below. The object's visibility is decided by an
253
- unrelated cell; where that cell is revealed, an off-map object that should be hidden renders.
254
- - **Fix**: clamp exactly as `computeAABBVisibility` does, using the sampler's own dimensions.
255
- - **Confidence**: Confirmed
256
-
257
- ### ECS-13 `computeAABBVisibility` clamps to `size - 1` but the distance sampler is `size + 2` wide
258
- - **Severity**: MINOR
259
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/fow/FogOfWar.js:628-629`
260
- - **Defect**: `worldToSamplePosition` emits sampler-space coordinates with the +1 border offset
261
- baked in, so the real world area occupies sampler columns `[1, size.x]`. The clamp bound is
262
- `this.size.x - 1`, two cells short of the valid maximum.
263
- - **Failure scenario**: 64×64 fog, `scale = 1`. `worldToSamplePosition` maps the world extent onto
264
- sample coordinates `[1, 64]`, but the clamp caps at `63`. An object at the far eastern edge maps to
265
- `sampleX = 64` and is read at column `63` instead — the outermost column/row of the map takes its
266
- clearance from the cell one step inward. An unexplored strip along the east/south map edge therefore
267
- reports the clearance of the explored cell beside it, and objects standing there are drawn through
268
- the fog.
269
- - **Fix**: clamp against the sampler, not the logical size: `clamp(sampleX, 0, this.sampler.width - 1)`.
270
- - **Confidence**: Confirmed
271
-
272
- ---
273
-
274
- ## Terrain
275
-
276
- ### ECS-14 `getTileByWorldPosition2D` reads the vertical axis as the grid row
277
- - **Severity**: CRITICAL
278
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/tiles/TerrainTileManager.js:461-472`
279
- - **Defect**: The point is packed as `Float32Array.of(x, 0, y)` — so the top-down Y lands at index
280
- **2** — inverse-transformed, and then passed as `getRawTileByPosition(v3[0], v3[1])`. `v3[1]` is
281
- the vertical component. Separately, `getRawTileByPosition` divides by `tileSize`, which is in grid
282
- units, while `v3` is in object units, so `this.scale` must be divided out first.
283
- - **Failure scenario**: 64×64 terrain, `gridScale = 1`, tile size 32 → a 2×2 tile grid, identity
284
- transform. `getTileByWorldPosition2D(40, 40)` → `getRawTileByPosition(40, 0)` →
285
- `tileX = 1, tileY = floor(0 / 32) = 0` → tile **(1,0)** instead of **(1,1)**. With an identity
286
- transform the row is *always* 0, so every lookup collapses onto the top row of tiles.
287
- `obtainTileAtWorldPosition2D` then builds the wrong tile.
288
- - **Fix**: `return this.getRawTileByPosition(v3[0] / this.scale.x, v3[2] / this.scale.y);`
289
- - **Confidence**: Confirmed
290
-
291
- ### ECS-15 `traverseVisibleTiles` sums per-frustum hit counts but always writes at offset 0
292
- - **Severity**: MAJOR
293
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/ecs/TerrainSystem.js:265-290`
294
- - **Defect**: `tile_count` is initialised to 0 and passed as the `result_offset` to
295
- `bvh_query_user_data_intersects_frustum` on every iteration but never advanced, so each frustum
296
- overwrites the previous frustum's results from index 0. `found_tiles` meanwhile accumulates the
297
- per-frustum counts, and the consumer loop iterates to that sum.
298
- - **Failure scenario**: two active `Camera` components (`CameraSystem.getActiveFrustums` yields one
299
- frustum per active camera). Frustum A returns 6 tiles, frustum B returns 4 → `tiles` holds 6
300
- entries but `found_tiles = 10`. Indices 6..9 are `undefined` →
301
- `tileManager.tiles[undefined] === undefined` → the callback dereferences `tile.isBuilt` /
302
- `tile.gridPosition` → `TypeError` out of `TerrainSystem.update` every frame.
303
- - **Fix**: `tile_count += bvh_query_user_data_intersects_frustum(tiles, tile_count, ...)` and iterate
304
- to `tile_count`.
305
- - **Confidence**: Confirmed
306
-
307
- ### ECS-16 `TerrainOverlay.paintSampler` passes the overlay instead of its sampler
308
- - **Severity**: MAJOR
309
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/overlay/TerrainOverlay.js:321`
310
- - **Defect**: `sampler2d_paint(this, scaled_source, ...)` supplies the `TerrainOverlay` as the
311
- destination `Sampler2D`. The pixel data lives on `this.sampler`.
312
- - **Failure scenario**: `sampler2d_paint` computes
313
- `_w = Math.min(width, source.width - 0, undefined - dx)` → `NaN`, so `for (x = 0; x < NaN; ...)`
314
- never executes. Nothing is written and no error is raised — `paintImage`/`paintSampler` are silent
315
- no-ops, while `texture.needsUpdate = true` re-uploads unchanged pixels every call.
316
- - **Fix**: `sampler2d_paint(this.sampler, scaled_source, 0, 0, dx, dy, dWidth, dHeight);`
317
- - **Confidence**: Confirmed
318
-
319
- ### ECS-17 Tile rebuild leaks the previous `BufferGeometry`
320
- - **Severity**: MAJOR
321
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/tiles/TerrainTile.js:551,586`
322
- - **Defect**: `build()` assigns `this.geometry = g` / `mesh.geometry = g` without disposing the
323
- geometry it replaces. The only disposal path, `dispose()`, early-returns on `if (!this.isBuilt) return;`
324
- — and `TerrainTileManager.rebuildTilesByUV` (`tiles/TerrainTileManager.js:300`) clears `isBuilt`
325
- while the geometry is still attached, so the old geometry can never be released.
326
- - **Failure scenario**: terrain height editing calls `rebuildTilesByUV`, clearing `isBuilt` on every
327
- overlapping tile; `TerrainSystem.update` then re-invokes `tile.build(tileData)`, installing a fresh
328
- geometry and orphaning the old VBO/IBO. For a 4-tile region at `resolution = 4` with 32×32 tiles
329
- (16 384 verts, ~590 KB of attributes each) that is ~2.4 MB of VRAM per edit, never reclaimed.
330
- `destroyTiles()` hits the same early return.
331
- - **Fix**: dispose the previous geometry at the top of `build()`, and replace the `!isBuilt` early
332
- return in `dispose()` with a `geometry !== null` check.
333
- - **Confidence**: Confirmed
334
-
335
- ### ECS-18 `TerrainTile.stitching` flags are never reset, so re-stitched seams copy instead of average
336
- - **Severity**: MAJOR
337
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/tiles/TerrainTile.js:99-110` (consumed at `:279`, `:333`)
338
- - **Defect**: Neither `build()`, `dispose()`, nor `TerrainTileManager.rebuildTilesByUV` clears the
339
- per-neighbour `stitching` state. `stitchHorizontal`/`stitchVertical` branch on those flags: when one
340
- side is already marked stitched, the other side's normals are *copied* wholesale rather than averaged.
341
- - **Failure scenario**: tiles A(0,0) and B(1,0) built and stitched, so `A.stitching.right === true`.
342
- A height edit clears `isBuilt` on both but leaves the flags. A rebuilds first — `stitchTile` sees B
343
- unbuilt and skips. B rebuilds — `stitchHorizontal(A, B)` finds the stale
344
- `left.stitching.right === true` and takes the copy branch, writing A's one-sided edge normals into B.
345
- The shared edge now carries normals computed from A's triangles only: a visible lighting crease down
346
- every rebuilt tile boundary after any terrain edit.
347
- - **Fix**: reset all eight `stitching` fields in `TerrainTile.build()` and `dispose()`, and clear the
348
- neighbours' matching flags in `rebuildTilesByUV`.
349
- - **Confidence**: Confirmed
350
-
351
- ### ECS-19 `ClingToTerrain.fromJSON` defaults `rotationSpeed` to `Infinity`; the field initialiser is `3`
352
- - **Severity**: MAJOR
353
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/ecs/cling/ClingToTerrain.js:54`
354
- - **Defect**: The class field is `rotationSpeed = 3`, and `ClingToTerrainSerializationUpgrader_0_1.js:22`
355
- explicitly writes `3`, calling it "the default value from the ClingToTerrain class". `fromJSON`
356
- destructures with `Number.POSITIVE_INFINITY` as the default.
357
- - **Failure scenario**: a blueprint carrying `{"ClingToTerrain": {"normalAlign": true}}` with no
358
- `rotationSpeed` → `rotationSpeed = Infinity`. In `ClingToTerrainSystem.processRaycastHit:272`
359
- `Number.isFinite` is false → `angularLimit = Infinity` → `alignToVector` computes
360
- `t = clamp01(Infinity / angle) = 1` → the entity snaps to the terrain normal instantly every frame
361
- instead of easing at 3 rad/s. The same entity constructed in code, or loaded from binary v0 through
362
- the upgrader, eases correctly — the behaviour differs by load path.
363
- - **Fix**: use `3` as the `fromJSON` destructuring default.
364
- - **Confidence**: Confirmed
365
-
366
- ### ECS-20 `Terrain` JSON and binary paths persist disjoint field sets, and disagree on a default
367
- - **Severity**: MAJOR
368
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/ecs/Terrain.js:930,981`;
369
- `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/serialization/TerrainSerializationAdapter.js:29,144`
370
- - **Defect**: Three fields are handled by exactly one of the two paths.
371
- `gridTransformKind` and `lightMapURL` are written into `extra` by `serialize`/`deserialize` but are
372
- absent from `toJSON`/`fromJSON`. `overlay.baseTileImage` is written by `toJSON` (as
373
- `overlayTileImage`) and restored by `fromJSON`, but is absent from the binary adapter. The two paths
374
- also disagree on the `gridTransformKind` default: `deserialize` falls back to
375
- `GridTransformKind.Legacy` (`:144`) while the class field initialiser is `GridTransformKind.Direct`
376
- (`Terrain.js:76`), which `fromJSON` leaves in place.
377
- - **Failure scenario**: a terrain with `gridTransformKind = Legacy`, `size = (64,64)`, `gridScale = 1`,
378
- saved via `toJSON` and reloaded. `fromJSON` leaves it at `Direct`, so `buildGridTransform` produces
379
- `scale = 1, offset = 0.5` instead of `scale = 64/63 ≈ 1.0159, offset = 0`. `mapPointGrid2World(63, 63)`
380
- returns `(63.5, ·, 63.5)` instead of `(64, ·, 64)` — every grid-anchored entity, marker and combat
381
- cell is displaced, growing to more than half a cell at the far edge. The same round-trip drops
382
- `lightMapURL`, so `build()` skips the `aoMap` load and the terrain renders unshadowed. Symmetrically,
383
- a custom `overlay.baseTileImage` saved to binary reloads as the default `WHITE_PIXEL_DATA_URL`.
384
- - **Fix**: add `gridTransformKind` and `lightMapURL` to `toJSON`/`fromJSON` (with the same default the
385
- binary path uses), and write `overlay.baseTileImage` into the binary adapter's `extra` blob.
386
- - **Confidence**: Confirmed
387
-
388
- ### ECS-21 `convertHeightMap2NormalMap` returns normals tilted uphill — both gradients unnegated
389
- - **Severity**: MAJOR
390
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/grid/HeightSampler2NormalSamplerSoft.js:54-56`
391
- - **Defect**: For a height field `h(x, y)` the surface normal is `normalize(-∂h/∂x, -∂h/∂y, k)`. The
392
- Sobel gradients `dX`/`dY` are computed with the correct sign but written out unnegated
393
- (`_x = dX / magnitude`, `_y = dY / magnitude`).
394
- - **Failure scenario**: a ramp `h(x, y) = x`. At an interior texel `dX = 8`, `dY = 0`,
395
- `magnitude = sqrt(64.25) ≈ 8.0156` → the function returns `(+0.998, 0, +0.0624)`. The true normal for
396
- a surface rising in `+x` leans toward `−x`: `(−0.998, 0, +0.0624)`. Every slope gets a normal pointing
397
- up-slope, so any lighting or slope test built on this map is exactly reversed on both horizontal
398
- axes. Flat regions are unaffected, which is why it is not obvious.
399
- - **Fix**: `const _x = -dX / magnitude; const _y = -dY / magnitude;`
400
- - **Confidence**: Likely (no in-repo consumer — public library surface; the maths is unambiguous)
401
-
402
- ### ECS-22 `Terrain.heightTexture` receives the `Sampler2D` object as its `height` argument
403
- - **Severity**: MINOR
404
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/ecs/Terrain.js:145`
405
- - **Defect**: `new DataTexture(this.samplerHeight.data, this.samplerHeight.width, this.samplerHeight, RedFormat, FloatType)`
406
- — the third parameter of `DataTexture(data, width, height, …)` gets the sampler instance rather than
407
- `this.samplerHeight.height`. Compare `graphics/ecs/water/Water.js:138`.
408
- - **Failure scenario**: immediately after `new Terrain()`, `terrain.heightTexture.image` is
409
- `{data: Float32Array(1), width: 1, height: <Sampler2D>}`. Any consumer that binds `heightTexture`
410
- before `build()` runs `updateHeightTexture()` (which repairs `image.width`/`image.height` at
411
- `:721-722`) uploads with a non-numeric height — `gl.texImage2D` raises `GL_INVALID_VALUE`.
412
- - **Fix**: pass `this.samplerHeight.height`.
413
- - **Confidence**: Confirmed
414
-
415
- ### ECS-23 `heightMap2AOMap` leaks the intermediate denoise source texture
416
- - **Severity**: MINOR
417
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/grid/HeightMap2AOMap.js:77`
418
- - **Defect**: `filter_result_to_texture(raw_ao.array, width, height)` creates a `DataTexture` that is
419
- uploaded by the denoise `ImageFilter` pass and then dropped without `.dispose()`. The sibling
420
- `pyramid_texture` *is* disposed at `:91`, so the omission is unintentional.
421
- - **Failure scenario**: `terrain.buildLightMap()` runs per generated map. For a 256×256 terrain at the
422
- default `quality = 4` the AO resolution is 1024×1024, so each bake orphans a 1024×1024 RGBA8 GPU
423
- texture (~4 MB VRAM) for the lifetime of the WebGL context. Each new run accumulates another.
424
- - **Fix**: hold the texture in a local and `.dispose()` it after the `smooth_ao` `ImageFilter` returns.
425
- - **Confidence**: Confirmed
426
-
427
- ### ECS-24 `loadVisibleTerrainTiles` does not reset its counters on retry, and can report success early
428
- - **Severity**: MINOR
429
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/util/loadVisibleTerrainTiles.js:70-84`
430
- - **Defect**: `handleFailedTile` bumps `attempt` and re-runs `init()`, which recomputes `totalTiles` and
431
- restarts `start_time`, but `loadedTiles` and `failedTiles` carry over from the aborted attempt. The
432
- `attemptId !== attempt` guard only suppresses *stale* callbacks; it does not roll back what they
433
- already counted.
434
- - **Failure scenario**: 20 visible tiles; 12 resolve, then one rejects
435
- (`'Original tile was destroyed during build process'`). `init()` re-runs with `totalTiles = 20` and
436
- `loadedTiles = 12`. The 12 already-built tiles resolve on the next microtask → `loadedTiles = 24 ≥ 20`
437
- → `cycleFunction` returns `TaskSignal.EndSuccess` while 8 tiles are still mid-build. The loading
438
- screen closes and the scene shows 8 tiles with no geometry.
439
- - **Fix**: reset `loadedTiles = 0` and `failedTiles = 0` at the top of `init()`.
440
- - **Confidence**: Confirmed
441
-
442
- ### ECS-25 `TerrainSystem.mapPointGrid2World` hardcodes the Legacy transform and drops the offset
443
- - **Severity**: MINOR
444
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/ecs/TerrainSystem.js:81-87,186-187`
445
- - **Defect**: `link()` computes `gridScaleX = g_w * gridScale / (g_w - 1)` unconditionally — the
446
- `GridTransformKind.Legacy` formula — ignoring `component.gridTransformKind`, and
447
- `mapPointGrid2World` applies no offset. `Terrain.mapPointGrid2World` (`Terrain.js:555`) uses
448
- `gridTransform`, whose `Direct` branch (the class default) is `scale = gridScale, offset = gridScale / 2`.
449
- - **Failure scenario**: 64×64 terrain, `gridScale = 1`, default `Direct` kind.
450
- `terrain.mapPointGrid2World(0, 0, v3)` → `(0.5, ·, 0.5)`; `terrainSystem.mapPointGrid2World(0, 0, v3)`
451
- → `(0, ·, 0)`. At `x = 63`: `63.5` vs `64.0`. Two public methods documented to perform the same
452
- mapping differ by half a cell at the origin and diverge with distance. `gridScaleX/Y` are also
453
- system-global, so with two `Terrain` entities they hold whichever linked last.
454
- - **Fix**: delete `TerrainSystem.mapPointGrid2World`/`gridScaleX`/`gridScaleY` and route callers to
455
- `Terrain.mapPointGrid2World`, or read `terrain.gridTransform` directly.
456
- - **Confidence**: Confirmed
457
-
458
- ---
459
-
460
- ## Animation / dynamic actions / evaluation
461
-
462
- ### ECS-26 `AnimationOptimizer` writes its results to itself — optimization is a silent no-op
463
- - **Severity**: MAJOR
464
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/animation/AnimationOptimizer.js:109-114`
465
- - **Defect**: `optimizeTrack(track)` is a port of three.js `KeyframeTrack.prototype.optimize()`, where
466
- `this` *is* the track. Here `this` is the `AnimationOptimizer`, so the compacted arrays are assigned
467
- to `this.times` / `this.values` on the optimizer; the `track` (whose arrays were only *copied* at
468
- lines 108-109) is never modified, and the method returns `this`.
469
- - **Failure scenario**: `GLTFAssetLoader.js:426` calls `animationOptimizer.optimize(animation)` on
470
- every animation of every loaded GLTF, using the module-level singleton. Load a GLTF whose track has
471
- 1000 keyframes of which 900 are redundant: the track still has 1000 keyframes afterwards. The
472
- singleton also permanently retains `this.times`/`this.values` — full copies of the last-optimized
473
- track's typed arrays, megabytes for a skeletal clip — for the module's lifetime.
474
- - **Fix**: write back to the track (`track.times = ...; track.values = ...;`) and return `track`.
475
- - **Confidence**: Confirmed
476
-
477
- ### ECS-27 `AnimationOptimizer`: `time[0]` should be `times[0]`
478
- - **Severity**: MINOR
479
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/animation/AnimationOptimizer.js:36`
480
- - **Defect**: `if (time !== timeNext && (i !== 1 || time !== time[0]))` — `time` is a number, so
481
- `time[0]` is `undefined` and the sub-condition is always true. three.js has `times[0]`.
482
- - **Failure scenario**: a track with `times = [0, 0, 1, 2]` and distinct values at index 1. three.js
483
- drops keyframe 1 as a duplicate of the keyframe at t=0; this code keeps it, leaving two keyframes
484
- scheduled at t=0 in the "optimized" output.
485
- - **Fix**: `time !== times[0]`.
486
- - **Confidence**: Confirmed
487
-
488
- ### ECS-28 `DynamicRuleDescription.equals` calls `.equals` on a primitive string
489
- - **Severity**: MAJOR
490
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescription.js:55`
491
- - **Defect**: `id` is a primitive string (`UUID.string()` returns `UUID.v4().toString()`, and
492
- `fromJSON` assigns the JSON string directly), but `equals` does `return this.id.equals(other.id)`.
493
- `String.prototype.equals` does not exist.
494
- - **Failure scenario**:
495
- `DynamicRuleDescription.fromJSON({id:"combat.long-idle.ai", ...}).equals(otherRule)` →
496
- `TypeError: this.id.equals is not a function`. Any container relying on the `equals` protocol
497
- (a default-configured `HashMap`/`HashSet` keyed by rules) throws on the first lookup.
498
- `DynamicActorSystem` only escapes because `__global_last_used_times` supplies a custom
499
- `keyEqualityFunction`.
500
- - **Fix**: `return this.id === other.id;`
501
- - **Confidence**: Confirmed
502
-
503
- ### ECS-29 Pooled `DynamicActorSystem` context keeps the previous entity's `RuleExecution`
504
- - **Severity**: MAJOR
505
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/dynamic_actions/DynamicActorSystem.js:43,74-90`
506
- - **Defect**: `Context` instances are pooled by `AbstractContextSystem`, and the pool's reset callback
507
- is an empty `// TODO` (`system/AbstractContextSystem.js:37-42`). `Context.link()` resets
508
- `next_idle_event_time` but never resets `execution`, so a recycled context arrives carrying the
509
- previous entity's `execution.rule` and `execution.executor`. `Context.unlink()` also never terminates
510
- the still-running executor.
511
- - **Failure scenario**: unit A executes a `SpeakLine` rule, whose executor tree is
512
- `ParallelBehavior([WaitForEventBehavior(FinishedSpeakingLine on A), SendEventBehavior])`. A is killed
513
- and removed; `removeEntity` deletes A's event listeners, so `FinishedSpeakingLine` can never arrive,
514
- the parallel never completes, `DieBehavior` never runs, and the executor `Entity` keeps
515
- `EntityFlags.Built` forever. A's context is released to the pool still pointing at that immortal
516
- executor. Unit B spawns and pops the same context. On B's first event, `attemptRuleExecution`
517
- (lines 193-199) sees `execution.executor.getFlag(Built) === true` and tests
518
- `rule.priority <= execution.rule.priority`. Every rule in
519
- `app/data/database/dynamic-actions/data.json` uses the default `priority = 0`, so `0 <= 0` →
520
- `return false`: **B never executes any rule for the rest of the session.** If a higher-priority rule
521
- ever does get through, `terminateActiveExecution` destroys entity A's executor from B's context.
522
- - **Fix**: reset execution state at the context boundary — in `Context.link()` set
523
- `this.execution.rule = null; this.execution.executor = null;`, and in `Context.unlink()` destroy a
524
- still-built executor before clearing.
525
- - **Confidence**: Confirmed
526
-
527
- ### ECS-30 `match()` hands actions a live view of a shared scope stack that it unwinds before the action runs
528
- - **Severity**: MAJOR
529
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/dynamic_actions/DynamicActorSystem.js:536-551`;
530
- `.../actions/definition/WriteToBlackboardActionDescription.js:24-35`
531
- - **Defect**: `match()` passes `this.scope.proxy` — a proxy over the system-wide, mutable `DataScope` —
532
- as the action `context`, then calls `this.scope.unwind(top)` on line 551, dropping every frame it
533
- just pushed (blackboards, `{now, entity}`, the event frame, the entity proxy scope).
534
- `WriteToBlackboardActionDescription` captures that proxy in an `ActionBehavior` closure and only
535
- calls `expression.evaluate(context)` when the behavior ticks. `BehaviorSystem.link()` only calls
536
- `behavior.initialize()`; behaviors are first ticked in `BehaviorSystem.update()`, i.e. no earlier
537
- than the next frame.
538
- - **Failure scenario**: a rule with `"action": {"type":"Write", "name":"last_bark_time", "value":"now"}`.
539
- `match()` pushes `{now: 12.5, entity}`, matches, builds the executor, then unwinds. Next frame the
540
- `ActionBehavior` ticks and evaluates `now` against an empty scope → `undefined`, so
541
- `blackboard.acquire("last_bark_time", …).set(undefined)` instead of `12.5`. With two actors handling
542
- events in the same frame the deferred read can also resolve against the *other* actor's frames if
543
- those are still on the stack.
544
- - **Fix**: snapshot the scope before handing it to an action — materialise the proxy into a plain
545
- object in `executeRule` (as `SendRequestActionDescription` already does eagerly for
546
- `action_context`), or have `WriteToBlackboardActionDescription.execute` evaluate the expression
547
- eagerly and defer only the blackboard write.
548
- - **Confidence**: Confirmed
549
-
550
- ### ECS-31 `PointFitnessGaussianBlur` serializes a `quality` field that is never stored
551
- - **Severity**: MAJOR
552
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/evaluation/json/serializeToJSON_PointFitnessFunction.js:61`
553
- - **Defect**: The serializer writes `quality: value.quality`, but `PointFitnessGaussianBlur` never
554
- stores it — `from()` consumes it only to compute `samples_x/y/z = size * quality` and discards it.
555
- The deserializer (`deserializeFromJSON_PointFitnessFunction.js:45,51`) destructures `quality` with no
556
- default and forwards it to `from()`, whose own default `quality = 5` then applies.
557
- - **Failure scenario**: `PointFitnessGaussianBlur.from(src, 4, 4, 4, 1)` → `samples = 4,4,4`
558
- (64 source evaluations per point). Serialize → `{size_x:4, size_y:4, size_z:4, quality: undefined}`
559
- (and `undefined` is dropped entirely by `JSON.stringify`). Deserialize → `quality = 5` →
560
- `samples = 20,20,20`, i.e. 8000 source evaluations per point and a numerically different blur.
561
- - **Fix**: store the value in `from()` (`r.quality = quality;`) with a matching class field, or derive
562
- it on serialize as `value.samples_x / value.size_x`.
563
- - **Confidence**: Confirmed
564
-
565
- ### ECS-32 `PointFitnessGaussianBlur` kernel is offset by half a sample on every axis
566
- - **Severity**: MINOR
567
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/evaluation/complex/PointFitnessGaussianBlur.js:67-69,80,89,99`
568
- - **Defect**: `half_samples_x = samplesX / 2` with `local_x = ix - half_samples_x` gives taps at
569
- `-n/2 … n/2 - 1`, which is not centred on the sample point. A centred kernel needs `(samplesX - 1) / 2`.
570
- - **Failure scenario**: `samples_x = 5, size_x = 10`. Taps land at
571
- `local_x ∈ {-2.5,-1.5,-0.5,0.5,1.5}` → spatial offsets `{-5,-3,-1,1,3}` instead of
572
- `{-5,-2.5,0,2.5,5}`. Both the gaussian weights and the sample positions are biased by half a tap, so
573
- blurring a step edge shifts it by 1 world unit in −x on each axis, and the point being blurred is
574
- never itself sampled.
575
- - **Fix**: `const half_samples_x = (samplesX - 1) / 2;` (and likewise for y, z).
576
- - **Confidence**: Confirmed
577
-
578
- ### ECS-33 `Animation.flags` is in neither serialization path, so a paused animation resumes after load
579
- - **Severity**: MINOR
580
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/animation/Animation.js:40,153-158`;
581
- `.../animation/AnimationSerializationAdapter.js:15-28`
582
- - **Defect**: `flags` (carrying `Playing` and `MeshSizeCulling`) is written by neither `toJSON` nor
583
- `serialize`, and the field initialiser unconditionally sets `Playing | MeshSizeCulling`.
584
- `equals()`/`hash()` also ignore `flags` while including `debtTime`.
585
- - **Failure scenario**: `animation.isPlaying = false` (a supported setter, covered by
586
- `Animation.spec.js:58-62`), save via `BinaryBufferSerializer`, reload → `flags` is back to the
587
- constructor default and the animation plays again. Two `Animation` components differing only in
588
- `isPlaying` also report `equals() === true` and identical `hash()`.
589
- - **Fix**: add `flags` to the binary adapter (bumping `version`) and to `toJSON`/`fromJSON`
590
- (defaulting to `MeshSizeCulling | Playing`), and include it in `equals`/`hash`.
591
- - **Confidence**: Confirmed
592
-
593
- ### ECS-34 `AnimationSystem` re-subscribes to clip signals on every mesh change and never unsubscribes
594
- - **Severity**: MINOR
595
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/systems/AnimationSystem.js:116-117,150,221-230`
596
- - **Defect**: `updateAnimationState` unconditionally does `clips.on.added.add(initAnimationClip)` and
597
- `clips.on.removed.add(...)` with fresh closures each time. It runs from `registerAnimation` (line 150),
598
- which runs both from `link()` and from every `MeshEvents.DataSet` event. `unlink()` removes only the
599
- `MeshEvents.DataSet` entity listener; the clip-signal handlers are never removed, and `Signal.add`
600
- does not deduplicate.
601
- - **Failure scenario**: `MeshSystem.process` sets a placeholder mesh (DataSet #1) then the real asset
602
- (DataSet #2) for every async-loaded mesh with `__use_placeholder_mesh` on — together with `link()`
603
- that is 3 registrations. Every later runtime mesh swap adds another. After N mesh sets, a single
604
- `animation.clips.add(clip)` invokes `startClip` N times, and the handler list grows without bound for
605
- the component's life; unlink/relink cycles accumulate across cycles too.
606
- - **Fix**: keep references to the two handlers on the component, remove any previous pair at the top of
607
- `updateAnimationState`, and remove them in `unlink()`.
608
- - **Confidence**: Confirmed
609
-
610
- ### ECS-35 `SerializationMetadata` binary adapter truncates a documented 32-bit flag field to 8 bits
611
- - **Severity**: MINOR
612
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/components/SerializationMetadataSerializationAdapter.js:15,24`
613
- - **Defect**: `flags` is documented as a "32 bit bitfield" (`SerializationMetadata.js:19-22`) and
614
- `toJSON` writes the full number, but the binary adapter uses `writeUint8`/`readUint8`.
615
- - **Failure scenario**: a consumer defines a ninth flag `Foo = 1 << 8`. `metadata.setFlag(Foo)` →
616
- `flags === 256`. `toJSON()` preserves `256`; the binary path writes `256 & 0xFF === 0` and reads back
617
- `flags === 0`, silently losing the flag on save/load.
618
- - **Fix**: `writeUint32`/`readUint32`, bumping the adapter `version` and adding an upgrader.
619
- - **Confidence**: Confirmed
620
-
621
- ---
622
-
623
- ## Storage / serialization
624
-
625
- ### ECS-36 Schema back-reference is looked up by address in the schema-*object* map, then dereferenced
626
- - **Severity**: MAJOR
627
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter2.js:416`
628
- - **Defect**: `#deserializeObject` resolves a `HEADER_TAG_REF_SCHEMA` record with
629
- `this.#schema_table.getRecord(schema_address)`. `getRecord(schema)` looks up `#schema_map`, which is
630
- keyed by the **schema object** (`#schema_map.set(record.schema, record)`); the address-keyed map is
631
- `#address_map`, reachable only via `getSchemaAt(address)`. Passing a number can never hit, so
632
- `record` is `undefined` and line 419 (`record.typeName`) throws. The whole `HEADER_TAG_REF_SCHEMA`
633
- decode branch can only crash.
634
- - **Failure scenario**: the `schema` parameter of `serialize(buffer, object, schema)` is a documented
635
- optional argument. `const S = {v: undefined}; a2.serialize(buf, {v:1}, S); a2.serialize(buf, {v:2}, S);`
636
- — the first object writes `HEADER_TAG_INLINE_SCHEMA` and interns `S`; the second takes the `else`
637
- branch at line 375 and writes `HEADER_TAG_REF_SCHEMA` + address. On read, the first decodes fine, the
638
- second throws `TypeError: Cannot read properties of undefined (reading 'typeName')`. Same for a single
639
- object whose explicit schema reuses one sub-schema object for two fields.
640
- - **Fix**: `record = this.#schema_table.getSchemaAt(schema_address);`. Also reset `#schema_table` when
641
- switching an instance between writing and reading — write-side records are registered at write-buffer
642
- positions and never cleared, so they can shadow read-side records at the same address.
643
- - **Confidence**: Confirmed
644
-
645
- ### ECS-37 Per-component transient flag is ignored whenever the dataset has no `SerializationMetadata` type
646
- - **Severity**: MAJOR
647
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/storage/BinaryBufferSerializer.js:26-34`
648
- - **Defect**: `isEntityTransient` returns `false` on `smComponentIndex === -1` **before** checking
649
- `componentInstance[COMPONENT_SERIALIZATION_TRANSIENT_FIELD]`. The two mechanisms are independent —
650
- the magic field is a per-instance opt-out that does not require a `SerializationMetadata` component
651
- to exist — but it is gated behind one.
652
- - **Failure scenario**: `EntityComponentDataset.computeComponentTypeIndex` returns `-1` for an
653
- unregistered type, and a type is only registered once some entity actually receives one. In a dataset
654
- where nothing ever adds a `SerializationMetadata`, `smComponentIndex === -1`. Mark any component
655
- transient the way the game does (`guiElement[COMPONENT_SERIALIZATION_TRANSIENT_FIELD] = true` in
656
- `setupStrategyHeadsUpDisplays.js:98`, `sockets[...] = true` in `CombatUnitEntityDecorator.js:660`,
657
- `this.__transform2GridPosition[...] = true` in `CharacterMoveAsynchronousAction.js:236`) and it is
658
- written to the save anyway, then resurrected on load as a duplicate of a runtime-only component.
659
- - **Fix**: move the `COMPONENT_SERIALIZATION_TRANSIENT_FIELD` check above the `smComponentIndex === -1`
660
- early return.
661
- - **Confidence**: Confirmed
662
-
663
- ### ECS-38 Object adapter resolves class identity through *inherited* `typeName`
664
- - **Severity**: MAJOR
665
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter.js:59-61`
666
- - **Defect**: `const typeName = Klass.typeName;` is a normal static lookup, so an ES class that does not
667
- declare its own `typeName` silently resolves to its ancestor's. The object-v2 writer explicitly
668
- guards this with `Object.prototype.hasOwnProperty.call(ctor, "typeName")`
669
- (`BinaryObjectSerializer.js:270`) and has a dedicated regression test, so the v1 adapter is the
670
- un-fixed half.
671
- - **Failure scenario**: `BehaviorComponentSerializationAdapter.serialize`
672
- (`engine/intelligence/behavior/ecs/BehaviorComponentSerializationAdapter.js:38`) routes every behavior
673
- through `this.objectAdapter.serialize(buffer, behavior)`. `Behavior.js:78` sets
674
- `Behavior.typeName = "Behavior"`, while `SelectorBehavior`, `WeightedRandomBehavior`, `BranchBehavior`
675
- and `ConditionalBehavior` declare none. Saving an entity whose behavior tree contains a
676
- `SelectorBehavior` therefore looks up adapter `"Behavior"` — not registered — and the save aborts with
677
- `No adapter found for class 'Behavior'`, naming a class that is not in the tree. If any ancestor *does*
678
- have a registered adapter the failure is worse: the subclass is written with the ancestor's payload
679
- and read back as the ancestor, dropping all subclass state.
680
- - **Fix**: require an own `typeName` —
681
- `Object.prototype.hasOwnProperty.call(Klass, "typeName") ? Klass.typeName : undefined` — and throw
682
- naming the actual constructor when absent, mirroring `BinaryObjectSerializer.writeValue`.
683
- - **Confidence**: Confirmed
684
-
685
- ### ECS-39 The last component type in a stream is never finalized on load
686
- - **Severity**: MINOR
687
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/storage/BinaryBufferDeSerializer.js:117-127`
688
- - **Defect**: `cycleFunction` finalizes the *previous* type at the top of the next loop iteration
689
- (`if (typesDecoded > 0) collectionDeSerializer.finalize();`), but the `typesLeft === 0` exit at
690
- line 117 returns `TaskSignal.EndSuccess` first. So `BinaryCollectionDeSerializer.finalize()` — and
691
- hence `adapter.finalize()` — never runs for the final type. The write path
692
- (`BinaryBufferSerializer.js:194`) finalizes every type correctly.
693
- - **Failure scenario**: load a buffer containing types `[Name, CombatUnit]`. `CombatUnit` elements are
694
- read to exhaustion, `typesLeft` is `0`, the task ends, and
695
- `CombatUnitSerializationAdapter.finalize()`
696
- (`model/game/ecs/component/unit/serialization/CombatUnitSerializationAdapter.js:51`) is never invoked,
697
- so its module-static `serialization_array` is left holding stale entries instead of being spliced
698
- empty. The deserializer is also left in `State.Ready`, so a subsequent `initialize()` on the same
699
- instance throws `IllegalStateException`. Which adapter is affected depends on which component type
700
- happens to be last, so the leak is non-deterministic across saves.
701
- - **Fix**: finalize the in-flight collection before returning `EndSuccess`, guarding against
702
- double-finalize on a re-entered cycle.
703
- - **Confidence**: Confirmed
704
-
705
- ---
706
-
707
- ## GUI / HUD / speaker
708
-
709
- ### ECS-40 `LineSetDescription.collect` walks the root's children instead of the popped node's — infinite loop
710
- - **Severity**: CRITICAL
711
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/speaker/lines/sets/LineSetDescription.js:56`
712
- - **Defect**: Inside the traversal loop the popped node is `set`, but the child-set expansion reads
713
- `const sets = this.sets;` — the *root's* children — on every iteration. The root's children are
714
- therefore re-pushed forever and `pointer` never drains.
715
- - **Failure scenario** (reproduced): a `voice-line-sets` entry with a non-empty `sets` array
716
- (schema-supported: `LineSetDescriptionTable.linkOne` reads `sets` from JSON, and `validateOne` exists
717
- purely to cycle-check that graph). Root `R{lines:[l-root], sets:[C]}`, `C{lines:[l-child]}` →
718
- `collect` writes `l-root, l-child, l-child, l-child, …` without bound; `destination` grows until OOM.
719
- `VoiceSystem.sayLineFromSet` calls `set.collect(temp_lines, 0)`, so the tab hangs on the first nested
720
- bark set anyone authors.
721
- - **Fix**: `const sets = set.sets;`
722
- - **Confidence**: Confirmed
723
-
724
- ### ECS-41 `GUIElementSystem.unlink` calls `Signal.remove` without the context it subscribed with
725
- - **Severity**: MAJOR
726
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/gui/GUIElementSystem.js:261` (subscribe at `:248`)
727
- - **Defect**: `link` does `component.visible.onChanged.add(handleComponentVisibilityChange, {el, system, entity})`;
728
- `unlink` does `component.visible.onChanged.remove(handleComponentVisibilityChange)` with no `thisArg`.
729
- `Signal.remove` matches on `handle === h && context === thisArg`, so it matches nothing and returns
730
- `false`.
731
- - **Failure scenario** (reproduced against the real `Signal`): `add(handler, ctx)` then `remove(handler)`
732
- → returns `false`, `hasHandlers()` stays `true`, the next `send1` still runs the handler. Concretely,
733
- `ecd.removeComponentFromEntity(entity, GUIElement)` (real caller:
734
- `model/game/scenes/combat/UnitStateManager.js:437`) or `Entity.destroy()` → `Entity.build()` leaves
735
- the old subscription in place, and the next `link` adds a second. With two handlers,
736
- `component.visible.set(true)` runs `attachComponent` twice → `parent.addChild(component.view)` twice
737
- → `View.addChild` (`view/View.js:365`) pushes the same child into `parent.children` twice (the
738
- `isLinked` assert that would catch it is stripped in production). `removeChild` then drops only the
739
- first entry, so `parent.children` keeps a stale entry whose `el` is no longer in the DOM; a later
740
- `removeAllChildren` calls `this.el.removeChild(child.el)` on a non-child and throws `NotFoundError`.
741
- - **Fix**: store the context object (as `ViewportPositionSystem` does in `this.data`) and pass it to
742
- `remove`.
743
- - **Confidence**: Confirmed
744
-
745
- ### ECS-42 `ViewportPositionSystem.unlink` never unsubscribes from `el.view.size.onChanged`
746
- - **Severity**: MAJOR
747
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/gui/position/ViewportPositionSystem.js:154` (unlink at `:167-183`)
748
- - **Defect**: `link` makes five `onChanged.add(updatePosition, eventContext)` subscriptions —
749
- `vp.position`, `vp.offset`, `vp.anchor`, `vp.enabled` and `el.view.size`. `unlink` removes only the
750
- first four.
751
- - **Failure scenario**: `GUIEngine.createModal` (`meep/src/engine/ui/GUIEngine.js:215`) does
752
- `window.removeComponent(ViewportPosition)` immediately after `openWindow` built the entity,
753
- deliberately freeing the modal from viewport positioning while the `GUIElement` and its
754
- `ui-modal-window-container` view stay live on screen. `unlink` runs, but `updatePosition` stays bound
755
- to that container's `size.onChanged` with a context holding the now-detached `vp`. Any later size
756
- change re-invokes `positionComponent(el, vp)`, overwriting `view.position` from the stale
757
- `vp.position`, overwriting `view.transformOrigin` with the stale `vp.anchor` (default `0,0`,
758
- discarding the `0.5,0.5` set at `GUIEngine.js:213`), and calling `el.visible.set(...)` — if the stale
759
- position is out of bounds and `stickToScreenEdge` is false (the default), `el.visible.set(false)`
760
- makes `GUIElementSystem` detach the modal from the DOM and the open modal vanishes.
761
- - **Fix**: add `el.view.size.onChanged.remove(updatePosition, eventContext);` inside the
762
- `eventContext !== undefined` block in `unlink`.
763
- - **Confidence**: Confirmed
764
-
765
- ### ECS-43 `HeadsUpDisplay.toJSON` writes `transformPerspectiveRotation`, `fromJSON` reads `perspectiveRotation`
766
- - **Severity**: MAJOR
767
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/gui/hud/HeadsUpDisplay.js:86` vs `:90`
768
- - **Defect**: The JSON key emitted for the `PerspectiveRotation` flag does not match the key the reader
769
- destructures, so the reader always falls back to its `false` default.
770
- - **Failure scenario** (reproduced): a HUD with `PerspectiveRotation` set — exactly what
771
- `makeEnemyGroupHud` in `model/game/scenes/strategy/gui/setupStrategyHeadsUpDisplays.js` configures.
772
- `toJSON()` yields `{"worldOffset":…,"transformWorldOffset":true,"transformPerspectiveRotation":true}`;
773
- `HeadsUpDisplay.fromJSON(that)` yields `flags = 1` instead of `3`,
774
- `getFlag(PerspectiveRotation) === false`, and `a.equals(b) === false`. The screen-space billboard
775
- rotation is silently dropped on any JSON round-trip. The binary adapter round-trips the flag
776
- correctly, so the two persistence paths disagree.
777
- - **Fix**: emit `perspectiveRotation` in `toJSON`, or rename the `fromJSON` destructure to
778
- `transformPerspectiveRotation`. Pick one and keep both sides identical.
779
- - **Confidence**: Confirmed
780
-
781
- ### ECS-44 `GUIElementSystem.link` logs "does not have a view" and then dereferences the missing view
782
- - **Severity**: MINOR
783
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/gui/GUIElementSystem.js:212`
784
- - **Defect**: The un-managed-with-no-view branch does `console.error(...)` with no `return`. Execution
785
- falls through to `if (component.visible.getValue()) this.attachComponent(component, entity)`
786
- (`visible` defaults to `true`), and `attachComponent` reaches `parent.addChild(component.view)` with
787
- `component.view === null`.
788
- - **Failure scenario**: `const c = new GUIElement(); c.clearFlag(GUIElementFlag.Managed);` added to an
789
- entity — precisely the state the error message describes. `View.addChild(null)` executes
790
- `child.parent = this;` (its guarding asserts are stripped in production) →
791
- `TypeError: Cannot set properties of null (setting 'parent')` propagates out of `link`, aborting the
792
- component-added observer dispatch for that entity so later systems never link.
793
- - **Fix**: `return;` after the `console.error` at `:212`.
794
- - **Confidence**: Confirmed
795
-
796
- ### ECS-45 `ButtonViewEntity.finalize` destroys the structural children created in its constructor
797
- - **Severity**: MINOR
798
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/gui/view/ButtonViewEntity.js:104-107` (children created at `:17-23`)
799
- - **Defect**: The constructor adds the `foreground` and `background` `EmptyView`s the button's CSS is
800
- built on. `finalize()` calls `removeAllChildren()`, which removes them too — but `ViewEntity.finalize`
801
- is documented as bringing the view "to initial state ready to be destroyed **or re-used**", and
802
- `GUIElementSystem` implements exactly that re-use contract (`unlink` calls `view.finalize()` and
803
- clears `Initialized`; `attachComponent` re-calls `view.initialize(...)` when it sees
804
- `Managed && !Initialized`).
805
- - **Failure scenario**: an entity carrying a `view.Button` `GUIElement` (registered at
806
- `model/game/GameClassRegistry.js:89`) goes through link → unlink → link. On the second `initialize`
807
- the label is re-added but `.foreground`/`.background` are gone, so the button renders with no
808
- background fill and dead hover/press styling. `LocalizedLabelViewEntity` is correct precisely because
809
- its constructor adds no children.
810
- - **Fix**: create the foreground/background children in `initialize()`, or have `finalize()` remove only
811
- the label child.
812
- - **Confidence**: Confirmed
813
-
814
- ### ECS-46 `LineSetDescriptionTable.validateOne` reports a false circular dependency for a shared child set
815
- - **Severity**: MINOR
816
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/speaker/lines/sets/LineSetDescriptionTable.js:79`
817
- - **Defect**: The check treats *any* second visit to a node as a cycle
818
- (`if (visited.hasOwnProperty(set.id))`) rather than tracking the current traversal path, so a DAG with
819
- a shared child is rejected.
820
- - **Failure scenario**: `A{sets:[B,C]}`, `B{sets:[D]}`, `C{sets:[D]}` — no cycle. Traversal pops `A`,
821
- pushes `B,C`; pops `C`, pushes `D`; pops `D`, marks visited; pops `B`, pushes `D`; pops `D` again →
822
- `visited` hit → `errorConsumer("Circular dependency detected: D -> C -> A")` and `return false`. Valid
823
- voice-line-set data fails validation at load, and the reported path names the wrong parent.
824
- - **Fix**: track the recursion stack (mark on entry, unmark on exit) or colour nodes white/grey/black,
825
- and only report a cycle on re-entering a grey node.
826
- - **Confidence**: Confirmed
827
-
828
- ### ECS-47 `VoiceSystem.sayLineFromSet` dereferences `undefined` when the set yields no lines
829
- - **Severity**: MINOR
830
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/speaker/VoiceSystem.js:313-315`
831
- - **Defect**: `weightedRandomFromArray` returns `undefined` when `length === 0`
832
- (`core/collection/array/weightedRandomFromArray.js:26-28`), and the result is used unguarded as
833
- `selected_line.id`. The neighbouring "set not found" case at `:303` warns and returns, so graceful
834
- handling of bad data is the intended contract here.
835
- - **Failure scenario**: a `voice-line-sets` entry with an empty or omitted `lines` array (`linkOne`
836
- defaults `lines = []`) — a placeholder or not-yet-authored bark set.
837
- `ecd.sendEvent(entity, VoiceEvents.SpeakSetLine, {id: thatSet})` → `collect` returns `0` →
838
- `TypeError: Cannot read properties of undefined (reading 'id')` thrown out of the entity-event dispatch.
839
- - **Fix**: guard on `selected_line === undefined` after the `weightedRandomFromArray` call.
840
- - **Confidence**: Confirmed
841
-
842
- ---
843
-
844
- ## Attachment / parent / transform / IK
845
-
846
- ### ECS-49 `EntityNode`'s `parent` setter strips `TransformAttachment` and never re-adds it
847
- - **Severity**: MAJOR
848
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/parent/EntityNode.js:240-270`
849
- - **Defect**: The setter removes both components up front
850
- (`let parent_entity = this_entity.removeComponent(ParentEntity); let attachment = this_entity.removeComponent(TransformAttachment);`)
851
- and, in the `node !== null` branch, explicitly re-adds `ParentEntity` via `this_entity.add(parent_entity)`
852
- — but the attachment is only put back through `this.__safe_get_attachment()`, which runs **only when
853
- `attachment === null`**. When the node already had a `TransformAttachment`, the removed instance is
854
- mutated (`attachment.parent = parent_entity_id`) and dropped on the floor.
855
- - **Failure scenario** (reproduced): three built `EntityNode`s A, B, C each with a `Transform`.
856
- ```js
857
- A.addChild(C); // C -> ParentEntity{0}, TransformAttachment{parent:0}
858
- C.parent = B; // public setter, the documented re-parent entry point
859
- ```
860
- Observed: `ParentEntity.entity` correctly becomes `1`, but
861
- `C.entity.getComponent(TransformAttachment)` is `null` and
862
- `ds.getComponent(C.entity.id, TransformAttachment)` is `undefined`. `TransformAttachmentSystem` has
863
- unlinked C, so C's world transform is no longer driven by any parent — the node visually detaches
864
- while still being logically parented (and still gets destroyed when B is destroyed).
865
- `addChild` escapes this only because it routes through `removeChild` → `node.parent = null` first,
866
- which nulls both.
867
- - **Fix**: after `attachment.parent = parent_entity_id`, put the component back —
868
- `if (this_entity.getComponent(TransformAttachment) === null) this_entity.add(attachment);` — or
869
- simply do not remove `TransformAttachment` at the top when `node !== null`.
870
- - **Confidence**: Confirmed
871
-
872
- ### ECS-50 `InverseKinematicsSystem` declares read-only access to `Mesh` because of an undefined enum member
873
- - **Severity**: MINOR
874
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/ik/InverseKinematicsSystem.js:18`
875
- - **Defect**: `ResourceAccessSpecification.from(Mesh, ResourceAccessKind.Read | ResourceAccessSpecification.Write)`
876
- — `Write` is a member of `ResourceAccessKind`, not of `ResourceAccessSpecification`. The latter has no
877
- such static, so the expression is `1 | undefined === 1`: the system declares `Read` only, while
878
- `TwoBoneInverseKinematicsSolver.solve` actually mutates the mesh's bone quaternions
879
- (`boneA.setRotationFromQuaternion(...)`, `TwoBoneInverseKinematicsSolver.js:173-174`).
880
- - **Failure scenario**: `EntityManager.updateExecutionOrder`'s `scoreSystem` weights a `Write` component
881
- at `attached_edge_count * 2` and a `Read` at `* 1`, and `computeSystemComponentDependencyGraph` only
882
- emits edges out of a system that declares `Write`/`Create`. With the typo, `InverseKinematicsSystem`
883
- contributes no dependency edges and is scored as a pure `Mesh` reader, so it can be ordered *after*
884
- the systems that consume mesh/bone state in the same `simulate()` call — IK corrections (feet planted
885
- on terrain) are applied one frame late, producing visible foot sliding / ground penetration on the
886
- frame a character's pose changes.
887
- - **Fix**: `ResourceAccessKind.Read | ResourceAccessKind.Write`.
888
- - **Confidence**: Confirmed
889
-
890
- ### ECS-51 `OneBoneSurfaceAlignmentSolver` recovers the alignment angle with `asin`, which cannot exceed 90°
891
- - **Severity**: MINOR
892
- - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js:170-175`
893
- - **Defect**: `axis.crossVectors(v3, Vector3.forward); const betaSine = axis.length(); const beta = Math.asin(clamp(betaSine, -1, 1));`
894
- — `|a × b| = sin θ` is ambiguous between `θ` and `π − θ`, and `asin` always returns the branch in
895
- `[0, π/2]`. The angle between the contact normal and the bone axis is not recoverable this way once it
896
- exceeds 90°.
897
- - **Failure scenario**: bone axis `(0,0,1)` in bone space, contact normal in bone space
898
- `(0, 0.9, −0.436)` — 116° apart. `|cross| = 0.9` → `beta = asin(0.9) = 64.2°`. The bone is rotated
899
- 64° instead of 116°, so instead of squaring up to the surface it ends up 52° off, tilted into the
900
- surface. Any surface whose normal is more than 90° from the bone's own axis (steep slopes,
901
- overhangs, an inverted foot-bone rig) is aligned to the wrong angle rather than clamped by
902
- `constraint.limit`.
903
- - **Fix**: recover the angle unambiguously — `Math.atan2(axis.length(), v3.dot(Vector3.forward))` —
904
- before normalising `axis`.
905
- - **Confidence**: Likely (the trigonometric limitation is unambiguous; I did not exercise it against a
906
- real rig, so how often a live skeleton crosses 90° is unverified)
907
-
908
- ---
909
-
910
- ## Status of previously-known items
911
-
912
- | Item | Status |
913
- | --- | --- |
914
- | `Entity.promiseEvent` calling `removeEventListener` instead of `add` | **FIXED** (`Entity.js:326-327` now `addEventListener`) |
915
- | `EntityComponentDataset.dropData` not clearing `__entityAnyEventListeners` | **FIXED** (`EntityComponentDataset.js:1882`) |
916
- | `HeadsUpDisplaySystem` scaling world-offset Z by `scale.y` | **FIXED** (`HeadsUpDisplaySystem.js:88` uses `transform_scale.z`) |
917
- | `obtainTileAtWorldPosition2D` dereferencing an out-of-bounds tile | **STILL PRESENT** (`TerrainTileManager.js:487-489`; `getTileByWorldPosition2D` can return `undefined`, then `tile.gridPosition`) |
918
- | `FogOfWar.rebuildSampler` width/height swap | **FIXED** (`FogOfWar.js:707-713`, correct `Sampler2D(data, itemSize, width, height)` order) |
919
- | `TerrainTileManager.stitchTile` floor-vs-ceil | **STILL PRESENT** (`:566` uses `floor`; `:353`, `:405`, `:408`, `:422` all use `ceil`) |
920
- | Event-dispatch pointer restore missing `try/finally` | **STILL PRESENT** (`EntityComponentDataset.js:2138-2162` and `2180-2204`) |
1
+ # Correctness review — `engine/ecs`
2
+
3
+ Scope: every non-spec source file under `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/`
4
+ (208 source files). Reviewed 2026-08-06.
5
+
6
+ Findings marked **Confirmed** were either reproduced by executing the code or read directly
7
+ off the source with an unambiguous trace. `assert.*` is treated as absent (stripped in prod).
8
+
9
+ ---
10
+
11
+ ## Core (Entity / EntityComponentDataset / EntityManager / System)
12
+
13
+ ### ECS-1 `Entity.readFromDataset` never watches destruction, so a stale `Built` flag corrupts recycled entity ids
14
+ - **Severity**: CRITICAL
15
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/Entity.js:508-525`
16
+ - **Defect**: `build()` registers `#handleEntityDestroyed` on `EventType.EntityRemoved` when the
17
+ `WatchDestruction` flag is set (line 487-489), which is on by default. `readFromDataset` sets
18
+ `EntityFlags.Built` and binds the reference but registers no such listener, so the returned
19
+ `Entity` keeps reporting `isBuilt === true` forever after the underlying entity is removed.
20
+ Since `EntityComponentDataset.createEntity` recycles ids (`entityOccupancy.nextClearBit(0)`),
21
+ every subsequent operation on that `Entity` lands on a *different* entity.
22
+ - **Failure scenario** (reproduced):
23
+ ```js
24
+ const ds = new EntityComponentDataset();
25
+ ds.setComponentTypeMap([A, B]);
26
+ const id = ds.createEntity(); // id = 0
27
+ ds.addComponentToEntity(id, new A());
28
+ const e = Entity.readFromDataset(id, ds);
29
+ ds.removeEntity(id);
30
+ const recycled = ds.createEntity(); // id = 0 again, generation 1
31
+ e.isBuilt // => true (should be false)
32
+ e.add(new B()); // => writes B onto `recycled`, an unrelated entity
33
+ e.destroy(); // => destroys `recycled`
34
+ ```
35
+ Output observed: `recycled entity now has B: true`, `after e.destroy(), recycled entity exists: false`.
36
+ The `EntityReference` already carries the generation needed to detect this (`e.generation === 0`
37
+ vs `ds.getEntityGeneration(recycled) === 1`) but nothing consults it.
38
+ - **Fix**: in `readFromDataset`, mirror `build()` — after setting the `Built` flag, do
39
+ `if (r.getFlag(EntityFlags.WatchDestruction)) dataset.addEntityEventListener(entity, EventType.EntityRemoved, r.#handleEntityDestroyed, r);`
40
+ (needs a small private accessor since `#handleEntityDestroyed` is instance-private).
41
+ - **Confidence**: Confirmed
42
+
43
+ ### ECS-2 `setComponentTypeMap` leaves the dataset permanently corrupted when observer rebuild throws
44
+ - **Severity**: MAJOR
45
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/EntityComponentDataset.js:674-731`
46
+ - **Defect**: `setComponentTypeMap` mutates state in four steps — `updateComponentOccupancy` (716),
47
+ `updateComponentStores` (718), `updateObservers` (720), then `componentTypeMap` / `componentTypeCount`
48
+ (722, 730). `updateObservers` calls `observer.build(map)` for every observer sitting in a *surviving*
49
+ component's store; `EntityObserver.build` **throws** if any of that observer's component types is
50
+ absent from the new map (`EntityObserver.js:102-104`). Because occupancy and the component stores
51
+ have already been re-strided at that point, the throw leaves the dataset with the new stride but the
52
+ old `componentTypeMap` / `componentTypeCount` / `__type_to_index_map`.
53
+ - **Failure scenario** (reproduced):
54
+ ```js
55
+ ds.setComponentTypeMap([A, B]);
56
+ ds.addObserver(new EntityObserver([A, B], onComplete, onBroken), false);
57
+ ds.unregisterComponentType(A); // A has no live instances, so the removal check passes
58
+ ```
59
+ Observed: throws `Component type[0] was not found in the supplied map`, then
60
+ `componentTypeMap = ['A','B']`, `componentTypeCount = 2`, but `components.length = 1`.
61
+ The next `ds.addComponentToEntity(e, new B())` throws
62
+ `TypeError: Cannot read properties of undefined (reading '0')` — `components[1]` no longer exists.
63
+ The dataset cannot be used again.
64
+ - **Fix**: validate first, mutate second. Before touching occupancy/stores, iterate every registered
65
+ observer and check `map` contains all of `observer.componentTypes`; throw there (dataset untouched).
66
+ Observers that watch only removed types should be disconnected explicitly (`observer.dataset = null`)
67
+ rather than silently dropped from the stores.
68
+ - **Confidence**: Confirmed
69
+
70
+ ### ECS-3 `traverseEntitiesCompactedFiltered` uses `-1` as a component index for unregistered classes
71
+ - **Severity**: MAJOR
72
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/EntityComponentDataset.js:1990-2020`
73
+ - **Defect**: `componentIndices[i] = this.computeComponentTypeIndex(klass)` stores `-1` for a class
74
+ that is not registered in this dataset, and nothing filters it out. The loop then does
75
+ `componentOccupancy.get(entityOccupancyAddress + (-1))` — reading the *last* component bit of the
76
+ *previous* entity — and, when that bit happens to be set, `this.components[-1][i]`.
77
+ `this.components[-1]` is `undefined`. (In production `BitSet.get`'s `assert.isNonNegativeInteger`
78
+ is stripped, so it silently returns the neighbouring entity's bit instead of throwing.)
79
+ - **Failure scenario** (reproduced): dataset registered with `[A, B]`; entity 0 has both A and B;
80
+ entity 1 has A. `ds.traverseEntitiesCompactedFiltered([C, A], visitor)` where `C` was never
81
+ registered. With asserts on it throws `bitIndex must be >= 0, instead was -1`. With asserts stripped,
82
+ entity 1 reads bit `2 - 1 = 1` (entity 0's B bit, which is set) and then dereferences
83
+ `this.components[-1][1]` → `TypeError: Cannot read properties of undefined`.
84
+ - **Fix**: skip unregistered classes when building `componentIndices` (build a compacted array of
85
+ valid indices only), the same way `traverseEntities` returns early on `-1`.
86
+ - **Confidence**: Confirmed
87
+
88
+ ### ECS-4 Bare `.sort()` on component indices is lexicographic
89
+ - **Severity**: MINOR
90
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/EntityComponentDataset.js:1996`
91
+ - **Defect**: `componentIndices.sort()` with no comparator sorts numbers as strings. The comment
92
+ says "sort component indices array for faster traversal"; with 10 or more registered component
93
+ types the array is not sorted at all, and the `components` array handed to the visitor comes out
94
+ in a scrambled, size-dependent order.
95
+ - **Failure scenario** (reproduced): 12 registered component types; an entity holds instances of
96
+ type index 2 and type index 10. The visitor receives `[K10, K2]` instead of `[K2, K10]`. Any
97
+ consumer of this "main utility … to facilitate serialization" method that depends on a stable
98
+ component ordering (e.g. a byte-for-byte save comparison or a diff) sees the order flip as soon
99
+ as the dataset crosses 10 component types.
100
+ - **Fix**: `componentIndices.sort((a, b) => a - b);`
101
+ - **Confidence**: Confirmed
102
+
103
+ ### ECS-5 `Entity.getComponentSafe` throws `ReferenceError` — its helper is neither imported nor exported
104
+ - **Severity**: MINOR
105
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/Entity.js:256`
106
+ - **Defect**: The error path calls `computeComponentClassName(klass)`, but `Entity.js` imports only
107
+ `assert`, `Signal`, `EntityFlags`, `EntityReference`, `EventType`. Worse,
108
+ `computeComponentClassName.js` declares the function without any `export` statement at all, so
109
+ there is nothing to import.
110
+ - **Failure scenario** (reproduced): `new Entity().getComponentSafe(SomeClass)` throws
111
+ `ReferenceError: computeComponentClassName is not defined` instead of the documented
112
+ `Error: Component of given class 'SomeClass' not found`. The diagnostic naming the missing
113
+ component — the entire point of the method over `getComponent` — is lost.
114
+ - **Fix**: add `export` to `computeComponentClassName.js` and import it in `Entity.js`.
115
+ - **Confidence**: Confirmed
116
+
117
+ ### ECS-6 Re-`build()`ing an `Entity` whose components drifted orphans the old entity and mis-aims its destruction watcher
118
+ - **Severity**: MINOR
119
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/Entity.js:442-493`
120
+ - **Defect**: `build()` returns early only when `checkExistingComponents` passes. Otherwise it falls
121
+ through and calls `dataset.createEntity()` **without** removing the entity it was previously bound
122
+ to and without unregistering the `EntityRemoved` listener registered on the old id at line 488.
123
+ - **Failure scenario** (reproduced):
124
+ ```js
125
+ const e = new Entity(); e.add(new A());
126
+ const id1 = e.build(ds); // 0
127
+ ds.removeComponentFromEntity(id1, A); // a system strips the component directly
128
+ const id2 = e.build(ds); // 1 -- entity 0 is now orphaned, entityCount = 2
129
+ ds.removeEntity(id1); // fires the stale watcher
130
+ e.isBuilt // => false, although entity 1 is alive
131
+ e.destroy() // => false; entity 1 leaks permanently
132
+ ```
133
+ - **Fix**: when the early-return check fails but `EntityFlags.Built` is set, call `this.destroy()`
134
+ first so the previous entity and its listener are released before rebinding.
135
+ - **Confidence**: Confirmed
136
+
137
+ ### ECS-7 `EntityManager.removeSystem` never clears `system.entityManager`
138
+ - **Severity**: MINOR
139
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/EntityManager.js:590-631`
140
+ - **Defect**: `startSystem` refuses to start a system whose `entityManager` is set to a different
141
+ manager (`EntityManager.js:739-741`). Neither `removeSystem` nor `stopSystem` resets the field.
142
+ - **Failure scenario**: `em1.removeSystem(s)` then `em2.addSystem(s); em2.startup()` →
143
+ `startSystem` throws `System is bound to another EntityManager`, caught at line 763, so
144
+ `errorCallback` fires and `em2.state` becomes `EntityManagerState.Failed`; the whole manager
145
+ never reaches `Running`. Reusing a system instance across manager instances (level reload, test
146
+ fixtures) is impossible.
147
+ - **Fix**: set `system.entityManager = null` in `removeSystem` after `stopSystem` resolves.
148
+ - **Confidence**: Confirmed
149
+
150
+ ### ECS-8 `system_validate_class` duplicate-component backtrace is off by one
151
+ - **Severity**: MINOR
152
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/system_validate_class.js:49`
153
+ - **Defect**: `for (let j = 0; j < i - 1; j++)` never compares `components_used[i]` against
154
+ `components_used[i - 1]`, so adjacent duplicate resource specifications slip through.
155
+ - **Failure scenario**: `components_used = [Spec(Transform, Read), Spec(Transform, Write)]` passes
156
+ validation. `System.getAccessForComponent` (`System.js:110-117`) `break`s at the first match, so
157
+ the `Write` access is silently discarded, and `EntityManager.updateExecutionOrder`'s `scoreSystem`
158
+ scores the system as a reader (`attached_edge_count` instead of `× 2`) — the system is scheduled
159
+ after the systems that should run after it.
160
+ - **Fix**: `for (let j = 0; j < i; j++)`.
161
+ - **Confidence**: Confirmed
162
+
163
+ ### ECS-48 `EntityManager.startup` reports `Running` while a system added during startup is still `STARTING`
164
+ - **Severity**: MINOR
165
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/EntityManager.js:795,814,567-574`
166
+ - **Defect**: `startup()` captures `expectedReadyCount = systems.length` before the start loop, then
167
+ `finalizeStartup()` fires when `readyCount === expectedReadyCount`. A system's own `startup()` may
168
+ call `entityManager.addSystem(...)`, which pushes onto the same `systems` array the `for…of` loop is
169
+ walking, so the loop does start the newcomer — but the target count is stale by one. Separately,
170
+ `addSystem` only auto-starts when `this.state === EntityManagerState.Running`; during
171
+ `EntityManagerState.Starting` it returns `Promise.resolve()` and never calls `startSystem`, so a
172
+ system added after the loop has drained is never started at all.
173
+ - **Failure scenario**: `SGMeshSystem.startup(em)` does
174
+ `if (!em.hasSystem(TransformAttachmentSystem)) await em.addSystem(new TransformAttachmentSystem());`
175
+ (`engine/graphics/ecs/mesh-v2/aggregate/SGMeshSystem.js:94-96`). With N systems registered,
176
+ `expectedReadyCount = N`; once the original N have reported ready, `finalizeStartup()` sets
177
+ `state = Running` and invokes `readyCallback()` while `TransformAttachmentSystem` is still in
178
+ `STARTING`. The caller begins `simulate()`; `updateExecutionOrder` skips non-`RUNNING` systems and its
179
+ observer has not been added to the dataset, so attachments silently do not update for those frames.
180
+ If the same `addSystem` were made from after an `await` (once the start loop has drained) the system
181
+ would never start at all, while its promise still resolves.
182
+ - **Fix**: read `systems.length` live at each `systemReady` (`readyCount === systems.length`) instead of
183
+ a captured count, and treat `EntityManagerState.Starting` like `Running` in `addSystem`'s auto-start
184
+ branch.
185
+ - **Confidence**: Confirmed
186
+
187
+ ---
188
+
189
+ ## Fog of war
190
+
191
+ ### ECS-9 `FogOfWarSystem.shutdown` removes none of the three render-loop subscriptions it made in `startup`
192
+ - **Severity**: MAJOR
193
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/fow/FogOfWarSystem.js:162-219`
194
+ - **Defect**: `startup` registers three anonymous closures on `graphics.on.preRender`,
195
+ `graphics.on.buffersRendered` and `graphics.on.postRender`. `shutdown` only calls
196
+ `this.graphics.layerComposer.remove(this.compositLayer)`. Because the handlers are inline
197
+ functions with no retained reference, they can never be removed even by a caller.
198
+ - **Failure scenario**: `entityManager.shutdown()` (or `removeSystem(fogOfWarSystem)`) removes the
199
+ composit layer, but on the very next frame the `preRender` handler still runs
200
+ `compositerLayer.enable()` on the removed layer and `depthFrameBuffer.referenceCount++`, and
201
+ `buffersRendered` still runs `fogOfWarRenderer.render(renderer, camera, scene, compositerLayer.renderTarget)`
202
+ into a render target that is no longer composited — burning a full-screen pass every frame for the
203
+ lifetime of the `GraphicsEngine`. The closures also pin `entityManager`, `graphics` and the
204
+ `FogOfWarRenderer` (its `ShaderMaterial`, `Mesh` and `Scene` are never disposed). Starting a second
205
+ `FogOfWarSystem` — e.g. a level reload on a shared `GraphicsEngine` — stacks a second set of handlers,
206
+ so each frame renders the fog twice.
207
+ - **Fix**: hoist the three handlers into named fields (or wrap them in `SignalBinding`s) in `startup`,
208
+ `remove` them in `shutdown`, and dispose `fogOfWarRenderer.material` / `fogOfWarRenderer.quad`.
209
+ - **Confidence**: Confirmed
210
+
211
+ ### ECS-10 A revealer on the *wrong* team still reveals fog whenever its radius changes
212
+ - **Severity**: MAJOR
213
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/fow/FogOfWarRevealerSystem.js:52-97`
214
+ - **Defect**: The team check lives only in `attemptToWatch`, which gates the *position* watcher.
215
+ `update()` itself contains no team test, and `radiusWatcher` (line 93-97) is linked
216
+ unconditionally for every linked entity regardless of team.
217
+ - **Failure scenario**: `new FogOfWarRevealerSystem(/* team */ 1)`. An enemy unit with
218
+ `Team = 2`, `Transform`, `FogOfWarRevealer` links: `attemptToWatch(2)` correctly leaves
219
+ `positionWatcher` unlinked, but `radiusWatcher.link()` runs anyway. The enemy then receives a
220
+ vision buff (`revealer.radius.set(8)`) — `revealer.radius.onChanged` fires `update()`, which maps
221
+ the *enemy's* position to the grid and calls `fow.reveal(...)` on the player's fog. The player's
222
+ map is permanently uncovered at the enemy's location.
223
+ - **Fix**: put the team check inside `update()` (`if (team.getValue() !== visibilityTeam) return;`),
224
+ or link/unlink `radiusWatcher` from `attemptToWatch` alongside `positionWatcher`.
225
+ - **Confidence**: Confirmed
226
+
227
+ ### ECS-11 `computeRevealedGridBoundingRectangle` returns an inverted AABB when nothing is revealed
228
+ - **Severity**: MINOR
229
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/fow/FogOfWar.js:532-563`
230
+ - **Defect**: `x0`/`y0` seed to `width - 1` / `height - 1` and `x1`/`y1` to `0`. If no cell differs
231
+ from 255 the loop body never runs and those seeds are written out unchanged, producing
232
+ `x0 > x1` and `y0 > y1`.
233
+ - **Failure scenario**: a freshly resized 64×64 fog (sampler 66×66, all 255) →
234
+ `result.set(65, 65, 0, 0)`. `MinimapFogOfWar.updateFocusArea`
235
+ (`view/minimap/gl/MinimapFogOfWar.js:87`) then does `bounds.grow(2)`, `bounds.move(-0.5, 0.5)`
236
+ and multiplies by the scale, feeding a negative-extent rectangle into `enforceAspectRatio` — the
237
+ minimap focus area is garbage on the first frame of any map before the first reveal.
238
+ - **Fix**: track a `found` flag and, when nothing is revealed, emit an explicitly empty rectangle
239
+ (`result.set(0, 0, 0, 0)`) or leave `result` untouched and return a boolean.
240
+ - **Confidence**: Confirmed
241
+
242
+ ### ECS-12 `getWorldClearance` reads the distance sampler unclamped
243
+ - **Severity**: MINOR
244
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/fow/FogOfWar.js:677-684`
245
+ - **Defect**: Its sibling `computeAABBVisibility` clamps the sample position before
246
+ `distanceSampler.readChannel` (line 628-629); `getWorldClearance` does not.
247
+ `Sampler2D.readChannel` computes `(y * width + x) * itemSize + channel` with no bounds check
248
+ (the `assert`s are stripped), so an x outside `[0, width)` silently indexes into an adjacent row.
249
+ - **Failure scenario**: `FogOfWarVisibilityPredicate.test(x, y, z)` (line 41) is called with an
250
+ object's world position. For a 64×64 fog with `scale = 1`, an object one cell east of the map at
251
+ grid row 20 produces `sampleX = 65`, `sampleY = 21`, giving index `21 * 66 + 65 = 1451` — which is
252
+ cell `(1, 22)`, the *west* border of the row below. The object's visibility is decided by an
253
+ unrelated cell; where that cell is revealed, an off-map object that should be hidden renders.
254
+ - **Fix**: clamp exactly as `computeAABBVisibility` does, using the sampler's own dimensions.
255
+ - **Confidence**: Confirmed
256
+
257
+ ### ECS-13 `computeAABBVisibility` clamps to `size - 1` but the distance sampler is `size + 2` wide
258
+ - **Severity**: MINOR
259
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/fow/FogOfWar.js:628-629`
260
+ - **Defect**: `worldToSamplePosition` emits sampler-space coordinates with the +1 border offset
261
+ baked in, so the real world area occupies sampler columns `[1, size.x]`. The clamp bound is
262
+ `this.size.x - 1`, two cells short of the valid maximum.
263
+ - **Failure scenario**: 64×64 fog, `scale = 1`. `worldToSamplePosition` maps the world extent onto
264
+ sample coordinates `[1, 64]`, but the clamp caps at `63`. An object at the far eastern edge maps to
265
+ `sampleX = 64` and is read at column `63` instead — the outermost column/row of the map takes its
266
+ clearance from the cell one step inward. An unexplored strip along the east/south map edge therefore
267
+ reports the clearance of the explored cell beside it, and objects standing there are drawn through
268
+ the fog.
269
+ - **Fix**: clamp against the sampler, not the logical size: `clamp(sampleX, 0, this.sampler.width - 1)`.
270
+ - **Confidence**: Confirmed
271
+
272
+ ---
273
+
274
+ ## Terrain
275
+
276
+ ### ECS-14 `getTileByWorldPosition2D` reads the vertical axis as the grid row
277
+ - **Severity**: CRITICAL
278
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/tiles/TerrainTileManager.js:461-472`
279
+ - **Defect**: The point is packed as `Float32Array.of(x, 0, y)` — so the top-down Y lands at index
280
+ **2** — inverse-transformed, and then passed as `getRawTileByPosition(v3[0], v3[1])`. `v3[1]` is
281
+ the vertical component. Separately, `getRawTileByPosition` divides by `tileSize`, which is in grid
282
+ units, while `v3` is in object units, so `this.scale` must be divided out first.
283
+ - **Failure scenario**: 64×64 terrain, `gridScale = 1`, tile size 32 → a 2×2 tile grid, identity
284
+ transform. `getTileByWorldPosition2D(40, 40)` → `getRawTileByPosition(40, 0)` →
285
+ `tileX = 1, tileY = floor(0 / 32) = 0` → tile **(1,0)** instead of **(1,1)**. With an identity
286
+ transform the row is *always* 0, so every lookup collapses onto the top row of tiles.
287
+ `obtainTileAtWorldPosition2D` then builds the wrong tile.
288
+ - **Fix**: `return this.getRawTileByPosition(v3[0] / this.scale.x, v3[2] / this.scale.y);`
289
+ - **Confidence**: Confirmed
290
+
291
+ ### ECS-15 `traverseVisibleTiles` sums per-frustum hit counts but always writes at offset 0
292
+ - **Severity**: MAJOR
293
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/ecs/TerrainSystem.js:265-290`
294
+ - **Defect**: `tile_count` is initialised to 0 and passed as the `result_offset` to
295
+ `bvh_query_user_data_intersects_frustum` on every iteration but never advanced, so each frustum
296
+ overwrites the previous frustum's results from index 0. `found_tiles` meanwhile accumulates the
297
+ per-frustum counts, and the consumer loop iterates to that sum.
298
+ - **Failure scenario**: two active `Camera` components (`CameraSystem.getActiveFrustums` yields one
299
+ frustum per active camera). Frustum A returns 6 tiles, frustum B returns 4 → `tiles` holds 6
300
+ entries but `found_tiles = 10`. Indices 6..9 are `undefined` →
301
+ `tileManager.tiles[undefined] === undefined` → the callback dereferences `tile.isBuilt` /
302
+ `tile.gridPosition` → `TypeError` out of `TerrainSystem.update` every frame.
303
+ - **Fix**: `tile_count += bvh_query_user_data_intersects_frustum(tiles, tile_count, ...)` and iterate
304
+ to `tile_count`.
305
+ - **Confidence**: Confirmed
306
+
307
+ ### ECS-16 `TerrainOverlay.paintSampler` passes the overlay instead of its sampler
308
+ - **Severity**: MAJOR
309
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/overlay/TerrainOverlay.js:321`
310
+ - **Defect**: `sampler2d_paint(this, scaled_source, ...)` supplies the `TerrainOverlay` as the
311
+ destination `Sampler2D`. The pixel data lives on `this.sampler`.
312
+ - **Failure scenario**: `sampler2d_paint` computes
313
+ `_w = Math.min(width, source.width - 0, undefined - dx)` → `NaN`, so `for (x = 0; x < NaN; ...)`
314
+ never executes. Nothing is written and no error is raised — `paintImage`/`paintSampler` are silent
315
+ no-ops, while `texture.needsUpdate = true` re-uploads unchanged pixels every call.
316
+ - **Fix**: `sampler2d_paint(this.sampler, scaled_source, 0, 0, dx, dy, dWidth, dHeight);`
317
+ - **Confidence**: Confirmed
318
+
319
+ ### ECS-17 Tile rebuild leaks the previous `BufferGeometry`
320
+ - **Severity**: MAJOR
321
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/tiles/TerrainTile.js:551,586`
322
+ - **Defect**: `build()` assigns `this.geometry = g` / `mesh.geometry = g` without disposing the
323
+ geometry it replaces. The only disposal path, `dispose()`, early-returns on `if (!this.isBuilt) return;`
324
+ — and `TerrainTileManager.rebuildTilesByUV` (`tiles/TerrainTileManager.js:300`) clears `isBuilt`
325
+ while the geometry is still attached, so the old geometry can never be released.
326
+ - **Failure scenario**: terrain height editing calls `rebuildTilesByUV`, clearing `isBuilt` on every
327
+ overlapping tile; `TerrainSystem.update` then re-invokes `tile.build(tileData)`, installing a fresh
328
+ geometry and orphaning the old VBO/IBO. For a 4-tile region at `resolution = 4` with 32×32 tiles
329
+ (16 384 verts, ~590 KB of attributes each) that is ~2.4 MB of VRAM per edit, never reclaimed.
330
+ `destroyTiles()` hits the same early return.
331
+ - **Fix**: dispose the previous geometry at the top of `build()`, and replace the `!isBuilt` early
332
+ return in `dispose()` with a `geometry !== null` check.
333
+ - **Confidence**: Confirmed
334
+
335
+ ### ECS-18 `TerrainTile.stitching` flags are never reset, so re-stitched seams copy instead of average
336
+ - **Severity**: MAJOR
337
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/tiles/TerrainTile.js:99-110` (consumed at `:279`, `:333`)
338
+ - **Defect**: Neither `build()`, `dispose()`, nor `TerrainTileManager.rebuildTilesByUV` clears the
339
+ per-neighbour `stitching` state. `stitchHorizontal`/`stitchVertical` branch on those flags: when one
340
+ side is already marked stitched, the other side's normals are *copied* wholesale rather than averaged.
341
+ - **Failure scenario**: tiles A(0,0) and B(1,0) built and stitched, so `A.stitching.right === true`.
342
+ A height edit clears `isBuilt` on both but leaves the flags. A rebuilds first — `stitchTile` sees B
343
+ unbuilt and skips. B rebuilds — `stitchHorizontal(A, B)` finds the stale
344
+ `left.stitching.right === true` and takes the copy branch, writing A's one-sided edge normals into B.
345
+ The shared edge now carries normals computed from A's triangles only: a visible lighting crease down
346
+ every rebuilt tile boundary after any terrain edit.
347
+ - **Fix**: reset all eight `stitching` fields in `TerrainTile.build()` and `dispose()`, and clear the
348
+ neighbours' matching flags in `rebuildTilesByUV`.
349
+ - **Confidence**: Confirmed
350
+
351
+ ### ECS-19 `ClingToTerrain.fromJSON` defaults `rotationSpeed` to `Infinity`; the field initialiser is `3`
352
+ - **Severity**: MAJOR
353
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/ecs/cling/ClingToTerrain.js:54`
354
+ - **Defect**: The class field is `rotationSpeed = 3`, and `ClingToTerrainSerializationUpgrader_0_1.js:22`
355
+ explicitly writes `3`, calling it "the default value from the ClingToTerrain class". `fromJSON`
356
+ destructures with `Number.POSITIVE_INFINITY` as the default.
357
+ - **Failure scenario**: a blueprint carrying `{"ClingToTerrain": {"normalAlign": true}}` with no
358
+ `rotationSpeed` → `rotationSpeed = Infinity`. In `ClingToTerrainSystem.processRaycastHit:272`
359
+ `Number.isFinite` is false → `angularLimit = Infinity` → `alignToVector` computes
360
+ `t = clamp01(Infinity / angle) = 1` → the entity snaps to the terrain normal instantly every frame
361
+ instead of easing at 3 rad/s. The same entity constructed in code, or loaded from binary v0 through
362
+ the upgrader, eases correctly — the behaviour differs by load path.
363
+ - **Fix**: use `3` as the `fromJSON` destructuring default.
364
+ - **Confidence**: Confirmed
365
+
366
+ ### ECS-20 `Terrain` JSON and binary paths persist disjoint field sets, and disagree on a default
367
+ - **Severity**: MAJOR
368
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/ecs/Terrain.js:930,981`;
369
+ `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/serialization/TerrainSerializationAdapter.js:29,144`
370
+ - **Defect**: Three fields are handled by exactly one of the two paths.
371
+ `gridTransformKind` and `lightMapURL` are written into `extra` by `serialize`/`deserialize` but are
372
+ absent from `toJSON`/`fromJSON`. `overlay.baseTileImage` is written by `toJSON` (as
373
+ `overlayTileImage`) and restored by `fromJSON`, but is absent from the binary adapter. The two paths
374
+ also disagree on the `gridTransformKind` default: `deserialize` falls back to
375
+ `GridTransformKind.Legacy` (`:144`) while the class field initialiser is `GridTransformKind.Direct`
376
+ (`Terrain.js:76`), which `fromJSON` leaves in place.
377
+ - **Failure scenario**: a terrain with `gridTransformKind = Legacy`, `size = (64,64)`, `gridScale = 1`,
378
+ saved via `toJSON` and reloaded. `fromJSON` leaves it at `Direct`, so `buildGridTransform` produces
379
+ `scale = 1, offset = 0.5` instead of `scale = 64/63 ≈ 1.0159, offset = 0`. `mapPointGrid2World(63, 63)`
380
+ returns `(63.5, ·, 63.5)` instead of `(64, ·, 64)` — every grid-anchored entity, marker and combat
381
+ cell is displaced, growing to more than half a cell at the far edge. The same round-trip drops
382
+ `lightMapURL`, so `build()` skips the `aoMap` load and the terrain renders unshadowed. Symmetrically,
383
+ a custom `overlay.baseTileImage` saved to binary reloads as the default `WHITE_PIXEL_DATA_URL`.
384
+ - **Fix**: add `gridTransformKind` and `lightMapURL` to `toJSON`/`fromJSON` (with the same default the
385
+ binary path uses), and write `overlay.baseTileImage` into the binary adapter's `extra` blob.
386
+ - **Confidence**: Confirmed
387
+
388
+ ### ECS-21 `convertHeightMap2NormalMap` returns normals tilted uphill — both gradients unnegated
389
+ - **Severity**: MAJOR
390
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/grid/HeightSampler2NormalSamplerSoft.js:54-56`
391
+ - **Defect**: For a height field `h(x, y)` the surface normal is `normalize(-∂h/∂x, -∂h/∂y, k)`. The
392
+ Sobel gradients `dX`/`dY` are computed with the correct sign but written out unnegated
393
+ (`_x = dX / magnitude`, `_y = dY / magnitude`).
394
+ - **Failure scenario**: a ramp `h(x, y) = x`. At an interior texel `dX = 8`, `dY = 0`,
395
+ `magnitude = sqrt(64.25) ≈ 8.0156` → the function returns `(+0.998, 0, +0.0624)`. The true normal for
396
+ a surface rising in `+x` leans toward `−x`: `(−0.998, 0, +0.0624)`. Every slope gets a normal pointing
397
+ up-slope, so any lighting or slope test built on this map is exactly reversed on both horizontal
398
+ axes. Flat regions are unaffected, which is why it is not obvious.
399
+ - **Fix**: `const _x = -dX / magnitude; const _y = -dY / magnitude;`
400
+ - **Confidence**: Likely (no in-repo consumer — public library surface; the maths is unambiguous)
401
+
402
+ ### ECS-22 `Terrain.heightTexture` receives the `Sampler2D` object as its `height` argument
403
+ - **Severity**: MINOR
404
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/ecs/Terrain.js:145`
405
+ - **Defect**: `new DataTexture(this.samplerHeight.data, this.samplerHeight.width, this.samplerHeight, RedFormat, FloatType)`
406
+ — the third parameter of `DataTexture(data, width, height, …)` gets the sampler instance rather than
407
+ `this.samplerHeight.height`. Compare `graphics/ecs/water/Water.js:138`.
408
+ - **Failure scenario**: immediately after `new Terrain()`, `terrain.heightTexture.image` is
409
+ `{data: Float32Array(1), width: 1, height: <Sampler2D>}`. Any consumer that binds `heightTexture`
410
+ before `build()` runs `updateHeightTexture()` (which repairs `image.width`/`image.height` at
411
+ `:721-722`) uploads with a non-numeric height — `gl.texImage2D` raises `GL_INVALID_VALUE`.
412
+ - **Fix**: pass `this.samplerHeight.height`.
413
+ - **Confidence**: Confirmed
414
+
415
+ ### ECS-23 `heightMap2AOMap` leaks the intermediate denoise source texture
416
+ - **Severity**: MINOR
417
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/grid/HeightMap2AOMap.js:77`
418
+ - **Defect**: `filter_result_to_texture(raw_ao.array, width, height)` creates a `DataTexture` that is
419
+ uploaded by the denoise `ImageFilter` pass and then dropped without `.dispose()`. The sibling
420
+ `pyramid_texture` *is* disposed at `:91`, so the omission is unintentional.
421
+ - **Failure scenario**: `terrain.buildLightMap()` runs per generated map. For a 256×256 terrain at the
422
+ default `quality = 4` the AO resolution is 1024×1024, so each bake orphans a 1024×1024 RGBA8 GPU
423
+ texture (~4 MB VRAM) for the lifetime of the WebGL context. Each new run accumulates another.
424
+ - **Fix**: hold the texture in a local and `.dispose()` it after the `smooth_ao` `ImageFilter` returns.
425
+ - **Confidence**: Confirmed
426
+
427
+ ### ECS-24 `loadVisibleTerrainTiles` does not reset its counters on retry, and can report success early
428
+ - **Severity**: MINOR
429
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/util/loadVisibleTerrainTiles.js:70-84`
430
+ - **Defect**: `handleFailedTile` bumps `attempt` and re-runs `init()`, which recomputes `totalTiles` and
431
+ restarts `start_time`, but `loadedTiles` and `failedTiles` carry over from the aborted attempt. The
432
+ `attemptId !== attempt` guard only suppresses *stale* callbacks; it does not roll back what they
433
+ already counted.
434
+ - **Failure scenario**: 20 visible tiles; 12 resolve, then one rejects
435
+ (`'Original tile was destroyed during build process'`). `init()` re-runs with `totalTiles = 20` and
436
+ `loadedTiles = 12`. The 12 already-built tiles resolve on the next microtask → `loadedTiles = 24 ≥ 20`
437
+ → `cycleFunction` returns `TaskSignal.EndSuccess` while 8 tiles are still mid-build. The loading
438
+ screen closes and the scene shows 8 tiles with no geometry.
439
+ - **Fix**: reset `loadedTiles = 0` and `failedTiles = 0` at the top of `init()`.
440
+ - **Confidence**: Confirmed
441
+
442
+ ### ECS-25 `TerrainSystem.mapPointGrid2World` hardcodes the Legacy transform and drops the offset
443
+ - **Severity**: MINOR
444
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/terrain/ecs/TerrainSystem.js:81-87,186-187`
445
+ - **Defect**: `link()` computes `gridScaleX = g_w * gridScale / (g_w - 1)` unconditionally — the
446
+ `GridTransformKind.Legacy` formula — ignoring `component.gridTransformKind`, and
447
+ `mapPointGrid2World` applies no offset. `Terrain.mapPointGrid2World` (`Terrain.js:555`) uses
448
+ `gridTransform`, whose `Direct` branch (the class default) is `scale = gridScale, offset = gridScale / 2`.
449
+ - **Failure scenario**: 64×64 terrain, `gridScale = 1`, default `Direct` kind.
450
+ `terrain.mapPointGrid2World(0, 0, v3)` → `(0.5, ·, 0.5)`; `terrainSystem.mapPointGrid2World(0, 0, v3)`
451
+ → `(0, ·, 0)`. At `x = 63`: `63.5` vs `64.0`. Two public methods documented to perform the same
452
+ mapping differ by half a cell at the origin and diverge with distance. `gridScaleX/Y` are also
453
+ system-global, so with two `Terrain` entities they hold whichever linked last.
454
+ - **Fix**: delete `TerrainSystem.mapPointGrid2World`/`gridScaleX`/`gridScaleY` and route callers to
455
+ `Terrain.mapPointGrid2World`, or read `terrain.gridTransform` directly.
456
+ - **Confidence**: Confirmed
457
+
458
+ ---
459
+
460
+ ## Animation / dynamic actions / evaluation
461
+
462
+ ### ECS-26 `AnimationOptimizer` writes its results to itself — optimization is a silent no-op
463
+ - **Severity**: MAJOR
464
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/animation/AnimationOptimizer.js:109-114`
465
+ - **Defect**: `optimizeTrack(track)` is a port of three.js `KeyframeTrack.prototype.optimize()`, where
466
+ `this` *is* the track. Here `this` is the `AnimationOptimizer`, so the compacted arrays are assigned
467
+ to `this.times` / `this.values` on the optimizer; the `track` (whose arrays were only *copied* at
468
+ lines 108-109) is never modified, and the method returns `this`.
469
+ - **Failure scenario**: `GLTFAssetLoader.js:426` calls `animationOptimizer.optimize(animation)` on
470
+ every animation of every loaded GLTF, using the module-level singleton. Load a GLTF whose track has
471
+ 1000 keyframes of which 900 are redundant: the track still has 1000 keyframes afterwards. The
472
+ singleton also permanently retains `this.times`/`this.values` — full copies of the last-optimized
473
+ track's typed arrays, megabytes for a skeletal clip — for the module's lifetime.
474
+ - **Fix**: write back to the track (`track.times = ...; track.values = ...;`) and return `track`.
475
+ - **Confidence**: Confirmed
476
+
477
+ ### ECS-27 `AnimationOptimizer`: `time[0]` should be `times[0]`
478
+ - **Severity**: MINOR
479
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/animation/AnimationOptimizer.js:36`
480
+ - **Defect**: `if (time !== timeNext && (i !== 1 || time !== time[0]))` — `time` is a number, so
481
+ `time[0]` is `undefined` and the sub-condition is always true. three.js has `times[0]`.
482
+ - **Failure scenario**: a track with `times = [0, 0, 1, 2]` and distinct values at index 1. three.js
483
+ drops keyframe 1 as a duplicate of the keyframe at t=0; this code keeps it, leaving two keyframes
484
+ scheduled at t=0 in the "optimized" output.
485
+ - **Fix**: `time !== times[0]`.
486
+ - **Confidence**: Confirmed
487
+
488
+ ### ECS-28 `DynamicRuleDescription.equals` calls `.equals` on a primitive string
489
+ - **Severity**: MAJOR
490
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescription.js:55`
491
+ - **Defect**: `id` is a primitive string (`UUID.string()` returns `UUID.v4().toString()`, and
492
+ `fromJSON` assigns the JSON string directly), but `equals` does `return this.id.equals(other.id)`.
493
+ `String.prototype.equals` does not exist.
494
+ - **Failure scenario**:
495
+ `DynamicRuleDescription.fromJSON({id:"combat.long-idle.ai", ...}).equals(otherRule)` →
496
+ `TypeError: this.id.equals is not a function`. Any container relying on the `equals` protocol
497
+ (a default-configured `HashMap`/`HashSet` keyed by rules) throws on the first lookup.
498
+ `DynamicActorSystem` only escapes because `__global_last_used_times` supplies a custom
499
+ `keyEqualityFunction`.
500
+ - **Fix**: `return this.id === other.id;`
501
+ - **Confidence**: Confirmed
502
+
503
+ ### ECS-29 Pooled `DynamicActorSystem` context keeps the previous entity's `RuleExecution`
504
+ - **Severity**: MAJOR
505
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/dynamic_actions/DynamicActorSystem.js:43,74-90`
506
+ - **Defect**: `Context` instances are pooled by `AbstractContextSystem`, and the pool's reset callback
507
+ is an empty `// TODO` (`system/AbstractContextSystem.js:37-42`). `Context.link()` resets
508
+ `next_idle_event_time` but never resets `execution`, so a recycled context arrives carrying the
509
+ previous entity's `execution.rule` and `execution.executor`. `Context.unlink()` also never terminates
510
+ the still-running executor.
511
+ - **Failure scenario**: unit A executes a `SpeakLine` rule, whose executor tree is
512
+ `ParallelBehavior([WaitForEventBehavior(FinishedSpeakingLine on A), SendEventBehavior])`. A is killed
513
+ and removed; `removeEntity` deletes A's event listeners, so `FinishedSpeakingLine` can never arrive,
514
+ the parallel never completes, `DieBehavior` never runs, and the executor `Entity` keeps
515
+ `EntityFlags.Built` forever. A's context is released to the pool still pointing at that immortal
516
+ executor. Unit B spawns and pops the same context. On B's first event, `attemptRuleExecution`
517
+ (lines 193-199) sees `execution.executor.getFlag(Built) === true` and tests
518
+ `rule.priority <= execution.rule.priority`. Every rule in
519
+ `app/data/database/dynamic-actions/data.json` uses the default `priority = 0`, so `0 <= 0` →
520
+ `return false`: **B never executes any rule for the rest of the session.** If a higher-priority rule
521
+ ever does get through, `terminateActiveExecution` destroys entity A's executor from B's context.
522
+ - **Fix**: reset execution state at the context boundary — in `Context.link()` set
523
+ `this.execution.rule = null; this.execution.executor = null;`, and in `Context.unlink()` destroy a
524
+ still-built executor before clearing.
525
+ - **Confidence**: Confirmed
526
+
527
+ ### ECS-30 `match()` hands actions a live view of a shared scope stack that it unwinds before the action runs
528
+ - **Severity**: MAJOR
529
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/dynamic_actions/DynamicActorSystem.js:536-551`;
530
+ `.../actions/definition/WriteToBlackboardActionDescription.js:24-35`
531
+ - **Defect**: `match()` passes `this.scope.proxy` — a proxy over the system-wide, mutable `DataScope` —
532
+ as the action `context`, then calls `this.scope.unwind(top)` on line 551, dropping every frame it
533
+ just pushed (blackboards, `{now, entity}`, the event frame, the entity proxy scope).
534
+ `WriteToBlackboardActionDescription` captures that proxy in an `ActionBehavior` closure and only
535
+ calls `expression.evaluate(context)` when the behavior ticks. `BehaviorSystem.link()` only calls
536
+ `behavior.initialize()`; behaviors are first ticked in `BehaviorSystem.update()`, i.e. no earlier
537
+ than the next frame.
538
+ - **Failure scenario**: a rule with `"action": {"type":"Write", "name":"last_bark_time", "value":"now"}`.
539
+ `match()` pushes `{now: 12.5, entity}`, matches, builds the executor, then unwinds. Next frame the
540
+ `ActionBehavior` ticks and evaluates `now` against an empty scope → `undefined`, so
541
+ `blackboard.acquire("last_bark_time", …).set(undefined)` instead of `12.5`. With two actors handling
542
+ events in the same frame the deferred read can also resolve against the *other* actor's frames if
543
+ those are still on the stack.
544
+ - **Fix**: snapshot the scope before handing it to an action — materialise the proxy into a plain
545
+ object in `executeRule` (as `SendRequestActionDescription` already does eagerly for
546
+ `action_context`), or have `WriteToBlackboardActionDescription.execute` evaluate the expression
547
+ eagerly and defer only the blackboard write.
548
+ - **Confidence**: Confirmed
549
+
550
+ ### ECS-31 `PointFitnessGaussianBlur` serializes a `quality` field that is never stored
551
+ - **Severity**: MAJOR
552
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/evaluation/json/serializeToJSON_PointFitnessFunction.js:61`
553
+ - **Defect**: The serializer writes `quality: value.quality`, but `PointFitnessGaussianBlur` never
554
+ stores it — `from()` consumes it only to compute `samples_x/y/z = size * quality` and discards it.
555
+ The deserializer (`deserializeFromJSON_PointFitnessFunction.js:45,51`) destructures `quality` with no
556
+ default and forwards it to `from()`, whose own default `quality = 5` then applies.
557
+ - **Failure scenario**: `PointFitnessGaussianBlur.from(src, 4, 4, 4, 1)` → `samples = 4,4,4`
558
+ (64 source evaluations per point). Serialize → `{size_x:4, size_y:4, size_z:4, quality: undefined}`
559
+ (and `undefined` is dropped entirely by `JSON.stringify`). Deserialize → `quality = 5` →
560
+ `samples = 20,20,20`, i.e. 8000 source evaluations per point and a numerically different blur.
561
+ - **Fix**: store the value in `from()` (`r.quality = quality;`) with a matching class field, or derive
562
+ it on serialize as `value.samples_x / value.size_x`.
563
+ - **Confidence**: Confirmed
564
+
565
+ ### ECS-32 `PointFitnessGaussianBlur` kernel is offset by half a sample on every axis
566
+ - **Severity**: MINOR
567
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/evaluation/complex/PointFitnessGaussianBlur.js:67-69,80,89,99`
568
+ - **Defect**: `half_samples_x = samplesX / 2` with `local_x = ix - half_samples_x` gives taps at
569
+ `-n/2 … n/2 - 1`, which is not centred on the sample point. A centred kernel needs `(samplesX - 1) / 2`.
570
+ - **Failure scenario**: `samples_x = 5, size_x = 10`. Taps land at
571
+ `local_x ∈ {-2.5,-1.5,-0.5,0.5,1.5}` → spatial offsets `{-5,-3,-1,1,3}` instead of
572
+ `{-5,-2.5,0,2.5,5}`. Both the gaussian weights and the sample positions are biased by half a tap, so
573
+ blurring a step edge shifts it by 1 world unit in −x on each axis, and the point being blurred is
574
+ never itself sampled.
575
+ - **Fix**: `const half_samples_x = (samplesX - 1) / 2;` (and likewise for y, z).
576
+ - **Confidence**: Confirmed
577
+
578
+ ### ECS-33 `Animation.flags` is in neither serialization path, so a paused animation resumes after load
579
+ - **Severity**: MINOR
580
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/animation/Animation.js:40,153-158`;
581
+ `.../animation/AnimationSerializationAdapter.js:15-28`
582
+ - **Defect**: `flags` (carrying `Playing` and `MeshSizeCulling`) is written by neither `toJSON` nor
583
+ `serialize`, and the field initialiser unconditionally sets `Playing | MeshSizeCulling`.
584
+ `equals()`/`hash()` also ignore `flags` while including `debtTime`.
585
+ - **Failure scenario**: `animation.isPlaying = false` (a supported setter, covered by
586
+ `Animation.spec.js:58-62`), save via `BinaryBufferSerializer`, reload → `flags` is back to the
587
+ constructor default and the animation plays again. Two `Animation` components differing only in
588
+ `isPlaying` also report `equals() === true` and identical `hash()`.
589
+ - **Fix**: add `flags` to the binary adapter (bumping `version`) and to `toJSON`/`fromJSON`
590
+ (defaulting to `MeshSizeCulling | Playing`), and include it in `equals`/`hash`.
591
+ - **Confidence**: Confirmed
592
+
593
+ ### ECS-34 `AnimationSystem` re-subscribes to clip signals on every mesh change and never unsubscribes
594
+ - **Severity**: MINOR
595
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/systems/AnimationSystem.js:116-117,150,221-230`
596
+ - **Defect**: `updateAnimationState` unconditionally does `clips.on.added.add(initAnimationClip)` and
597
+ `clips.on.removed.add(...)` with fresh closures each time. It runs from `registerAnimation` (line 150),
598
+ which runs both from `link()` and from every `MeshEvents.DataSet` event. `unlink()` removes only the
599
+ `MeshEvents.DataSet` entity listener; the clip-signal handlers are never removed, and `Signal.add`
600
+ does not deduplicate.
601
+ - **Failure scenario**: `MeshSystem.process` sets a placeholder mesh (DataSet #1) then the real asset
602
+ (DataSet #2) for every async-loaded mesh with `__use_placeholder_mesh` on — together with `link()`
603
+ that is 3 registrations. Every later runtime mesh swap adds another. After N mesh sets, a single
604
+ `animation.clips.add(clip)` invokes `startClip` N times, and the handler list grows without bound for
605
+ the component's life; unlink/relink cycles accumulate across cycles too.
606
+ - **Fix**: keep references to the two handlers on the component, remove any previous pair at the top of
607
+ `updateAnimationState`, and remove them in `unlink()`.
608
+ - **Confidence**: Confirmed
609
+
610
+ ### ECS-35 `SerializationMetadata` binary adapter truncates a documented 32-bit flag field to 8 bits
611
+ - **Severity**: MINOR
612
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/components/SerializationMetadataSerializationAdapter.js:15,24`
613
+ - **Defect**: `flags` is documented as a "32 bit bitfield" (`SerializationMetadata.js:19-22`) and
614
+ `toJSON` writes the full number, but the binary adapter uses `writeUint8`/`readUint8`.
615
+ - **Failure scenario**: a consumer defines a ninth flag `Foo = 1 << 8`. `metadata.setFlag(Foo)` →
616
+ `flags === 256`. `toJSON()` preserves `256`; the binary path writes `256 & 0xFF === 0` and reads back
617
+ `flags === 0`, silently losing the flag on save/load.
618
+ - **Fix**: `writeUint32`/`readUint32`, bumping the adapter `version` and adding an upgrader.
619
+ - **Confidence**: Confirmed
620
+
621
+ ---
622
+
623
+ ## Storage / serialization
624
+
625
+ ### ECS-36 Schema back-reference is looked up by address in the schema-*object* map, then dereferenced
626
+ - **Severity**: MAJOR
627
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter2.js:416`
628
+ - **Defect**: `#deserializeObject` resolves a `HEADER_TAG_REF_SCHEMA` record with
629
+ `this.#schema_table.getRecord(schema_address)`. `getRecord(schema)` looks up `#schema_map`, which is
630
+ keyed by the **schema object** (`#schema_map.set(record.schema, record)`); the address-keyed map is
631
+ `#address_map`, reachable only via `getSchemaAt(address)`. Passing a number can never hit, so
632
+ `record` is `undefined` and line 419 (`record.typeName`) throws. The whole `HEADER_TAG_REF_SCHEMA`
633
+ decode branch can only crash.
634
+ - **Failure scenario**: the `schema` parameter of `serialize(buffer, object, schema)` is a documented
635
+ optional argument. `const S = {v: undefined}; a2.serialize(buf, {v:1}, S); a2.serialize(buf, {v:2}, S);`
636
+ — the first object writes `HEADER_TAG_INLINE_SCHEMA` and interns `S`; the second takes the `else`
637
+ branch at line 375 and writes `HEADER_TAG_REF_SCHEMA` + address. On read, the first decodes fine, the
638
+ second throws `TypeError: Cannot read properties of undefined (reading 'typeName')`. Same for a single
639
+ object whose explicit schema reuses one sub-schema object for two fields.
640
+ - **Fix**: `record = this.#schema_table.getSchemaAt(schema_address);`. Also reset `#schema_table` when
641
+ switching an instance between writing and reading — write-side records are registered at write-buffer
642
+ positions and never cleared, so they can shadow read-side records at the same address.
643
+ - **Confidence**: Confirmed
644
+
645
+ ### ECS-37 Per-component transient flag is ignored whenever the dataset has no `SerializationMetadata` type
646
+ - **Severity**: MAJOR
647
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/storage/BinaryBufferSerializer.js:26-34`
648
+ - **Defect**: `isEntityTransient` returns `false` on `smComponentIndex === -1` **before** checking
649
+ `componentInstance[COMPONENT_SERIALIZATION_TRANSIENT_FIELD]`. The two mechanisms are independent —
650
+ the magic field is a per-instance opt-out that does not require a `SerializationMetadata` component
651
+ to exist — but it is gated behind one.
652
+ - **Failure scenario**: `EntityComponentDataset.computeComponentTypeIndex` returns `-1` for an
653
+ unregistered type, and a type is only registered once some entity actually receives one. In a dataset
654
+ where nothing ever adds a `SerializationMetadata`, `smComponentIndex === -1`. Mark any component
655
+ transient the way the game does (`guiElement[COMPONENT_SERIALIZATION_TRANSIENT_FIELD] = true` in
656
+ `setupStrategyHeadsUpDisplays.js:98`, `sockets[...] = true` in `CombatUnitEntityDecorator.js:660`,
657
+ `this.__transform2GridPosition[...] = true` in `CharacterMoveAsynchronousAction.js:236`) and it is
658
+ written to the save anyway, then resurrected on load as a duplicate of a runtime-only component.
659
+ - **Fix**: move the `COMPONENT_SERIALIZATION_TRANSIENT_FIELD` check above the `smComponentIndex === -1`
660
+ early return.
661
+ - **Confidence**: Confirmed
662
+
663
+ ### ECS-38 Object adapter resolves class identity through *inherited* `typeName`
664
+ - **Severity**: MAJOR
665
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter.js:59-61`
666
+ - **Defect**: `const typeName = Klass.typeName;` is a normal static lookup, so an ES class that does not
667
+ declare its own `typeName` silently resolves to its ancestor's. The object-v2 writer explicitly
668
+ guards this with `Object.prototype.hasOwnProperty.call(ctor, "typeName")`
669
+ (`BinaryObjectSerializer.js:270`) and has a dedicated regression test, so the v1 adapter is the
670
+ un-fixed half.
671
+ - **Failure scenario**: `BehaviorComponentSerializationAdapter.serialize`
672
+ (`engine/intelligence/behavior/ecs/BehaviorComponentSerializationAdapter.js:38`) routes every behavior
673
+ through `this.objectAdapter.serialize(buffer, behavior)`. `Behavior.js:78` sets
674
+ `Behavior.typeName = "Behavior"`, while `SelectorBehavior`, `WeightedRandomBehavior`, `BranchBehavior`
675
+ and `ConditionalBehavior` declare none. Saving an entity whose behavior tree contains a
676
+ `SelectorBehavior` therefore looks up adapter `"Behavior"` — not registered — and the save aborts with
677
+ `No adapter found for class 'Behavior'`, naming a class that is not in the tree. If any ancestor *does*
678
+ have a registered adapter the failure is worse: the subclass is written with the ancestor's payload
679
+ and read back as the ancestor, dropping all subclass state.
680
+ - **Fix**: require an own `typeName` —
681
+ `Object.prototype.hasOwnProperty.call(Klass, "typeName") ? Klass.typeName : undefined` — and throw
682
+ naming the actual constructor when absent, mirroring `BinaryObjectSerializer.writeValue`.
683
+ - **Confidence**: Confirmed
684
+
685
+ ### ECS-39 The last component type in a stream is never finalized on load
686
+ - **Severity**: MINOR
687
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/storage/BinaryBufferDeSerializer.js:117-127`
688
+ - **Defect**: `cycleFunction` finalizes the *previous* type at the top of the next loop iteration
689
+ (`if (typesDecoded > 0) collectionDeSerializer.finalize();`), but the `typesLeft === 0` exit at
690
+ line 117 returns `TaskSignal.EndSuccess` first. So `BinaryCollectionDeSerializer.finalize()` — and
691
+ hence `adapter.finalize()` — never runs for the final type. The write path
692
+ (`BinaryBufferSerializer.js:194`) finalizes every type correctly.
693
+ - **Failure scenario**: load a buffer containing types `[Name, CombatUnit]`. `CombatUnit` elements are
694
+ read to exhaustion, `typesLeft` is `0`, the task ends, and
695
+ `CombatUnitSerializationAdapter.finalize()`
696
+ (`model/game/ecs/component/unit/serialization/CombatUnitSerializationAdapter.js:51`) is never invoked,
697
+ so its module-static `serialization_array` is left holding stale entries instead of being spliced
698
+ empty. The deserializer is also left in `State.Ready`, so a subsequent `initialize()` on the same
699
+ instance throws `IllegalStateException`. Which adapter is affected depends on which component type
700
+ happens to be last, so the leak is non-deterministic across saves.
701
+ - **Fix**: finalize the in-flight collection before returning `EndSuccess`, guarding against
702
+ double-finalize on a re-entered cycle.
703
+ - **Confidence**: Confirmed
704
+
705
+ ---
706
+
707
+ ## GUI / HUD / speaker
708
+
709
+ ### ECS-40 `LineSetDescription.collect` walks the root's children instead of the popped node's — infinite loop
710
+ - **Severity**: CRITICAL
711
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/speaker/lines/sets/LineSetDescription.js:56`
712
+ - **Defect**: Inside the traversal loop the popped node is `set`, but the child-set expansion reads
713
+ `const sets = this.sets;` — the *root's* children — on every iteration. The root's children are
714
+ therefore re-pushed forever and `pointer` never drains.
715
+ - **Failure scenario** (reproduced): a `voice-line-sets` entry with a non-empty `sets` array
716
+ (schema-supported: `LineSetDescriptionTable.linkOne` reads `sets` from JSON, and `validateOne` exists
717
+ purely to cycle-check that graph). Root `R{lines:[l-root], sets:[C]}`, `C{lines:[l-child]}` →
718
+ `collect` writes `l-root, l-child, l-child, l-child, …` without bound; `destination` grows until OOM.
719
+ `VoiceSystem.sayLineFromSet` calls `set.collect(temp_lines, 0)`, so the tab hangs on the first nested
720
+ bark set anyone authors.
721
+ - **Fix**: `const sets = set.sets;`
722
+ - **Confidence**: Confirmed
723
+
724
+ ### ECS-41 `GUIElementSystem.unlink` calls `Signal.remove` without the context it subscribed with
725
+ - **Severity**: MAJOR
726
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/gui/GUIElementSystem.js:261` (subscribe at `:248`)
727
+ - **Defect**: `link` does `component.visible.onChanged.add(handleComponentVisibilityChange, {el, system, entity})`;
728
+ `unlink` does `component.visible.onChanged.remove(handleComponentVisibilityChange)` with no `thisArg`.
729
+ `Signal.remove` matches on `handle === h && context === thisArg`, so it matches nothing and returns
730
+ `false`.
731
+ - **Failure scenario** (reproduced against the real `Signal`): `add(handler, ctx)` then `remove(handler)`
732
+ → returns `false`, `hasHandlers()` stays `true`, the next `send1` still runs the handler. Concretely,
733
+ `ecd.removeComponentFromEntity(entity, GUIElement)` (real caller:
734
+ `model/game/scenes/combat/UnitStateManager.js:437`) or `Entity.destroy()` → `Entity.build()` leaves
735
+ the old subscription in place, and the next `link` adds a second. With two handlers,
736
+ `component.visible.set(true)` runs `attachComponent` twice → `parent.addChild(component.view)` twice
737
+ → `View.addChild` (`view/View.js:365`) pushes the same child into `parent.children` twice (the
738
+ `isLinked` assert that would catch it is stripped in production). `removeChild` then drops only the
739
+ first entry, so `parent.children` keeps a stale entry whose `el` is no longer in the DOM; a later
740
+ `removeAllChildren` calls `this.el.removeChild(child.el)` on a non-child and throws `NotFoundError`.
741
+ - **Fix**: store the context object (as `ViewportPositionSystem` does in `this.data`) and pass it to
742
+ `remove`.
743
+ - **Confidence**: Confirmed
744
+
745
+ ### ECS-42 `ViewportPositionSystem.unlink` never unsubscribes from `el.view.size.onChanged`
746
+ - **Severity**: MAJOR
747
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/gui/position/ViewportPositionSystem.js:154` (unlink at `:167-183`)
748
+ - **Defect**: `link` makes five `onChanged.add(updatePosition, eventContext)` subscriptions —
749
+ `vp.position`, `vp.offset`, `vp.anchor`, `vp.enabled` and `el.view.size`. `unlink` removes only the
750
+ first four.
751
+ - **Failure scenario**: `GUIEngine.createModal` (`meep/src/engine/ui/GUIEngine.js:215`) does
752
+ `window.removeComponent(ViewportPosition)` immediately after `openWindow` built the entity,
753
+ deliberately freeing the modal from viewport positioning while the `GUIElement` and its
754
+ `ui-modal-window-container` view stay live on screen. `unlink` runs, but `updatePosition` stays bound
755
+ to that container's `size.onChanged` with a context holding the now-detached `vp`. Any later size
756
+ change re-invokes `positionComponent(el, vp)`, overwriting `view.position` from the stale
757
+ `vp.position`, overwriting `view.transformOrigin` with the stale `vp.anchor` (default `0,0`,
758
+ discarding the `0.5,0.5` set at `GUIEngine.js:213`), and calling `el.visible.set(...)` — if the stale
759
+ position is out of bounds and `stickToScreenEdge` is false (the default), `el.visible.set(false)`
760
+ makes `GUIElementSystem` detach the modal from the DOM and the open modal vanishes.
761
+ - **Fix**: add `el.view.size.onChanged.remove(updatePosition, eventContext);` inside the
762
+ `eventContext !== undefined` block in `unlink`.
763
+ - **Confidence**: Confirmed
764
+
765
+ ### ECS-43 `HeadsUpDisplay.toJSON` writes `transformPerspectiveRotation`, `fromJSON` reads `perspectiveRotation`
766
+ - **Severity**: MAJOR
767
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/gui/hud/HeadsUpDisplay.js:86` vs `:90`
768
+ - **Defect**: The JSON key emitted for the `PerspectiveRotation` flag does not match the key the reader
769
+ destructures, so the reader always falls back to its `false` default.
770
+ - **Failure scenario** (reproduced): a HUD with `PerspectiveRotation` set — exactly what
771
+ `makeEnemyGroupHud` in `model/game/scenes/strategy/gui/setupStrategyHeadsUpDisplays.js` configures.
772
+ `toJSON()` yields `{"worldOffset":…,"transformWorldOffset":true,"transformPerspectiveRotation":true}`;
773
+ `HeadsUpDisplay.fromJSON(that)` yields `flags = 1` instead of `3`,
774
+ `getFlag(PerspectiveRotation) === false`, and `a.equals(b) === false`. The screen-space billboard
775
+ rotation is silently dropped on any JSON round-trip. The binary adapter round-trips the flag
776
+ correctly, so the two persistence paths disagree.
777
+ - **Fix**: emit `perspectiveRotation` in `toJSON`, or rename the `fromJSON` destructure to
778
+ `transformPerspectiveRotation`. Pick one and keep both sides identical.
779
+ - **Confidence**: Confirmed
780
+
781
+ ### ECS-44 `GUIElementSystem.link` logs "does not have a view" and then dereferences the missing view
782
+ - **Severity**: MINOR
783
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/gui/GUIElementSystem.js:212`
784
+ - **Defect**: The un-managed-with-no-view branch does `console.error(...)` with no `return`. Execution
785
+ falls through to `if (component.visible.getValue()) this.attachComponent(component, entity)`
786
+ (`visible` defaults to `true`), and `attachComponent` reaches `parent.addChild(component.view)` with
787
+ `component.view === null`.
788
+ - **Failure scenario**: `const c = new GUIElement(); c.clearFlag(GUIElementFlag.Managed);` added to an
789
+ entity — precisely the state the error message describes. `View.addChild(null)` executes
790
+ `child.parent = this;` (its guarding asserts are stripped in production) →
791
+ `TypeError: Cannot set properties of null (setting 'parent')` propagates out of `link`, aborting the
792
+ component-added observer dispatch for that entity so later systems never link.
793
+ - **Fix**: `return;` after the `console.error` at `:212`.
794
+ - **Confidence**: Confirmed
795
+
796
+ ### ECS-45 `ButtonViewEntity.finalize` destroys the structural children created in its constructor
797
+ - **Severity**: MINOR
798
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/gui/view/ButtonViewEntity.js:104-107` (children created at `:17-23`)
799
+ - **Defect**: The constructor adds the `foreground` and `background` `EmptyView`s the button's CSS is
800
+ built on. `finalize()` calls `removeAllChildren()`, which removes them too — but `ViewEntity.finalize`
801
+ is documented as bringing the view "to initial state ready to be destroyed **or re-used**", and
802
+ `GUIElementSystem` implements exactly that re-use contract (`unlink` calls `view.finalize()` and
803
+ clears `Initialized`; `attachComponent` re-calls `view.initialize(...)` when it sees
804
+ `Managed && !Initialized`).
805
+ - **Failure scenario**: an entity carrying a `view.Button` `GUIElement` (registered at
806
+ `model/game/GameClassRegistry.js:89`) goes through link → unlink → link. On the second `initialize`
807
+ the label is re-added but `.foreground`/`.background` are gone, so the button renders with no
808
+ background fill and dead hover/press styling. `LocalizedLabelViewEntity` is correct precisely because
809
+ its constructor adds no children.
810
+ - **Fix**: create the foreground/background children in `initialize()`, or have `finalize()` remove only
811
+ the label child.
812
+ - **Confidence**: Confirmed
813
+
814
+ ### ECS-46 `LineSetDescriptionTable.validateOne` reports a false circular dependency for a shared child set
815
+ - **Severity**: MINOR
816
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/speaker/lines/sets/LineSetDescriptionTable.js:79`
817
+ - **Defect**: The check treats *any* second visit to a node as a cycle
818
+ (`if (visited.hasOwnProperty(set.id))`) rather than tracking the current traversal path, so a DAG with
819
+ a shared child is rejected.
820
+ - **Failure scenario**: `A{sets:[B,C]}`, `B{sets:[D]}`, `C{sets:[D]}` — no cycle. Traversal pops `A`,
821
+ pushes `B,C`; pops `C`, pushes `D`; pops `D`, marks visited; pops `B`, pushes `D`; pops `D` again →
822
+ `visited` hit → `errorConsumer("Circular dependency detected: D -> C -> A")` and `return false`. Valid
823
+ voice-line-set data fails validation at load, and the reported path names the wrong parent.
824
+ - **Fix**: track the recursion stack (mark on entry, unmark on exit) or colour nodes white/grey/black,
825
+ and only report a cycle on re-entering a grey node.
826
+ - **Confidence**: Confirmed
827
+
828
+ ### ECS-47 `VoiceSystem.sayLineFromSet` dereferences `undefined` when the set yields no lines
829
+ - **Severity**: MINOR
830
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/speaker/VoiceSystem.js:313-315`
831
+ - **Defect**: `weightedRandomFromArray` returns `undefined` when `length === 0`
832
+ (`core/collection/array/weightedRandomFromArray.js:26-28`), and the result is used unguarded as
833
+ `selected_line.id`. The neighbouring "set not found" case at `:303` warns and returns, so graceful
834
+ handling of bad data is the intended contract here.
835
+ - **Failure scenario**: a `voice-line-sets` entry with an empty or omitted `lines` array (`linkOne`
836
+ defaults `lines = []`) — a placeholder or not-yet-authored bark set.
837
+ `ecd.sendEvent(entity, VoiceEvents.SpeakSetLine, {id: thatSet})` → `collect` returns `0` →
838
+ `TypeError: Cannot read properties of undefined (reading 'id')` thrown out of the entity-event dispatch.
839
+ - **Fix**: guard on `selected_line === undefined` after the `weightedRandomFromArray` call.
840
+ - **Confidence**: Confirmed
841
+
842
+ ---
843
+
844
+ ## Attachment / parent / transform / IK
845
+
846
+ ### ECS-49 `EntityNode`'s `parent` setter strips `TransformAttachment` and never re-adds it
847
+ - **Severity**: MAJOR
848
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/parent/EntityNode.js:240-270`
849
+ - **Defect**: The setter removes both components up front
850
+ (`let parent_entity = this_entity.removeComponent(ParentEntity); let attachment = this_entity.removeComponent(TransformAttachment);`)
851
+ and, in the `node !== null` branch, explicitly re-adds `ParentEntity` via `this_entity.add(parent_entity)`
852
+ — but the attachment is only put back through `this.__safe_get_attachment()`, which runs **only when
853
+ `attachment === null`**. When the node already had a `TransformAttachment`, the removed instance is
854
+ mutated (`attachment.parent = parent_entity_id`) and dropped on the floor.
855
+ - **Failure scenario** (reproduced): three built `EntityNode`s A, B, C each with a `Transform`.
856
+ ```js
857
+ A.addChild(C); // C -> ParentEntity{0}, TransformAttachment{parent:0}
858
+ C.parent = B; // public setter, the documented re-parent entry point
859
+ ```
860
+ Observed: `ParentEntity.entity` correctly becomes `1`, but
861
+ `C.entity.getComponent(TransformAttachment)` is `null` and
862
+ `ds.getComponent(C.entity.id, TransformAttachment)` is `undefined`. `TransformAttachmentSystem` has
863
+ unlinked C, so C's world transform is no longer driven by any parent — the node visually detaches
864
+ while still being logically parented (and still gets destroyed when B is destroyed).
865
+ `addChild` escapes this only because it routes through `removeChild` → `node.parent = null` first,
866
+ which nulls both.
867
+ - **Fix**: after `attachment.parent = parent_entity_id`, put the component back —
868
+ `if (this_entity.getComponent(TransformAttachment) === null) this_entity.add(attachment);` — or
869
+ simply do not remove `TransformAttachment` at the top when `node !== null`.
870
+ - **Confidence**: Confirmed
871
+
872
+ ### ECS-50 `InverseKinematicsSystem` declares read-only access to `Mesh` because of an undefined enum member
873
+ - **Severity**: MINOR
874
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/ik/InverseKinematicsSystem.js:18`
875
+ - **Defect**: `ResourceAccessSpecification.from(Mesh, ResourceAccessKind.Read | ResourceAccessSpecification.Write)`
876
+ — `Write` is a member of `ResourceAccessKind`, not of `ResourceAccessSpecification`. The latter has no
877
+ such static, so the expression is `1 | undefined === 1`: the system declares `Read` only, while
878
+ `TwoBoneInverseKinematicsSolver.solve` actually mutates the mesh's bone quaternions
879
+ (`boneA.setRotationFromQuaternion(...)`, `TwoBoneInverseKinematicsSolver.js:173-174`).
880
+ - **Failure scenario**: `EntityManager.updateExecutionOrder`'s `scoreSystem` weights a `Write` component
881
+ at `attached_edge_count * 2` and a `Read` at `* 1`, and `computeSystemComponentDependencyGraph` only
882
+ emits edges out of a system that declares `Write`/`Create`. With the typo, `InverseKinematicsSystem`
883
+ contributes no dependency edges and is scored as a pure `Mesh` reader, so it can be ordered *after*
884
+ the systems that consume mesh/bone state in the same `simulate()` call — IK corrections (feet planted
885
+ on terrain) are applied one frame late, producing visible foot sliding / ground penetration on the
886
+ frame a character's pose changes.
887
+ - **Fix**: `ResourceAccessKind.Read | ResourceAccessKind.Write`.
888
+ - **Confidence**: Confirmed
889
+
890
+ ### ECS-51 `OneBoneSurfaceAlignmentSolver` recovers the alignment angle with `asin`, which cannot exceed 90°
891
+ - **Severity**: MINOR
892
+ - **File**: `H:/git/moh/app/src/mir-engine/meep/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js:170-175`
893
+ - **Defect**: `axis.crossVectors(v3, Vector3.forward); const betaSine = axis.length(); const beta = Math.asin(clamp(betaSine, -1, 1));`
894
+ — `|a × b| = sin θ` is ambiguous between `θ` and `π − θ`, and `asin` always returns the branch in
895
+ `[0, π/2]`. The angle between the contact normal and the bone axis is not recoverable this way once it
896
+ exceeds 90°.
897
+ - **Failure scenario**: bone axis `(0,0,1)` in bone space, contact normal in bone space
898
+ `(0, 0.9, −0.436)` — 116° apart. `|cross| = 0.9` → `beta = asin(0.9) = 64.2°`. The bone is rotated
899
+ 64° instead of 116°, so instead of squaring up to the surface it ends up 52° off, tilted into the
900
+ surface. Any surface whose normal is more than 90° from the bone's own axis (steep slopes,
901
+ overhangs, an inverted foot-bone rig) is aligned to the wrong angle rather than clamped by
902
+ `constraint.limit`.
903
+ - **Fix**: recover the angle unambiguously — `Math.atan2(axis.length(), v3.dot(Vector3.forward))` —
904
+ before normalising `axis`.
905
+ - **Confidence**: Likely (the trigonometric limitation is unambiguous; I did not exercise it against a
906
+ real rig, so how often a live skeleton crosses 90° is unverified)
907
+
908
+ ---
909
+
910
+ ## Status of previously-known items
911
+
912
+ | Item | Status |
913
+ | --- | --- |
914
+ | `Entity.promiseEvent` calling `removeEventListener` instead of `add` | **FIXED** (`Entity.js:326-327` now `addEventListener`) |
915
+ | `EntityComponentDataset.dropData` not clearing `__entityAnyEventListeners` | **FIXED** (`EntityComponentDataset.js:1882`) |
916
+ | `HeadsUpDisplaySystem` scaling world-offset Z by `scale.y` | **FIXED** (`HeadsUpDisplaySystem.js:88` uses `transform_scale.z`) |
917
+ | `obtainTileAtWorldPosition2D` dereferencing an out-of-bounds tile | **STILL PRESENT** (`TerrainTileManager.js:487-489`; `getTileByWorldPosition2D` can return `undefined`, then `tile.gridPosition`) |
918
+ | `FogOfWar.rebuildSampler` width/height swap | **FIXED** (`FogOfWar.js:707-713`, correct `Sampler2D(data, itemSize, width, height)` order) |
919
+ | `TerrainTileManager.stitchTile` floor-vs-ceil | **STILL PRESENT** (`:566` uses `floor`; `:353`, `:405`, `:408`, `:422` all use `ceil`) |
920
+ | Event-dispatch pointer restore missing `try/finally` | **STILL PRESENT** (`EntityComponentDataset.js:2138-2162` and `2180-2204`) |