@woosh/meep-engine 3.0.2 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +188 -188
- package/README.md +152 -145
- package/editor/Editor.js +598 -598
- package/editor/EditorCameraNavigation.js +205 -205
- package/editor/EditorKeyMap.js +182 -182
- package/editor/SelectionVisualizer.js +127 -127
- package/editor/actions/concrete/ActionInvalidateSampler.js +27 -27
- package/editor/actions/concrete/PatchTerrainHeightAction.js +85 -85
- package/editor/camera/camera_framing.js +104 -104
- package/editor/clipboard.js +184 -184
- package/editor/ecs/component/editors/ObservedIntegerEditor.js +22 -22
- package/editor/ecs/component/editors/ObservedStringEditor.js +34 -34
- package/editor/ecs/component/editors/common/two_way_sync.js +50 -50
- package/editor/ecs/component/editors/ecs/GridObstacleEditor.js +19 -19
- package/editor/ecs/component/editors/ecs/ParameterLookupTableEditor.js +35 -35
- package/editor/ecs/component/editors/ecs/ParameterTrackEditor.js +17 -17
- package/editor/ecs/component/editors/ecs/SGMeshEditor.js +24 -24
- package/editor/ecs/component/editors/ecs/ShadedGeometryEditor.js +33 -33
- package/editor/ecs/component/editors/ecs/Trail3DEditor.js +45 -45
- package/editor/ecs/component/editors/ecs/buildGridObstaclePreview.js +19 -19
- package/editor/ecs/component/editors/ecs/terrain/TerrainEditor.js +72 -72
- package/editor/ecs/component/editors/shade/MeshletGeometryEditor.js +30 -30
- package/editor/ecs/component/editors/shade/ShadeImageEditor.js +70 -70
- package/editor/ecs/component/editors/shade/ShadeMaterialEditor.js +34 -34
- package/editor/ecs/component/editors/shade/ShadeTextureEditor.js +46 -46
- package/editor/ecs/component/editors/shade/StandardShadeMaterialEditor.js +24 -24
- package/editor/ecs/component/prototypeObjectEditor.js +265 -265
- package/editor/ecs/component/registerBasicTypeEditors.js +100 -100
- package/editor/ecs/component/registerEngineComponentEditors.js +95 -95
- package/editor/entity_world_bounds.js +27 -27
- package/editor/particles/lut_ops.js +148 -148
- package/editor/particles/particleEditEvents.js +12 -12
- package/editor/particles/rebuildParticleEmitter.js +31 -31
- package/editor/persistence/EditorLayoutStore.js +71 -71
- package/editor/persistence/SceneMetadata.js +52 -52
- package/editor/persistence/SceneSlotManager.js +144 -144
- package/editor/persistence/buildNewSceneDataset.js +67 -67
- package/editor/persistence/files.js +68 -68
- package/editor/persistence/scene_import.js +143 -143
- package/editor/persistence/scene_serialization.js +79 -79
- package/editor/persistence/string_buffer_codec.js +41 -41
- package/editor/process/GridDisplayProcess.js +125 -125
- package/editor/process/SymbolicDisplayProcess.js +106 -108
- package/editor/process/symbolic/CameraSymbolicDisplay.js +146 -146
- package/editor/process/symbolic/LightSymbolicDisplay.js +172 -172
- package/editor/process/symbolic/PathSymbolicDisplay.js +118 -118
- package/editor/process/symbolic/PositionedMarkerSymbolicDisplay.js +79 -79
- package/editor/process/symbolic/SoundEmitterSymbolicDisplay.js +57 -57
- package/editor/process/symbolic/SymbolicDisplay.js +62 -62
- package/editor/process/symbolic/gizmo_draw_wire_box_oriented.js +85 -85
- package/editor/process/symbolic/gizmo_draw_wire_circle.js +53 -53
- package/editor/prototypeEditorShell.js +156 -156
- package/editor/selection/editor_pick.js +130 -130
- package/editor/selection/screen_rect_frustum.js +119 -119
- package/editor/style/controls.scss +168 -168
- package/editor/style/inspector.scss +223 -223
- package/editor/templates/entity_templates.js +167 -167
- package/editor/tools/GridPaintTool.js +273 -273
- package/editor/tools/SelectionTool.js +307 -307
- package/editor/tools/TransformToolV2.js +378 -378
- package/editor/tools/engine/ToolEngine.js +188 -188
- package/editor/tools/paint/TerrainTexturePaintTool.js +250 -250
- package/editor/tools/v2/BlenderCameraOrientationGizmo.js +501 -501
- package/editor/tools/v2/TransformControls.js +971 -971
- package/editor/tools/v2/handle/gizmo_drag_plane.js +141 -141
- package/editor/tools/v2/handle/gizmo_draw.js +399 -399
- package/editor/tools/v2/handle/gizmo_handles.js +364 -364
- package/editor/tools/v2/handle/gizmo_ray_shapes.js +239 -239
- package/editor/view/ecs/HierarchicalEntityListView.js +339 -339
- package/editor/view/ecs/components/DatGuiController.js +2 -2
- package/editor/view/ecs/components/common/AutoCanvasView.js +100 -100
- package/editor/view/ecs/components/common/TextController.js +59 -59
- package/editor/view/library/MeshLibraryView.js +175 -175
- package/editor/view/library/mesh_library_drag.js +63 -63
- package/editor/view/library/model_thumbnail_source.js +86 -86
- package/editor/view/makeEntityDecorators.js +125 -125
- package/editor/view/node-graph/NodeGraphSelection.js +89 -89
- package/editor/view/node-graph/NodeView.js +211 -211
- package/editor/view/node-graph/actions/ConnectionDeleteAction.js +36 -36
- package/editor/view/node-graph/actions/NodesMoveAction.js +41 -41
- package/editor/view/node-graph/connection_wire_geometry.js +107 -107
- package/editor/view/particles/ColorLutGradientView.js +291 -291
- package/editor/view/particles/ScalarLutCurveView.js +279 -279
- package/editor/view/prepareMeshLibrary.js +188 -188
- package/editor/view/shell/SceneTreePanelView.js +119 -119
- package/editor/view/v2/SplitView.js +2 -2
- package/package.json +94 -94
- package/samples/generation/generators/interactive/mir_generator_place_buff_objects.js +362 -362
- package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +738 -738
- package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +79 -79
- package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +93 -93
- package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +80 -80
- package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +164 -164
- package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +69 -69
- package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +102 -102
- package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +129 -129
- package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +72 -72
- package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +74 -74
- package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +169 -169
- package/src/DUPLICATION_AUDIT_2026_08_07.md +254 -254
- package/src/DUPLICATION_AUDIT_REMEDIATION.md +250 -250
- package/src/REVIEW_2026_08_06/adv-core.md +511 -511
- package/src/REVIEW_2026_08_06/adv-gfx.md +480 -480
- package/src/REVIEW_2026_08_06/adv-view.md +446 -446
- package/src/REVIEW_2026_08_06/api-core.md +296 -296
- package/src/REVIEW_2026_08_06/api-engine.md +195 -195
- package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +507 -507
- package/src/REVIEW_2026_08_06/core-binary.md +348 -348
- package/src/REVIEW_2026_08_06/core-collection.md +376 -376
- package/src/REVIEW_2026_08_06/core-math.md +221 -221
- package/src/REVIEW_2026_08_06/core-process.md +266 -266
- package/src/REVIEW_2026_08_06/core-science.md +223 -223
- package/src/REVIEW_2026_08_06/engine-ai-nav.md +279 -279
- package/src/REVIEW_2026_08_06/engine-animation.md +167 -167
- package/src/REVIEW_2026_08_06/engine-asset.md +166 -166
- package/src/REVIEW_2026_08_06/engine-ecs.md +920 -920
- package/src/REVIEW_2026_08_06/engine-input.md +197 -197
- package/src/REVIEW_2026_08_06/engine-misc.md +248 -248
- package/src/REVIEW_2026_08_06/engine-terrain.md +52 -52
- package/src/REVIEW_2026_08_06/generation.md +176 -176
- package/src/REVIEW_2026_08_06/geom-2d.md +434 -434
- package/src/REVIEW_2026_08_06/geom-3d.md +117 -117
- package/src/REVIEW_2026_08_06/gfx-gi.md +143 -143
- package/src/REVIEW_2026_08_06/gfx-render.md +740 -740
- package/src/REVIEW_2026_08_06/gfx-texture.md +690 -690
- package/src/REVIEW_2026_08_06/prior-criticals.md +49 -49
- package/src/REVIEW_2026_08_06/prior-highs.md +138 -138
- package/src/REVIEW_2026_08_06/test-gaps.md +364 -364
- package/src/REVIEW_2026_08_06/view.md +667 -667
- package/src/REVIEW_2026_08_06.md +610 -610
- package/src/avif/codec/dec/avif_dec.cpp +233 -233
- package/src/avif/codec/dec/avif_dec.js +16 -16
- package/src/core/binary/BitImage2.js +246 -246
- package/src/core/binary/align_32.js +21 -21
- package/src/core/binary/data_view/DataType2DataViewReaders.js +23 -23
- package/src/core/binary/data_view/DataType2DataViewWriters.js +25 -25
- package/src/core/binary/hash/XXH128Hash.js +55 -55
- package/src/core/binary/hash/XXH3_SECRET.js +25 -25
- package/src/core/binary/hash/xxh3_128.js +341 -341
- package/src/core/binary/hash/xxh3_64.js +241 -241
- package/src/core/binary/hash/xxh3_common.js +476 -476
- package/src/core/binary/lz4/LZ4_MAX_INPUT_SIZE.js +9 -9
- package/src/core/binary/lz4/lz4_compress_block.js +385 -385
- package/src/core/binary/lz4/lz4_compress_bound.js +25 -25
- package/src/core/binary/lz4/lz4_decompress_block.js +139 -139
- package/src/core/binary/lz4/lz4_decompress_bound.js +30 -30
- package/src/core/binary/meshopt/MESHOPT_GROUP_SIZE.js +18 -18
- package/src/core/binary/meshopt/MeshoptCompressionFilter.js +30 -30
- package/src/core/binary/meshopt/MeshoptCompressionMode.js +21 -21
- package/src/core/binary/meshopt/__meshopt_test_streams.js +237 -237
- package/src/core/binary/meshopt/meshopt_decode.js +73 -73
- package/src/core/binary/meshopt/meshopt_decode_byte_group.js +67 -67
- package/src/core/binary/meshopt/meshopt_decode_data_block.js +59 -59
- package/src/core/binary/meshopt/meshopt_decode_filter_color.js +69 -69
- package/src/core/binary/meshopt/meshopt_decode_filter_exponential.js +43 -43
- package/src/core/binary/meshopt/meshopt_decode_filter_octahedral.js +60 -60
- package/src/core/binary/meshopt/meshopt_decode_filter_quaternion.js +71 -71
- package/src/core/binary/meshopt/meshopt_decode_index_buffer.js +294 -294
- package/src/core/binary/meshopt/meshopt_decode_index_sequence.js +115 -115
- package/src/core/binary/meshopt/meshopt_decode_vertex_buffer.js +381 -381
- package/src/core/binary/meshopt/meshopt_read_uint_var.js +59 -59
- package/src/core/binary/reinterpret_float32_as_uint32.js +13 -13
- package/src/core/binary/reinterpret_uint32_as_float32.js +14 -14
- package/src/core/binary/type/DataType2TypedArrayConstructorMapping.js +35 -35
- package/src/core/bvh2/bvh3/ebvh_geometry_query_any_triangle_ray.js +149 -149
- package/src/core/bvh2/bvh3/ebvh_sort_for_traversal_depth_first.js +96 -96
- package/src/core/bvh2/bvh3/query/bvh_query_depth_range_in_frustum.js +101 -101
- package/src/core/bvh2/bvh3/query/bvh_query_leaves_generic.js +77 -77
- package/src/core/bvh2/bvh3/query/bvh_query_leaves_ray.js +90 -90
- package/src/core/bvh2/bvh3/query/bvh_query_leaves_ray_segment.js +94 -94
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_generic.js +86 -86
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_capsule.js +101 -101
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_sphere.js +92 -92
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_ray.js +96 -96
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_ray_segment.js +100 -100
- package/src/core/bvh2/visual/BVHGeometry.js +131 -131
- package/src/core/bvh8/BVH8.js +436 -436
- package/src/core/bvh8/aabb3_round_to_float32.js +22 -22
- package/src/core/bvh8/build/BVH8Converter.js +588 -588
- package/src/core/bvh8/build/TriangleCluster.js +122 -122
- package/src/core/bvh8/build/aabb3_compute_merge_cost.js +28 -28
- package/src/core/bvh8/build/aabb3_from_triangle_by_index.js +17 -17
- package/src/core/bvh8/build/bvh8_build_for_geometry.js +303 -303
- package/src/core/bvh8/build/bvh8_from_proxy.js +256 -256
- package/src/core/bvh8/build/byte.js +9 -9
- package/src/core/bvh8/build/encode_bounds_e.js +11 -11
- package/src/core/bvh8/bvh8_convert_to_dot.js +132 -132
- package/src/core/bvh8/bvh8_count_primitives.js +98 -98
- package/src/core/bvh8/bvh8_geometry_validate.js +138 -138
- package/src/core/bvh8/bvh8_geometry_validate_indirect.js +166 -166
- package/src/core/bvh8/bvh8_get_node_bounds.js +34 -34
- package/src/core/bvh8/bvh8_get_node_child_bounds.js +52 -52
- package/src/core/bvh8/bvh8_node_child_surface_area.js +18 -18
- package/src/core/bvh8/bvh8_node_count_triangles.js +27 -27
- package/src/core/bvh8/bvh8_quality.js +72 -72
- package/src/core/bvh8/bvh8_validate_structure.js +87 -87
- package/src/core/clipboard/obtainClipBoard.js +29 -29
- package/src/core/clipboard/safeClipboardReadText.js +55 -55
- package/src/core/clipboard/safeClipboardWriteText.js +23 -23
- package/src/core/collection/array/typed/typed_array_value_denormalize.js +52 -52
- package/src/core/collection/array/typed/typed_array_value_normalize.js +51 -51
- package/src/core/collection/union-find/union_find.js +79 -79
- package/src/core/color/YCoCg/rgb_to_ycocg.js +20 -20
- package/src/core/color/YCoCg/ycocg_to_rgb.js +17 -17
- package/src/core/color/colormap/MAGMA_LUT.js +26 -26
- package/src/core/color/illuminant/D65_TRISTIMULUS_XYZ.js +19 -19
- package/src/core/color/lab/lab_apply_hunt_adjustment.js +27 -27
- package/src/core/color/lab/lab_distance_hyab.js +23 -23
- package/src/core/color/lab/xyz_to_lab.js +62 -62
- package/src/core/color/operations/color_darken.js +24 -24
- package/src/core/color/operations/color_desaturate.js +24 -24
- package/src/core/color/operations/color_lighten.js +24 -24
- package/src/core/color/operations/color_saturate.js +24 -24
- package/src/core/color/operations/color_scale_okhsv_channel.js +45 -45
- package/src/core/color/tonemap/tonemap_aces.js +41 -41
- package/src/core/color/ycxcz/xyz_to_ycxcz.js +28 -28
- package/src/core/color/ycxcz/ycxcz_to_xyz.js +19 -19
- package/src/core/dom/isImageBitmap.js +12 -12
- package/src/core/function/frameThrottle.js +23 -23
- package/src/core/geom/2d/aabb/aabb2_array_combine.js +7 -7
- package/src/core/geom/2d/aabb/aabb2_compute_area.js +7 -7
- package/src/core/geom/2d/aabb/aabb2_compute_center_from_multiple.js +7 -7
- package/src/core/geom/2d/aabb/aabb2_compute_overlap.js +7 -7
- package/src/core/geom/2d/aabb/aabb2_from_v2_array.js +7 -7
- package/src/core/geom/2d/aabb/aabb2_intersects_point.js +7 -7
- package/src/core/geom/2d/aabb/aabb2_overlap_exists.js +7 -7
- package/src/core/geom/2d/circle/circle_compute_circle_intersection.js +7 -7
- package/src/core/geom/2d/circle/circle_compute_circle_penetration.js +7 -7
- package/src/core/geom/2d/circle/circle_intersects_point.js +7 -7
- package/src/core/geom/2d/compute_polygon_area_2d.js +7 -7
- package/src/core/geom/2d/convex-hull/fixed_convex_hull_humus.js +7 -7
- package/src/core/geom/2d/convex-hull/fixed_convex_hull_relaxation.js +7 -7
- package/src/core/geom/2d/convex-hull/orientation3_array.js +7 -7
- package/src/core/geom/2d/intersect_ray_2d.js +7 -7
- package/src/core/geom/2d/line/line2_distance_to_point_sqr.js +7 -7
- package/src/core/geom/2d/line/line2_segment_intersection_fraction_2d.js +44 -44
- package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_2d.js +7 -7
- package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_array_2d.js +7 -7
- package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_vectors_2d.js +7 -7
- package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_vectors_array_2d.js +7 -7
- package/src/core/geom/2d/line/line_segment_intersection_fraction_2d.js +7 -7
- package/src/core/geom/2d/line/line_segment_line_segment_intersection_exists_2d.js +7 -7
- package/src/core/geom/2d/line/segment2_intersects_segment2_2d.js +25 -25
- package/src/core/geom/2d/line/segment2_segment_compute_intersection_array_2d.js +46 -46
- package/src/core/geom/2d/line/segment2_segment_intersection_fractions.js +52 -52
- package/src/core/geom/2d/oriented-box/min_area_rect_2d.js +69 -69
- package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +433 -433
- package/src/core/geom/2d/polygon/polygon2_clip_axis_halfplane.js +51 -51
- package/src/core/geom/2d/polygon/polygon2_clip_halfplane.js +50 -50
- package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.js +84 -84
- package/src/core/geom/2d/polygon/polygon2_signed_area.js +52 -52
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.js +86 -86
- package/src/core/geom/2d/polygon/polygon2_triangulate.js +1280 -1280
- package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.js +77 -77
- package/src/core/geom/2d/quad-tree/qt_collect_by_circle.js +39 -39
- package/src/core/geom/2d/quad-tree/qt_match_data_by_circle.js +101 -101
- package/src/core/geom/2d/quad-tree/qt_query_data_nearest_to_point.js +7 -7
- package/src/core/geom/2d/rectangle_to_aabb.js +7 -7
- package/src/core/geom/2d/spline_bezier2_2d.js +7 -7
- package/src/core/geom/2d/triangle/tri2_rasterize_conservative.js +99 -99
- package/src/core/geom/2d/triangle/tri2_signed_area.js +35 -35
- package/src/core/geom/2d/triangle2d_compute_area.js +7 -7
- package/src/core/geom/2d/triangle2d_contains_point.js +7 -7
- package/src/core/geom/2d/triangle2d_get_barycentric.js +7 -7
- package/src/core/geom/2d/v2_morton_encode.js +23 -23
- package/src/core/geom/3d/aabb/aabb3_array_intersects_frustum_degree.js +7 -7
- package/src/core/geom/3d/aabb/aabb3_box_surface_area_2.js +7 -7
- package/src/core/geom/3d/aabb/aabb3_build_frustum.js +5 -5
- package/src/core/geom/3d/aabb/aabb3_closest_point_on_surface.js +57 -57
- package/src/core/geom/3d/aabb/aabb3_combined_surface_area.js +7 -7
- package/src/core/geom/3d/aabb/aabb3_compute_half_surface_area.js +7 -7
- package/src/core/geom/3d/aabb/aabb3_compute_projected_area.js +7 -7
- package/src/core/geom/3d/aabb/aabb3_compute_surface_area.js +7 -7
- package/src/core/geom/3d/aabb/aabb3_detailed_volume_intersection.js +7 -7
- package/src/core/geom/3d/aabb/aabb3_estimate_projected_area2.js +7 -7
- package/src/core/geom/3d/aabb/aabb3_from_threejs_geometry.js +7 -7
- package/src/core/geom/3d/aabb/aabb3_from_triangle_group.js +46 -46
- package/src/core/geom/3d/aabb/aabb3_intersects_frustum_degree.js +7 -7
- package/src/core/geom/3d/aabb/aabb3_intersects_line_segment.js +7 -7
- package/src/core/geom/3d/aabb/aabb3_near_distance_to_intersection_ray_segment.js +7 -7
- package/src/core/geom/3d/aabb/aabb3_nearest_point_on_surface.js +7 -7
- package/src/core/geom/3d/aabb/aabb3_ray_segment_entry_distance.js +95 -95
- package/src/core/geom/3d/aabb/aabb3_raycast.js +7 -7
- package/src/core/geom/3d/aabb/aabb3_transform_oriented.js +93 -93
- package/src/core/geom/3d/aabb/aabb3_transform_oriented_inverse.js +82 -82
- package/src/core/geom/3d/aabb/compute_aabb_from_points.js +7 -7
- package/src/core/geom/3d/aabb/compute_triangle_group_aabb3.js +7 -7
- package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +486 -486
- package/src/core/geom/3d/atlas/BFF_PLAN.md +174 -174
- package/src/core/geom/3d/atlas/Chart.js +77 -77
- package/src/core/geom/3d/atlas/REVIEW_02.md +392 -392
- package/src/core/geom/3d/atlas/atlas_bench_lib.js +315 -315
- package/src/core/geom/3d/atlas/atlas_chart_type.js +16 -16
- package/src/core/geom/3d/atlas/atlas_compute_charts.js +188 -188
- package/src/core/geom/3d/atlas/atlas_extract_chart.js +107 -107
- package/src/core/geom/3d/atlas/atlas_generate.js +50 -50
- package/src/core/geom/3d/atlas/atlas_test_fixtures.js +266 -266
- package/src/core/geom/3d/atlas/atlas_write_chart_uvs.js +36 -36
- package/src/core/geom/3d/atlas/data/atlas_build_edge_dihedrals.js +62 -62
- package/src/core/geom/3d/atlas/data/atlas_build_edge_lengths.js +33 -33
- package/src/core/geom/3d/atlas/data/atlas_build_face_areas.js +50 -50
- package/src/core/geom/3d/atlas/io/atlas_mesh_weld_colocals.js +39 -39
- package/src/core/geom/3d/atlas/io/bt_loop_uv.js +35 -35
- package/src/core/geom/3d/atlas/pack/ChartPlacement.js +46 -46
- package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.js +269 -269
- package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.js +209 -209
- package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_ortho.js +38 -38
- package/src/core/geom/3d/atlas/param/atlas_chart_stretch_metric.js +86 -86
- package/src/core/geom/3d/atlas/param/atlas_piecewise_resegment.js +183 -183
- package/src/core/geom/3d/atlas/param/fit_plane_basis.js +66 -66
- package/src/core/geom/3d/atlas/segment/RegionGraph.js +40 -40
- package/src/core/geom/3d/atlas/segment/atlas_build_region_graph.js +136 -136
- package/src/core/geom/3d/atlas/segment/atlas_chart_roundness_delta.js +30 -30
- package/src/core/geom/3d/atlas/segment/atlas_compute_planar_regions.js +77 -77
- package/src/core/geom/3d/atlas/segment/atlas_merge_charts.js +116 -116
- package/src/core/geom/3d/atlas/segment/atlas_segment_charts.js +271 -271
- package/src/core/geom/3d/atlas/segment/atlas_segment_charts_atomic.js +191 -191
- package/src/core/geom/3d/box/box3_projected_half_extent.js +35 -35
- package/src/core/geom/3d/capsule/capsule_intersects_aabb3_closed.js +67 -67
- package/src/core/geom/3d/capsule/capsule_intersects_aabb3_iterative.js +137 -137
- package/src/core/geom/3d/compute_circle_bounding_box.js +7 -7
- package/src/core/geom/3d/cone/computeConeBoundingBox.js +7 -7
- package/src/core/geom/3d/cone/computeConePlaneSide.js +7 -7
- package/src/core/geom/3d/cone/compute_bounding_cone_of_2_cones.js +7 -7
- package/src/core/geom/3d/cylinder/cylinder3_raycast.d.ts +38 -0
- package/src/core/geom/3d/cylinder/cylinder3_raycast.d.ts.map +1 -0
- package/src/core/geom/3d/cylinder/cylinder3_raycast.js +105 -0
- package/src/core/geom/3d/equirectangular/equirectangular_direction_to_uv.js +18 -18
- package/src/core/geom/3d/equirectangular/equirectangular_uv_to_direction.js +24 -24
- package/src/core/geom/3d/frustum/clipping_volume_matrix4_project.js +7 -7
- package/src/core/geom/3d/frustum/frustum3_computeNearestPointToPoint.js +7 -7
- package/src/core/geom/3d/frustum/frustum3_compute_closest_point.js +117 -117
- package/src/core/geom/3d/frustum/frustum_matrix4_project.js +7 -7
- package/src/core/geom/3d/frustum/hexahedron_from_aabb.js +7 -7
- package/src/core/geom/3d/frustum/read_frustum_corner.js +17 -17
- package/src/core/geom/3d/frustum/read_three_planes_to_array.js +7 -7
- package/src/core/geom/3d/frustum/slice_frustum_linear_to_points.js +7 -7
- package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +146 -146
- package/src/core/geom/3d/gjk/gjk.js +430 -430
- package/src/core/geom/3d/gjk/gjk_epa_penetration.js +520 -520
- package/src/core/geom/3d/gjk/minkowski_support.js +71 -71
- package/src/core/geom/3d/gjk/mpr.js +368 -368
- package/src/core/geom/3d/line/line3_closest_points_segment_segment.js +7 -7
- package/src/core/geom/3d/line/line3_compute_nearest_point_to_point.js +7 -7
- package/src/core/geom/3d/line/line3_compute_point_segment_point_distance_unfolded.js +64 -64
- package/src/core/geom/3d/line/line3_compute_segment_closest_point_to_aabb3_t.js +153 -153
- package/src/core/geom/3d/line/line3_compute_segment_nearest_point_to_aabb3_t.js +7 -7
- package/src/core/geom/3d/line/line3_compute_segment_nearest_point_to_point_t.js +7 -7
- package/src/core/geom/3d/line/line3_compute_segment_segment_closest_points.js +88 -88
- package/src/core/geom/3d/line/line3_segment_segment_closest_point_pair.js +71 -71
- package/src/core/geom/3d/line/segment3_point_distance_to_segments.js +25 -25
- package/src/core/geom/3d/mat4/allocate_m4.js +7 -7
- package/src/core/geom/3d/mat4/apply_mat4_transform_to_direction_v3_array.js +7 -7
- package/src/core/geom/3d/mat4/apply_mat4_transform_to_v3_array.js +7 -7
- package/src/core/geom/3d/mat4/compose_matrix4_array.js +7 -7
- package/src/core/geom/3d/mat4/decompose_matrix_4_array.js +7 -7
- package/src/core/geom/3d/mat4/eulerAnglesFromMatrix.js +7 -7
- package/src/core/geom/3d/mat4/m4_compose.js +22 -22
- package/src/core/geom/3d/mat4/m4_compute_normal_matrix3.js +7 -7
- package/src/core/geom/3d/mat4/m4_decompose.js +64 -64
- package/src/core/geom/3d/mat4/m4_decompose_array.js +157 -157
- package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.js +32 -32
- package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_scalar.js +67 -67
- package/src/core/geom/3d/mat4/m4_linear_matrix3.js +29 -29
- package/src/core/geom/3d/mat4/m4_look_at.js +96 -96
- package/src/core/geom/3d/mat4/m4_make_rotation_x.js +47 -47
- package/src/core/geom/3d/mat4/m4_make_rotation_y.js +47 -47
- package/src/core/geom/3d/mat4/m4_make_rotation_z.js +47 -47
- package/src/core/geom/3d/mat4/m4_normal_matrix3.js +39 -39
- package/src/core/geom/3d/mat4/m4_perspective.js +66 -66
- package/src/core/geom/3d/mat4/m4_transpose.js +48 -48
- package/src/core/geom/3d/morton/mortonEncode_LUT.js +7 -7
- package/src/core/geom/3d/morton/mortonEncode_magicbits.js +7 -7
- package/src/core/geom/3d/normal/hemioct/encode_unit3_hemioct.js +5 -5
- package/src/core/geom/3d/normal/hemioct/encode_unit_to_hemioct.js +28 -28
- package/src/core/geom/3d/normal/lambert_azimuth/decode_azimuth_to_unit.js +30 -30
- package/src/core/geom/3d/normal/lambert_azimuth/encode_unit_to_azimuth.js +19 -19
- package/src/core/geom/3d/normal/spherical/decode_spheremap_to_unit.js +38 -38
- package/src/core/geom/3d/normal/spherical/encode_unit_to_spheremap.js +30 -30
- package/src/core/geom/3d/plane/is_point_within_planes.js +7 -7
- package/src/core/geom/3d/plane/orient3d_fast_point.js +47 -47
- package/src/core/geom/3d/plane/plane3_compute_convex_3_plane_intersection.js +7 -7
- package/src/core/geom/3d/plane/plane3_compute_plane_intersection.js +7 -7
- package/src/core/geom/3d/plane/plane3_intersect_plane.js +7 -7
- package/src/core/geom/3d/plane/plane3_three_compute_convex_3_plane_intersection.js +7 -7
- package/src/core/geom/3d/point/point3_raycast.d.ts +33 -0
- package/src/core/geom/3d/point/point3_raycast.d.ts.map +1 -0
- package/src/core/geom/3d/point/point3_raycast.js +47 -0
- package/src/core/geom/3d/polygon/polygon3_compute_normal.js +60 -60
- package/src/core/geom/3d/polygon/polygon3_triangulate.js +94 -94
- package/src/core/geom/3d/polyhedron/convex_polyhedron3_raycast.d.ts +40 -0
- package/src/core/geom/3d/polyhedron/convex_polyhedron3_raycast.d.ts.map +1 -0
- package/src/core/geom/3d/polyhedron/convex_polyhedron3_raycast.js +94 -0
- package/src/core/geom/3d/quadric/QUADRIC3_SIZE.js +14 -14
- package/src/core/geom/3d/quadric/Quadric3.js +282 -282
- package/src/core/geom/3d/quadric/quadric3_add.js +33 -33
- package/src/core/geom/3d/quadric/quadric3_clear.js +25 -25
- package/src/core/geom/3d/quadric/quadric3_copy.js +27 -27
- package/src/core/geom/3d/quadric/quadric3_distance.js +36 -36
- package/src/core/geom/3d/quadric/quadric3_evaluate.js +25 -25
- package/src/core/geom/3d/quadric/quadric3_optimize.js +45 -45
- package/src/core/geom/3d/quadric/quadric3_scale.js +32 -32
- package/src/core/geom/3d/quadric/quadric3_set_from_plane_through_point.js +30 -30
- package/src/core/geom/3d/quadric/quadric3_set_from_vector4.js +35 -35
- package/src/core/geom/3d/quadric/quadric3_sub.js +36 -36
- package/src/core/geom/3d/quadric/quadric3_to_tensor_m3.js +28 -28
- package/src/core/geom/3d/quadric/quadric3_to_tensor_m3_inverse.js +56 -56
- package/src/core/geom/3d/quadric/quadric3_to_vector3.js +19 -19
- package/src/core/geom/3d/quaternion/quat3_createFromAxisAngle.js +7 -7
- package/src/core/geom/3d/quaternion/quat3_integrate.js +62 -62
- package/src/core/geom/3d/quaternion/quat3_multiply.js +31 -31
- package/src/core/geom/3d/quaternion/quat3_nlerp.js +72 -72
- package/src/core/geom/3d/quaternion/quat3_to_matrix3.js +69 -69
- package/src/core/geom/3d/quaternion/quat_decode_from_uint32.js +7 -7
- package/src/core/geom/3d/quaternion/quat_encode_to_uint32.js +7 -7
- package/src/core/geom/3d/ray/ray3_array_compose.js +7 -7
- package/src/core/geom/3d/ray/ray3_compute_nearest_point_to_point.js +7 -7
- package/src/core/geom/3d/ray/ray3_distance_to_point.js +7 -7
- package/src/core/geom/3d/ray/ray3_shift_origin_along_direction.js +7 -7
- package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts +17 -0
- package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/ConvexHullShape3D.js +43 -0
- package/src/core/geom/3d/shape/CylinderShape3D.d.ts +1 -0
- package/src/core/geom/3d/shape/CylinderShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/CylinderShape3D.js +21 -0
- package/src/core/geom/3d/shape/PointShape3D.d.ts +7 -0
- package/src/core/geom/3d/shape/PointShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/PointShape3D.js +88 -63
- package/src/core/geom/3d/shape/PosedShape3D.d.ts +6 -0
- package/src/core/geom/3d/shape/PosedShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/PosedShape3D.js +27 -0
- package/src/core/geom/3d/shape/TransformedShape3D.d.ts +10 -0
- package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/TransformedShape3D.js +13 -0
- package/src/core/geom/3d/shape/Triangle3D.d.ts +1 -0
- package/src/core/geom/3d/shape/Triangle3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/Triangle3D.js +26 -0
- package/src/core/geom/3d/shape/UnionShape3D.d.ts +6 -0
- package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/UnionShape3D.js +7 -0
- package/src/core/geom/3d/shape/UnitCubeShape3D.js +45 -45
- package/src/core/geom/3d/shape/UnitSphereShape3D.js +44 -44
- package/src/core/geom/3d/shape/shape3_mesh_from_geometry.js +64 -64
- package/src/core/geom/3d/shape/shape_mesh_from_geometry.js +7 -7
- package/src/core/geom/3d/shape/util/compute_signed_distance_gradient_by_sampling.js +51 -51
- package/src/core/geom/3d/shape/util/shape3d_voxelize_to_grid.js +7 -7
- package/src/core/geom/3d/sphere/compute_bounding_sphere_of_2_spheres.js +7 -7
- package/src/core/geom/3d/sphere/harmonics/sh3_convolve_with_zh.js +53 -53
- package/src/core/geom/3d/sphere/harmonics/sh3_ggx_zh.js +30 -30
- package/src/core/geom/3d/sphere/harmonics/sh3_rotate_bl.js +7 -7
- package/src/core/geom/3d/sphere/harmonics/sh_compute_K.js +7 -7
- package/src/core/geom/3d/sphere/sphere_array_intersects_point.js +7 -7
- package/src/core/geom/3d/sphere/sphere_array_intersects_ray.js +7 -7
- package/src/core/geom/3d/sphere/sphere_intersects_point.js +7 -7
- package/src/core/geom/3d/sphere/sphere_matrix4_transform.js +5 -5
- package/src/core/geom/3d/sphere/sphere_projected_sphere_radius_sqr.js +7 -7
- package/src/core/geom/3d/sphere/sphere_radius_sqr_from_v3_array_transformed.js +7 -7
- package/src/core/geom/3d/sphere/spherical_from_cartesian.js +7 -7
- package/src/core/geom/3d/tetrahedra/compute_tetrahedral_mesh_from_surface.js +174 -174
- package/src/core/geom/3d/tetrahedra/compute_tetrahedron_quality.js +7 -7
- package/src/core/geom/3d/tetrahedra/compute_tetrahedron_volume.js +7 -7
- package/src/core/geom/3d/tetrahedra/delaunay/fill_in_a_cavity.js +7 -7
- package/src/core/geom/3d/tetrahedra/graphics/build_tetrahedral_mesh_geometry.js +39 -39
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.js +48 -48
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.js +173 -173
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_compute_tet_quality.js +22 -22
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_find_tets_around_edge.js +146 -146
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_23.js +232 -232
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_32.js +255 -255
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.js +387 -387
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.js +140 -140
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_tet_get_neighbours.js +7 -7
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.js +97 -97
- package/src/core/geom/3d/tetrahedra/tetrahedron_compute_quality.js +66 -66
- package/src/core/geom/3d/tetrahedra/triangle/triangle_decode_depth.js +59 -59
- package/src/core/geom/3d/tetrahedra/triangle/triangle_rasterized_depth_sample_index.js +54 -54
- package/src/core/geom/3d/tetrahedra/validate_neighbour.js +70 -70
- package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.js +314 -314
- package/src/core/geom/3d/topology/struct/binary/BinaryDataLayer.js +364 -364
- package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.js +567 -567
- package/src/core/geom/3d/topology/struct/binary/BinaryTopology.js +1085 -1085
- package/src/core/geom/3d/topology/struct/binary/io/bt_boundary_loop.js +73 -73
- package/src/core/geom/3d/topology/struct/binary/io/bt_loop_kill.js +35 -35
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_append.js +119 -119
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.js +84 -84
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compact.js +172 -172
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.js +56 -56
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadratics.js +7 -7
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.js +227 -227
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_decouple.js +161 -161
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_island_erode.js +490 -490
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_fill_small_holes.js +120 -120
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_from_indexed_geometry.js +120 -120
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_collapse.js +52 -52
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_create.js +115 -115
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_flip.js +142 -142
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_get_or_create.js +21 -21
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.js +92 -92
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_split.js +179 -179
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_swap_vertex_slots.js +28 -28
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_kill_only_edge.js +8 -8
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_fuse_duplicate_edges.js +83 -83
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_kill_short_edges.js +89 -89
- package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_kill.js +25 -25
- package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_poke.js +127 -127
- package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.js +268 -268
- package/src/core/geom/3d/topology/struct/binary/io/face/bt_kill_only_face.js +8 -8
- package/src/core/geom/3d/topology/struct/binary/io/face/bt_mesh_kill_degenerate_faces.js +72 -72
- package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_kill_only_vert.js +7 -7
- package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_merge_verts_by_distance.js +7 -7
- package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_mesh_relative_merge_distance.js +48 -48
- package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_mesh_split_pinched_vertices.js +256 -256
- package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_mesh_vertex_merge_distance.js +77 -77
- package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_fuse_duplicate_edges.js +7 -7
- package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vert_kill.js +7 -7
- package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_fuse_duplicate_edges.js +148 -148
- package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_kill_only.js +7 -7
- package/src/core/geom/3d/topology/struct/binary/query/bt_collect_boundary_segments.js +29 -29
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_breaks_link_condition.js +107 -107
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_flip_is_legal.js +41 -41
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_flip_would_fold.js +120 -120
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_get.js +42 -42
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_is_boundary.js +20 -20
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_read_diamond.js +96 -96
- package/src/core/geom/3d/topology/struct/binary/query/bt_face_compute_newell_normal.js +72 -72
- package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_attached_faces.js +7 -7
- package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_edge_adjacent_faces.js +39 -39
- package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_edge_adjacent_faces_weighted.js +93 -93
- package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_neighbour_faces.js +7 -7
- package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_shared_loop.js +48 -48
- package/src/core/geom/3d/topology/struct/binary/query/bt_face_island_flood_fill.js +45 -45
- package/src/core/geom/3d/topology/struct/binary/query/bt_face_read_triangle.js +66 -66
- package/src/core/geom/3d/topology/struct/binary/query/bt_faces_shared_loop.js +7 -7
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.js +44 -44
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_boundary_euclidean_distance_field.js +32 -32
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_compute_face_islands.js +53 -53
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path.js +703 -703
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.js +181 -181
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_segment_penetrates_surface.js +133 -133
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_surface_ray_parity.js +82 -82
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.js +599 -599
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_walk_boundary_loops.js +108 -108
- package/src/core/geom/3d/topology/struct/binary/query/bt_query_edge_is_boundary.js +7 -7
- package/src/core/geom/3d/triangle/computeTriangleClosestPointToPointBarycentric.js +7 -7
- package/src/core/geom/3d/triangle/computeTriangleRayIntersection.js +7 -7
- package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentric.js +7 -7
- package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentricEdge.js +7 -7
- package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentricGeometry.js +7 -7
- package/src/core/geom/3d/triangle/compute_triangle_area_3d.js +7 -7
- package/src/core/geom/3d/triangle/compute_triangle_normal.js +7 -7
- package/src/core/geom/3d/triangle/rayTriangleIntersection.js +7 -7
- package/src/core/geom/3d/triangle/tri3_closest_point.js +65 -65
- package/src/core/geom/3d/triangle/tri3_closest_point_barycentric.js +171 -171
- package/src/core/geom/3d/triangle/tri3_flatten_isometric.js +79 -79
- package/src/core/geom/3d/triangle/tri3_ray_intersection.js +160 -160
- package/src/core/geom/3d/triangle/tri3_ray_intersection_barycentric.js +96 -96
- package/src/core/geom/3d/triangle/tri3_raycast.d.ts +42 -0
- package/src/core/geom/3d/triangle/tri3_raycast.d.ts.map +1 -0
- package/src/core/geom/3d/triangle/tri3_raycast.js +94 -0
- package/src/core/geom/3d/triangle/triangle3_incenter.js +7 -7
- package/src/core/geom/3d/triangle/triangle_compute_plane_side.js +7 -7
- package/src/core/geom/3d/triangle/triangle_compute_signed_volume.js +7 -7
- package/src/core/geom/3d/triangle/triangle_intersects_clipping_volume.js +7 -7
- package/src/core/geom/3d/triangle/triangle_mesh_compute_signed_volume.js +7 -7
- package/src/core/geom/3d/triangle/v3_compute_triangle_normal.js +7 -7
- package/src/core/geom/3d/util/make_justified_point_grid.js +7 -7
- package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +381 -381
- package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +684 -684
- package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +554 -554
- package/src/core/geom/mat3/m3_cm_from_translation.js +7 -7
- package/src/core/geom/mat3/m3_cm_invert.js +7 -7
- package/src/core/geom/mat3/m3_make_rotation.js +5 -5
- package/src/core/geom/mat3/m3_make_rotation_TBN.js +31 -31
- package/src/core/geom/mat3/m3_multiply_vec3.js +7 -7
- package/src/core/geom/packing/computeBoundingSphereOfSpheres.js +7 -7
- package/src/core/geom/packing/max-rect/cost/costByBestShortSide.js +7 -7
- package/src/core/geom/packing/max-rect/cost/costByRemainingArea.js +7 -7
- package/src/core/geom/packing/max-rect/cutArea.js +7 -7
- package/src/core/geom/packing/max-rect/findBestContainer.js +7 -7
- package/src/core/geom/packing/max-rect/packMaxRectangles.js +7 -7
- package/src/core/geom/packing/max-rect/packOneBox.js +7 -7
- package/src/core/geom/packing/max-rect/removeRedundantBoxes.js +7 -7
- package/src/core/geom/packing/max-rect/removeRedundantBoxesArray.js +7 -7
- package/src/core/geom/random/randomPointInBox.js +7 -7
- package/src/core/geom/random/randomPointInCircle.js +7 -7
- package/src/core/geom/random/randomPointInPoint.js +7 -7
- package/src/core/geom/random/randomPointInSphere.js +7 -7
- package/src/core/geom/random/randomPointOnBox.js +7 -7
- package/src/core/geom/random/randomPointOnSphere.js +7 -7
- package/src/core/geom/random/random_orthonormal_basis.js +31 -31
- package/src/core/geom/vec/vector_axpy.js +15 -15
- package/src/core/geom/vec/vector_axpy_offset.js +20 -20
- package/src/core/geom/vec/vector_copy.js +7 -7
- package/src/core/geom/vec/vector_dot_offset.js +25 -25
- package/src/core/geom/vec/vector_scale_array.js +7 -7
- package/src/core/geom/vec2/v2_rotate.js +21 -21
- package/src/core/geom/vec2/v2_rotate_cos_sin.js +21 -21
- package/src/core/geom/vec3/v3_angle_atan2_between.js +7 -7
- package/src/core/geom/vec3/v3_angle_between.js +7 -7
- package/src/core/geom/vec3/v3_angle_cos_between.js +7 -7
- package/src/core/geom/vec3/v3_array_add_in_place.js +7 -7
- package/src/core/geom/vec3/v3_array_centroid.js +7 -7
- package/src/core/geom/vec3/v3_array_copy.js +7 -7
- package/src/core/geom/vec3/v3_array_displace_in_direction.js +7 -7
- package/src/core/geom/vec3/v3_array_equals.js +7 -7
- package/src/core/geom/vec3/v3_array_immediate_add.js +7 -7
- package/src/core/geom/vec3/v3_array_matrix4_rotate.js +7 -7
- package/src/core/geom/vec3/v3_array_normalize.js +7 -7
- package/src/core/geom/vec3/v3_array_scale.js +7 -7
- package/src/core/geom/vec3/v3_compute_interior_angle.js +7 -7
- package/src/core/geom/vec3/v3_cotangent.js +33 -33
- package/src/core/geom/vec3/v3_dot_array_array.js +7 -7
- package/src/core/geom/vec3/v3_matrix3_multiply.js +30 -30
- package/src/core/geom/vec3/v3_matrix3_rotate.js +49 -49
- package/src/core/geom/vec3/v3_matrix4_rotate.js +7 -7
- package/src/core/geom/vec3/v3_multiply.js +23 -23
- package/src/core/geom/vec3/v3_multiply_scalar.js +7 -7
- package/src/core/geom/vec3/v3_orthonormal_matrix_from_normal.js +49 -49
- package/src/core/geom/vec3/v3_quat3_apply.js +7 -7
- package/src/core/geom/vec3/v3_quat3_apply_inverse.js +7 -7
- package/src/core/geom/vec3/v3_quaternion_apply.js +39 -39
- package/src/core/geom/vec3/v3_quaternion_apply_inverse.js +41 -41
- package/src/core/geom/vec3/v3_reflect.js +24 -24
- package/src/core/geom/vec3/v3_scale_array.js +18 -18
- package/src/core/geom/vec3/v3_triple_cross.js +45 -45
- package/src/core/geom/vec3/v3_triple_cross_product.js +7 -7
- package/src/core/geom/vec3/v3_uniform_sample_cone.js +21 -21
- package/src/core/geom/vec4/v4_multiply_mat4.js +7 -7
- package/src/core/graph/csr/csr_graph_build_distance_field.js +112 -112
- package/src/core/graph/csr/csr_graph_find_edge.js +25 -25
- package/src/core/graph/csr/csr_graph_find_shortest_path.js +105 -105
- package/src/core/graph/csr/csr_graph_from_undirected_edge_list.js +94 -94
- package/src/core/graph/graph_k_means_cluster.js +250 -250
- package/src/core/graph/layout/CircleLayout.js +1 -1
- package/src/core/graph/layout/box/BoxLayouter.js +1 -1
- package/src/core/graph/layout/graph_peel_topological_layer.js +45 -45
- package/src/core/graph/metis/BinaryTopologyFaceGraph.js +49 -49
- package/src/core/graph/metis/bt_mesh_build_face_graph.js +111 -111
- package/src/core/graph/metis/cluster_mesh_metis.js +137 -137
- package/src/core/graph/metis/native/bisection/bisect_graph.js +100 -100
- package/src/core/graph/metis/native/bisection/compute_2way_params.js +86 -86
- package/src/core/graph/metis/native/bisection/fm_2way.js +297 -297
- package/src/core/graph/metis/native/bisection/grow_bisection.js +137 -137
- package/src/core/graph/metis/native/bisection/split_graph_two_way.js +119 -119
- package/src/core/graph/metis/native/coarsen/coarsen_graph.js +94 -94
- package/src/core/graph/metis/native/coarsen/create_coarse_graph.js +158 -158
- package/src/core/graph/metis/native/coarsen/match_shem.js +175 -175
- package/src/core/graph/metis/native/initial/initial_kway_bfs.js +122 -122
- package/src/core/graph/metis/native/initial/initial_kway_recursive_bisection.js +170 -170
- package/src/core/graph/metis/native/metis_partition_kway.js +126 -126
- package/src/core/graph/metis/native/refine/RefinementScratch.js +53 -53
- package/src/core/graph/metis/native/refine/fm_kway.js +487 -487
- package/src/core/graph/metis/native/refine/project_kway.js +43 -43
- package/src/core/graph/metis/native/refine/refine_kway.js +43 -43
- package/src/core/lang/reactive/pegjs/parser.js +3458 -3458
- package/src/core/math/complex/complex_horner_eval.js +31 -31
- package/src/core/math/computeWholeDivisorLow.js +33 -33
- package/src/core/math/f32_next_down.js +24 -24
- package/src/core/math/f32_next_up.js +27 -27
- package/src/core/math/hash/hash_mix2.js +22 -22
- package/src/core/math/hash/hash_mix3.js +19 -19
- package/src/core/math/linalg/cg/cg_solve.js +106 -106
- package/src/core/math/linalg/cg/cg_solve_normal_equations.js +59 -59
- package/src/core/math/linalg/cubic_residual_times_derivative_accumulate.js +43 -43
- package/src/core/math/linalg/eigen/matrix_householder_in_place.js +122 -122
- package/src/core/math/linalg/eigen/matrix_qr_in_place.js +190 -190
- package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.js +117 -117
- package/src/core/math/linalg/givens/givens_apply_rows.js +28 -28
- package/src/core/math/linalg/givens/givens_apply_rows_offset.js +30 -30
- package/src/core/math/linalg/givens/givens_rotation_coefficients.js +39 -39
- package/src/core/math/linalg/lu_factor_linear_system.js +108 -108
- package/src/core/math/linalg/lu_solve_linear_system.js +53 -53
- package/src/core/math/linalg/polynomial_add_into.js +29 -29
- package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.js +159 -159
- package/src/core/math/linalg/polynomial_cubic_derivative_eval.js +16 -16
- package/src/core/math/linalg/polynomial_cubic_horner_eval.js +19 -19
- package/src/core/math/linalg/polynomial_cubic_second_derivative_eval.js +14 -14
- package/src/core/math/linalg/polynomial_multiply.js +41 -41
- package/src/core/math/linalg/polynomial_real_roots_in_interval.js +211 -211
- package/src/core/math/linalg/polynomial_root_bound_cauchy.js +30 -30
- package/src/core/math/linalg/polynomial_scale_into.js +17 -17
- package/src/core/math/linalg/polynomial_sub_into.js +29 -29
- package/src/core/math/linalg/solve_linear_system_GEPP_2x2.js +96 -96
- package/src/core/math/linalg/sor_optimal_omega.js +42 -42
- package/src/core/math/linalg/sparse/SparseMatrixCSR.js +88 -88
- package/src/core/math/linalg/sparse/spm_matvec.js +22 -22
- package/src/core/math/linalg/sparse/spm_matvec_transpose.js +29 -29
- package/src/core/math/lookup/LUT_HEATMAP.js +30 -30
- package/src/core/math/lookup/ParameterLookupTable.js +495 -495
- package/src/core/math/lookup/ParameterLookupTableFlags.js +6 -6
- package/src/core/math/noise/curl_noise_3d.js +18 -18
- package/src/core/math/physics/brdf/cone_cosine_from_roughness.js +28 -28
- package/src/core/math/physics/brdf/reflection_sample_weight.js +48 -48
- package/src/core/math/physics/kinematics/computeInterceptPoint.js +79 -79
- package/src/core/math/random/randomSeed.js +16 -16
- package/src/core/math/random/random_pick_weighted_index.js +26 -26
- package/src/core/math/round_half_away_from_zero.js +13 -13
- package/src/core/math/spline/spline3_hermite_apply_transform.js +107 -107
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.js +125 -125
- package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.js +50 -50
- package/src/core/math/spline/spline3_hermite_reverse.js +49 -49
- package/src/core/math/spline/spline3_hermite_to_monomial.js +37 -37
- package/src/core/math/statistics/hammersley_sequence.js +53 -53
- package/src/core/model/reactive/model/util/createRandomReactiveExpression.js +185 -185
- package/src/core/path/convertPathToURL.js +107 -107
- package/src/core/process/delay.js +16 -16
- package/src/core/science/units/MEASUREMENT_UNITS_SCIENTIFIC.js +87 -87
- package/src/core/science/units/MEASUREMENT_UNITS_SI.js +17 -17
- package/src/core/science/units/NamedUnit.js +48 -48
- package/src/core/science/units/UNIT_DIMENSION_MAPPING.js +29 -29
- package/src/core/science/units/UnitDimension.js +28 -28
- package/src/core/science/units/format_quantity.js +74 -74
- package/src/core/science/units/magnitude_prefix.js +127 -127
- package/src/core/science/units/si/AMPERE.js +13 -13
- package/src/core/science/units/si/CANDELA.js +13 -13
- package/src/core/science/units/si/DIMENSIONLESS.js +14 -14
- package/src/core/science/units/si/KELVIN.js +13 -13
- package/src/core/science/units/si/KILOGRAM.js +13 -13
- package/src/core/science/units/si/METER.js +13 -13
- package/src/core/science/units/si/MOLE.js +13 -13
- package/src/core/science/units/si/SECOND.js +13 -13
- package/src/core/science/units/si/derived/BECQUEREL.js +16 -16
- package/src/core/science/units/si/derived/COULOMB.js +13 -13
- package/src/core/science/units/si/derived/FARAD.js +13 -13
- package/src/core/science/units/si/derived/GRAY.js +16 -16
- package/src/core/science/units/si/derived/HENRY.js +13 -13
- package/src/core/science/units/si/derived/HERTZ.js +16 -16
- package/src/core/science/units/si/derived/JOULE.js +13 -13
- package/src/core/science/units/si/derived/KATAL.js +13 -13
- package/src/core/science/units/si/derived/LUMEN.js +14 -14
- package/src/core/science/units/si/derived/LUX.js +13 -13
- package/src/core/science/units/si/derived/NEWTON.js +13 -13
- package/src/core/science/units/si/derived/OHM.js +13 -13
- package/src/core/science/units/si/derived/PASCAL.js +13 -13
- package/src/core/science/units/si/derived/SIEMENS.js +13 -13
- package/src/core/science/units/si/derived/SIEVERT.js +16 -16
- package/src/core/science/units/si/derived/TESLA.js +13 -13
- package/src/core/science/units/si/derived/VOLT.js +13 -13
- package/src/core/science/units/si/derived/WATT.js +13 -13
- package/src/core/science/units/si/derived/WEBER.js +13 -13
- package/src/core/science/units/unit_matrix_to_string.js +403 -403
- package/src/engine/Engine.js +726 -726
- package/src/engine/EngineHarness.js +590 -590
- package/src/engine/REVIEW_2026_07_03.md +211 -211
- package/src/engine/animation/AnimatedActions.js +81 -81
- package/src/engine/animation/AnimationUtils.js +252 -252
- package/src/engine/animation/async/TimeSeries.js +300 -300
- package/src/engine/animation/curve/animation_curve_binary_codec.js +60 -60
- package/src/engine/animation/curve/draw/canvas_point_to_curve.js +22 -22
- package/src/engine/animation/curve/draw/curve_point_to_canvas.js +38 -38
- package/src/engine/animation/curve/draw/position_canvas_to_curve.js +22 -22
- package/src/engine/animation/curve/draw/position_curve_to_canvas.js +22 -22
- package/src/engine/asset/GameAssetType.d.ts +0 -1
- package/src/engine/asset/GameAssetType.js +0 -1
- package/src/engine/asset/load_model_scene_bundle.js +27 -27
- package/src/engine/asset/loaders/font/FontAsset.js +21 -21
- package/src/engine/asset/loaders/font/FontAssetLoader.js +20 -20
- package/src/engine/asset/loaders/gltf_test_fixtures.js +358 -358
- package/src/engine/asset/loaders/image/ImageBitmapAssetLoader.js +52 -52
- package/src/engine/asset/loaders/image/ImageRGBADataLoader.js +94 -94
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterAverage.js +59 -59
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.js +55 -55
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterPaeth.js +74 -74
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.js +34 -34
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.js +46 -46
- package/src/engine/control/first-person/DESIGN.md +640 -640
- package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +563 -563
- package/src/engine/control/first-person/FirstPersonMotionPhase.js +134 -134
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +2045 -2045
- package/src/engine/control/first-person/mastery/DecisionPoint.js +30 -30
- package/src/engine/control/first-person/math/Spring.js +71 -71
- package/src/engine/control/first-person/math/computeJumpFromApex.js +23 -23
- package/src/engine/control/first-person/math/computeLRCBreathRate.js +41 -41
- package/src/engine/control/first-person/math/computeMassRatios.js +44 -44
- package/src/engine/control/first-person/math/criticallyDampedSpring.js +34 -34
- package/src/engine/control/first-person/math/dampedSpringStep.js +72 -72
- package/src/engine/control/first-person/math/jumpDynamics.js +108 -108
- package/src/engine/control/first-person/math/stepTowards.js +20 -20
- package/src/engine/control/first-person/pose/FirstPersonPose.js +137 -137
- package/src/engine/control/first-person/pose/FirstPersonPosture.js +27 -27
- package/src/engine/control/first-person/test/buildTestPlayer.js +36 -36
- package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +531 -531
- package/src/engine/ecs/EntityManager.d.ts +7 -3
- package/src/engine/ecs/EntityManager.d.ts.map +1 -1
- package/src/engine/ecs/EntityManager.js +10 -5
- package/src/engine/ecs/HIERARCHY_REVIEW.md +717 -717
- package/src/engine/ecs/async/SystemWorkerHost.js +329 -329
- package/src/engine/ecs/async/SystemWorkerLoopback.js +143 -143
- package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +246 -246
- package/src/engine/ecs/async/WorkerSystem.js +702 -702
- package/src/engine/ecs/async/WorkerSystemProtocol.js +150 -150
- package/src/engine/ecs/fow/FogOfWar.js +707 -707
- package/src/engine/ecs/fow/FogOfWarEdgeMode.js +40 -40
- package/src/engine/ecs/fow/fog_of_war_mask_world_bounds.js +24 -24
- package/src/engine/ecs/fow/fog_of_war_uv_transform.js +30 -30
- package/src/engine/ecs/gui/hud/testHudEdgeStick.js +132 -134
- package/src/engine/ecs/hierarchy/EntityChildIndex.js +140 -140
- package/src/engine/ecs/ik/IKProblem.js +42 -42
- package/src/engine/ecs/ik/InverseKinematicsSystem.js +193 -193
- package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +134 -134
- package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.js +97 -97
- package/src/engine/ecs/ik/ik_bone_chain.js +85 -85
- package/src/engine/ecs/ik/ik_probe_terrain_contact.js +119 -119
- package/src/engine/ecs/parent/EntityNode.d.ts +1 -1
- package/src/engine/ecs/parent/EntityNode.js +2 -2
- package/src/engine/ecs/parent/ParentEntitySystem.js +255 -255
- package/src/engine/ecs/speaker/VoiceSystem.d.ts +2 -2
- package/src/engine/ecs/speaker/VoiceSystem.d.ts.map +1 -1
- package/src/engine/ecs/speaker/VoiceSystem.js +13 -7
- package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +269 -269
- package/src/engine/ecs/storage/binary/object-v2/BinaryObjectDeSerializer.js +315 -315
- package/src/engine/ecs/storage/binary/object-v2/BinaryObjectSerializer.js +331 -331
- package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +199 -199
- package/src/engine/ecs/storage/binary/object-v2/ObjectBinaryFormat.js +57 -57
- package/src/engine/ecs/storage/binary/object-v2/ObjectSerializationAdapter.js +56 -56
- package/src/engine/ecs/storage/binary/object-v2/README.md +110 -110
- package/src/engine/ecs/storage/binary/object-v2/objectStringCodec.js +48 -48
- package/src/engine/ecs/storage/binary/object-v2/serializerTestHarness.js +44 -44
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +265 -265
- package/src/engine/ecs/terrain/TerrainClouds.js +97 -97
- package/src/engine/ecs/terrain/ecs/StubTerrainBuildWorker.js +65 -65
- package/src/engine/ecs/terrain/ecs/Terrain.d.ts +4 -0
- package/src/engine/ecs/terrain/ecs/Terrain.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/Terrain.js +790 -790
- package/src/engine/ecs/terrain/ecs/TerrainSystem.js +209 -209
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +316 -316
- package/src/engine/ecs/terrain/ecs/layers/TerrainLayers.js +416 -416
- package/src/engine/ecs/terrain/ecs/splat/SplatMapping.js +525 -525
- package/src/engine/ecs/terrain/overlay/TerrainOverlay.js +302 -302
- package/src/engine/ecs/terrain/tiles/TerrainTile.js +561 -561
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +896 -896
- package/src/engine/ecs/terrain/tiles/tile_geometry_to_meshlets.js +36 -36
- package/src/engine/ecs/terrain/util/loadVisibleTerrainTiles.js +129 -129
- package/src/engine/ecs/transform/Transform64.js +814 -814
- package/src/engine/ecs/transform/t64_copy_from_transform.js +35 -35
- package/src/engine/ecs/transform/t64_equals_transform.js +55 -55
- package/src/engine/ecs/transform/t64_look_rotation.js +36 -36
- package/src/engine/ecs/transform/t64_transform_direction.js +25 -25
- package/src/engine/ecs/transform/transform_copy_from_t64.js +28 -28
- package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.js +345 -345
- package/src/engine/ecs/transform-attachment/transform_attachment_find_descendant_by_name.js +65 -65
- package/src/engine/ecs/transform-attachment/transform_attachment_parent_of.js +24 -24
- package/src/engine/ecs/util/hideEntityGracefully.js +208 -208
- package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +446 -446
- package/src/engine/graphics/FULL_SCREEN_QUAD_VERTEX_SHADER.js +14 -14
- package/src/engine/graphics/FrameThrottle.js +2 -2
- package/src/engine/graphics/ecs/animation/animator/graph/AnimationState.js +207 -207
- package/src/engine/graphics/ecs/animation/animator/graph/AnimationTransition.js +151 -151
- package/src/engine/graphics/ecs/animation/animator/graph/definition/serialization/AnimationGraphDefinitionAssetLoader.js +22 -22
- package/src/engine/graphics/ecs/camera/Camera.js +165 -165
- package/src/engine/graphics/ecs/camera/camera_active_frustum.js +25 -25
- package/src/engine/graphics/ecs/camera/camera_find_active.js +28 -28
- package/src/engine/graphics/ecs/camera/camera_traverse_active.js +18 -18
- package/src/engine/graphics/ecs/camera/pp/PerfectPanner.js +170 -170
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationAdapter.js +68 -68
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +296 -296
- package/src/engine/graphics/ecs/decal/v2/Decal.js +220 -220
- package/src/engine/graphics/ecs/decal/v2/DecalSerializationAdapter.js +56 -56
- package/src/engine/graphics/ecs/decal/v2/DecalSerializationUpgrader_0_1.js +38 -38
- package/src/engine/graphics/ecs/light/shadow/compute_view_frustum_aabb_in_space.js +62 -62
- package/src/engine/graphics/ecs/light/shadow/extend_shadow_camera_near_for_casters.js +77 -77
- package/src/engine/graphics/ecs/make_bvh_depth_range_computer.js +33 -33
- package/src/engine/graphics/ecs/mesh-v2/DeferredBoundsQueue.js +88 -88
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +428 -428
- package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMesh.js +303 -303
- package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMeshSystem.d.ts +1 -1
- package/src/engine/graphics/ecs/mesh-v2/aggregate/serialization/SGMeshSerializationAdapter.js +39 -39
- package/src/engine/graphics/ecs/mesh-v2/sg_hierarchy_compute_bounding_box_via_parent_entity.js +36 -36
- package/src/engine/graphics/ecs/path/tube/build/GeometryStream.js +94 -94
- package/src/engine/graphics/ecs/path/tube/build/build_stream_catmullrom.js +310 -310
- package/src/engine/graphics/ecs/path/tube/build/build_stream_linear.js +73 -73
- package/src/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +199 -199
- package/src/engine/graphics/ecs/path/tube/build/makeTubeStream.js +217 -217
- package/src/engine/graphics/ecs/path/tube/build/make_cap.js +340 -340
- package/src/engine/graphics/ecs/path/tube/build/make_ring_faces.js +40 -40
- package/src/engine/graphics/ecs/path/tube/build/make_ring_vertices.js +153 -153
- package/src/engine/graphics/ecs/render_layers_compute_depth_range.js +64 -64
- package/src/engine/graphics/ecs/trail3d/Trail3D.js +483 -483
- package/src/engine/graphics/ecs/trail3d/Trail3DFlags.js +26 -26
- package/src/engine/graphics/ecs/trail3d/advance_trail.js +42 -42
- package/src/engine/graphics/ecs/trail3d/make_gradient_stroke.js +58 -58
- package/src/engine/graphics/ecs/trail3d/seed_trail_stroke.js +90 -90
- package/src/engine/graphics/ecs/trail3d/seed_trail_tube.js +75 -75
- package/src/engine/graphics/ecs/trail3d/trail_head_position.js +19 -19
- package/src/engine/graphics/ecs/water/Water.js +104 -104
- package/src/engine/graphics/geometry/AttributeData.js +84 -84
- package/src/engine/graphics/geometry/buffered/geometry_compute_vertex_normals_indexed.js +49 -49
- package/src/engine/graphics/geometry/buffered/query/GeometrySpatialQueryAccelerator.js +288 -288
- package/src/engine/graphics/geometry/buffered/query/nearest_point_on_triangle_record.js +87 -87
- package/src/engine/graphics/geometry/buffered/read_triangle_vertices.js +102 -102
- package/src/engine/graphics/geometry/bvh/buffered/bvh32_from_buffered_geometry.js +52 -52
- package/src/engine/graphics/geometry/bvh/buffered/bvh32_from_indexed_geometry.js +20 -20
- package/src/engine/graphics/geometry/bvh/buffered/bvh32_from_unindexed_geometry.js +19 -19
- package/src/engine/graphics/geometry/optimization/geometry_optimize_uv_tension.js +135 -135
- package/src/engine/graphics/impostors/voxel/README.md +149 -149
- package/src/engine/graphics/impostors/voxel/VoxelImpostorDescription.js +141 -141
- package/src/engine/graphics/particles/node-based/codegen/glsl/getTypeByteSize.js +20 -20
- package/src/engine/graphics/particles/node-based/nodes/FloatConstant.d.ts +2 -0
- package/src/engine/graphics/particles/node-based/nodes/FloatConstant.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/ReadFloatUniform.d.ts +2 -0
- package/src/engine/graphics/particles/node-based/nodes/ReadFloatUniform.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/ReadVelocity.d.ts +2 -0
- package/src/engine/graphics/particles/node-based/nodes/ReadVelocity.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/ShaderNode.d.ts +1 -2
- package/src/engine/graphics/particles/node-based/nodes/ShaderNode.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/Vector3Constant.d.ts +2 -0
- package/src/engine/graphics/particles/node-based/nodes/Vector3Constant.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/Vector3Merge.d.ts +2 -0
- package/src/engine/graphics/particles/node-based/nodes/Vector3Merge.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/Vector3Split.d.ts +2 -0
- package/src/engine/graphics/particles/node-based/nodes/Vector3Split.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/WriteVelocity.d.ts +2 -0
- package/src/engine/graphics/particles/node-based/nodes/WriteVelocity.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/attribute/ReadVector3Attribute.d.ts +2 -0
- package/src/engine/graphics/particles/node-based/nodes/attribute/ReadVector3Attribute.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/attribute/WriteVector3Attribute.d.ts +2 -0
- package/src/engine/graphics/particles/node-based/nodes/attribute/WriteVector3Attribute.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/math/AddFloatNode.d.ts +2 -0
- package/src/engine/graphics/particles/node-based/nodes/math/AddFloatNode.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Add.d.ts +2 -0
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Add.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Divide.d.ts +2 -0
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Divide.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Multiply.d.ts +2 -0
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Multiply.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Subtract.d.ts +2 -0
- package/src/engine/graphics/particles/node-based/nodes/math/Vector3Subtract.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/noise/CurlNoiseNode.d.ts +2 -0
- package/src/engine/graphics/particles/node-based/nodes/noise/CurlNoiseNode.d.ts.map +1 -1
- package/src/engine/graphics/particles/node-based/nodes/particle_data_type_component_count.js +45 -45
- package/src/engine/graphics/particles/particular/engine/ParticularEngine.js +229 -229
- package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts +2 -10
- package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts.map +1 -1
- package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +1149 -1149
- package/src/engine/graphics/particles/particular/engine/emitter/ParticlePool.js +472 -472
- package/src/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.js +2 -2
- package/src/engine/graphics/particles/particular/engine/parameter/ParameterLookupTableFlags.js +2 -2
- package/src/engine/graphics/particles/particular/engine/parameter/ParameterSet.d.ts.map +1 -1
- package/src/engine/graphics/particles/particular/engine/parameter/ParameterSet.js +3 -5
- package/src/engine/graphics/particles/particular/engine/shader/ShaderManager.js +135 -135
- package/src/engine/graphics/particles/particular/engine/utils/distribute_points_on_indexed_triangles.js +168 -168
- package/src/engine/graphics/render/forward_plus/plugin/forwardPlusMaterialMarker.js +18 -18
- package/src/engine/graphics/render/forward_plus/read_frustum_corner.js +2 -2
- package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +219 -219
- package/src/engine/graphics/render/frame_graph/FrameGraph.js +900 -900
- package/src/engine/graphics/render/frame_graph/FrameGraphScope.js +72 -72
- package/src/engine/graphics/render/frame_graph/FrameGraphState.js +39 -39
- package/src/engine/graphics/render/frame_graph/ResourceEntry.js +96 -96
- package/src/engine/graphics/render/gizmo/GizmoShapeBuffer.js +105 -105
- package/src/engine/graphics/render/gizmo/GizmoShapeRenderingInterface.js +162 -162
- package/src/engine/graphics/render/layers/RenderLayer.js +95 -95
- package/src/engine/graphics/sh3/build_injected_shader.js +56 -56
- package/src/engine/graphics/sh3/grid_dilate_average.js +175 -175
- package/src/engine/graphics/sh3/path_tracer/BufferedGeometryBVH.js +506 -506
- package/src/engine/graphics/sh3/path_tracer/PathTracedMesh.js +161 -161
- package/src/engine/graphics/sh3/path_tracer/PathTracedRenderer.js +205 -205
- package/src/engine/graphics/sh3/path_tracer/PathTracedScene.js +698 -698
- package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +490 -490
- package/src/engine/graphics/sh3/path_tracer/geometry/compute_triangle_group_aabb3.js +2 -2
- package/src/engine/graphics/sh3/path_tracer/make_sky_hosek.js +45 -45
- package/src/engine/graphics/sh3/path_tracer/populate_path_traced_scene_from_ecd.js +73 -73
- package/src/engine/graphics/sh3/path_tracer/ray_hit_apply_transform.js +32 -32
- package/src/engine/graphics/sh3/path_tracer/texture/apply_texture_clamping_to_coordinate.js +28 -28
- package/src/engine/graphics/sh3/path_tracer/texture/sample_material.js +142 -142
- package/src/engine/graphics/sh3/sky/hosek/render_hosek_sky_to_equirectangular.js +70 -70
- package/src/engine/graphics/sh3/sky/hosek/sky_hosek_compute_irradiance_by_direction.js +388 -388
- package/src/engine/graphics/shaders/chunks/glsl_octahedral_impostor.js +277 -277
- package/src/engine/graphics/shaders/chunks/glsl_voxel_impostor_splat.js +57 -57
- package/src/engine/graphics/shadows/vsm.glsl.js +43 -43
- package/src/engine/graphics/texture/3d/scs3d_sample_linear3.js +81 -81
- package/src/engine/graphics/texture/EnvironmentTextureProjection.js +15 -15
- package/src/engine/graphics/texture/channel_count_to_format_prefix.js +26 -26
- package/src/engine/graphics/texture/isImageBitmap.js +2 -2
- package/src/engine/graphics/texture/reflection/convolve_equirectangular_reflection.js +189 -189
- package/src/engine/graphics/texture/reflection/equirectangular_reflection_roughness.js +51 -51
- package/src/engine/graphics/texture/sampler/HarmonicDiffusionGrid.js +145 -145
- package/src/engine/graphics/texture/sampler/Sampler2DArray.js +172 -172
- package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +466 -466
- package/src/engine/graphics/texture/sampler/compare/flip/FlipParameters.js +18 -18
- package/src/engine/graphics/texture/sampler/compare/flip/FlipPooling.js +33 -33
- package/src/engine/graphics/texture/sampler/compare/flip/flip_build_feature_kernel.js +147 -147
- package/src/engine/graphics/texture/sampler/compare/flip/flip_build_spatial_kernel.js +177 -177
- package/src/engine/graphics/texture/sampler/compare/flip/flip_compute_color_difference.js +104 -104
- package/src/engine/graphics/texture/sampler/compare/flip/flip_compute_feature_difference.js +76 -76
- package/src/engine/graphics/texture/sampler/compare/flip/flip_compute_pixels_per_degree.js +34 -34
- package/src/engine/graphics/texture/sampler/compare/flip/flip_constants.js +77 -77
- package/src/engine/graphics/texture/sampler/compare/flip/flip_filter_feature.js +48 -48
- package/src/engine/graphics/texture/sampler/compare/flip/flip_filter_spatial.js +86 -86
- package/src/engine/graphics/texture/sampler/compare/flip/flip_pool.js +71 -71
- package/src/engine/graphics/texture/sampler/compare/flip/flip_srgb_to_ycxcz.js +55 -55
- package/src/engine/graphics/texture/sampler/compare/flip/sampler2d_flip_ldr.js +80 -80
- package/src/engine/graphics/texture/sampler/filter/SeparableKernel.js +82 -82
- package/src/engine/graphics/texture/sampler/filter/sampler2d_convolve_separable.js +122 -122
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.js +148 -148
- package/src/engine/graphics/texture/sampler/sampler2d_channel_apply_lut.js +82 -82
- package/src/engine/graphics/texture/sampler/sampler2d_combine.js +67 -67
- package/src/engine/graphics/texture/sampler/sampler2d_linear_f16_to_srgb_u8.js +54 -54
- package/src/engine/graphics/texture/sampler/sampler2d_sample_equirectangular_direction.js +63 -63
- package/src/engine/graphics/texture/sampler/sampler2d_srgb_to_linear.js +33 -33
- package/src/engine/graphics/texture/sampler/sampler2d_srgb_u8_to_linear_f16.js +68 -68
- package/src/engine/graphics/texture/shade_image_preview.js +103 -103
- package/src/engine/graphics/trail/TrailSpawnMode.js +20 -20
- package/src/engine/graphics/trail/tube/TubeX.js +573 -573
- package/src/engine/graphics/trail/tube/TubeXMaterialSpec.js +58 -58
- package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js +34 -34
- package/src/engine/graphics/trail/tube/tube_attributes_spec.js +69 -69
- package/src/engine/graphics/trail/tube/tube_frame.js +83 -83
- package/src/engine/graphics3/AnimationGraphSystem3.js +329 -329
- package/src/engine/graphics3/AnimationSystem3.js +352 -352
- package/src/engine/graphics3/CameraSystem3.js +170 -170
- package/src/engine/graphics3/DecalSystem3.js +441 -441
- package/src/engine/graphics3/FogOfWarSystem3.js +265 -265
- package/src/engine/graphics3/GraphicsEngine3.js +776 -776
- package/src/engine/graphics3/HighlightSystem3.js +241 -241
- package/src/engine/graphics3/LightSystem3.js +211 -211
- package/src/engine/graphics3/MeshSystem3.js +646 -646
- package/src/engine/graphics3/ParticleEmitterSystem3.js +289 -289
- package/src/engine/graphics3/PathDisplaySystem3.js +309 -309
- package/src/engine/graphics3/PickingQuery.js +48 -48
- package/src/engine/graphics3/PickingResult.js +47 -47
- package/src/engine/graphics3/PickingSystem3.js +273 -273
- package/src/engine/graphics3/ShadeCameraAdapter.js +55 -55
- package/src/engine/graphics3/ShadedGeometrySystem3.js +169 -169
- package/src/engine/graphics3/TerrainSystem3.js +566 -566
- package/src/engine/graphics3/TooltipComponentSystem3.js +328 -328
- package/src/engine/graphics3/Trail3DSystem3.js +221 -221
- package/src/engine/graphics3/WaterSystem3.js +293 -293
- package/src/engine/graphics3/animation/ClipPlayback.js +84 -84
- package/src/engine/graphics3/animation/wrap_clip_time.js +23 -23
- package/src/engine/graphics3/animation/write_graph_playback.js +48 -48
- package/src/engine/graphics3/camera_sync_from_transform.js +29 -29
- package/src/engine/graphics3/debug/gizmo_view_from_camera.js +48 -48
- package/src/engine/graphics3/decal/DECAL_CLUSTER.js +61 -61
- package/src/engine/graphics3/decal/DECAL_GPU_RECORD.js +117 -117
- package/src/engine/graphics3/decal/DecalAtlas.js +194 -194
- package/src/engine/graphics3/decal/DecalAtlasFill.js +32 -32
- package/src/engine/graphics3/decal/GPUDecalRenderer.js +360 -360
- package/src/engine/graphics3/decal/chunk_decal_intersects_frustum.js +59 -59
- package/src/engine/graphics3/decal/chunk_decal_sampling_info.js +57 -57
- package/src/engine/graphics3/decal/chunk_decal_surface_frame.js +54 -54
- package/src/engine/graphics3/decal/decal_atlas_border.js +69 -69
- package/src/engine/graphics3/decal/decal_gpu_records.js +120 -120
- package/src/engine/graphics3/decal/decal_material_samplers.js +109 -109
- package/src/engine/graphics3/decal/graph_build_decal_clusters.js +212 -212
- package/src/engine/graphics3/decal/shader_cluster_assign_decals.js +207 -207
- package/src/engine/graphics3/decal/shader_cull_decals_frustum.js +108 -108
- package/src/engine/graphics3/highlight/GPUHighlightRenderer.js +151 -151
- package/src/engine/graphics3/highlight/HIGHLIGHT_OUTLINE.js +23 -23
- package/src/engine/graphics3/highlight/chunk_highlight_lookup.js +53 -53
- package/src/engine/graphics3/highlight/pack_highlight_color.js +44 -44
- package/src/engine/graphics3/highlight/pack_highlight_table.js +74 -74
- package/src/engine/graphics3/highlight/shader_highlight_outline.js +150 -150
- package/src/engine/graphics3/load_cube_environment.js +68 -68
- package/src/engine/graphics3/make_default_environment.js +107 -107
- package/src/engine/graphics3/particles/particle_gpu_records.js +221 -221
- package/src/engine/graphics3/path/build_tube_path_streams.js +98 -98
- package/src/engine/graphics3/path/tube_stream_write_geometry.js +90 -90
- package/src/engine/graphics3/preview/ModelPreviewFraming.js +35 -35
- package/src/engine/graphics3/preview/make_model_preview_scene.js +154 -154
- package/src/engine/graphics3/preview/make_model_thumbnail.js +48 -48
- package/src/engine/graphics3/preview/model_preview_framing.js +70 -70
- package/src/engine/graphics3/shade_bundle_bounds.js +55 -55
- package/src/engine/graphics3/shade_camera_projection_ray.js +77 -77
- package/src/engine/graphics3/shade_node_to_entity_composition.js +84 -84
- package/src/engine/graphics3/terrain/GPUTerrainSplatRenderer.js +481 -481
- package/src/engine/graphics3/terrain/chunk_terrain_grid_overlay.js +61 -61
- package/src/engine/graphics3/terrain/chunk_terrain_splat_mix.js +66 -66
- package/src/engine/graphics3/terrain/pack_terrain_row_table.js +53 -53
- package/src/engine/graphics3/terrain/shader_terrain_splat.js +215 -215
- package/src/engine/graphics3/trail/trail_tube_write_geometry.js +158 -158
- package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts +2 -2
- package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts.map +1 -1
- package/src/engine/input/GAMEPAD_API_PROPOSAL.md +717 -717
- package/src/engine/input/analog/apply_radial_dead_zone.js +38 -38
- package/src/engine/input/devices/GamepadDevice.js +466 -466
- package/src/engine/input/devices/ManualInputDevice.js +152 -152
- package/src/engine/input/devices/PointerCoordinates.js +26 -26
- package/src/engine/input/devices/events/GamepadEvents.js +10 -10
- package/src/engine/input/devices/gamepad/GamepadAxes.js +19 -19
- package/src/engine/input/devices/gamepad/GamepadButtons.js +29 -29
- package/src/engine/input/devices/gamepad/GamepadCoordinates.js +17 -17
- package/src/engine/input/devices/gamepad/GamepadDeviceLayout.js +323 -323
- package/src/engine/input/devices/gamepad/GamepadHandle.js +286 -286
- package/src/engine/input/devices/gamepad/GamepadLayout.js +160 -160
- package/src/engine/input/devices/gamepad/GamepadStandardMapping.js +31 -31
- package/src/engine/input/devices/gamepad/GamepadStick.js +169 -169
- package/src/engine/input/devices/gamepad/GamepadStickCalibration.js +119 -119
- package/src/engine/input/devices/gamepad/GamepadStickCalibrator.js +250 -250
- package/src/engine/input/devices/gamepad/gamepad_layout_database.js +144 -144
- package/src/engine/input/devices/gamepad/gamepad_read_hat_axis.js +61 -61
- package/src/engine/input/devices/gamepad/gamepad_write_dpad_vector.js +30 -30
- package/src/engine/input/devices/gamepad/prototypeGamepadTester.js +2124 -2124
- package/src/engine/input/devices/mouse/MouseButtons.js +22 -22
- package/src/engine/input/ecs/components/Input.js +118 -118
- package/src/engine/input/ecs/components/InputController.js +78 -78
- package/src/engine/input/ecs/ism/ISM_DESIGN.md +1523 -1523
- package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +627 -627
- package/src/engine/input/ecs/ism/InputActionEvent.js +40 -40
- package/src/engine/input/ecs/ism/InputBinding.js +71 -71
- package/src/engine/input/ecs/ism/InputCoordinateKind.js +25 -25
- package/src/engine/input/ecs/ism/InputCoordinateSource.js +86 -86
- package/src/engine/input/ecs/ism/InputDirection.js +33 -33
- package/src/engine/input/ecs/ism/InputMapProgram.js +236 -236
- package/src/engine/input/ecs/ism/InputMapSystem.js +976 -976
- package/src/engine/input/ecs/ism/InputOcclusion.js +31 -31
- package/src/engine/input/ecs/ism/InputSource.js +67 -67
- package/src/engine/input/ecs/ism/InputSourceTable.js +107 -107
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannel.js +93 -93
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannelType.js +11 -11
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.js +153 -153
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateSwitches.js +160 -160
- package/src/engine/input/ecs/ism/device/GAMEPAD_INPUT_DEVICE_ID.js +9 -9
- package/src/engine/input/ecs/ism/device/GamepadInputDeviceAdapter.js +88 -88
- package/src/engine/input/ecs/ism/device/InputDeviceAdapter.js +72 -72
- package/src/engine/input/ecs/ism/device/KEYBOARD_INPUT_DEVICE_ID.js +9 -9
- package/src/engine/input/ecs/ism/device/KeyboardInputDeviceAdapter.js +63 -63
- package/src/engine/input/ecs/ism/device/ManualInputDeviceAdapter.js +55 -55
- package/src/engine/input/ecs/ism/device/POINTER_INPUT_DEVICE_ID.js +9 -9
- package/src/engine/input/ecs/ism/device/PointerInputDeviceAdapter.js +160 -160
- package/src/engine/input/ecs/ism/map/InputBinding.js +88 -88
- package/src/engine/input/ecs/ism/map/InputCoordinateBinding.js +102 -102
- package/src/engine/input/ecs/ism/map/InputLayer.js +220 -220
- package/src/engine/input/ecs/ism/map/InputMap.js +331 -331
- package/src/engine/input/ecs/ism/prototypeInputMap.js +309 -309
- package/src/engine/input/ecs/ism/trigger/InputTrigger.js +148 -148
- package/src/engine/input/ecs/ism/trigger/InputTriggerChord.js +173 -173
- package/src/engine/input/ecs/ism/trigger/InputTriggerGamepadButton.js +37 -37
- package/src/engine/input/ecs/ism/trigger/InputTriggerHold.js +176 -176
- package/src/engine/input/ecs/ism/trigger/InputTriggerKey.js +39 -39
- package/src/engine/input/ecs/ism/trigger/InputTriggerMouseButton.js +38 -38
- package/src/engine/input/ecs/ism/trigger/InputTriggerRelease.js +120 -120
- package/src/engine/input/ecs/ism/trigger/InputTriggerSequence.js +315 -315
- package/src/engine/input/ecs/ism/trigger/InputTriggerSwitch.js +105 -105
- package/src/engine/input/ecs/ism/trigger/InputTriggerType.js +17 -17
- package/src/engine/input/ecs/systems/InputControllerSystem.js +253 -253
- package/src/engine/input/ecs/systems/InputSystem.js +162 -162
- package/src/engine/intelligence/behavior/primitive/FailingBehaviorSerializationAdapter.js +25 -25
- package/src/engine/intelligence/behavior/primitive/SucceedingBehaviorSerializationAdapter.js +26 -26
- package/src/engine/intelligence/behavior/util/AsynchronousDelayAction.js +55 -55
- package/src/engine/interpolation/BinaryInterpolationAdapter.js +56 -56
- package/src/engine/interpolation/Interpoland.js +49 -49
- package/src/engine/interpolation/Interpolated.js +149 -149
- package/src/engine/interpolation/PoseInterpolationAdapter.js +61 -61
- package/src/engine/interpolation/TransformPoseSerializationAdapter.js +57 -57
- package/src/engine/interpolation/pose_interpoland.js +27 -27
- package/src/engine/navigation/ecs/path_following/PathFollower.js +222 -222
- package/src/engine/navigation/mesh/NavigationMesh.js +145 -145
- package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +661 -0
- package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +229 -229
- package/src/engine/navigation/mesh/build/clip_soup_against_overhangs.d.ts.map +1 -1
- package/src/engine/navigation/mesh/build/clip_soup_against_overhangs.js +628 -520
- package/src/engine/network/CONGESTION_CONTROL.md +87 -87
- package/src/engine/network/README.md +52 -52
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.js +43 -43
- package/src/engine/network/adapters/TransformInterpolationAdapter.js +84 -84
- package/src/engine/network/adapters/TransformReplicationAdapter.js +87 -87
- package/src/engine/network/adapters/Vector3InterpolationAdapter.js +46 -46
- package/src/engine/network/convertPathToURL.js +2 -2
- package/src/engine/network/core/MalformedPacketError.js +26 -26
- package/src/engine/network/core/quantize/quantize_float.js +73 -73
- package/src/engine/network/core/quantize/quantize_position.js +65 -65
- package/src/engine/network/core/sequence/ack_bitfield.js +77 -77
- package/src/engine/network/core/sequence/seq16.js +69 -69
- package/src/engine/network/core/sequence/seq32.js +73 -73
- package/src/engine/network/ecs/NetworkSystem.js +84 -84
- package/src/engine/network/ecs/components/NetworkIdentity.js +73 -73
- package/src/engine/network/ecs/owner_authorization.js +55 -55
- package/src/engine/network/ecs/serialization/NetworkIdentitySerializationAdapter.js +64 -64
- package/src/engine/network/replication/ScopeFilter.js +71 -71
- package/src/engine/network/sim/ActionLog.js +152 -152
- package/src/engine/network/sim/SimAction.js +273 -273
- package/src/engine/network/sim/Snapshotter.js +222 -222
- package/src/engine/network/state/FrameBufferRing.js +168 -168
- package/src/engine/network/state/InputRing.js +134 -134
- package/src/engine/network/state/PriorityAccumulator.js +180 -180
- package/src/engine/network/time/TimeDilation.js +62 -62
- package/src/engine/network/transport/Channel.js +336 -336
- package/src/engine/network/transport/Transport.js +203 -203
- package/src/engine/network/transport/adapters/NodeUDPTransport.js +186 -186
- package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.js +105 -105
- package/src/engine/network/transport/fragments/fragment_send.js +147 -147
- package/src/engine/network/transport/fragments/packet_size.js +101 -101
- package/src/engine/network/xhr.js +23 -23
- package/src/engine/physics/BULLET_REVIEW.md +945 -945
- package/src/engine/physics/CANNON_REVIEW.md +1300 -1300
- package/src/engine/physics/CONSTRAINT_SOLVER_BENCH_LOG.md +208 -208
- package/src/engine/physics/CONSTRAINT_SOLVER_IMPROVEMENTS_PLAN.md +364 -364
- package/src/engine/physics/INTEPOLATION_SYSTEM_PLAN.md +287 -287
- package/src/engine/physics/JOLT_REVIEW.md +913 -913
- package/src/engine/physics/PLAN.md +1120 -1094
- package/src/engine/physics/RAPIER_REVIEW.md +934 -934
- package/src/engine/physics/REVIEW_001_ACTION_PLAN.md +642 -642
- package/src/engine/physics/REVIEW_002.md +151 -151
- package/src/engine/physics/REVIEW_003.md +166 -166
- package/src/engine/physics/broadphase/compute_fat_world_aabb.js +97 -97
- package/src/engine/physics/constraint/DofMode.js +35 -35
- package/src/engine/physics/constraint/solve_constraints.js +1 -1
- package/src/engine/physics/contact/combine_material.js +35 -35
- package/src/engine/physics/ecs/BodyKind.js +29 -29
- package/src/engine/physics/ecs/Collider.js +177 -177
- package/src/engine/physics/ecs/ColliderFlags.js +15 -15
- package/src/engine/physics/ecs/ColliderSerializationAdapter.js +37 -37
- package/src/engine/physics/ecs/Joint.js +475 -475
- package/src/engine/physics/ecs/JointSerializationAdapter.js +72 -72
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +3 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +7 -2
- package/src/engine/physics/ecs/RigidBodySerializationUpgrader_0_1.js +37 -37
- package/src/engine/physics/ecs/SleepState.js +12 -12
- package/src/engine/physics/ecs/find_non_finite_physics_state.js +76 -76
- package/src/engine/physics/fluid/FluidField.js +867 -867
- package/src/engine/physics/fluid/FluidSimulator.js +832 -832
- package/src/engine/physics/fluid/REVIEW_02_PLAN.md +243 -243
- package/src/engine/physics/fluid/SliceVisualiser.js +190 -190
- package/src/engine/physics/fluid/ecs/FluidComponent.js +239 -239
- package/src/engine/physics/fluid/ecs/FluidEffectorsComponent.js +89 -89
- package/src/engine/physics/fluid/ecs/FluidObstacle.js +97 -97
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.js +348 -348
- package/src/engine/physics/fluid/ecs/FluidSystem.js +142 -142
- package/src/engine/physics/fluid/ecs/FluidWorkerCore.js +123 -123
- package/src/engine/physics/fluid/ecs/WorkerFluidSystem.js +395 -395
- package/src/engine/physics/fluid/ecs/fluid.worker.js +12 -12
- package/src/engine/physics/fluid/ecs/fluid_build_world_to_grid.js +32 -32
- package/src/engine/physics/fluid/ecs/fluid_gather_visible_effectors.js +47 -47
- package/src/engine/physics/fluid/ecs/fluid_reanchor_field.js +85 -85
- package/src/engine/physics/fluid/ecs/fluid_sync_effectors_from_transform.js +16 -16
- package/src/engine/physics/fluid/ecs/fluid_worker_protocol.js +111 -111
- package/src/engine/physics/fluid/ecs/makeFluidWorker.js +18 -18
- package/src/engine/physics/fluid/effector/AbstractFluidEffector.js +81 -81
- package/src/engine/physics/fluid/effector/GlobalFluidEffector.js +202 -202
- package/src/engine/physics/fluid/effector/ImpulseFluidEffector.js +257 -257
- package/src/engine/physics/fluid/effector/WakeFluidEffector.js +405 -405
- package/src/engine/physics/fluid/prototype.js +126 -126
- package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_scalar.js +143 -143
- package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.js +162 -162
- package/src/engine/physics/fluid/solver/v3_grid_advect_sl_velocity.js +107 -107
- package/src/engine/physics/fluid/solver/v3_grid_apply_vorticity_confinement.js +131 -131
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js +90 -90
- package/src/engine/physics/fluid/solver/v3_grid_limit8_at_indices.js +38 -38
- package/src/engine/physics/fluid/solver/v3_grid_patch_edges_constant.js +77 -77
- package/src/engine/physics/fluid/solver/v3_grid_patch_edges_uniform.js +113 -113
- package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.js +92 -92
- package/src/engine/physics/fluid/solver/v3_grid_shift_in_place.js +107 -107
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.js +135 -135
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js +511 -511
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +108 -108
- package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.js +317 -317
- package/src/engine/physics/fluid/solver/v3_mac_advect_scalar.js +236 -236
- package/src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.js +217 -217
- package/src/engine/physics/fluid/solver/v3_mac_apply_vorticity_confinement.js +174 -174
- package/src/engine/physics/fluid/solver/v3_mac_clip_trace.js +95 -95
- package/src/engine/physics/fluid/solver/v3_mac_compute_divergence.js +77 -77
- package/src/engine/physics/fluid/solver/v3_mac_compute_face_solid.js +131 -131
- package/src/engine/physics/fluid/solver/v3_mac_subtract_pressure_gradient.js +104 -104
- package/src/engine/physics/inertia/world_inverse_inertia.js +160 -160
- package/src/engine/physics/integration/integrate_position.js +97 -97
- package/src/engine/physics/narrowphase/box_box_manifold.js +681 -681
- package/src/engine/physics/narrowphase/box_triangle_contact.js +906 -906
- package/src/engine/physics/narrowphase/capsule_triangle_contact.js +395 -395
- package/src/engine/physics/narrowphase/compute_penetration.js +369 -369
- package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.js +55 -55
- package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.js +37 -37
- package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.js +117 -117
- package/src/engine/physics/narrowphase/narrowphase_step.js +1738 -1738
- package/src/engine/physics/narrowphase/reduce_manifold_contacts.js +74 -74
- package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +12 -11
- package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/refine_ray_hit.js +228 -36
- package/src/engine/physics/narrowphase/sphere_sphere_contact.js +52 -52
- package/src/engine/physics/narrowphase/sphere_triangle_contact.js +138 -138
- package/src/engine/physics/persistence/solver_caches.js +309 -309
- package/src/engine/physics/queries/overlap_shape.js +187 -187
- package/src/engine/physics/queries/raycast.d.ts +7 -4
- package/src/engine/physics/queries/raycast.d.ts.map +1 -1
- package/src/engine/physics/queries/raycast.js +11 -5
- package/src/engine/physics/queries/shape_cast.js +417 -417
- package/src/engine/physics/solver/apply_impulse_to_body.js +86 -86
- package/src/engine/physics/solver/friction_cone.js +37 -37
- package/src/engine/physics/solver/solve_contacts.js +1 -1
- package/src/engine/platform/WebEnginePlatform.d.ts.map +1 -1
- package/src/engine/platform/WebEnginePlatform.js +1 -2
- package/src/engine/scene/SerializedScene.d.ts +2 -2
- package/src/engine/scene/SerializedScene.d.ts.map +1 -1
- package/src/engine/scene/transitionToScene.js +121 -121
- package/src/engine/simulation/Ticker.d.ts.map +1 -1
- package/src/engine/simulation/Ticker.js +33 -0
- package/src/engine/sound/ecs/audio/LiveEmitterSet.js +345 -345
- package/src/engine/sound/ecs/audio/SpatialAudioIndex.js +140 -140
- package/src/engine/sound/simulation/AcousticSimulator.js +646 -646
- package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +432 -432
- package/src/engine/sound/simulation/PROBE_PLACEMENT.md +250 -250
- package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +322 -322
- package/src/engine/sound/simulation/README.md +277 -277
- package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +261 -261
- package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +121 -121
- package/src/engine/sound/simulation/configureAcousticSimulation.js +48 -48
- package/src/engine/sound/simulation/core/AcousticMedium.js +113 -113
- package/src/engine/sound/simulation/core/AcousticMediumSerializationAdapter.js +27 -27
- package/src/engine/sound/simulation/core/AcousticOccluderIndex.js +312 -312
- package/src/engine/sound/simulation/core/AcousticRegionIndex.js +159 -159
- package/src/engine/sound/simulation/core/AcousticSolution.js +90 -90
- package/src/engine/sound/simulation/core/AcousticSourceState.js +109 -109
- package/src/engine/sound/simulation/core/AcousticVolumeRegion.js +73 -73
- package/src/engine/sound/simulation/core/OcclusionSolver.js +112 -112
- package/src/engine/sound/simulation/core/RayHit.js +50 -50
- package/src/engine/sound/simulation/core/VolumeField.js +149 -149
- package/src/engine/sound/simulation/definition/AcousticDirectivity.js +114 -114
- package/src/engine/sound/simulation/definition/AcousticDirectivitySerializationAdapter.js +30 -30
- package/src/engine/sound/simulation/definition/AcousticMaterial.js +122 -122
- package/src/engine/sound/simulation/definition/AcousticMaterialSerializationAdapter.js +32 -32
- package/src/engine/sound/simulation/ecs/AcousticBody.js +48 -48
- package/src/engine/sound/simulation/ecs/AcousticBodySerializationAdapter.js +34 -34
- package/src/engine/sound/simulation/ecs/AcousticProbeFieldSystem.js +64 -64
- package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.js +75 -75
- package/src/engine/sound/simulation/ecs/AcousticVolume.js +74 -74
- package/src/engine/sound/simulation/ecs/AcousticVolumeSerializationAdapter.js +37 -37
- package/src/engine/sound/simulation/ecs/AcousticVolumeSystem.js +66 -66
- package/src/engine/sound/simulation/probe/AcousticProbeField.js +578 -578
- package/src/engine/sound/simulation/probe/AcousticProbeFieldSerializationAdapter.js +60 -60
- package/src/engine/sound/simulation/probe/ProbeFieldPathingSolution.js +64 -64
- package/src/engine/sound/simulation/probe/ProbeVisibilityRecheck.js +132 -132
- package/src/engine/sound/simulation/probe/acoustic_probe_transfer.js +103 -103
- package/src/engine/sound/simulation/probe/acoustic_trace_energy.js +136 -136
- package/src/engine/sound/simulation/probe/bakeProbeField.js +169 -169
- package/src/engine/sound/simulation/probe/bakeProbeReflectors.js +110 -110
- package/src/engine/sound/simulation/probe/bakeReverbBands.js +110 -110
- package/src/engine/sound/simulation/probe/bakeVisibility.js +148 -148
- package/src/engine/sound/simulation/probe/echogram_band_rt60.js +83 -83
- package/src/engine/sound/simulation/probe/probe_delaunay_edges.js +215 -215
- package/src/engine/sound/simulation/probe/probe_densify_portals.js +140 -140
- package/src/engine/sound/simulation/probe/probe_detect_portals.js +196 -196
- package/src/engine/sound/simulation/probe/probe_edge_key.js +38 -38
- package/src/engine/sound/simulation/probe/probe_edge_visible.js +41 -41
- package/src/engine/sound/simulation/probe/probe_place_sdf_cover.js +82 -82
- package/src/engine/sound/simulation/probe/probe_refine_connectivity.js +181 -181
- package/src/engine/sound/simulation/probe/probe_sdf_grid.js +171 -171
- package/src/engine/sound/simulation/probe/probe_visibility_augment.js +146 -146
- package/src/engine/sound/simulation/probe/probe_visibility_bridge_components.js +167 -167
- package/src/engine/sound/simulation/render/FoaProbeReverbRenderer.js +354 -354
- package/src/engine/sound/simulation/render/ProbeReverbRenderer.js +231 -231
- package/src/engine/sound/simulation/render/foaReverbImpulseResponse.js +105 -105
- package/src/engine/sound/simulation/render/reverbImpulseResponse.js +66 -66
- package/src/engine/sound/simulation/render/reverb_band_tails.js +93 -93
- package/src/engine/sound/simulation/serialization/populateAcousticSerializationRegistry.js +25 -25
- package/src/engine/sound/sopra/README.md +643 -643
- package/src/engine/sound/sopra/SopraEngine.js +432 -432
- package/src/engine/sound/sopra/asset/AssetManagerBufferProvider.js +71 -71
- package/src/engine/sound/sopra/asset/BufferProvider.js +29 -29
- package/src/engine/sound/sopra/asset/StubBufferProvider.js +58 -58
- package/src/engine/sound/sopra/definition/BusDefinition.js +142 -142
- package/src/engine/sound/sopra/definition/BusDefinitionSerializationAdapter.js +54 -54
- package/src/engine/sound/sopra/definition/DuckingRule.js +106 -106
- package/src/engine/sound/sopra/definition/DuckingRuleSerializationAdapter.js +31 -31
- package/src/engine/sound/sopra/definition/EventDescription.js +262 -262
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.js +55 -55
- package/src/engine/sound/sopra/definition/MixerSnapshot.js +83 -83
- package/src/engine/sound/sopra/definition/MixerSnapshotSerializationAdapter.js +39 -39
- package/src/engine/sound/sopra/definition/ParameterDefinition.js +117 -117
- package/src/engine/sound/sopra/definition/ParameterDefinitionSerializationAdapter.js +31 -31
- package/src/engine/sound/sopra/definition/VoiceStealMode.js +18 -18
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +109 -109
- package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.js +185 -185
- package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClipSerializationAdapter.js +59 -59
- package/src/engine/sound/sopra/definition/clip/ContainerAudioClip.js +128 -128
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.js +226 -226
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClipSerializationAdapter.js +54 -54
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +191 -191
- package/src/engine/sound/sopra/definition/clip/SampleAudioClipSerializationAdapter.js +39 -39
- package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.js +79 -79
- package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClipSerializationAdapter.js +42 -42
- package/src/engine/sound/sopra/definition/clip/SilenceAudioClip.js +77 -77
- package/src/engine/sound/sopra/definition/clip/SilenceAudioClipSerializationAdapter.js +27 -27
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +5 -0
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.js +125 -125
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClipSerializationAdapter.js +41 -41
- package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.js +24 -24
- package/src/engine/sound/sopra/definition/effect/CompressorEffect.js +120 -120
- package/src/engine/sound/sopra/definition/effect/CompressorEffectSerializationAdapter.js +31 -31
- package/src/engine/sound/sopra/definition/effect/EqEffect.js +128 -128
- package/src/engine/sound/sopra/definition/effect/EqEffectSerializationAdapter.js +29 -29
- package/src/engine/sound/sopra/definition/effect/ReverbEffect.js +113 -113
- package/src/engine/sound/sopra/definition/effect/ReverbEffectSerializationAdapter.js +25 -25
- package/src/engine/sound/sopra/runtime/BusGraph.js +231 -231
- package/src/engine/sound/sopra/runtime/ParameterStore.js +98 -98
- package/src/engine/sound/sopra/runtime/SopraPlaybackContext.js +68 -68
- package/src/engine/sound/sopra/runtime/Voice.js +145 -145
- package/src/engine/sound/sopra/runtime/VoiceManager.js +136 -136
- package/src/engine/sound/sopra/runtime/VoicePool.js +17 -17
- package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.js +42 -42
- package/src/engine/sound/sopra/serialization/sopraJSON.js +99 -99
- package/src/engine/sound/sopra/serialization/sopraSerializationHarness.js +49 -49
- package/src/engine/sound/sopra/util/MockAudioContext.js +224 -224
- package/src/engine/sound/sopra/util/fadeOutAndStop.js +60 -60
- package/src/engine/ui/DraggableAspect.js +115 -115
- package/src/engine/ui/apply_pointer_tilt_3d.js +50 -50
- package/src/generation/COORDINATES.md +54 -54
- package/src/generation/GridGenerationResource.js +24 -24
- package/src/generation/GridTaskGroup.js +214 -214
- package/src/generation/REVIEW_01_ACTION_PLAN.md +628 -628
- package/src/generation/automata/CaveGeneratorCellularAutomata.js +79 -79
- package/src/generation/automata/CellularAutomata.js +22 -22
- package/src/generation/compute_writes_union.js +25 -25
- package/src/generation/filtering/CellFilter.js +117 -117
- package/src/generation/filtering/core/CellFilterBinaryOperation.js +37 -37
- package/src/generation/filtering/core/CellFilterOperationTertiary.js +43 -43
- package/src/generation/filtering/core/CellFilterUnaryOperation.js +29 -29
- package/src/generation/filtering/numeric/process/computeCellFilterSobel.js +36 -36
- package/src/generation/filtering/numeric/process/computeFilterSurfaceNormal.js +42 -42
- package/src/generation/filtering/numeric/util/populateSampler2DFromCellFilter.js +18 -18
- package/src/generation/filtering/numeric/util/write_cell_filter_to_sampler2d.js +47 -47
- package/src/generation/filtering/util/cell_filter_weights_pick.js +54 -54
- package/src/generation/grid/GridData.js +233 -233
- package/src/generation/grid/coords/grid_to_texel.js +10 -10
- package/src/generation/grid/coords/texel_to_grid.js +10 -10
- package/src/generation/grid/generation/GridTaskApplyActionToCells.js +90 -90
- package/src/generation/grid/generation/GridTaskDensityMarkerDistribution.js +388 -388
- package/src/generation/grid/generation/GridTaskExecuteRuleTimes.js +127 -127
- package/src/generation/grid/generation/GridTaskSequence.js +58 -58
- package/src/generation/grid/generation/NoopGridTaskGenerator.js +12 -12
- package/src/generation/grid/generation/discrete/GridTaskActionRuleSet.js +48 -48
- package/src/generation/grid/generation/discrete/GridTaskCellularAutomata.js +146 -146
- package/src/generation/grid/generation/discrete/GridTaskConnectRooms.js +540 -540
- package/src/generation/grid/generation/discrete/layer/GridTaskBuildSourceDistanceMap.js +222 -222
- package/src/generation/grid/generation/grid/GridTaskAddNodesFixed.js +44 -44
- package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +614 -614
- package/src/generation/markers/GridCellActionPlaceMarker.js +244 -244
- package/src/generation/markers/GridCellActionPlaceMarkerGroup.js +86 -86
- package/src/generation/markers/actions/terrain/MarkerNodeActionPaintTerrain.js +266 -266
- package/src/generation/markers/emitter/MarkerNodeEmitterGridCellAction.js +49 -49
- package/src/generation/placement/GridCellActionTransformNearbyMarkers.js +120 -120
- package/src/generation/placement/action/GridCellAction.js +45 -45
- package/src/generation/placement/action/GridCellActionPlaceTags.js +140 -140
- package/src/generation/placement/action/GridCellActionWriteFilterToLayer.js +89 -89
- package/src/generation/placement/action/random/CellActionSelectRandom.js +69 -69
- package/src/generation/placement/action/random/weighted/CellActionSelectWeightedRandom.js +91 -91
- package/src/generation/placement/action/util/GridCellActionSequence.js +61 -61
- package/src/generation/placement/action/util/GridCellDisplacedAction.js +61 -61
- package/src/generation/rules/cell/CellMatcherRemovalPreservesConnectivity.js +183 -183
- package/src/generation/theme/ThemeEngine.js +666 -666
- package/src/shade/RENDERER_CONTRACT.md +568 -0
- package/src/shade/descriptor/texture/format/gpu_texture_format_info.js +267 -267
- package/src/shade/device/ShadeDeviceFailure.js +104 -104
- package/src/shade/device/ShadeDeviceFailureReason.js +41 -41
- package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +255 -255
- package/src/shade/device/mock/REVIEW_2026_08_22.md +527 -527
- package/src/shade/device/mock/SoftwareGPUBindGroup.js +23 -23
- package/src/shade/device/mock/SoftwareGPUBindGroupLayout.js +22 -22
- package/src/shade/device/mock/SoftwareGPUError.js +63 -63
- package/src/shade/device/mock/SoftwareGPUPipeline.js +75 -75
- package/src/shade/device/mock/SoftwareGPUQuerySet.js +113 -113
- package/src/shade/device/mock/SoftwareGPUSampler.js +30 -30
- package/src/shade/device/mock/SoftwareGPUShaderModule.js +32 -32
- package/src/shade/device/mock/SoftwareGPUTextureView.js +22 -22
- package/src/shade/device/mock/SoftwareGPUUncapturedErrorEvent.js +23 -23
- package/src/shade/device/mock/normalize_dynamic_offsets.js +26 -26
- package/src/shade/device/mock/texture_copy.js +287 -287
- package/src/shade/device/timing/GPUTimer.js +285 -285
- package/src/shade/device/timing/GPUTimerArray.js +205 -205
- package/src/shade/playground/add_random_lights.js +49 -49
- package/src/shade/playground/add_random_meshes.js +83 -83
- package/src/shade/playground/animation_prototype.js +508 -508
- package/src/shade/playground/animation_prototype_skinning.js +647 -647
- package/src/shade/playground/animation_prototype_stress.js +765 -765
- package/src/shade/playground/enable_picker.js +59 -59
- package/src/shade/playground/gltf_skinned_prototype.js +560 -560
- package/src/shade/playground/gltf_skinned_stress_prototype.js +387 -387
- package/src/shade/playground/gltf_viewer.js +282 -282
- package/src/shade/playground/load_default_environment.js +19 -19
- package/src/shade/playground/main.js +2727 -2727
- package/src/shade/playground/make_animated_instances.js +117 -117
- package/src/shade/playground/prototype_virtual_texture.js +274 -274
- package/src/shade/playground/prototype_volumetrics_01.js +206 -206
- package/src/shade/playground/sample.js +234 -234
- package/src/shade/playground/sample_load_gltf.js +102 -102
- package/src/shade/playground/spread_node3d_grid.js +19 -19
- package/src/shade/renderer/Renderer.js +2436 -2436
- package/src/shade/renderer/ShadeIndirectLightingMode.js +14 -14
- package/src/shade/renderer/animation/ShadeAnimationChannel.js +414 -414
- package/src/shade/renderer/animation/compute_skinned_mesh_clip_bounding_sphere.js +531 -531
- package/src/shade/renderer/animation/pose/PosePlayback.js +55 -55
- package/src/shade/renderer/animation/pose/pose_evaluate_local.js +179 -179
- package/src/shade/renderer/animation/pose/pose_evaluate_world.js +76 -76
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +1024 -1024
- package/src/shade/renderer/animation/skinning/SKINNING_BINDING_STRUCT.js +79 -79
- package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.js +56 -56
- package/src/shade/renderer/binding/BindGroupManager.js +282 -282
- package/src/shade/renderer/binding/adapt_cache_capacity.js +25 -25
- package/src/shade/renderer/buffer/GPUBufferAllocator.js +116 -116
- package/src/shade/renderer/buffer/buffer_pool_ops.js +123 -123
- package/src/shade/renderer/buffer/table/GPU_DATABASE_PAGE_SIZE.js +9 -9
- package/src/shade/renderer/buffer/table/GPU_DATABASE_UPLOAD_BUFFER_LIMIT.js +8 -8
- package/src/shade/renderer/buffer/table/PageBufferPool.js +71 -71
- package/src/shade/renderer/buffer/table/PageSlotAllocator.js +95 -95
- package/src/shade/renderer/camera/Camera.js +281 -281
- package/src/shade/renderer/camera/GPUCameraContext.js +233 -233
- package/src/shade/renderer/camera/orbital/README.md +84 -84
- package/src/shade/renderer/deferred/shader_composite_volumetrics.js +75 -75
- package/src/shade/renderer/dynamic/DYNAMIC_MESH_VERTEX.js +25 -25
- package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.js +342 -342
- package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.js +167 -167
- package/src/shade/renderer/dynamic/shader_dynamic_mesh.js +170 -170
- package/src/shade/renderer/extension/FrameContext.js +263 -263
- package/src/shade/renderer/extension/FramePhase.js +104 -104
- package/src/shade/renderer/extension/FrameRecord.js +126 -126
- package/src/shade/renderer/extension/GBufferTextures.js +83 -83
- package/src/shade/renderer/extension/PresentTarget.js +34 -34
- package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +918 -918
- package/src/shade/renderer/extension/RenderExtension.js +69 -69
- package/src/shade/renderer/extension/RenderExtensionRegistry.js +319 -319
- package/src/shade/renderer/extension/SceneColor.js +40 -40
- package/src/shade/renderer/extension/ViewTextures.js +78 -78
- package/src/shade/renderer/fow/GPUFogOfWarRenderer.js +182 -182
- package/src/shade/renderer/geometry/Geometry.js +608 -608
- package/src/shade/renderer/geometry/geometry_build_from_meshlet_geometry.js +342 -342
- package/src/shade/renderer/geometry/meshlet_geometry_transfer.js +41 -41
- package/src/shade/renderer/geometry/primitives/make_box_geometry.js +153 -153
- package/src/shade/renderer/geometry/primitives/make_cylinder_geometry.js +240 -240
- package/src/shade/renderer/geometry/primitives/make_plane_geometry.js +145 -145
- package/src/shade/renderer/geometry/primitives/make_polyhedron_geometry.js +327 -327
- package/src/shade/renderer/geometry/primitives/make_torus_geometry.js +107 -107
- package/src/shade/renderer/geometry/primitives/make_torus_knot_geometry.js +165 -165
- package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +607 -607
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +66 -66
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildResult.js +32 -32
- package/src/shade/renderer/geometry/virtual/build/VGeoFaceGraph.js +103 -103
- package/src/shade/renderer/geometry/virtual/build/VGeoGatheredFaces.js +25 -25
- package/src/shade/renderer/geometry/virtual/build/VGeoLevel.js +33 -33
- package/src/shade/renderer/geometry/virtual/build/VGeoLevelOptions.js +62 -62
- package/src/shade/renderer/geometry/virtual/build/VGeoLevelSummary.js +49 -49
- package/src/shade/renderer/geometry/virtual/build/VGeoSubmesh.js +20 -20
- package/src/shade/renderer/geometry/virtual/build/VirtualGeometryMesh.js +214 -214
- package/src/shade/renderer/geometry/virtual/build/gltf/GltfByteSource.js +122 -122
- package/src/shade/renderer/geometry/virtual/build/gltf/GltfCollectReport.js +29 -29
- package/src/shade/renderer/geometry/virtual/build/gltf/GltfDocument.js +210 -210
- package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometry.js +48 -48
- package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometryInstance.js +25 -25
- package/src/shade/renderer/geometry/virtual/build/gltf/GltfGeometrySelection.js +28 -28
- package/src/shade/renderer/geometry/virtual/build/gltf/gltf_collect_geometries.js +296 -296
- package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.js +166 -166
- package/src/shade/renderer/geometry/virtual/build/gltf/gltf_read_geometry.js +199 -199
- package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.js +173 -173
- package/src/shade/renderer/geometry/virtual/build/vgeo_bounding_sphere.js +28 -28
- package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +200 -200
- package/src/shade/renderer/geometry/virtual/build/vgeo_build_levels.js +1786 -1786
- package/src/shade/renderer/geometry/virtual/build/vgeo_build_page_nodes.js +157 -157
- package/src/shade/renderer/geometry/virtual/build/vgeo_build_root_group.js +26 -26
- package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_normals.js +75 -75
- package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_tangents.js +143 -143
- package/src/shade/renderer/geometry/virtual/build/vgeo_containing_sphere.js +44 -44
- package/src/shade/renderer/geometry/virtual/build/vgeo_extract_submesh.js +41 -41
- package/src/shade/renderer/geometry/virtual/build/vgeo_gather_faces.js +71 -71
- package/src/shade/renderer/geometry/virtual/build/vgeo_graph_components.js +123 -123
- package/src/shade/renderer/geometry/virtual/build/vgeo_narrow_indices.js +26 -26
- package/src/shade/renderer/geometry/virtual/build/vgeo_normalize_source.js +112 -112
- package/src/shade/renderer/geometry/virtual/build/vgeo_partition_graph.js +56 -56
- package/src/shade/renderer/geometry/virtual/build/vgeo_sphere_inflate_to_contain.js +70 -70
- package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set.js +187 -187
- package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set_packed.js +207 -207
- package/src/shade/renderer/geometry/virtual/format/VGEO_DEFECT.js +18 -18
- package/src/shade/renderer/geometry/virtual/format/VGeoDefect.js +38 -38
- package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +64 -64
- package/src/shade/renderer/geometry/virtual/format/attribute/VGEO_ATTRIBUTE.js +13 -13
- package/src/shade/renderer/geometry/virtual/format/attribute/VGEO_ATTRIBUTES.js +50 -50
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attribute_allocate.js +28 -28
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attribute_mask.js +23 -23
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.js +31 -31
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_vertex.js +32 -32
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.js +46 -46
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_like.js +19 -19
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.js +27 -27
- package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_asset_id_preimage.js +62 -62
- package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_asset_id.js +13 -13
- package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_directory_block_checksum.js +41 -41
- package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_frame_checksum.js +41 -41
- package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_header_checksum.js +25 -25
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.js +20 -20
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_RECORD_SIZE.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CHILD_REF.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CLUSTER_TRIANGLE_COUNT.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_MAX_CLUSTER_VERTEX_COUNT.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_NO_CHILD.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_NO_GROUP.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_SAME_PAGE.js +6 -6
- package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.js +81 -81
- package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryGroup.js +37 -37
- package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryPageNode.js +55 -55
- package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_BLOCK_RECORDS.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_FLAG_ROOT_PAGE.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/directory/VGEO_DIRECTORY_RECORD_SIZE.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/directory/vgeo_directory_block_count.js +12 -12
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CHILD_OFFSET.js +11 -11
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CHILD_TABLE_ENTRY_SIZE.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.js +11 -11
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_RAW.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_ALIGNMENT.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_HEADER_SIZE.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_FRAME_OFFSET.js +13 -13
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_PAGE_MAGIC.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/frame/VGeoFrameBlob.js +21 -21
- package/src/shade/renderer/geometry/virtual/format/frame/VGeoFrameExtent.js +59 -59
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_codec_name.js +27 -27
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.js +84 -84
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js +56 -56
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.js +35 -35
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_scan_frames.js +74 -74
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.js +6 -6
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_HASH_ALGO_XXH3.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_OFFSET.js +31 -31
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_SIZE.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_LEVEL_OFFSET.js +20 -20
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_LEVEL_RECORD_SIZE.js +9 -9
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAGIC.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_PAYLOAD_ENCODING_SHADE_MESHLET_V1.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/header/VGeoLevelRecord.js +41 -41
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_RECORD_SIZE.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_GROUP_CLUSTER_COUNT.js +6 -6
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_METRIC_OFFSET.js +21 -21
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_METRIC_SIZE.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_FANOUT.js +9 -9
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_FLAG_LEAF.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_OFFSET.js +10 -10
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_NODE_RECORD_SIZE.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_HEADER_SIZE.js +5 -5
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.js +16 -16
- package/src/shade/renderer/geometry/virtual/format/payload/VGEO_ENCODING_FLAGS_MASK.js +6 -6
- package/src/shade/renderer/geometry/virtual/format/payload/VGeoEncodedPayload.js +18 -18
- package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js +200 -200
- package/src/shade/renderer/geometry/virtual/format/payload/vgeo_payload_attribute_offset.js +39 -39
- package/src/shade/renderer/geometry/virtual/format/payload/vgeo_payload_size.js +20 -20
- package/src/shade/renderer/geometry/virtual/format/prototypeVGEOFormatViewer.js +2063 -2063
- package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +1622 -1622
- package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +485 -485
- package/src/shade/renderer/gpu_primitive/sort/ffx/graph_radix_sort.js +115 -115
- package/src/shade/renderer/gpu_primitive/sort/one_sweep/verify_radix_sort_one_sweep.js +110 -110
- package/src/shade/renderer/light/environment/octahedral_resample_kernel.js +50 -50
- package/src/shade/renderer/light/environment/octahedral_uv_to_direction.js +30 -30
- package/src/shade/renderer/light/environment/resample_cube_to_octahedral.js +157 -157
- package/src/shade/renderer/light/make_sunlight.js +35 -35
- package/src/shade/renderer/light/model/io/light_record_point.js +39 -39
- package/src/shade/renderer/loader/gltf/GLTF_PRIMITIVE_MODE.js +23 -23
- package/src/shade/renderer/loader/gltf/MESHOPT_COMPRESSION_PLAN.md +793 -793
- package/src/shade/renderer/loader/gltf/ext/GltfBufferViewExtension.js +54 -54
- package/src/shade/renderer/loader/gltf/ext/GltfBufferViewExtensionSet.js +109 -109
- package/src/shade/renderer/loader/gltf/ext/gltf_apply_buffer_view_extensions.js +69 -69
- package/src/shade/renderer/loader/gltf/ext/gltf_buffer_view_extensions.js +22 -22
- package/src/shade/renderer/loader/gltf/ext/gltf_geometry_extensions.js +39 -39
- package/src/shade/renderer/loader/gltf/ext/meshopt/MeshoptBufferViewExtension.js +301 -301
- package/src/shade/renderer/loader/gltf/gltf_component_type.js +223 -223
- package/src/shade/renderer/loader/gltf/gltf_node_world_matrices.js +137 -137
- package/src/shade/renderer/loader/gltf/gltf_parse_container.js +241 -241
- package/src/shade/renderer/loader/gltf/gltf_read_accessor.js +294 -294
- package/src/shade/renderer/loader/gltf/gltf_used_image_indices.js +49 -49
- package/src/shade/renderer/loader/gltf/load_gltf.js +1253 -1253
- package/src/shade/renderer/loader/gltf/tiny-gltf.js +646 -646
- package/src/shade/renderer/loader/usd/UsdError.js +33 -33
- package/src/shade/renderer/loader/usd/UsdExtensionRegistry.js +69 -69
- package/src/shade/renderer/loader/usd/load_usd.js +226 -226
- package/src/shade/renderer/loader/usd/parse_usda.js +931 -931
- package/src/shade/renderer/loader/usd/unpack_usdz.js +106 -106
- package/src/shade/renderer/loader/usd/usd_build_scene.js +658 -658
- package/src/shade/renderer/loader/usd/usd_compose_transform.js +258 -258
- package/src/shade/renderer/loader/usd/usd_decode_image.js +384 -384
- package/src/shade/renderer/loader/usd/usd_lz4.js +182 -182
- package/src/shade/renderer/loader/usd/usd_triangulate.js +159 -159
- package/src/shade/renderer/material/ShadeMaterial.d.ts +2 -2
- package/src/shade/renderer/material/ShadeMaterial.js +2 -2
- package/src/shade/renderer/material/standard/fragment_gbuffer.js +334 -334
- package/src/shade/renderer/material/standard/fragment_gbuffer_vt.js +169 -169
- package/src/shade/renderer/material/standard/material_gbuffer_vertex.js +45 -45
- package/src/shade/renderer/material/standard_material_to_struct.js +23 -23
- package/src/shade/renderer/particles/prototypeParticleSystem.js +450 -450
- package/src/shade/renderer/particles/runtime/spawn_request_encoding.js +52 -52
- package/src/shade/renderer/pool/TransientResourcePool.js +392 -392
- package/src/shade/renderer/postprocess/bloom/bloom_chain_resolution.js +29 -29
- package/src/shade/renderer/postprocess/bloom/graph_bloom_downsample.js +109 -109
- package/src/shade/renderer/postprocess/bloom/graph_postprocess_bloom.js +155 -155
- package/src/shade/renderer/postprocess/cas/shader_ffx_rcas.js +114 -114
- package/src/shade/renderer/postprocess/dof/DOF_AUTOFOCUS_PARAMS_STRUCT.js +25 -25
- package/src/shade/renderer/postprocess/dof/DOF_FOCUS_STATE_STRUCT.js +23 -23
- package/src/shade/renderer/postprocess/dof/DOF_PRESETS.js +27 -27
- package/src/shade/renderer/postprocess/dof/chunk_dof_coc_band_diameter.js +41 -41
- package/src/shade/renderer/postprocess/dof/chunk_dof_coc_band_radius.js +32 -32
- package/src/shade/renderer/postprocess/dof/chunk_dof_coc_coefficient.js +26 -26
- package/src/shade/renderer/postprocess/dof/compute_dof_autofocus_params.js +64 -64
- package/src/shade/renderer/postprocess/dof/compute_dof_uniform.js +54 -54
- package/src/shade/renderer/postprocess/dof/gather/DOF2_CONFIG.js +91 -91
- package/src/shade/renderer/postprocess/dof/gather/DOF2_PRESETS.js +9 -9
- package/src/shade/renderer/postprocess/dof/gather/DOF2_SETTINGS_STRUCT.js +66 -66
- package/src/shade/renderer/postprocess/dof/gather/DOF2_TEXTURE_FORMAT.js +22 -22
- package/src/shade/renderer/postprocess/dof/gather/DepthOfFieldU.js +292 -292
- package/src/shade/renderer/postprocess/dof/gather/chunk_dof2_coc_to_near_far.js +25 -25
- package/src/shade/renderer/postprocess/dof/gather/chunk_dof2_gather_weight.js +30 -30
- package/src/shade/renderer/postprocess/dof/gather/compute_dof2_uniform.js +54 -54
- package/src/shade/renderer/postprocess/dof/gather/graph_postprocess_dof2.js +194 -194
- package/src/shade/renderer/postprocess/dof/gather/shader_dof2_fill.js +68 -68
- package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_far.js +140 -140
- package/src/shade/renderer/postprocess/dof/gather/shader_dof2_gather_near.js +142 -142
- package/src/shade/renderer/postprocess/dof/gather/shader_dof2_recombine.js +99 -99
- package/src/shade/renderer/postprocess/dof/gather/shader_dof2_setup.js +102 -102
- package/src/shade/renderer/postprocess/dof/gather/shader_dof2_tile_dilate.js +75 -75
- package/src/shade/renderer/postprocess/dof/gather/shader_dof2_tile_max.js +73 -73
- package/src/shade/renderer/postprocess/dof/raymarch/DOF_SETTINGS_STRUCT.js +49 -49
- package/src/shade/renderer/postprocess/dof/raymarch/DepthOfField.js +298 -298
- package/src/shade/renderer/postprocess/dof/raymarch/graph_postprocess_dof.js +244 -244
- package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_composite.js +91 -91
- package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_firefly_cleanup.js +61 -61
- package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_prefilter.js +90 -90
- package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch_compute.js +267 -267
- package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_slice_compute.js +271 -271
- package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_tile_dilate.js +70 -70
- package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_tile_max.js +72 -72
- package/src/shade/renderer/postprocess/dof/shader_dof_autofocus.js +99 -99
- package/src/shade/renderer/postprocess/gtao/generate_magic_square_noise.js +51 -51
- package/src/shade/renderer/postprocess/motion_blur/MotionBlur.js +56 -56
- package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +78 -78
- package/src/shade/renderer/postprocess/upscale-dither/UpscaleDither.js +95 -95
- package/src/shade/renderer/postprocess/upscale-dither/fragment_shader_upscale_dither.js +204 -204
- package/src/shade/renderer/rasterize/bucket/construct_primitive_state.js +58 -58
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +230 -230
- package/src/shade/renderer/rasterize/native/graph_rasterize_meshes_alpha_tested.js +278 -278
- package/src/shade/renderer/rasterize/native/oit/chunk_material_side_should_draw.js +26 -26
- package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments_ibl.js +178 -178
- package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +235 -235
- package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +208 -208
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +206 -206
- package/src/shade/renderer/scene/DynamicMesh.js +70 -70
- package/src/shade/renderer/scene/DynamicMeshBatch.js +90 -90
- package/src/shade/renderer/scene/GPUSceneContext.js +901 -901
- package/src/shade/renderer/scene/SceneNodeBinding.js +56 -56
- package/src/shade/renderer/scene/SkinnedMesh.js +55 -55
- package/src/shade/renderer/scene/TransformAuthority.js +33 -33
- package/src/shade/renderer/scene/optimization/deduplicate_geometries.js +38 -38
- package/src/shade/renderer/scene/optimization/deduplicate_images.js +93 -93
- package/src/shade/renderer/scene/optimization/geometry/internal/match_refit_validate.js +127 -127
- package/src/shade/renderer/scene/optimization/geometry/internal/match_solve_transform.js +492 -492
- package/src/shade/renderer/scene/optimization/optimize_texture_set.js +81 -81
- package/src/shade/renderer/scene/optimization/source_to_sampler2d.js +21 -21
- package/src/shade/renderer/scene/serialization/STANDARD_MATERIAL_SERIALIZED_BYTE_SIZE.js +12 -12
- package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_transform_point.js +26 -26
- package/src/shade/renderer/shader/chunk/noise/chunk_interleaved_gradient_noise.js +20 -20
- package/src/shade/renderer/shader/chunk/spherical_harmonics/dering/chunk_sh2_dering_optimize_positive.js +61 -61
- package/src/shade/renderer/shader/chunk/texture/chunk_texture_filter_fxaa_lite.js +225 -225
- package/src/shade/renderer/shader/chunk/texture/dither/chunk_dither_color_8bit_gradient_noise.js +25 -25
- package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.js +384 -384
- package/src/shade/renderer/shadow/map/PointLightShadowmap.js +105 -105
- package/src/shade/renderer/shadow/map/SpotLightShadowmap.js +63 -63
- package/src/shade/renderer/shadow/map/TetrahedronPointLightShadowmap.js +89 -89
- package/src/shade/renderer/shadow/map/shadowmap_update_score.js +232 -232
- package/src/shade/renderer/texture/GPUTextureAllocator.js +85 -85
- package/src/shade/renderer/texture/texture_pool_ops.js +171 -171
- package/src/shade/renderer/texture/virtual/PageTableQuadAllocator.js +253 -253
- package/src/shade/renderer/texture/virtual/README.md +276 -276
- package/src/shade/renderer/texture/virtual/VTPageTable.js +330 -330
- package/src/shade/renderer/texture/virtual/VTPhysicalCache.js +349 -349
- package/src/shade/renderer/texture/virtual/VT_CONSTANTS.js +154 -154
- package/src/shade/renderer/texture/virtual/VT_STACK_INFO_STRUCT.js +20 -20
- package/src/shade/renderer/texture/virtual/VirtualTextureManager.js +1276 -1276
- package/src/shade/renderer/texture/virtual/VirtualTextureStack.js +106 -106
- package/src/shade/renderer/texture/virtual/build_chunk_vt_lookup.js +150 -150
- package/src/shade/renderer/texture/virtual/build_shader_vt_feedback.js +232 -232
- package/src/shade/renderer/texture/virtual/chunk_vt_request_key.js +28 -28
- package/src/shade/renderer/texture/virtual/source/VTSourceImage.js +207 -207
- package/src/shade/renderer/texture/virtual/source/VTSourceProcedural.js +135 -135
- package/src/shade/renderer/texture/virtual/source/VTSourceTiled.js +132 -132
- package/src/shade/renderer/water/GPUWaterRenderer.js +167 -167
- package/src/shade/renderer/water/shader_water.js +246 -246
- package/src/shade/renderer/water/water_height_texture_from_sampler.js +56 -56
- package/src/shade/vitest.setup.mjs +89 -89
- package/src/shade/wgsl/validate_wgsl_source.js +316 -316
- package/src/view/controller/controls/NumericIntervalControl.js +90 -90
- package/src/view/controller/controls/Vector2Control.js +83 -83
- package/src/view/controller/controls/Vector3Control.js +90 -90
- package/src/view/elements/Group.js +56 -56
- package/src/view/elements/drawCheckers.js +33 -33
- package/src/view/elements/windrose/WindRoseDiagram.js +369 -369
- package/src/view/graphics/WebGLContextFailureView.js +76 -76
- package/src/view/layout/DockPanel.js +51 -51
- package/src/view/layout/SplitView.js +365 -365
- package/src/view/layout/show_popup_menu.js +81 -81
- package/src/view/minimap/MinimapCanvasView.js +411 -411
- package/src/view/minimap/MinimapImageCache.js +88 -88
- package/src/view/minimap/MinimapMarkerCollection.d.ts +0 -1
- package/src/view/minimap/MinimapMarkerCollection.d.ts.map +1 -1
- package/src/view/minimap/MinimapMarkerCollection.js +180 -180
- package/src/view/minimap/dom/MinimapCameraView.js +206 -206
- package/src/view/minimap/minimap_focus_area.js +97 -97
- package/editor/process/symbolic/SocketsSymbolicDisplay.js +0 -192
- package/src/core/geom/3d/topology/struct/binary/io/moved_tests.txt +0 -37
- package/src/engine/ecs/attachment/Attachment.d.ts +0 -40
- package/src/engine/ecs/attachment/Attachment.d.ts.map +0 -1
- package/src/engine/ecs/attachment/Attachment.js +0 -57
- package/src/engine/ecs/attachment/AttachmentBinding.d.ts +0 -28
- package/src/engine/ecs/attachment/AttachmentBinding.d.ts.map +0 -1
- package/src/engine/ecs/attachment/AttachmentBinding.js +0 -41
- package/src/engine/ecs/attachment/AttachmentSystem.d.ts +0 -61
- package/src/engine/ecs/attachment/AttachmentSystem.d.ts.map +0 -1
- package/src/engine/ecs/attachment/AttachmentSystem.js +0 -307
- package/src/engine/ecs/attachment/BoneAttachmentBinding.d.ts +0 -27
- package/src/engine/ecs/attachment/BoneAttachmentBinding.d.ts.map +0 -1
- package/src/engine/ecs/attachment/BoneAttachmentBinding.js +0 -69
- package/src/engine/ecs/attachment/TransformAttachmentBinding.d.ts +0 -4
- package/src/engine/ecs/attachment/TransformAttachmentBinding.d.ts.map +0 -1
- package/src/engine/ecs/attachment/TransformAttachmentBinding.js +0 -51
- package/src/engine/ecs/sockets/AttachmentSocket.d.ts +0 -32
- package/src/engine/ecs/sockets/AttachmentSocket.d.ts.map +0 -1
- package/src/engine/ecs/sockets/AttachmentSocket.js +0 -60
- package/src/engine/ecs/sockets/AttachmentSocketType.d.ts +0 -6
- package/src/engine/ecs/sockets/AttachmentSocketType.d.ts.map +0 -1
- package/src/engine/ecs/sockets/AttachmentSocketType.js +0 -8
- package/src/engine/ecs/sockets/AttachmentSockets.d.ts +0 -57
- package/src/engine/ecs/sockets/AttachmentSockets.d.ts.map +0 -1
- package/src/engine/ecs/sockets/AttachmentSockets.js +0 -130
- package/src/engine/ecs/sockets/BoneAttachmentSocket.d.ts +0 -29
- package/src/engine/ecs/sockets/BoneAttachmentSocket.d.ts.map +0 -1
- package/src/engine/ecs/sockets/BoneAttachmentSocket.js +0 -56
- package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.d.ts +0 -6
- package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.d.ts.map +0 -1
- package/src/engine/ecs/sockets/serialization/AttachmentSocketsAssetLoader.js +0 -19
- package/src/shade/renderer/animation/_dump_resolve.d.mts +0 -2
- package/src/shade/renderer/animation/_dump_resolve.d.mts.map +0 -1
- package/src/shade/renderer/animation/_dump_resolve.mjs +0 -5
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
# Domain E — engine/physics
|
|
2
|
-
|
|
3
|
-
Scope: `engine/physics/**` (+ `engine/ecs/ik/*` where the task named it). Every finding below was verified by opening the cited files. `*.spec.js`, `prototype*.js`, `test*.js` excluded.
|
|
4
|
-
|
|
5
|
-
The mechanical brief was thin and its three "A. token-identical" hits turned out to be the *least* interesting things in the domain (two are explicitly, correctly justified). The real duplication here is semantic re-derivation, and it clusters in four places: (1) the fluid solver's collocated-vs-MAC twin kernel families, (2) the narrowphase contact-emission epilogues, (3) the two solvers (contacts / constraints) which independently implement the same impulse application, (4) core primitives that return *parameters* where every single caller wants *points*.
|
|
6
|
-
|
|
7
|
-
## Findings
|
|
8
|
-
|
|
9
|
-
| # | Severity | Concept | Sites | Evidence | Verdict | Proposed common path | Effort/Risk |
|
|
10
|
-
|---|---|---|---|---|---|---|---|
|
|
11
|
-
| 1 | HIGH | **Solver impulse application written twice, verbatim.** `Δv = P·invM; Δω = Iw⁻¹·(r×P)` with the identical `invM===0` guard, identical sign folding, identical `world_inverse_inertia_apply_raw` argument order and identical AV write-back. | `engine/physics/constraint/solve_constraints.js:250` `apply_impulse(ss, base, invM, rx,ry,rz, Px,Py,Pz, sign)` ⟷ `engine/physics/solver/solve_contacts.js:401` `apply_impulse_to_body(ss, base, invM, rx,ry,rz, Px,Py,Pz, sign, scratch_inertia)` | Bodies are line-for-line identical (15 statements each). The *only* difference is that the contacts version takes `scratch_inertia` as an 11th parameter where the constraints version closes over a module-static one. Both call the same kernel with the same operand order. | UNJUSTIFIED | Promote one copy to `engine/physics/solver/apply_impulse_to_body.js` (next to `friction_cone.js`) with the explicit `scratch` parameter; both solvers import it. Keep the parameter form — it is already the more explicit of the two. | Low effort. **Determinism: LOW risk** — identical operand order means bit-identical output; `determinism.spec.js` is the gate. This is the single most load-bearing routine in the step (per-row × per-iteration), so re-measure the rain/KEVA benches: a cross-module call here must stay inlined. |
|
|
12
|
-
| 2 | HIGH | **SAT minimum-overlap magnitude computed three ways, one of which diverges.** Same invariant ("how far along this axis must I push to separate?"), three independent expressions. | `engine/physics/narrowphase/box_box_manifold.js:135` `overlap = (rA + rB) - dist`; `engine/physics/narrowphase/box_triangle_contact.js:209-211` `overlap = min(bmax - tmin, tmax - bmin)`; `engine/physics/narrowphase/convex_convex_manifold.js:261` `overlap = Math.min(maxA,maxB) - Math.max(minA,minB)` | The first two are the minimum-translation (push-out) distance and agree. The third is the *interval intersection length*, which is smaller whenever one projection interval contains the other (A=[0,10], B=[4,5] → push-out 5, intersection 1). SAT then picks the axis by minimum overlap, so an understated value makes a containment axis *more* likely to win and be reported as the depth. `convex_convex_manifold` is documented as being for "the SMALL convex pieces of a decomposed mesh" — exactly where a thin piece is contained in a fat one. The three sites also carry three different degeneracy tests (`PARALLEL_EPS_SQR` absolute in two files, `PARALLEL_EPS_REL * ref2` relative in the third) and only two of three have the `TIE_REL/TIE_ABS` deadband. | UNJUSTIFIED (and needs a correctness decision before unification) | Extract `sat_track_min_overlap(state, ux,uy,uz, minA,maxA, minB,maxB, kind, sign_ref)` covering: normalise-and-reject, separation test, push-out overlap, deadband tie-break, centre-delta sign orientation. Each caller keeps its own *projection* (box half-extent vs 3 vertices vs vertex loop) — that part is legitimately shape-specific. | Medium effort. **Determinism: HIGH risk** and behaviour-changing: fixing `convex_convex`'s formula changes chosen axes on containment configs. Do it test-first against `convex_convex_manifold`'s own suite, then the determinism gate. |
|
|
13
|
-
| 3 | HIGH | **Whole collocated advection family shadows the MAC family and has zero callers.** `FluidSimulator` uses only the `v3_mac_*` kernels (imports at `engine/physics/fluid/FluidSimulator.js:6-11`). | Zero non-spec importers: `v3_grid_advect_sl_velocity.js`, `v3_grid_apply_advection_forward.js`, `v3_grid_apply_scalar_advection.js`, `v3_grid_advect_maccormack_scalar.js`, `v3_grid_advect_maccormack_velocity.js` (≈555 LOC) | Verified by importer count across `engine/` and `app/` (spec files excluded). Each is a collocated-grid re-implementation of an algorithm the MAC file next to it also implements (SL trace, MacCormack forward/backward/correct, Selle limiter, solid pass-through). A fix in the MAC limiter or the solid handling silently misses five files. | UNJUSTIFIED (as duplication) — but **NOT dead code**: meep is a library, these are public surface. | Do not delete. Collapse the *internal* redundancy first (findings 4 and 5), then leave the collocated family as thin, correct, exported kernels. Add a one-line "the simulator uses the MAC family; this is the collocated variant" note to each so the next reviewer does not re-derive this. | Low effort for 4/5; the family itself stays. Determinism: none (unreferenced by the sim). |
|
|
14
|
-
| 4 | MEDIUM | **MacCormack pass 1 re-implements the SL kernel that sits in the same directory.** The MAC file does this correctly (calls the SL kernel); the grid file does not. | `engine/physics/fluid/solver/v3_grid_advect_maccormack_scalar.js:66-82` (pass 1) ⟷ `v3_grid_apply_scalar_advection.js:38-58`; `v3_grid_advect_maccormack_velocity.js:73-94` (pass 1) ⟷ `v3_grid_advect_sl_velocity.js:69-96` | Both pairs are statement-for-statement identical: same solid pass-through, same `s = x - vel[c]*dt`, same sampler call. Contrast `v3_mac_advect_scalar.js:146`, which *does* call `v3_mac_advect_sl_scalar(fwd, ...)` for its forward pass — proving the delegation is viable and this is an oversight, not a design choice. | UNJUSTIFIED | Replace each pass-1 loop with the call to its SL sibling, exactly as the MAC scalar file already does. | Low effort. **Determinism: LOW** — identical arithmetic and identical loop order; the only change is a function boundary. |
|
|
15
|
-
| 5 | MEDIUM | **`v3_grid_apply_advection_forward` is a slower copy of `v3_grid_advect_sl_velocity`, and says so in its own docstring.** | `engine/physics/fluid/solver/v3_grid_apply_advection_forward.js:32` ⟷ `v3_grid_advect_sl_velocity.js:38` | The docstring at `v3_grid_apply_advection_forward.js:16` states: it "is exactly `v3_grid_advect_sl_velocity(outputs, inputs, inputs...)`". The body differs only by issuing three separate `scs3d_sample_linear` calls instead of one fused `scs3d_sample_linear3` — i.e. it recomputes the trilinear corner indices and weights three times per cell. MinHash flagged the pair at 0.813. | UNJUSTIFIED | Keep the export (public surface, meaningful name); make the body a one-line delegation to `v3_grid_advect_sl_velocity`. | Low effort. **Determinism: MEDIUM** — the fused 3-channel sampler may associate weights differently from three scalar samples. Diff numerically before/after; `quality.spec.js` is the fluid gate. |
|
|
16
|
-
| 6 | MEDIUM | **Selle monotone limiter written four times, twice under the same name `limit8` with different signatures in the same directory.** | `engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.js:155` `limit8(value, data, c0..c7)`; `v3_mac_advect_maccormack_velocity.js:293` `limit8(value, data, lx,ly,lz, dim_x,dim_y,dim_z, row, slice)`; hand-inlined at `v3_grid_advect_maccormack_scalar.js:107-132`; hand-inlined **with solid masking** at `v3_mac_advect_scalar.js:191-232` | The two named `limit8`s are different functions with the same name two files apart — a genuine reader hazard, and invisible to the mechanical pass because neither is exported (brief §G is empty). The grid-scalar inline is a *byte-level* match for the MAC-velocity `limit8` signature (`limit8(corrected, source, x-vx, y-vy, z-vz, res_x,res_y,res_z, res_x, slice_size)` reproduces it exactly). The MAC-scalar inline is a genuine superset (skips solid corners, `Infinity` init, fallback to the forward value). | UNJUSTIFIED for 3 of 4 | Extract two files: `v3_limit8_lattice.js` (position-form, absorbs the MAC-velocity version, the grid-scalar inline and — via a precomputed-index overload or by the caller passing the position — the grid-velocity version) and `v3_limit8_lattice_masked.js` for the solid-aware variant. Do **not** merge masked and unmasked behind a flag (uniform-control-flow rule). | Low-medium effort. **Determinism: LOW** for the unmasked merge (identical comparison order). These are per-cell kernels over the whole grid, so verify the extracted call stays inlined — the bodies are branchy but loop-free, which is the inlinable shape. |
|
|
17
|
-
| 7 | MEDIUM | **Vorticity confinement: the MAC kernel collocates to cell centres and then re-derives the collocated kernel's two passes verbatim.** | `engine/physics/fluid/solver/v3_grid_apply_vorticity_confinement.js:78-130` (pass 1 curl+\|ω\|, pass 2 N×ω) ⟷ `v3_mac_apply_vorticity_confinement.js:98-164` (pass 2, pass 3) | The MAC file's pass 1 (lines 70-90) averages faces into `scratch_cu/cv/cw` cell-centred arrays; its passes 2 and 3 are then character-identical to the grid file's passes 1 and 2 — same central-difference curl expressions, same `scratch_wmag` fill, same `magnitude_floor = 1e-10`, same `s = epsilon_dt / g_len`, same `N × ω` cross. Only the final deposit differs (cell write vs `0.5 *` scatter to two faces per axis). ≈60 lines. | UNJUSTIFIED | Extract `v3_grid_compute_vorticity(scratch_wx,wy,wz,wmag, vel_x,vel_y,vel_z, res, solid)` and `v3_grid_compute_confinement_force(out3, scratch_*, c, res_x, cell_slice, epsilon_dt)`; the MAC kernel calls both and keeps only its collocate-in / scatter-out ends. | Medium effort. **Determinism: LOW** (same expressions, same order). Per-cell whole-grid kernel — measure; the force helper writing 3 floats to a module-static may cost vs the current fully-inlined form. |
|
|
18
|
-
| 8 | MEDIUM | **Two-bone and one-bone IK solvers share a ~75-line terrain-probe + influence prologue verbatim.** | `engine/ecs/ik/TwoBoneInverseKinematicsSolver.js:76-151` (`solve`) ⟷ `engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js:74-151` (`solve`) | Identical: the ray direction from parent→bone, the primary `terrain.raycastFirstSync`, the early return, the secondary cast back along the contact normal from the bone + normal, the second early return, `constraint.offset * <scale>` → `v3_displace_in_direction`, the `delta_contact_c_*` triple, `v3_dot` side test, `v3_length`, the `dot_contact_side < 0 ? 1 : 1 - clamp01(constraint.distance.normalizeValue(inverseLerp(0, <scale>, d)))` influence, and the `influence <= 0` bail. Only two things differ and both are plain inputs: the ray origin (parent bone world position vs root bone A world position) and the length scale (`boneWorldScale.length() * 1/√3` vs limb length A→B→C). The mechanical brief missed this entirely because its path filter was `^engine/physics/`. | UNJUSTIFIED | Extract `ik_probe_surface_influence(contact_out, terrain, origin_xyz, direction_xyz, effector_xyz, length_scale, constraint) -> number` returning influence (0 = no contact / no influence) and leaving the contact in the supplied `SurfacePoint3`. Both solvers keep only their own post-influence rotation math, which is genuinely different. | Low-medium effort. No determinism exposure (animation-side, not the physics step). Note: the task hypothesis "two competing two-bone IK solvers" is **false** — `TwoBoneInverseKinematicsSolver` *imports* `two_joint_ik` (line 11). The real duplication is this sibling prologue. |
|
|
19
|
-
| 9 | MEDIUM | **Closest-point-pair → contact epilogue written five times.** Given two witness points and two radii, produce (unit normal B→A, depth, witness on A, witness on B) with a degenerate-distance tie-break. | `engine/physics/narrowphase/sphere_sphere_contact.js:27-51`; `sphere_triangle_contact.js:99-141`; `capsule_contacts.js:89-116` (`capsule_sphere_contact`); `capsule_contacts.js:158-197` (`capsule_capsule_contact`); `capsule_contacts.js:293-324` (`capsule_capsule_multi_contacts` inner loop) | All five: `d = pa - pb`, `dist_sqr`, `sum = ra + rb`, `if (dist_sqr >= sum*sum) return false`, `dist = sqrt`, `if (dist > 0) normalize else <tie-break>`, `out[3] = sum - dist`, `out[4..6] = pa - n*ra`, `out[7..9] = pb + n*rb`. The tie-break direction is the only real variation: `(1,0,0)` at two sites, segment-centre delta at one, body-centre delta at two — i.e. it is an *input*, not a mode. | UNJUSTIFIED | `narrowphase/emit_pair_contact.js`: `emit_pair_contact(out, pax,pay,paz, pbx,pby,pbz, ra, rb, fbx,fby,fbz) -> boolean`, fallback direction always supplied by the caller (no null-sentinel). `sphere_sphere_contact` writes only lanes 0-3 today and would need to keep that shorter contract or gain witness lanes — check its callers first. | Low-medium effort. Per-contact hot path but not per-cell; a leaf function of this size is a normal V8 inline candidate. **Determinism: LOW** (identical order), but the tie-break unification changes `capsule_sphere_contact`'s `(1,0,0)` if you "improve" it — don't; pass `(1,0,0)` explicitly. |
|
|
20
|
-
| 10 | MEDIUM | **Cap-endpoint contact append + spatial dedup written twice, with the same magic constant.** | `engine/physics/narrowphase/capsule_contacts.js:582-619` (endpoint loop inside `capsule_box_multi_contacts`) ⟷ `capsule_triangle_contact.js:360-395` (`try_endpoint_contact`) | Both: run a sphere-vs-X query at a cap centre, unpack the 10-float `(n, depth, A, B)` scratch, loop `k < count` comparing the capsule-side world position against every already-emitted contact, reject below `DEDUPE_DIST_SQR`, then write the `(A, B, n, depth)` stride-10 record. `DEDUPE_DIST_SQR = 1e-6` is declared independently in both files (`capsule_contacts.js:510`, `capsule_triangle_contact.js:64`) with near-identical justifying comments, and both files independently declare a stride-10 constant (`CAPSULE_BOX_CONTACT_STRIDE`, `CAPSULE_TRIANGLE_CONTACT_STRIDE`). | UNJUSTIFIED | `narrowphase/append_deduped_contact.js`: `append_deduped_contact(out, count, scratch10) -> number`, with `DEDUPE_DIST_SQR` and the stride-10 candidate layout defined once (the layout is already shared by three files — put it next to `decomposition/triangle_buffer_layout.js`). | Low effort. **Determinism: LOW.** Real payoff is the single definition of the tolerance: today a tuning change to one leaves the other. |
|
|
21
|
-
| 11 | MEDIUM | **Reference-face manifold emission epilogue written three times.** Project each clipped point, reject non-penetrating, push `(x,y,z,depth)` candidates, `reduce_manifold_contacts`, then per kept point project onto the reference plane and swap A/B by `ref_is_A`. | `engine/physics/narrowphase/convex_convex_manifold.js:596-622` (`emit_face_manifold`); `box_box_manifold.js:578-670`; `box_triangle_contact.js:455-497` (`emit_box_face_manifold`) | All three build a stride-4 (or 7) candidate array, call `reduce_manifold_contacts(cand, n, stride, MAX_CONTACTS, depth_lane)`, then run the identical "ref-side witness = point − ref_out · dist_to_plane" projection and the identical `if (ref_is_A) {A=proj,B=pt} else {A=pt,B=proj}` swap into `4 + k*CONTACT_STRIDE`. A previous review already lifted the *reduction* out of these three (see the "Each previously carried its own byte-identical copy" note at `reduce_manifold_contacts.js:14-18`) — this is the un-lifted residue of the same block. | UNJUSTIFIED | Finish the extraction the previous review started: `emit_reference_face_manifold(out, cand, n, stride, ref_is_A, r0x,r0y,r0z, rmx,rmy,rmz)` doing reduce + project + swap + write. The *clipping* stays per-shape. | Medium effort. **Determinism: LOW** if the projection expression is copied verbatim. The three sites use different candidate strides (4 and 7), so the stride stays a parameter — `reduce_manifold_contacts` already set that precedent. |
|
|
22
|
-
| 12 | MEDIUM | **A general 2D half-plane Sutherland–Hodgman clipper exists in core, unused, while narrowphase ships its own.** | `core/geom/2d/polygon/polygon2_clip_halfplane.js:20` (exported, spec'd, **zero consumers**) ⟷ `engine/physics/narrowphase/box_triangle_contact.js:110` `clip_against_half_plane_uv` | Same algorithm: walk edges, evaluate a signed side function at both endpoints, emit the start point if inside, emit the crossing at `t = sa/(sa-sb)`. The core version parameterises the line by point+direction and tests `cross(dir, P-e)`; the physics version by point+normal and tests `(P-e)·n` — algebraically the same half-plane with `n = perp(dir)`. Note `box_box_manifold.js:3` and `box_triangle_contact.js:3` *already* import the sibling `polygon2_clip_axis_halfplane` from core, so the boundary crossing is established practice. | UNJUSTIFIED | `box_triangle_contact` imports `polygon2_clip_halfplane` and converts its normal to a direction (or add a normal-form export next to it). | Low effort. **Determinism: MEDIUM** — the core version carries `POINT_EPS = 1e-9` inside/outside slack; the physics version uses exact `<= 0`. That changes the surviving vertex count on boundary-coincident points, hence the manifold. Diff on the box-triangle suite before adopting. |
|
|
23
|
-
| 13 | MEDIUM | **Effector face-lattice splat scaffolding written six times (3 axes × 2 effectors).** | `engine/physics/fluid/effector/ImpulseFluidEffector.js:105-132, 134-161, 163-191` ⟷ `WakeFluidEffector.js:249-273, 275-298, 300-~330` | Each block: derive the per-lattice index range as `max(0, floor(box_min [+0.5 on own axis]))` … `min(res_axis [−1 off-axis], ceil(box_max [+0.5]))`, triple loop, compute the lattice offset (`sx*res_y` / `res_x*(res_y+1)` / `res_x*res_y`), evaluate a falloff weight at the face position (offset −0.5 on the own axis), `continue` on zero weight, `continue` on `pin[f] !== 0`, accumulate `v[f] += impulse_axis * w`. The only per-block variation is the weight function — and `WakeFluidEffector` already proves this can be a callback (`tube_weight` closure at line 230). | UNJUSTIFIED | `fluid/effector/mac_splat_axis.js`: `mac_splat_axis(velocity, pin, axis, res_x,res_y,res_z, box6, deposit, weight_fn)`. The two effectors then differ only in their weight function and box, which is the actual content. | Medium effort. **Determinism: LOW.** Perf: this is a *bounded* splat (influence radius), not a whole-grid kernel, and the Wake path already pays a closure call per face — so the reuse-over-micro-opt rule applies cleanly here. |
|
|
24
|
-
| 14 | PROMOTION | **`tri3_closest_point_barycentric` returns barycentrics; all 12 callers immediately reconstruct the world point.** The missing primitive is `tri3_closest_point`. | Physics: `sphere_triangle_contact.js:89-96`; `capsule_triangle_contact.js:186-191`, `:210-215`, `:234-239`; `compute_penetration.js:331-335`. Core/graphics (context): `core/geom/3d/shape/Triangle3D.js:169-175` and `:212-…`; `core/geom/3d/topology/struct/binary/query/bt_mesh_find_nearest_face.js:131-137`; `core/geom/3d/shape/MeshShape3D.js:439`, `:491`; `core/geom/3d/shape/ConvexHullShape3D.js:254`, `:285`; `engine/graphics/geometry/buffered/query/bvh32_geometry_nearest.js:203`; `.../query_bvh_geometry_nearest.js:164` | Every site writes the same four lines: `const c = 1 - a - b; qx = a*ax + b*bx + c*cx; qy = …; qz = …`. Several then compute the same `d = p - q; d_sqr` follow-up. This is the block the brief flagged as shared between `Triangle3D.js:162`, `capsule_triangle_contact.js:181`, `bt_mesh_find_nearest_face.js` and the two BVH nearest queries. | PROMOTION | Add `core/geom/3d/triangle/tri3_closest_point.js` — `tri3_closest_point(out, out_offset, px,py,pz, ax..cz)` writing the world point (wrapping the barycentric version) — and `tri3_point_distance_sqr(...)` for the distance-only callers. Keep the barycentric export (public surface; a few callers genuinely want the weights). | Low effort per site, 12 sites. **Determinism: LOW** if the reconstruction expression is copied verbatim (`a*ax + b*bx + c*cx` in that order — `bt_mesh_find_nearest_face.js:135` writes `ax*wa + bx*wb + cx*wc`, which is the same order of operands; check each before converting). |
|
|
25
|
-
| 15 | PROMOTION | **`line3_compute_segment_segment_closest_points` returns `(s, t)`; all four physics callers immediately lerp both segments.** | `engine/physics/narrowphase/capsule_contacts.js:142-156`; `capsule_triangle_contact.js:105-117` (`test_edge`); `convex_convex_manifold.js:501-516` (`emit_edge_pair_contact`); `box_triangle_contact.js:874-885` (`emit_edge_cross_contact`) | All four: call the core function into a length-2 scratch, then six `p = a0 + (a1 - a0) * s` lines. This is the bulk of the brief's 39 `inline_lerp` hits in narrowphase. `core/geom/3d/line/` already contains a differently-named `line3_closest_points_segment_segment.js` — worth checking whether *it* is the point-returning variant that was never adopted. | PROMOTION | Add `line3_segment_segment_closest_point_pair(out6, a0x..a1z, b0x..b1z)` writing both world points (and returning the squared distance, which three of the four sites compute next anyway). | Low effort. **Determinism: LOW** — the lerp form `a + (b-a)*t` is used identically at all four sites; preserve it exactly (do not "improve" to `(1-t)*a + t*b`). |
|
|
26
|
-
| 16 | LOW/MEDIUM | **Two hash-combine conventions coexist inside `engine/physics`, producing different values for the same inputs.** | Hand-rolled `h = (h*31 + x) | 0`, seeded with the first field: `engine/physics/ecs/Collider.js:157`, `RigidBody.js:279`, `Joint.js:442`. Canonical `combine_hash(...)`: `engine/physics/fluid/effector/GlobalFluidEffector.js:182`, `ImpulseFluidEffector.js:228`. A third variant `h = ((h<<5) - h) + v; h \|= 0` seeded with `n`: `engine/physics/fluid/ecs/FluidEffectorsComponent.js:77` | `core/collection/array/combine_hash.js` → `computeIntegerArrayHash` → `computeStridedIntegerArrayHash` is the canonical fold: `hash = length`, then `hash = ((hash<<5) - hash) + (v >>> 0)`, returning **uint32**. `(h<<5)-h` and `h*31` are the same multiply; the divergences are the seed (`length` vs first field) and the output signedness (`>>> 0` vs `\| 0`). Three conventions in one package. | UNJUSTIFIED as a *convention* split; the hand-rolled fold itself is JUSTIFIED (`combine_hash(...args)` allocates a rest-array per call — `RigidBody.hash()` folds 12 fields and runs during dirty-checking) | Do **not** route the ECS components through `combine_hash`. Instead add a non-allocating `hash_combine_step(h, v)`-style primitive next to `computeStridedIntegerArrayHash` and have all three physics families use it, so the seed and signedness are decided once. Then `FluidEffectorsComponent` and the two effectors stop being a third and fourth answer. | Low effort. No determinism exposure (hashes are change-detection, not simulation state) — but do check nothing persists these values across versions before changing seed/signedness. |
|
|
27
|
-
| 17 | LOW/MEDIUM | **`solve_constraints` re-derives `angular_effective_mass_raw`, which was extracted for exactly this purpose.** | `engine/physics/constraint/solve_constraints.js:191-204` `axis_effective_mass` ⟷ `engine/physics/inertia/world_inverse_inertia.js:102` `angular_effective_mass_raw` | The core of `axis_effective_mass` is `cx,cy,cz = r × d; world_inverse_inertia_apply_raw(scratch, …, cx,cy,cz); k += cx*s[0] + cy*s[1] + cz*s[2]` — character-for-character the body of `angular_effective_mass_raw`. That kernel's own docstring (lines 80-84) says it is "The shared kernel behind the contact/joint solver's SoA path and the raycast-vehicle's component path — each previously inlined the cross product + apply_raw + dot themselves." `solve_contacts.js:526` and `RaycastVehicle.js:54` adopted it; `solve_constraints.js` was missed. | UNJUSTIFIED (incomplete prior extraction) | `axis_effective_mass` becomes `invM === 0 ? 0 : invM + angular_effective_mass_raw(iix,iiy,iiz, qx,qy,qz,qw, rx,ry,rz, dx,dy,dz, scratch_inertia)`, keeping the existing zero-inertia short-circuit. | Low effort. **Determinism: LOW** — identical operand order by construction. Note `angular_axis_effective_mass` at `solve_constraints.js:146` is genuinely different (`a·Iw⁻¹·a`, no lever arm) and should be left alone. |
|
|
28
|
-
| 18 | LOW/MEDIUM | **EPA→MPR fallback + MTV sign-validation + normalise written three times.** | `engine/physics/narrowphase/narrowphase_step.js:1128-1138` and `:1245-1255` (concave-triangle path and general convex fallback, same file) ⟷ `engine/physics/queries/shape_cast.js:394-408` (MPR-only variant) | The two `narrowphase_step` copies are identical: `gjk_epa_penetration` → `pen_depth > 0 && Number.isFinite(pen_depth)` → scale unit axis by depth, else `mpr()` → `depth = Math.sqrt(ex*ex+ey*ey+ez*ez)` → `!(depth > 0) \|\| !Number.isFinite(depth)` bail. All three then dot the MTV against a centre-delta reference, flip on negative, and emit `-e * (1/depth)` as the B→A normal (`shape_cast.js:401-407` even says "same pattern as `narrowphase_step`'s EPA path"). | UNJUSTIFIED for the two same-file copies | `narrowphase/resolve_penetration_mtv.js`: `resolve_penetration_mtv(out4, posed_a, posed_b, ref_x, ref_y, ref_z) -> boolean` writing unit normal + depth. `shape_cast` keeps its MPR-only path (it deliberately skips EPA — documented at `shape_cast.js:361-366` — so folding it in would need a mode flag; leave it). | Low effort for the two `narrowphase_step` copies. **Determinism: LOW.** Do not unify the third site — that would need the sentinel-parameter shape the house rules forbid. |
|
|
29
|
-
| 19 | LOW | **The `(p + 0.5) \| 0` + clamp + solid-lookup convention is written six times.** | `engine/physics/fluid/solver/v3_mac_clip_trace.js:83` `point_in_solid`; `v3_mac_advect_scalar.js:23` `end_in_solid`; `v3_mac_advect_maccormack_velocity.js:23` `end_in_solid`; inline ×3 at `v3_mac_advect_sl_velocity.js:112-118`, `:155-161`, `:200-206` | The two `end_in_solid` copies are byte-identical (brief §A, 120 tokens ×3 with `point_in_solid`). | **JUSTIFIED — with one exception.** `v3_mac_clip_trace.js:25-29` carries an explicit, measured performance contract: the loop in `v3_mac_clip_trace` blocks V8 inlining and "a per-face call costs several times the entire surrounding advection body (measured 8.6× on the SL kernel)", so hot kernels must inline the fluid test. The three inline copies in `v3_mac_advect_sl_velocity` are unambiguously correct. **But** the two `end_in_solid` *functions* are already function calls — the perf argument that justifies inlining does not justify a private copy of a loop-free leaf. | Export `point_in_solid` from `v3_mac_clip_trace.js` and have the two advect kernels import it instead of redeclaring it; leave the three genuine inline expansions alone. | Low effort, **but measure first** — that is the whole point of the contract comment. If the imported leaf does not inline as well as the module-local one, revert and instead cross-reference the copies in a comment. Determinism: none (integer indexing). |
|
|
30
|
-
| 20 | LOW | **Unnormalised triangle face normal (`(B−A) × (C−A)`) inline-derived at five physics sites.** | `engine/physics/narrowphase/sphere_triangle_contact.js:73-78`; `capsule_triangle_contact.js:162-166` and `:320-325`; `refine_ray_concave.js:142-146`; `compute_penetration.js:263-271` | Same three cross-product lines each time, three of the five followed by the same `mag = sqrt(...)`, degeneracy bail, `1/mag` normalise. Context: `core/geom/3d/triangle/` already offers **three** names for this — `compute_triangle_normal.js`, `tri3_compute_normal.js`, `v3_compute_triangle_normal.js` — which is likely why nobody reaches for one. | UNJUSTIFIED, but low value | Pick the canonical core name (a domain-A decision) and use it. Worth doing only as part of that consolidation, not on its own. | Low effort. **Determinism: LOW** if operand order matches. Per-triangle in a concave dispatch loop — inlining is defensible here; this is a readability finding, not a perf one. |
|
|
31
|
-
| 21 | LOW | **"Perpendicular axis pair from an axis index" written three times.** | `engine/physics/narrowphase/box_box_manifold.js:417-432`; `box_triangle_contact.js:835-842`; and again in `box_triangle_contact.js`'s `emit_box_face_manifold` (half-extent selection at `:441-442` region) | Identical `if (idx===0){u=1;v=2}else if(idx===1){u=2;v=0}else{u=0;v=1}` plus the identical `half_u = u===0 ? hx : (u===1 ? hy : hz)` ternary chain. | UNJUSTIFIED, low value | Two module-static `Uint8Array` lookup tables (`PERP_U = [1,2,0]`, `PERP_V = [2,0,1]`) in a shared `narrowphase/box_axis_tables.js`, plus a 3-float half-extent read. Typed constants, no string keys. | Trivial effort, **determinism: none** (integer selection only). |
|
|
32
|
-
| 22 | LOW | **World→grid direction transform and uniform-scale extraction re-derived across the fluid effectors.** | Direction transform `m[0]*x + m[4]*y + m[8]*z` (etc.): `GlobalFluidEffector.js:99-101` and `:117-119`, `ImpulseFluidEffector.js:75-77`. Uniform scale `sqrt(m[0]²+m[1]²+m[2]²)`: `ImpulseFluidEffector.js:88`, `WakeFluidEffector.js:172` | `core/geom/vec3/` has `v3_matrix4_multiply` (point, with perspective divide) and `v3_matrix4_rotate_unit` (direction, **but it normalises the output**) — neither fits, because the effectors need the *unnormalised* linear part so world units/s² become grid units/s². So the re-derivation is caused by a genuine gap in the core surface. `v3_matrix4_rotate.js` is just a deprecated re-export of `v3_matrix4_rotate_unit`. | PROMOTION | Add `core/geom/vec3/v3_matrix4_transform_direction.js` (linear part, no normalise, no divide) and `m4_uniform_scale.js`. Three and two sites respectively. | Trivial effort. **Determinism: LOW.** Cold code (once per effector per frame), so this is a pure clarity/gap-closing win. |
|
|
33
|
-
|
|
34
|
-
## Dismissed leads
|
|
35
|
-
|
|
36
|
-
- **"Two competing two-bone IK solvers"** (`engine/ecs/ik/TwoBoneInverseKinematicsSolver.js` vs `engine/physics/inverse_kinematics/two_joint_ik.js`) — not duplicates. The former *imports and calls* the latter at `TwoBoneInverseKinematicsSolver.js:11,157`; it is the ECS/terrain wrapper, `two_joint_ik` is the pure analytic core. Correctly factored. The real finding in that pair of files is #8.
|
|
37
|
-
- **FABRIK vs `two_joint_ik`** — `engine/physics/inverse_kinematics/fabrik/fabrik3d_solve_primitive.js` is an iterative N-joint positional solver; `two_joint_ik` is an analytic 2-joint rotational solver on a different data model (Vector3/Quaternion objects vs flat Float32Array). No shared body. FABRIK has no internal callers, but meep is a library.
|
|
38
|
-
- **`GlobalFluidEffector.equals`/`hash` vs `ImpulseFluidEffector.equals`/`hash`** (brief §A, 118 and 71 tokens) — same *shape*, different *fields* (`drag`/`force`/`wind` vs `radius`/`position`/`force`). Unifying needs a field-list registry, i.e. exactly the string-keyed metaprogramming the house rules forbid. Correctly written out per class.
|
|
39
|
-
- **`v3_grid_patch_edges_uniform` vs `v3_grid_patch_edges_constant`** (brief §C, 0.75) — genuinely different operations (replicate the adjacent valid slab vs write a constant), and the constant version exploits `fill` contiguity the replication version cannot. Shared surface is a ~10-line dispatch preamble whose `|shift| >= res` branches *differ* (return vs full fill). Not worth unifying.
|
|
40
|
-
- **`v3_grid_compute_divergence` vs `v3_mac_compute_divergence`** — different discretisations (central differences with one-sided boundary handling on collocated cells vs exact face differences on the staggered lattice). Different algorithms, not a copy. Same for the two `subtract_pressure_gradient` kernels.
|
|
41
|
-
- **`Collider.setFlag/clearFlag/getFlag/writeFlag` vs `RigidBody.*`** (brief §E) — the same four one-line bodies appear in **32** engine classes. A codebase-wide idiom over a `this.flags` int, no shared state, one line each; a base class would impose an inheritance constraint on unrelated component hierarchies. Not a physics finding.
|
|
42
|
-
- **`reduce_manifold_contacts`** — already extracted by a previous review; `reduce_manifold_contacts.js:14-18` documents that box_box, convex_convex, box_triangle and the dispatcher "each previously carried its own byte-identical copy". `box_triangle_contact.js:146 reduce_contacts` is a 1-line binding, not a copy. Do not re-flag.
|
|
43
|
-
- **`mls-mpm` vs `fluid`** — `MLS_MPM.js` is 2D with quadratic B-spline P2G/G2P weights; the fluid stack is 3D trilinear on a MAC lattice. No shared indexing or weight code. It is also plainly a prototype (`Math.random()` at `:388-389`, per-step array allocation in `advance`, a `testMLS_MPM.js` harness beside it). No reuse opportunity.
|
|
44
|
-
- **"Second broadphase / AABB tree inside physics"** — none. `engine/physics/broadphase/generate_pairs.js:1` queries `core/bvh2`, and `compute_fat_world_aabb.js:1` uses `core/geom/3d/aabb/aabb3_transform_oriented`. Clean.
|
|
45
|
-
- **`solve_contacts.js:458 apply_impulse_to_pseudo` vs `:401 apply_impulse_to_body`** — same math, but the destination is a different buffer with a different stride and split lin/ang lanes. Unifying needs `(dst, lin_offset, ang_offset)` threading through the hottest routine in the solver for one caller's benefit. Borderline; folded into finding #1 only if that extraction happens anyway.
|
|
46
|
-
- **`SBS_AV_X/SBS_INV_I_X` block shared by `solve_constraints.js` and `solve_contacts.js`** (brief §E) — that is an import list of shared lane constants from `body/SolverBodyState.js`. Correct reuse, not duplication.
|
|
47
|
-
- **`convex_convex_manifold.js:243` vs `box_box_manifold.js:117` vs `box_triangle_contact.js:182` — the *projection* halves of `test_axis`** — legitimately shape-specific (symmetric half-extent projection vs three explicit vertex dots vs a vertex-buffer min/max loop). Only the tracking half is duplicated; that is finding #2.
|
|
48
|
-
- **The `g_bb_*` / `g_bt_*` / `g_ct_*` module-static accumulator idiom** appearing in three narrowphase files — each carries a comment explaining it replaced a per-call closure allocation ("D3"). Same *technique*, different state. Correct as-is.
|
|
49
|
-
|
|
50
|
-
## Notes
|
|
51
|
-
|
|
52
|
-
**Why duplication accumulates here, structurally:**
|
|
53
|
-
|
|
54
|
-
1. **Twin kernel families.** The fluid solver carries a full collocated (`v3_grid_*`) and a full staggered-MAC (`v3_mac_*`) implementation of advection. The MAC family superseded the collocated one (memory: "MAC staggered, exact projection; MacCormack velocity default") but the collocated family stayed as library surface — so every algorithm in it (SL trace, MacCormack, Selle limiter, solid pass-through) now exists twice, and the two copies have already begun to drift (findings 4, 5, 6, 7). This is the largest single source of duplication in the domain by line count.
|
|
55
|
-
|
|
56
|
-
2. **Core primitives that return parameters, not points.** `tri3_closest_point_barycentric` returns `(α, β)` and `line3_compute_segment_segment_closest_points` returns `(s, t)`. Sixteen call sites across physics, core and graphics then write the same reconstruction arithmetic. This is the highest-leverage class of finding (#14, #15) because the fix is additive — two new core functions, no behaviour change, and the existing exports stay.
|
|
57
|
-
|
|
58
|
-
3. **Partial prior extractions.** Three separate extractions were done and each missed one site: `reduce_manifold_contacts` left the surrounding emit epilogue behind (#11), `angular_effective_mass_raw` was adopted by `solve_contacts` and `RaycastVehicle` but not `solve_constraints` (#17), and `polygon2_clip_halfplane` was written in core but never consumed (#12). Grepping for "each previously carried its own copy"-style comments is a reliable way to find the residue.
|
|
59
|
-
|
|
60
|
-
4. **Axis triplication.** The MAC kernels and the effectors write x/y/z as three near-identical blocks with different strides. In the *kernels* (`v3_mac_advect_sl_velocity.js`, `v3_mac_advect_maccormack_velocity.js`) this is legitimate — see below. In the *effectors* it is not (#13), because those loops are radius-bounded, not grid-bounded.
|
|
61
|
-
|
|
62
|
-
**Where inlining is legitimately correct (do not "fix" these):**
|
|
63
|
-
|
|
64
|
-
- `v3_mac_clip_trace.js:25-29` is an explicit measured performance contract (8.6× on the SL kernel) and the three inline solid tests in `v3_mac_advect_sl_velocity.js` honour it. These run per *face* over the whole lattice — millions of evaluations per step. Finding #19 targets only the two redundant *function declarations*, and even that is gated on measurement.
|
|
65
|
-
- The x/y/z block triplication inside `v3_mac_advect_sl_velocity.js` and `v3_mac_advect_maccormack_velocity.js`: each block has different index strides (`sx*res_y`, `res_x*sy`, `cell_slice`) and different loop bounds (`<= res_x` on the own axis). Parameterising would put stride variables in the innermost loop of a whole-grid kernel. Correct as written; I did not flag it.
|
|
66
|
-
- `capsule_contacts.js` / `capsule_triangle_contact.js` / the manifold builders are **per-contact**, not per-cell — a few thousand calls per frame, not millions. Extracting 20-line leaf helpers there (#9, #10, #11) is squarely on the reuse side of the line, and the codebase already does exactly this (`reduce_manifold_contacts`, `angular_effective_mass_raw`).
|
|
67
|
-
- `solve_contacts` / `solve_constraints` inner rows are per-contact-row × per-iteration — the hottest code in the engine. Finding #1 is still worth doing because it is a *verbatim* duplicate of the most correctness-critical routine present, but it must be benchmarked (rain and KEVA-mini) and must preserve operand order exactly for `determinism.spec.js`.
|
|
68
|
-
|
|
69
|
-
**Determinism summary:** findings 2, 5 and 12 can change simulation output and need before/after numerical diffs. Findings 1, 4, 6, 7, 9, 10, 11, 14, 15, 17, 18 are order-preserving by construction *provided* the extracted body is copied verbatim rather than rewritten — that is the whole risk. Findings 16, 20, 21, 22 have no simulation-state exposure.
|
|
1
|
+
# Domain E — engine/physics
|
|
2
|
+
|
|
3
|
+
Scope: `engine/physics/**` (+ `engine/ecs/ik/*` where the task named it). Every finding below was verified by opening the cited files. `*.spec.js`, `prototype*.js`, `test*.js` excluded.
|
|
4
|
+
|
|
5
|
+
The mechanical brief was thin and its three "A. token-identical" hits turned out to be the *least* interesting things in the domain (two are explicitly, correctly justified). The real duplication here is semantic re-derivation, and it clusters in four places: (1) the fluid solver's collocated-vs-MAC twin kernel families, (2) the narrowphase contact-emission epilogues, (3) the two solvers (contacts / constraints) which independently implement the same impulse application, (4) core primitives that return *parameters* where every single caller wants *points*.
|
|
6
|
+
|
|
7
|
+
## Findings
|
|
8
|
+
|
|
9
|
+
| # | Severity | Concept | Sites | Evidence | Verdict | Proposed common path | Effort/Risk |
|
|
10
|
+
|---|---|---|---|---|---|---|---|
|
|
11
|
+
| 1 | HIGH | **Solver impulse application written twice, verbatim.** `Δv = P·invM; Δω = Iw⁻¹·(r×P)` with the identical `invM===0` guard, identical sign folding, identical `world_inverse_inertia_apply_raw` argument order and identical AV write-back. | `engine/physics/constraint/solve_constraints.js:250` `apply_impulse(ss, base, invM, rx,ry,rz, Px,Py,Pz, sign)` ⟷ `engine/physics/solver/solve_contacts.js:401` `apply_impulse_to_body(ss, base, invM, rx,ry,rz, Px,Py,Pz, sign, scratch_inertia)` | Bodies are line-for-line identical (15 statements each). The *only* difference is that the contacts version takes `scratch_inertia` as an 11th parameter where the constraints version closes over a module-static one. Both call the same kernel with the same operand order. | UNJUSTIFIED | Promote one copy to `engine/physics/solver/apply_impulse_to_body.js` (next to `friction_cone.js`) with the explicit `scratch` parameter; both solvers import it. Keep the parameter form — it is already the more explicit of the two. | Low effort. **Determinism: LOW risk** — identical operand order means bit-identical output; `determinism.spec.js` is the gate. This is the single most load-bearing routine in the step (per-row × per-iteration), so re-measure the rain/KEVA benches: a cross-module call here must stay inlined. |
|
|
12
|
+
| 2 | HIGH | **SAT minimum-overlap magnitude computed three ways, one of which diverges.** Same invariant ("how far along this axis must I push to separate?"), three independent expressions. | `engine/physics/narrowphase/box_box_manifold.js:135` `overlap = (rA + rB) - dist`; `engine/physics/narrowphase/box_triangle_contact.js:209-211` `overlap = min(bmax - tmin, tmax - bmin)`; `engine/physics/narrowphase/convex_convex_manifold.js:261` `overlap = Math.min(maxA,maxB) - Math.max(minA,minB)` | The first two are the minimum-translation (push-out) distance and agree. The third is the *interval intersection length*, which is smaller whenever one projection interval contains the other (A=[0,10], B=[4,5] → push-out 5, intersection 1). SAT then picks the axis by minimum overlap, so an understated value makes a containment axis *more* likely to win and be reported as the depth. `convex_convex_manifold` is documented as being for "the SMALL convex pieces of a decomposed mesh" — exactly where a thin piece is contained in a fat one. The three sites also carry three different degeneracy tests (`PARALLEL_EPS_SQR` absolute in two files, `PARALLEL_EPS_REL * ref2` relative in the third) and only two of three have the `TIE_REL/TIE_ABS` deadband. | UNJUSTIFIED (and needs a correctness decision before unification) | Extract `sat_track_min_overlap(state, ux,uy,uz, minA,maxA, minB,maxB, kind, sign_ref)` covering: normalise-and-reject, separation test, push-out overlap, deadband tie-break, centre-delta sign orientation. Each caller keeps its own *projection* (box half-extent vs 3 vertices vs vertex loop) — that part is legitimately shape-specific. | Medium effort. **Determinism: HIGH risk** and behaviour-changing: fixing `convex_convex`'s formula changes chosen axes on containment configs. Do it test-first against `convex_convex_manifold`'s own suite, then the determinism gate. |
|
|
13
|
+
| 3 | HIGH | **Whole collocated advection family shadows the MAC family and has zero callers.** `FluidSimulator` uses only the `v3_mac_*` kernels (imports at `engine/physics/fluid/FluidSimulator.js:6-11`). | Zero non-spec importers: `v3_grid_advect_sl_velocity.js`, `v3_grid_apply_advection_forward.js`, `v3_grid_apply_scalar_advection.js`, `v3_grid_advect_maccormack_scalar.js`, `v3_grid_advect_maccormack_velocity.js` (≈555 LOC) | Verified by importer count across `engine/` and `app/` (spec files excluded). Each is a collocated-grid re-implementation of an algorithm the MAC file next to it also implements (SL trace, MacCormack forward/backward/correct, Selle limiter, solid pass-through). A fix in the MAC limiter or the solid handling silently misses five files. | UNJUSTIFIED (as duplication) — but **NOT dead code**: meep is a library, these are public surface. | Do not delete. Collapse the *internal* redundancy first (findings 4 and 5), then leave the collocated family as thin, correct, exported kernels. Add a one-line "the simulator uses the MAC family; this is the collocated variant" note to each so the next reviewer does not re-derive this. | Low effort for 4/5; the family itself stays. Determinism: none (unreferenced by the sim). |
|
|
14
|
+
| 4 | MEDIUM | **MacCormack pass 1 re-implements the SL kernel that sits in the same directory.** The MAC file does this correctly (calls the SL kernel); the grid file does not. | `engine/physics/fluid/solver/v3_grid_advect_maccormack_scalar.js:66-82` (pass 1) ⟷ `v3_grid_apply_scalar_advection.js:38-58`; `v3_grid_advect_maccormack_velocity.js:73-94` (pass 1) ⟷ `v3_grid_advect_sl_velocity.js:69-96` | Both pairs are statement-for-statement identical: same solid pass-through, same `s = x - vel[c]*dt`, same sampler call. Contrast `v3_mac_advect_scalar.js:146`, which *does* call `v3_mac_advect_sl_scalar(fwd, ...)` for its forward pass — proving the delegation is viable and this is an oversight, not a design choice. | UNJUSTIFIED | Replace each pass-1 loop with the call to its SL sibling, exactly as the MAC scalar file already does. | Low effort. **Determinism: LOW** — identical arithmetic and identical loop order; the only change is a function boundary. |
|
|
15
|
+
| 5 | MEDIUM | **`v3_grid_apply_advection_forward` is a slower copy of `v3_grid_advect_sl_velocity`, and says so in its own docstring.** | `engine/physics/fluid/solver/v3_grid_apply_advection_forward.js:32` ⟷ `v3_grid_advect_sl_velocity.js:38` | The docstring at `v3_grid_apply_advection_forward.js:16` states: it "is exactly `v3_grid_advect_sl_velocity(outputs, inputs, inputs...)`". The body differs only by issuing three separate `scs3d_sample_linear` calls instead of one fused `scs3d_sample_linear3` — i.e. it recomputes the trilinear corner indices and weights three times per cell. MinHash flagged the pair at 0.813. | UNJUSTIFIED | Keep the export (public surface, meaningful name); make the body a one-line delegation to `v3_grid_advect_sl_velocity`. | Low effort. **Determinism: MEDIUM** — the fused 3-channel sampler may associate weights differently from three scalar samples. Diff numerically before/after; `quality.spec.js` is the fluid gate. |
|
|
16
|
+
| 6 | MEDIUM | **Selle monotone limiter written four times, twice under the same name `limit8` with different signatures in the same directory.** | `engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.js:155` `limit8(value, data, c0..c7)`; `v3_mac_advect_maccormack_velocity.js:293` `limit8(value, data, lx,ly,lz, dim_x,dim_y,dim_z, row, slice)`; hand-inlined at `v3_grid_advect_maccormack_scalar.js:107-132`; hand-inlined **with solid masking** at `v3_mac_advect_scalar.js:191-232` | The two named `limit8`s are different functions with the same name two files apart — a genuine reader hazard, and invisible to the mechanical pass because neither is exported (brief §G is empty). The grid-scalar inline is a *byte-level* match for the MAC-velocity `limit8` signature (`limit8(corrected, source, x-vx, y-vy, z-vz, res_x,res_y,res_z, res_x, slice_size)` reproduces it exactly). The MAC-scalar inline is a genuine superset (skips solid corners, `Infinity` init, fallback to the forward value). | UNJUSTIFIED for 3 of 4 | Extract two files: `v3_limit8_lattice.js` (position-form, absorbs the MAC-velocity version, the grid-scalar inline and — via a precomputed-index overload or by the caller passing the position — the grid-velocity version) and `v3_limit8_lattice_masked.js` for the solid-aware variant. Do **not** merge masked and unmasked behind a flag (uniform-control-flow rule). | Low-medium effort. **Determinism: LOW** for the unmasked merge (identical comparison order). These are per-cell kernels over the whole grid, so verify the extracted call stays inlined — the bodies are branchy but loop-free, which is the inlinable shape. |
|
|
17
|
+
| 7 | MEDIUM | **Vorticity confinement: the MAC kernel collocates to cell centres and then re-derives the collocated kernel's two passes verbatim.** | `engine/physics/fluid/solver/v3_grid_apply_vorticity_confinement.js:78-130` (pass 1 curl+\|ω\|, pass 2 N×ω) ⟷ `v3_mac_apply_vorticity_confinement.js:98-164` (pass 2, pass 3) | The MAC file's pass 1 (lines 70-90) averages faces into `scratch_cu/cv/cw` cell-centred arrays; its passes 2 and 3 are then character-identical to the grid file's passes 1 and 2 — same central-difference curl expressions, same `scratch_wmag` fill, same `magnitude_floor = 1e-10`, same `s = epsilon_dt / g_len`, same `N × ω` cross. Only the final deposit differs (cell write vs `0.5 *` scatter to two faces per axis). ≈60 lines. | UNJUSTIFIED | Extract `v3_grid_compute_vorticity(scratch_wx,wy,wz,wmag, vel_x,vel_y,vel_z, res, solid)` and `v3_grid_compute_confinement_force(out3, scratch_*, c, res_x, cell_slice, epsilon_dt)`; the MAC kernel calls both and keeps only its collocate-in / scatter-out ends. | Medium effort. **Determinism: LOW** (same expressions, same order). Per-cell whole-grid kernel — measure; the force helper writing 3 floats to a module-static may cost vs the current fully-inlined form. |
|
|
18
|
+
| 8 | MEDIUM | **Two-bone and one-bone IK solvers share a ~75-line terrain-probe + influence prologue verbatim.** | `engine/ecs/ik/TwoBoneInverseKinematicsSolver.js:76-151` (`solve`) ⟷ `engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js:74-151` (`solve`) | Identical: the ray direction from parent→bone, the primary `terrain.raycastFirstSync`, the early return, the secondary cast back along the contact normal from the bone + normal, the second early return, `constraint.offset * <scale>` → `v3_displace_in_direction`, the `delta_contact_c_*` triple, `v3_dot` side test, `v3_length`, the `dot_contact_side < 0 ? 1 : 1 - clamp01(constraint.distance.normalizeValue(inverseLerp(0, <scale>, d)))` influence, and the `influence <= 0` bail. Only two things differ and both are plain inputs: the ray origin (parent bone world position vs root bone A world position) and the length scale (`boneWorldScale.length() * 1/√3` vs limb length A→B→C). The mechanical brief missed this entirely because its path filter was `^engine/physics/`. | UNJUSTIFIED | Extract `ik_probe_surface_influence(contact_out, terrain, origin_xyz, direction_xyz, effector_xyz, length_scale, constraint) -> number` returning influence (0 = no contact / no influence) and leaving the contact in the supplied `SurfacePoint3`. Both solvers keep only their own post-influence rotation math, which is genuinely different. | Low-medium effort. No determinism exposure (animation-side, not the physics step). Note: the task hypothesis "two competing two-bone IK solvers" is **false** — `TwoBoneInverseKinematicsSolver` *imports* `two_joint_ik` (line 11). The real duplication is this sibling prologue. |
|
|
19
|
+
| 9 | MEDIUM | **Closest-point-pair → contact epilogue written five times.** Given two witness points and two radii, produce (unit normal B→A, depth, witness on A, witness on B) with a degenerate-distance tie-break. | `engine/physics/narrowphase/sphere_sphere_contact.js:27-51`; `sphere_triangle_contact.js:99-141`; `capsule_contacts.js:89-116` (`capsule_sphere_contact`); `capsule_contacts.js:158-197` (`capsule_capsule_contact`); `capsule_contacts.js:293-324` (`capsule_capsule_multi_contacts` inner loop) | All five: `d = pa - pb`, `dist_sqr`, `sum = ra + rb`, `if (dist_sqr >= sum*sum) return false`, `dist = sqrt`, `if (dist > 0) normalize else <tie-break>`, `out[3] = sum - dist`, `out[4..6] = pa - n*ra`, `out[7..9] = pb + n*rb`. The tie-break direction is the only real variation: `(1,0,0)` at two sites, segment-centre delta at one, body-centre delta at two — i.e. it is an *input*, not a mode. | UNJUSTIFIED | `narrowphase/emit_pair_contact.js`: `emit_pair_contact(out, pax,pay,paz, pbx,pby,pbz, ra, rb, fbx,fby,fbz) -> boolean`, fallback direction always supplied by the caller (no null-sentinel). `sphere_sphere_contact` writes only lanes 0-3 today and would need to keep that shorter contract or gain witness lanes — check its callers first. | Low-medium effort. Per-contact hot path but not per-cell; a leaf function of this size is a normal V8 inline candidate. **Determinism: LOW** (identical order), but the tie-break unification changes `capsule_sphere_contact`'s `(1,0,0)` if you "improve" it — don't; pass `(1,0,0)` explicitly. |
|
|
20
|
+
| 10 | MEDIUM | **Cap-endpoint contact append + spatial dedup written twice, with the same magic constant.** | `engine/physics/narrowphase/capsule_contacts.js:582-619` (endpoint loop inside `capsule_box_multi_contacts`) ⟷ `capsule_triangle_contact.js:360-395` (`try_endpoint_contact`) | Both: run a sphere-vs-X query at a cap centre, unpack the 10-float `(n, depth, A, B)` scratch, loop `k < count` comparing the capsule-side world position against every already-emitted contact, reject below `DEDUPE_DIST_SQR`, then write the `(A, B, n, depth)` stride-10 record. `DEDUPE_DIST_SQR = 1e-6` is declared independently in both files (`capsule_contacts.js:510`, `capsule_triangle_contact.js:64`) with near-identical justifying comments, and both files independently declare a stride-10 constant (`CAPSULE_BOX_CONTACT_STRIDE`, `CAPSULE_TRIANGLE_CONTACT_STRIDE`). | UNJUSTIFIED | `narrowphase/append_deduped_contact.js`: `append_deduped_contact(out, count, scratch10) -> number`, with `DEDUPE_DIST_SQR` and the stride-10 candidate layout defined once (the layout is already shared by three files — put it next to `decomposition/triangle_buffer_layout.js`). | Low effort. **Determinism: LOW.** Real payoff is the single definition of the tolerance: today a tuning change to one leaves the other. |
|
|
21
|
+
| 11 | MEDIUM | **Reference-face manifold emission epilogue written three times.** Project each clipped point, reject non-penetrating, push `(x,y,z,depth)` candidates, `reduce_manifold_contacts`, then per kept point project onto the reference plane and swap A/B by `ref_is_A`. | `engine/physics/narrowphase/convex_convex_manifold.js:596-622` (`emit_face_manifold`); `box_box_manifold.js:578-670`; `box_triangle_contact.js:455-497` (`emit_box_face_manifold`) | All three build a stride-4 (or 7) candidate array, call `reduce_manifold_contacts(cand, n, stride, MAX_CONTACTS, depth_lane)`, then run the identical "ref-side witness = point − ref_out · dist_to_plane" projection and the identical `if (ref_is_A) {A=proj,B=pt} else {A=pt,B=proj}` swap into `4 + k*CONTACT_STRIDE`. A previous review already lifted the *reduction* out of these three (see the "Each previously carried its own byte-identical copy" note at `reduce_manifold_contacts.js:14-18`) — this is the un-lifted residue of the same block. | UNJUSTIFIED | Finish the extraction the previous review started: `emit_reference_face_manifold(out, cand, n, stride, ref_is_A, r0x,r0y,r0z, rmx,rmy,rmz)` doing reduce + project + swap + write. The *clipping* stays per-shape. | Medium effort. **Determinism: LOW** if the projection expression is copied verbatim. The three sites use different candidate strides (4 and 7), so the stride stays a parameter — `reduce_manifold_contacts` already set that precedent. |
|
|
22
|
+
| 12 | MEDIUM | **A general 2D half-plane Sutherland–Hodgman clipper exists in core, unused, while narrowphase ships its own.** | `core/geom/2d/polygon/polygon2_clip_halfplane.js:20` (exported, spec'd, **zero consumers**) ⟷ `engine/physics/narrowphase/box_triangle_contact.js:110` `clip_against_half_plane_uv` | Same algorithm: walk edges, evaluate a signed side function at both endpoints, emit the start point if inside, emit the crossing at `t = sa/(sa-sb)`. The core version parameterises the line by point+direction and tests `cross(dir, P-e)`; the physics version by point+normal and tests `(P-e)·n` — algebraically the same half-plane with `n = perp(dir)`. Note `box_box_manifold.js:3` and `box_triangle_contact.js:3` *already* import the sibling `polygon2_clip_axis_halfplane` from core, so the boundary crossing is established practice. | UNJUSTIFIED | `box_triangle_contact` imports `polygon2_clip_halfplane` and converts its normal to a direction (or add a normal-form export next to it). | Low effort. **Determinism: MEDIUM** — the core version carries `POINT_EPS = 1e-9` inside/outside slack; the physics version uses exact `<= 0`. That changes the surviving vertex count on boundary-coincident points, hence the manifold. Diff on the box-triangle suite before adopting. |
|
|
23
|
+
| 13 | MEDIUM | **Effector face-lattice splat scaffolding written six times (3 axes × 2 effectors).** | `engine/physics/fluid/effector/ImpulseFluidEffector.js:105-132, 134-161, 163-191` ⟷ `WakeFluidEffector.js:249-273, 275-298, 300-~330` | Each block: derive the per-lattice index range as `max(0, floor(box_min [+0.5 on own axis]))` … `min(res_axis [−1 off-axis], ceil(box_max [+0.5]))`, triple loop, compute the lattice offset (`sx*res_y` / `res_x*(res_y+1)` / `res_x*res_y`), evaluate a falloff weight at the face position (offset −0.5 on the own axis), `continue` on zero weight, `continue` on `pin[f] !== 0`, accumulate `v[f] += impulse_axis * w`. The only per-block variation is the weight function — and `WakeFluidEffector` already proves this can be a callback (`tube_weight` closure at line 230). | UNJUSTIFIED | `fluid/effector/mac_splat_axis.js`: `mac_splat_axis(velocity, pin, axis, res_x,res_y,res_z, box6, deposit, weight_fn)`. The two effectors then differ only in their weight function and box, which is the actual content. | Medium effort. **Determinism: LOW.** Perf: this is a *bounded* splat (influence radius), not a whole-grid kernel, and the Wake path already pays a closure call per face — so the reuse-over-micro-opt rule applies cleanly here. |
|
|
24
|
+
| 14 | PROMOTION | **`tri3_closest_point_barycentric` returns barycentrics; all 12 callers immediately reconstruct the world point.** The missing primitive is `tri3_closest_point`. | Physics: `sphere_triangle_contact.js:89-96`; `capsule_triangle_contact.js:186-191`, `:210-215`, `:234-239`; `compute_penetration.js:331-335`. Core/graphics (context): `core/geom/3d/shape/Triangle3D.js:169-175` and `:212-…`; `core/geom/3d/topology/struct/binary/query/bt_mesh_find_nearest_face.js:131-137`; `core/geom/3d/shape/MeshShape3D.js:439`, `:491`; `core/geom/3d/shape/ConvexHullShape3D.js:254`, `:285`; `engine/graphics/geometry/buffered/query/bvh32_geometry_nearest.js:203`; `.../query_bvh_geometry_nearest.js:164` | Every site writes the same four lines: `const c = 1 - a - b; qx = a*ax + b*bx + c*cx; qy = …; qz = …`. Several then compute the same `d = p - q; d_sqr` follow-up. This is the block the brief flagged as shared between `Triangle3D.js:162`, `capsule_triangle_contact.js:181`, `bt_mesh_find_nearest_face.js` and the two BVH nearest queries. | PROMOTION | Add `core/geom/3d/triangle/tri3_closest_point.js` — `tri3_closest_point(out, out_offset, px,py,pz, ax..cz)` writing the world point (wrapping the barycentric version) — and `tri3_point_distance_sqr(...)` for the distance-only callers. Keep the barycentric export (public surface; a few callers genuinely want the weights). | Low effort per site, 12 sites. **Determinism: LOW** if the reconstruction expression is copied verbatim (`a*ax + b*bx + c*cx` in that order — `bt_mesh_find_nearest_face.js:135` writes `ax*wa + bx*wb + cx*wc`, which is the same order of operands; check each before converting). |
|
|
25
|
+
| 15 | PROMOTION | **`line3_compute_segment_segment_closest_points` returns `(s, t)`; all four physics callers immediately lerp both segments.** | `engine/physics/narrowphase/capsule_contacts.js:142-156`; `capsule_triangle_contact.js:105-117` (`test_edge`); `convex_convex_manifold.js:501-516` (`emit_edge_pair_contact`); `box_triangle_contact.js:874-885` (`emit_edge_cross_contact`) | All four: call the core function into a length-2 scratch, then six `p = a0 + (a1 - a0) * s` lines. This is the bulk of the brief's 39 `inline_lerp` hits in narrowphase. `core/geom/3d/line/` already contains a differently-named `line3_closest_points_segment_segment.js` — worth checking whether *it* is the point-returning variant that was never adopted. | PROMOTION | Add `line3_segment_segment_closest_point_pair(out6, a0x..a1z, b0x..b1z)` writing both world points (and returning the squared distance, which three of the four sites compute next anyway). | Low effort. **Determinism: LOW** — the lerp form `a + (b-a)*t` is used identically at all four sites; preserve it exactly (do not "improve" to `(1-t)*a + t*b`). |
|
|
26
|
+
| 16 | LOW/MEDIUM | **Two hash-combine conventions coexist inside `engine/physics`, producing different values for the same inputs.** | Hand-rolled `h = (h*31 + x) | 0`, seeded with the first field: `engine/physics/ecs/Collider.js:157`, `RigidBody.js:279`, `Joint.js:442`. Canonical `combine_hash(...)`: `engine/physics/fluid/effector/GlobalFluidEffector.js:182`, `ImpulseFluidEffector.js:228`. A third variant `h = ((h<<5) - h) + v; h \|= 0` seeded with `n`: `engine/physics/fluid/ecs/FluidEffectorsComponent.js:77` | `core/collection/array/combine_hash.js` → `computeIntegerArrayHash` → `computeStridedIntegerArrayHash` is the canonical fold: `hash = length`, then `hash = ((hash<<5) - hash) + (v >>> 0)`, returning **uint32**. `(h<<5)-h` and `h*31` are the same multiply; the divergences are the seed (`length` vs first field) and the output signedness (`>>> 0` vs `\| 0`). Three conventions in one package. | UNJUSTIFIED as a *convention* split; the hand-rolled fold itself is JUSTIFIED (`combine_hash(...args)` allocates a rest-array per call — `RigidBody.hash()` folds 12 fields and runs during dirty-checking) | Do **not** route the ECS components through `combine_hash`. Instead add a non-allocating `hash_combine_step(h, v)`-style primitive next to `computeStridedIntegerArrayHash` and have all three physics families use it, so the seed and signedness are decided once. Then `FluidEffectorsComponent` and the two effectors stop being a third and fourth answer. | Low effort. No determinism exposure (hashes are change-detection, not simulation state) — but do check nothing persists these values across versions before changing seed/signedness. |
|
|
27
|
+
| 17 | LOW/MEDIUM | **`solve_constraints` re-derives `angular_effective_mass_raw`, which was extracted for exactly this purpose.** | `engine/physics/constraint/solve_constraints.js:191-204` `axis_effective_mass` ⟷ `engine/physics/inertia/world_inverse_inertia.js:102` `angular_effective_mass_raw` | The core of `axis_effective_mass` is `cx,cy,cz = r × d; world_inverse_inertia_apply_raw(scratch, …, cx,cy,cz); k += cx*s[0] + cy*s[1] + cz*s[2]` — character-for-character the body of `angular_effective_mass_raw`. That kernel's own docstring (lines 80-84) says it is "The shared kernel behind the contact/joint solver's SoA path and the raycast-vehicle's component path — each previously inlined the cross product + apply_raw + dot themselves." `solve_contacts.js:526` and `RaycastVehicle.js:54` adopted it; `solve_constraints.js` was missed. | UNJUSTIFIED (incomplete prior extraction) | `axis_effective_mass` becomes `invM === 0 ? 0 : invM + angular_effective_mass_raw(iix,iiy,iiz, qx,qy,qz,qw, rx,ry,rz, dx,dy,dz, scratch_inertia)`, keeping the existing zero-inertia short-circuit. | Low effort. **Determinism: LOW** — identical operand order by construction. Note `angular_axis_effective_mass` at `solve_constraints.js:146` is genuinely different (`a·Iw⁻¹·a`, no lever arm) and should be left alone. |
|
|
28
|
+
| 18 | LOW/MEDIUM | **EPA→MPR fallback + MTV sign-validation + normalise written three times.** | `engine/physics/narrowphase/narrowphase_step.js:1128-1138` and `:1245-1255` (concave-triangle path and general convex fallback, same file) ⟷ `engine/physics/queries/shape_cast.js:394-408` (MPR-only variant) | The two `narrowphase_step` copies are identical: `gjk_epa_penetration` → `pen_depth > 0 && Number.isFinite(pen_depth)` → scale unit axis by depth, else `mpr()` → `depth = Math.sqrt(ex*ex+ey*ey+ez*ez)` → `!(depth > 0) \|\| !Number.isFinite(depth)` bail. All three then dot the MTV against a centre-delta reference, flip on negative, and emit `-e * (1/depth)` as the B→A normal (`shape_cast.js:401-407` even says "same pattern as `narrowphase_step`'s EPA path"). | UNJUSTIFIED for the two same-file copies | `narrowphase/resolve_penetration_mtv.js`: `resolve_penetration_mtv(out4, posed_a, posed_b, ref_x, ref_y, ref_z) -> boolean` writing unit normal + depth. `shape_cast` keeps its MPR-only path (it deliberately skips EPA — documented at `shape_cast.js:361-366` — so folding it in would need a mode flag; leave it). | Low effort for the two `narrowphase_step` copies. **Determinism: LOW.** Do not unify the third site — that would need the sentinel-parameter shape the house rules forbid. |
|
|
29
|
+
| 19 | LOW | **The `(p + 0.5) \| 0` + clamp + solid-lookup convention is written six times.** | `engine/physics/fluid/solver/v3_mac_clip_trace.js:83` `point_in_solid`; `v3_mac_advect_scalar.js:23` `end_in_solid`; `v3_mac_advect_maccormack_velocity.js:23` `end_in_solid`; inline ×3 at `v3_mac_advect_sl_velocity.js:112-118`, `:155-161`, `:200-206` | The two `end_in_solid` copies are byte-identical (brief §A, 120 tokens ×3 with `point_in_solid`). | **JUSTIFIED — with one exception.** `v3_mac_clip_trace.js:25-29` carries an explicit, measured performance contract: the loop in `v3_mac_clip_trace` blocks V8 inlining and "a per-face call costs several times the entire surrounding advection body (measured 8.6× on the SL kernel)", so hot kernels must inline the fluid test. The three inline copies in `v3_mac_advect_sl_velocity` are unambiguously correct. **But** the two `end_in_solid` *functions* are already function calls — the perf argument that justifies inlining does not justify a private copy of a loop-free leaf. | Export `point_in_solid` from `v3_mac_clip_trace.js` and have the two advect kernels import it instead of redeclaring it; leave the three genuine inline expansions alone. | Low effort, **but measure first** — that is the whole point of the contract comment. If the imported leaf does not inline as well as the module-local one, revert and instead cross-reference the copies in a comment. Determinism: none (integer indexing). |
|
|
30
|
+
| 20 | LOW | **Unnormalised triangle face normal (`(B−A) × (C−A)`) inline-derived at five physics sites.** | `engine/physics/narrowphase/sphere_triangle_contact.js:73-78`; `capsule_triangle_contact.js:162-166` and `:320-325`; `refine_ray_concave.js:142-146`; `compute_penetration.js:263-271` | Same three cross-product lines each time, three of the five followed by the same `mag = sqrt(...)`, degeneracy bail, `1/mag` normalise. Context: `core/geom/3d/triangle/` already offers **three** names for this — `compute_triangle_normal.js`, `tri3_compute_normal.js`, `v3_compute_triangle_normal.js` — which is likely why nobody reaches for one. | UNJUSTIFIED, but low value | Pick the canonical core name (a domain-A decision) and use it. Worth doing only as part of that consolidation, not on its own. | Low effort. **Determinism: LOW** if operand order matches. Per-triangle in a concave dispatch loop — inlining is defensible here; this is a readability finding, not a perf one. |
|
|
31
|
+
| 21 | LOW | **"Perpendicular axis pair from an axis index" written three times.** | `engine/physics/narrowphase/box_box_manifold.js:417-432`; `box_triangle_contact.js:835-842`; and again in `box_triangle_contact.js`'s `emit_box_face_manifold` (half-extent selection at `:441-442` region) | Identical `if (idx===0){u=1;v=2}else if(idx===1){u=2;v=0}else{u=0;v=1}` plus the identical `half_u = u===0 ? hx : (u===1 ? hy : hz)` ternary chain. | UNJUSTIFIED, low value | Two module-static `Uint8Array` lookup tables (`PERP_U = [1,2,0]`, `PERP_V = [2,0,1]`) in a shared `narrowphase/box_axis_tables.js`, plus a 3-float half-extent read. Typed constants, no string keys. | Trivial effort, **determinism: none** (integer selection only). |
|
|
32
|
+
| 22 | LOW | **World→grid direction transform and uniform-scale extraction re-derived across the fluid effectors.** | Direction transform `m[0]*x + m[4]*y + m[8]*z` (etc.): `GlobalFluidEffector.js:99-101` and `:117-119`, `ImpulseFluidEffector.js:75-77`. Uniform scale `sqrt(m[0]²+m[1]²+m[2]²)`: `ImpulseFluidEffector.js:88`, `WakeFluidEffector.js:172` | `core/geom/vec3/` has `v3_matrix4_multiply` (point, with perspective divide) and `v3_matrix4_rotate_unit` (direction, **but it normalises the output**) — neither fits, because the effectors need the *unnormalised* linear part so world units/s² become grid units/s². So the re-derivation is caused by a genuine gap in the core surface. `v3_matrix4_rotate.js` is just a deprecated re-export of `v3_matrix4_rotate_unit`. | PROMOTION | Add `core/geom/vec3/v3_matrix4_transform_direction.js` (linear part, no normalise, no divide) and `m4_uniform_scale.js`. Three and two sites respectively. | Trivial effort. **Determinism: LOW.** Cold code (once per effector per frame), so this is a pure clarity/gap-closing win. |
|
|
33
|
+
|
|
34
|
+
## Dismissed leads
|
|
35
|
+
|
|
36
|
+
- **"Two competing two-bone IK solvers"** (`engine/ecs/ik/TwoBoneInverseKinematicsSolver.js` vs `engine/physics/inverse_kinematics/two_joint_ik.js`) — not duplicates. The former *imports and calls* the latter at `TwoBoneInverseKinematicsSolver.js:11,157`; it is the ECS/terrain wrapper, `two_joint_ik` is the pure analytic core. Correctly factored. The real finding in that pair of files is #8.
|
|
37
|
+
- **FABRIK vs `two_joint_ik`** — `engine/physics/inverse_kinematics/fabrik/fabrik3d_solve_primitive.js` is an iterative N-joint positional solver; `two_joint_ik` is an analytic 2-joint rotational solver on a different data model (Vector3/Quaternion objects vs flat Float32Array). No shared body. FABRIK has no internal callers, but meep is a library.
|
|
38
|
+
- **`GlobalFluidEffector.equals`/`hash` vs `ImpulseFluidEffector.equals`/`hash`** (brief §A, 118 and 71 tokens) — same *shape*, different *fields* (`drag`/`force`/`wind` vs `radius`/`position`/`force`). Unifying needs a field-list registry, i.e. exactly the string-keyed metaprogramming the house rules forbid. Correctly written out per class.
|
|
39
|
+
- **`v3_grid_patch_edges_uniform` vs `v3_grid_patch_edges_constant`** (brief §C, 0.75) — genuinely different operations (replicate the adjacent valid slab vs write a constant), and the constant version exploits `fill` contiguity the replication version cannot. Shared surface is a ~10-line dispatch preamble whose `|shift| >= res` branches *differ* (return vs full fill). Not worth unifying.
|
|
40
|
+
- **`v3_grid_compute_divergence` vs `v3_mac_compute_divergence`** — different discretisations (central differences with one-sided boundary handling on collocated cells vs exact face differences on the staggered lattice). Different algorithms, not a copy. Same for the two `subtract_pressure_gradient` kernels.
|
|
41
|
+
- **`Collider.setFlag/clearFlag/getFlag/writeFlag` vs `RigidBody.*`** (brief §E) — the same four one-line bodies appear in **32** engine classes. A codebase-wide idiom over a `this.flags` int, no shared state, one line each; a base class would impose an inheritance constraint on unrelated component hierarchies. Not a physics finding.
|
|
42
|
+
- **`reduce_manifold_contacts`** — already extracted by a previous review; `reduce_manifold_contacts.js:14-18` documents that box_box, convex_convex, box_triangle and the dispatcher "each previously carried its own byte-identical copy". `box_triangle_contact.js:146 reduce_contacts` is a 1-line binding, not a copy. Do not re-flag.
|
|
43
|
+
- **`mls-mpm` vs `fluid`** — `MLS_MPM.js` is 2D with quadratic B-spline P2G/G2P weights; the fluid stack is 3D trilinear on a MAC lattice. No shared indexing or weight code. It is also plainly a prototype (`Math.random()` at `:388-389`, per-step array allocation in `advance`, a `testMLS_MPM.js` harness beside it). No reuse opportunity.
|
|
44
|
+
- **"Second broadphase / AABB tree inside physics"** — none. `engine/physics/broadphase/generate_pairs.js:1` queries `core/bvh2`, and `compute_fat_world_aabb.js:1` uses `core/geom/3d/aabb/aabb3_transform_oriented`. Clean.
|
|
45
|
+
- **`solve_contacts.js:458 apply_impulse_to_pseudo` vs `:401 apply_impulse_to_body`** — same math, but the destination is a different buffer with a different stride and split lin/ang lanes. Unifying needs `(dst, lin_offset, ang_offset)` threading through the hottest routine in the solver for one caller's benefit. Borderline; folded into finding #1 only if that extraction happens anyway.
|
|
46
|
+
- **`SBS_AV_X/SBS_INV_I_X` block shared by `solve_constraints.js` and `solve_contacts.js`** (brief §E) — that is an import list of shared lane constants from `body/SolverBodyState.js`. Correct reuse, not duplication.
|
|
47
|
+
- **`convex_convex_manifold.js:243` vs `box_box_manifold.js:117` vs `box_triangle_contact.js:182` — the *projection* halves of `test_axis`** — legitimately shape-specific (symmetric half-extent projection vs three explicit vertex dots vs a vertex-buffer min/max loop). Only the tracking half is duplicated; that is finding #2.
|
|
48
|
+
- **The `g_bb_*` / `g_bt_*` / `g_ct_*` module-static accumulator idiom** appearing in three narrowphase files — each carries a comment explaining it replaced a per-call closure allocation ("D3"). Same *technique*, different state. Correct as-is.
|
|
49
|
+
|
|
50
|
+
## Notes
|
|
51
|
+
|
|
52
|
+
**Why duplication accumulates here, structurally:**
|
|
53
|
+
|
|
54
|
+
1. **Twin kernel families.** The fluid solver carries a full collocated (`v3_grid_*`) and a full staggered-MAC (`v3_mac_*`) implementation of advection. The MAC family superseded the collocated one (memory: "MAC staggered, exact projection; MacCormack velocity default") but the collocated family stayed as library surface — so every algorithm in it (SL trace, MacCormack, Selle limiter, solid pass-through) now exists twice, and the two copies have already begun to drift (findings 4, 5, 6, 7). This is the largest single source of duplication in the domain by line count.
|
|
55
|
+
|
|
56
|
+
2. **Core primitives that return parameters, not points.** `tri3_closest_point_barycentric` returns `(α, β)` and `line3_compute_segment_segment_closest_points` returns `(s, t)`. Sixteen call sites across physics, core and graphics then write the same reconstruction arithmetic. This is the highest-leverage class of finding (#14, #15) because the fix is additive — two new core functions, no behaviour change, and the existing exports stay.
|
|
57
|
+
|
|
58
|
+
3. **Partial prior extractions.** Three separate extractions were done and each missed one site: `reduce_manifold_contacts` left the surrounding emit epilogue behind (#11), `angular_effective_mass_raw` was adopted by `solve_contacts` and `RaycastVehicle` but not `solve_constraints` (#17), and `polygon2_clip_halfplane` was written in core but never consumed (#12). Grepping for "each previously carried its own copy"-style comments is a reliable way to find the residue.
|
|
59
|
+
|
|
60
|
+
4. **Axis triplication.** The MAC kernels and the effectors write x/y/z as three near-identical blocks with different strides. In the *kernels* (`v3_mac_advect_sl_velocity.js`, `v3_mac_advect_maccormack_velocity.js`) this is legitimate — see below. In the *effectors* it is not (#13), because those loops are radius-bounded, not grid-bounded.
|
|
61
|
+
|
|
62
|
+
**Where inlining is legitimately correct (do not "fix" these):**
|
|
63
|
+
|
|
64
|
+
- `v3_mac_clip_trace.js:25-29` is an explicit measured performance contract (8.6× on the SL kernel) and the three inline solid tests in `v3_mac_advect_sl_velocity.js` honour it. These run per *face* over the whole lattice — millions of evaluations per step. Finding #19 targets only the two redundant *function declarations*, and even that is gated on measurement.
|
|
65
|
+
- The x/y/z block triplication inside `v3_mac_advect_sl_velocity.js` and `v3_mac_advect_maccormack_velocity.js`: each block has different index strides (`sx*res_y`, `res_x*sy`, `cell_slice`) and different loop bounds (`<= res_x` on the own axis). Parameterising would put stride variables in the innermost loop of a whole-grid kernel. Correct as written; I did not flag it.
|
|
66
|
+
- `capsule_contacts.js` / `capsule_triangle_contact.js` / the manifold builders are **per-contact**, not per-cell — a few thousand calls per frame, not millions. Extracting 20-line leaf helpers there (#9, #10, #11) is squarely on the reuse side of the line, and the codebase already does exactly this (`reduce_manifold_contacts`, `angular_effective_mass_raw`).
|
|
67
|
+
- `solve_contacts` / `solve_constraints` inner rows are per-contact-row × per-iteration — the hottest code in the engine. Finding #1 is still worth doing because it is a *verbatim* duplicate of the most correctness-critical routine present, but it must be benchmarked (rain and KEVA-mini) and must preserve operand order exactly for `determinism.spec.js`.
|
|
68
|
+
|
|
69
|
+
**Determinism summary:** findings 2, 5 and 12 can change simulation output and need before/after numerical diffs. Findings 1, 4, 6, 7, 9, 10, 11, 14, 15, 17, 18 are order-preserving by construction *provided* the extracted body is copied verbatim rather than rewritten — that is the whole risk. Findings 16, 20, 21, 22 have no simulation-state exposure.
|
|
@@ -1,102 +1,102 @@
|
|
|
1
|
-
# Domain F — engine/sound (legacy + sopra + acoustic simulation)
|
|
2
|
-
|
|
3
|
-
All paths relative to `H:/git/moh/app/src/mir-engine/meep/src/`. Every finding below was verified by
|
|
4
|
-
opening the cited files.
|
|
5
|
-
|
|
6
|
-
## Findings
|
|
7
|
-
|
|
8
|
-
| # | Severity | Concept | Sites | Evidence | Verdict | Proposed common path | Effort/Risk |
|
|
9
|
-
|---|---|---|---|---|---|---|---|
|
|
10
|
-
| 1 | HIGH | "Is this segment clear of occluders?" shadow-ray test written **3×**, each with its own module-static `Ray3` | `engine/sound/simulation/probe/probe_edge_visible.js:20` `probe_edge_visible`; `engine/sound/simulation/probe/probe_refine_connectivity.js:168` `segment_clear`; `engine/sound/simulation/probe/AcousticProbeField.js:328-345` (filter arrow in `nearestVisibleIndex`) | Brief C sim=0.75. All three do: zero-length ⇒ visible; `inv = 1/d`; `setOrigin/setDirection/tMax = d`; `!index.anyHit(RAY)`. `probe_refine_connectivity.js:167` comment literally says *"mirrors probe_edge_visible"*. `probe_edge_visible.js:8-9` claims it is *"the one visibility test shared by the offline bake and the runtime recheck, so they agree exactly"* — a claim the other two sites falsify | UNJUSTIFIED | `engine/sound/simulation/core/segment_clear.js` — `segment_clear(index, ax,ay,az, bx,by,bz)` with ONE scratch ray; `probe_edge_visible` becomes a 4-line probe-index wrapper; `nearestVisibleIndex`'s filter calls it directly | S / low |
|
|
11
|
-
| 2 | HIGH | Undirected edge-list → CSR (degree count → prefix sum → fill via write cursor) written twice; plus a hand-rolled sorted-row `isNeighbour` binary search | `engine/sound/simulation/probe/bakeVisibility.js:109-141` (weighted, → `CSRGraph.fromArrays`); `engine/sound/simulation/probe/probe_visibility_augment.js:50-70` (unweighted + per-row `.sort()`), with `isNeighbour` at `:167-182` | Both consume the same `{a,b}` edge arrays produced by stages A–D of the same bake. `core/graph/csr/` has `CSRGraph`, `csr_graph_find_edge`, `csr_graph_build_distance_field`, `csr_graph_find_shortest_path` — but **no builder**, so every caller re-derives one | PROMOTION | `core/graph/csr/csr_graph_from_undirected_edge_list.js(vertex_count, a, b, weights?, {sorted_rows})`; `isNeighbour` → `csr_graph_find_edge !== -1` (give the core one a sorted-row fast path) | M / low |
|
|
12
|
-
| 3 | HIGH | Listener resolution + `sopra.update(currentTime)` duplicated in two ECS systems that share ONE `SopraEngine` | `engine/sound/ecs/emitter/SoundEmitterSystem.js:137-158` `update`; `engine/sound/ecs/audio/AudioEmitterSystem.js:246-315` `update` | Both do `ecd.getAnyComponent(SoundListener)` → `getComponent(entity, Transform)` → `sopra.setListener(pos)` → `sopra.update(this.webAudioContext.currentTime)`. The engine is shared (`SoundEngine.obtainSopra`, `SoundEngine.js:82-88`), and BOTH class docs state coexistence is the design (`SoundEngine.js:76-78`, `AudioEmitterSystem.js:35-36`). Co-registering them double-runs `SopraEngine.update` (`sopra/SopraEngine.js:340-351`: snapshot + iterate all active instances + `#updateDuckers`). Harmless-but-wasted today because `EventInstance.#evaluate` guards on `entry.voice === null`; the copy-pasted "missing Transform" tolerance comment has already diverged (AudioEmitterSystem additionally skips `liveSet.refresh`) | UNJUSTIFIED | One owner for the tick: a `SopraTickSystem` (or fold listener-resolve + tick into `SoundListenerSystem`, which already owns the `SoundListener`+`Transform` pair). Emitter systems then only push emitter state | M / medium |
|
|
13
|
-
| 4 | HIGH | The container child-hash fold is copy-pasted into all 4 concrete containers instead of living on the base that owns `children` — and has already diverged | `engine/sound/sopra/definition/clip/SequenceContainerAudioClip.js:26-35`; `RandomContainerAudioClip.js:130-144`; `SwitchContainerAudioClip.js:83-96`; `BlendContainerAudioClip.js:126-139` (all `hash()`) | Brief A [70×2] + E. `ContainerAudioClip` owns `children` and DOES implement the parallel `equals()` fold (`ContainerAudioClip.js:80-99`) but has no `hash()`. Divergence already present: `BlendContainerAudioClip.hash()` folds `parameter`+`defaultValue` but **not** `blends`, though `equals()` compares them (`:117-121`) and the adapter serializes them — two blend containers differing only in curves hash identically. `EventDescription` polyphony buckets are keyed by event content, so clip hashes are load-bearing | UNJUSTIFIED | `ContainerAudioClip.hash()` = `super.hash()` folded with each child's `hash()`; each subclass folds only its own fields on top (and Blend adds `blends`) | S / low |
|
|
14
|
-
| 5 | MEDIUM | `AnimationCurve` binary keyframe codec hand-written twice | `engine/sound/sopra/definition/EventDescriptionSerializationAdapter.js:24-33` + `:55-64`; `engine/sound/sopra/definition/clip/BlendContainerAudioClipSerializationAdapter.js:20-33` + `:52-66` | Both carry the same excuse comment: *"AnimationCurve has no binary adapter of its own"*. Identical wire format (`uintVar` count, then `time/value/inTangent/outTangent` float32s). A payload change (wrap modes, tangent packing) must land in both or one silently mis-reads | PROMOTION | `engine/animation/curve/AnimationCurveSerializationAdapter.js` (or a `serialize_animation_curve` / `deserialize_animation_curve` pair beside `AnimationCurve.js`), registered so `this.objectAdapter` handles it | S / low |
|
|
15
|
-
| 6 | MEDIUM | 3-band decaying-noise IR synthesis kernel duplicated | `engine/sound/simulation/render/reverbImpulseResponse.js:26` `reverbImpulseResponse` (kernel `:33-72`); `engine/sound/simulation/render/foaReverbImpulseResponse.js:33` `foaReverbImpulseResponse` (kernel `:41-96`) | Brief E (4-line window). Identical: bin-length derivation from `decayBands × sampleRate`, one-pole coefficients `a = 1 − e^(−2πf/fs)`, the energy-preserving `low/mid/high` split, and the three `Math.pow(1 − i/len, decayPower)` envelopes. Only the write-out differs (omni sum vs direction-weighted B-format). Retuning the crossover or envelope must land in both | UNJUSTIFIED (the shared part is a per-buffer producer, not a per-sample call) | A `write_band_tails(outLow, outMid, outHigh, length, lenLow, lenMid, lenHigh, decayPower, sampleRate, random)` both consume; scalar sums, FOA weights by direction | M / medium (numeric output must stay bit-identical — the specs pin it) |
|
|
16
|
-
| 7 | MEDIUM | A **third** decaying-noise IR generator, unseeded | `engine/sound/sopra/definition/effect/ReverbEffect.js:45-66` `build` | Brief F `raw_math_random`: `data[i] = (Math.random() * 2 - 1) * Math.pow(1 - t, this.decayPower)` — the single-band case of finding 6. `reverbImpulseResponse.js:18` documents *"with three equal bands this reduces to the plain scalar tail"*, i.e. it already subsumes this. Divergence with teeth: the acoustic path is explicitly seeded (`ProbeReverbRenderer.js:97`, `FoaProbeReverbRenderer.js:161` → `seededRandom(1)`), so a sopra bus reverb is the only non-reproducible IR in the engine | UNJUSTIFIED | `ReverbEffect.build` → `reverbImpulseResponse(ctx, [d,d,d], this.decayPower, this.#random)` with a seeded RNG field | S / low |
|
|
17
|
-
| 8 | MEDIUM | Probe-reverb renderer scaffolding duplicated, with a divergent re-bake trigger | `engine/sound/simulation/render/ProbeReverbRenderer.js:108-136` (ctor) + `:192-230` `setBands`; `engine/sound/simulation/render/FoaProbeReverbRenderer.js:172-236` (ctor) + `:296-333` `#rebake` | Same double-buffered convolver bank, same `#active` flip, same `rampGain(..., FADE_EPSILON, crossfadeSeconds, ctx)` crossfade, same `max(bands) >= minDecay` audibility gate, same `setProbeField`/`input`/`output` surface. But the scalar re-bakes on per-band hysteresis (`changeThreshold`, `:198-208`) while the FOA re-bakes on nearest-probe-index change plus a `#dirty` force-flag (`:123-131`, `:257-260`) that the scalar **lacks** — a `setProbeField` swap between two fields whose bands agree at the listener leaves the scalar renderer's IR stale | UNJUSTIFIED for the crossfade/gate scaffolding (JUSTIFIED for the differing WebAudio graph shape) | Extract an N-channel `ConvolverCrossfadeBank` (build, `swapTo(buffers[])`, `mute()`) both renderers own; give both the same `#dirty` semantics | M / medium |
|
|
18
|
-
| 9 | MEDIUM | "Randomly orient a Fibonacci sample set" written twice, bypassing `random_point_on_sphere` | `engine/sound/simulation/core/OcclusionSolver.js:43-51` (in `solve`); `engine/sound/simulation/probe/bakeReverbBands.js:69-74` | Identical four lines: `z = 2·random()−1`, `azimuth = 2π·random()`, `ring = sqrt(max(0, 1−z²))`, then `v3_orthonormal_matrix_from_normal(ROTATION, 0, ring·cos, ring·sin, z)`. `core/geom/random/random_point_on_sphere.js` already produces the uniform direction | UNJUSTIFIED | `core/geom/random/random_orthonormal_basis(out9, out_offset, random)` beside `v3_orthonormal_matrix_from_normal`; at minimum call `random_point_on_sphere` for the direction | S / low |
|
|
19
|
-
| 10 | MEDIUM | Canonical undirected edge key `min·n + max` written **4×** (and decoded in a 5th place) | `engine/sound/simulation/probe/ProbeVisibilityRecheck.js:72-75` `#edgeId` (decode at `:112-113`); `engine/sound/simulation/probe/probe_densify_portals.js:141-143` `edgeKey`; `engine/sound/simulation/probe/probe_visibility_bridge_components.js:84-86` (inline `rlo·n + rhi`); `engine/sound/simulation/probe/probe_visibility_augment.js:147` (inline `u·n + v`, correctness relies on the caller's `u<v` canonicalisation at `:96`) | A bug-prone invariant (must use the probe count as the radix AND canonicalise the pair) replicated across four modules of one bake pipeline; changing the radix (e.g. for >65k probes packing) silently breaks the ones you miss | UNJUSTIFIED | `engine/sound/simulation/probe/probe_edge_key.js` — `probe_edge_key(a, b, n)` + `probe_edge_decode(key, n, out2)` | S / low |
|
|
20
|
-
| 11 | MEDIUM | `SoundControllerSystem` ≡ `AnimationControllerSystem` — the whole "component.rules → per-entity event-listener registration + teardown" ECS lifecycle | `engine/sound/ecs/SoundControllerSystem.js:49-101` `link` / `:103-113` `unlink`; `engine/graphics/ecs/animation/AnimationControllerSystem.js:18-62` `link` / `:64-72` `unlink` | Brief A [59×2] (`unlink` is token-identical) + E. Both hold `this.removers = []`, build `callbacks = this.removers[entity] = []`, register `{name, handler}` pairs for `rule.startEvent`/`rule.stopEvent` via `dataset.addEntityEventListener`, and tear down identically. Only the two handler bodies differ | UNJUSTIFIED (cross-domain, reported here per instructions) | A typed `EntityEventRuleBinding` value (owns the `{name, handler}` list, `bind(dataset, entity, name, handler)` / `release(dataset, entity)`) that both systems compose — no string-keyed registry involved | S / low |
|
|
21
|
-
| 11b | LOW | `randomFromArray` re-derived inline | `engine/sound/ecs/SoundControllerSystem.js:13-24` `pickTackFromRule` | Brief F `raw_math_random`: `Math.floor(Math.random() * tracks.length)`. `core/math/random/randomFromArray.js` exists and is used correctly two directories away in `engine/sound/material/concrete/RandomSoundMaterial.js:17` | UNJUSTIFIED | `randomFromArray(Math.random, rule.tracks)` | XS / none |
|
|
22
|
-
| 12 | MEDIUM | Emitter → `sopra.playEvent(...)` option mapping duplicated; drives a 9-positional-argument API | `engine/sound/ecs/audio/AudioEmitterSystem.js:209-233` `#play`; `engine/sound/ecs/audio/LiveEmitterSet.js:281-323` `#promote` (fed by `LiveEmitterSet.add` at `:158-163`, called with 9 positional args at `AudioEmitterSystem.js:139`) | Both derive `oneShot = !event.rootClip.loops()`, build the same `{busId, position, oneShot, acoustic, pathing, sourceRadius, orientation, directivity}` bag, then re-apply `setGainDb`. `LiveEmitterSet`'s record type (`:77`) is a hand-mirrored copy of that bag. Every new per-source acoustic property (`orientation`, `directivity` were the last two) must be threaded through the record, the 9-arg `add` signature, `#promote`, AND `#play` | UNJUSTIFIED | One typed `AudioEmitterPlaySpec` built from `(emitter, transform, acousticSim)`; `LiveEmitterSet.add(entity, spec)` stores it, `#promote` and `#play` both hand it to `playEvent` | M / low |
|
|
23
|
-
| 13 | MEDIUM | Three hand-written "dynamic BVH of handle-addressed objects" wrappers inside one domain | `engine/sound/simulation/core/AcousticOccluderIndex.js:40-122`; `engine/sound/simulation/core/AcousticRegionIndex.js:23-134`; `engine/sound/ecs/audio/SpatialAudioIndex.js:24-138` | Identical skeleton in all three: `#bvh = new BVH()`, a parallel `BvhClient` store, retire-a-slot-by-null, a `#fitLeaf(client, x)` that ends in `client.resize(x0…z1)`, a reused `#candidates` gather buffer, and a query that maps returned handles back to user objects. Only the leaf-bounds policy and the refit trigger (reactive `onChanged` / per-frame `refit()` / explicit `setTransform`) differ | UNJUSTIFIED | `core/bvh2/bvh3/BvhObjectIndex.js` — typed container holding `T[]` + clients, with `computeBounds(out, item)` supplied by the owner; the three sound indices become thin policy classes | M / medium |
|
|
24
|
-
| 13b | LOW | AABB union re-derived | `engine/sound/simulation/core/AcousticOccluderIndex.js:252-279` `computeWorldBounds` | Hand-written 6-component min/max fold; `core/geom/3d/aabb/aabb3_array_combine.js` does exactly this | UNJUSTIFIED | `aabb3_array_combine` | XS / none |
|
|
25
|
-
| 14 | MEDIUM | Trilinear sampling of a Float32Array 3D scalar grid re-implemented | `engine/sound/simulation/probe/probe_sdf_grid.js:104-122` `grid_sample_trilinear` (+ `clampInt` at `:181-183`) | `engine/graphics/texture/3d/scs3d_sample_linear.js:15` is the same function: same `z·(rx·ry) + y·rx + x` layout, same edge clamping, same trilinear blend. `SingleChannelSampler3D` is the matching container. `probe_sdf_grid.js:1` already imports `clamp` yet still hand-rolls `clampInt` | UNJUSTIFIED | Back `SdfGrid` with `SingleChannelSampler3D` and call `scs3d_sample_linear`; delete `clampInt` | S / low |
|
|
26
|
-
| 15 | LOW | `{type: ctor.typeName, data: x.toJSON()}` wrapper written 3× with two different `constructor` lookups | `engine/sound/material/concrete/json/serializeSoundMaterialToJSON.js:1-6` (`material.constructor.typeName`); `engine/sound/sopra/serialization/sopraJSON.js:22-27` `serializeAudioClipToJSON` and `:68-73` `serializeAudioEffectToJSON` (`Object.getPrototypeOf(x).constructor.typeName`) | Brief A [70×2], C sim=1. The **switch-based deserializers** (`deserializeSoundMaterialFromJSON.js:12`, `sopraJSON.js:33`, `sopraJSON.js:79`) are **JUSTIFIED** — the alternative in core (`core/json/abstractJSONDeserializer.js`) is precisely the string-keyed `Map<string, fn>` registry the house rules reject, and `sopraJSON.js:11-16` documents deliberate call-time class binding to survive an import cycle. Only the three one-line serializers are gratuitous, and they disagree on how to reach the constructor | UNJUSTIFIED (serializers only) | One `type_tagged_to_json(value)` helper (typed, no registry); pick one constructor lookup | XS / none |
|
|
27
|
-
| 16 | LOW | `dB2Volume` re-derived inline in a file that already imports it | `engine/sound/sopra/runtime/EventInstance.js:33-35` `dbTiltToGain` → `Math.pow(10, -excessDb / 20)`; `dB2Volume` imported at `:2` | Called 4× per acoustic instance per frame (`:542-543`, `:561-562`) — not per-sample, so no inlining justification | UNJUSTIFIED | `excessDb <= 0 ? 1 : dB2Volume(-excessDb)` | XS / none |
|
|
28
|
-
| 17 | LOW | `BusGraph` repeats the same lookup-or-throw preamble 6× | `engine/sound/sopra/runtime/BusGraph.js:143` `getInput`, `:159` `getOutput`, `:173` `getVolume`, `:187` `setVolume`, `:214` `rampVolume`, `:232` `approachVolume` | Six copies of `const bus = this.buses.get(id); if (bus === undefined) { throw new Error(\`sopra: no bus '${id}'\`); }` — ~30 lines | UNJUSTIFIED | A `#bus(id)` private that does the lookup + throw | XS / none |
|
|
29
|
-
| 18 | LOW / PROMOTION | The `setFlag`/`clearFlag`/`writeFlag`/`getFlag` quad, byte-identical in **35 files** | In this domain: `engine/sound/ecs/emitter/SoundEmitter.js:156-189`; `engine/sound/ecs/emitter/SoundTrack.js:138-171`; `engine/sound/asset/SoundAssetPlaybackSpec.js`; `engine/ecs/speaker/Voice.js:13-46` | Brief E (30-file window). `grep -l "clearFlag(flag) {"` returns 35 files spanning core/, engine/ecs, graphics, physics, sound, view | PROMOTION (codebase-wide, not sound-specific — flagged for the cross-domain roll-up, not for a sound-local fix) | A `FlagCarrier` base (or a shared `flags_set/clear/write/read` quartet on the flat `flags` word); JS single inheritance makes the base awkward where a class already extends, so the function quartet is the safer shape | L / medium |
|
|
30
|
-
| 19 | LOW | Well-conditioned perpendicular-axis construction re-derived | `engine/sound/simulation/probe/probe_detect_portals.js:170-184` `throatIsAperture` | Hand-rolls "cross with world-up, swap reference if `|ey| > 0.9`, normalize". `core/geom/vec3/v3_orthonormal_matrix_from_normal.js` builds exactly this stable basis and is already used twice in this same package (`OcclusionSolver.js:48`, `bakeReverbBands.js:74`) | UNJUSTIFIED | `v3_orthonormal_matrix_from_normal` and read the tangent column | XS / low |
|
|
31
|
-
| 20 | LOW | Vector normalize written by hand, inconsistently with the same file | `engine/sound/simulation/AcousticSimulator.js:598-621` `#directivityGainToward` (`Math.sqrt(dx*dx+dy*dy+dz*dz)` at `:605`) vs `:454, :455, :459, :460` in `#applyPath` (`Math.hypot`) | Brief F `inline_length3`. Cold path (once per non-omni source per frame). `v3_length` / `v3_normalize_array` exist; the file already imports `line3_compute_segment_closest_point_t` from the same family | UNJUSTIFIED (cold) | `v3_length`, or at least pick one of `Math.hypot`/`sqrt` within the file | XS / none |
|
|
32
|
-
| 21 | LOW / PROMOTION | Union-find connected components over probe connectivity written twice | `engine/sound/simulation/probe/probe_visibility_bridge_components.js:35-39` (from an `{a,b}` edge list); `engine/sound/simulation/probe/probe_refine_connectivity.js:50-56` (from a `CSRGraph`) | Both then use `uf_find(parent, i) === uf_find(parent, j)` as the same-component predicate. `core/graph/graph_compute_disconnected_clusters.js` exists but only for the object-graph `Graph`, not `CSRGraph` | PROMOTION | `core/graph/csr/csr_graph_connected_components(parent, graph)` beside `csr_graph_build_distance_field`; the edge-list variant folds into finding 2's builder | S / low |
|
|
33
|
-
|
|
34
|
-
## Strangler-migration debt
|
|
35
|
-
|
|
36
|
-
Context, not a defect list. Legacy-vs-sopra parallelism is the deliberate migration shape; what matters is
|
|
37
|
-
where the seam has stalled and where a fix would have to land twice.
|
|
38
|
-
|
|
39
|
-
| Legacy | sopra / native counterpart | Is legacy still wired? | Note |
|
|
40
|
-
|---|---|---|---|
|
|
41
|
-
| `ecs/emitter/SoundEmitterSystem` + `SoundEmitterComponentContext` | `ecs/audio/AudioEmitterSystem` + `AudioEmitter` + `LiveEmitterSet` | **YES** — `mir-engine/model/game/makeMirEngineConfig.js:182` | The seam is genuinely healthy here: legacy is a *translator* (`sopra/legacy/soundEmitterToEventDescription.js`) onto sopra, not a second renderer. Conversely `AudioEmitter*` has **zero** consumers outside `engine/sound/` — the new path is unused in the game |
|
|
42
|
-
| `ecs/SoundControllerSystem` | none — its own JSDoc names a *"forthcoming `AudioEventTrigger`"* (`SoundControllerSystem.js:27-30`) | **YES** — `makeMirEngineConfig.js:183` | `@deprecated` and still registered with no replacement in the tree. Findings 11/11b live here |
|
|
43
|
-
| `material/**` (`AbstractSoundMaterialDefinition`, `Single`/`Random`/`Composition`, `json/`) | sopra clips — `SwitchContainerAudioClip.js:11` *"Supersedes the dormant material/ surface-switch concept"*; `BlendContainerAudioClip.js:13` *"Supersedes the dormant material/ composition concept"* | **NO** — grep finds zero references outside `engine/sound/material/` (only a doc mention in `sopraJSON.js:13`) | Dead but public surface (meep is a library). Origin of the JSON copies in finding 15 |
|
|
44
|
-
| `SoundEmitterChannels` (`effects`/`music`/`ambient`) | sopra `SopraDefaultBus` + `BusDefinition` | **YES** — 7 app files import it (UnitMaker, StoryManager, WorldEventManager, …) | Already proxied 1:1: `SoundEmitterSystem.get/setChannelVolume` (`:91-102`) forwards to `sopra.get/setBusVolume` |
|
|
45
|
-
| `ecs/SoundListenerSystem` (WebAudio `AudioListener` pose) | `sopra.setListener(position)` inside the emitter systems | **YES** — `makeMirEngineConfig.js:184` | THREE readers of the same `SoundListener`→`Transform` relation. sopra's listener is position-only; the WebAudio listener also carries orientation. See finding 3 |
|
|
46
|
-
| `ProbeReverbRenderer` (scalar) | `FoaProbeReverbRenderer` (directional/B-format) | scalar is what `configureAcousticSimulation.js:31` documents; FOA is referenced only by `prototypeAcousticSolver.js:515` + its spec | Neither is deprecated; both live. Findings 6/8 mean an IR or crossfade fix must land in both |
|
|
47
|
-
| `ecs/emitter/SoundPanningModelType` | `sopra/definition/SopraPanningModel` | **YES** — translated by `soundEmitterToEventDescription.js:57-61` | Identical 2-value enums; translation is one ternary. Fine |
|
|
48
|
-
| `SoundAttenuationFunction` + `core/math/physics/irradiance/interpolate_irradiance_*` | authored `AnimationCurve` via `sopra/util/buildAttenuationCurve.js` | **YES** | This is the *good* seam: the legacy falloffs are REUSED to sample the new curve rather than reimplemented. Called out as a model, not debt |
|
|
49
|
-
|
|
50
|
-
## Dismissed leads
|
|
51
|
-
|
|
52
|
-
- Brief A/C `makePhysicsGroundResolver` / `recomputeMove` / `bindMoveKey` / `buildInputBindings` / `logHelp` (`engine/control/first-person/prototype_first_person_controller.js` ⟷ `engine/sound/simulation/prototypeAcousticSolver.js`) — both sites are `prototype*.js` harnesses, out of scope. **One line as instructed:** a real shared debug fly/walk camera *with* a physics ground resolver would be worth promoting to `engine/control/`, since it is now copied verbatim for every new subsystem demo.
|
|
53
|
-
- `DuckingRuleSerializationAdapter` / `CompressorEffectSerializationAdapter` / `ParameterDefinitionSerializationAdapter` `serialize`+`deserialize` (brief A [50×3] and [45×3]) — read all three: different field names and types (`writeUTF8String`+3×float32+float32 / 5×float32 / string+3×float32+uint8), zero shared logic. Exactly the per-class shape boilerplate the house rules bless.
|
|
54
|
-
- `SequenceContainerAudioClipSerializationAdapter.deserialize` ⟷ `SwitchContainerAudioClipSerializationAdapter.deserialize` (brief C sim=0.781) — same verdict; Switch additionally reads `parameter`/`defaultValue`.
|
|
55
|
-
- The container adapters' repeated `gain`/`pitch` writes and children loop (brief E) — wire order is per-class protocol; hoisting it into a base couples four independent binary formats. Deliberate.
|
|
56
|
-
- `MixerSnapshotSerializationAdapter` bus-entry codec vs `BusDefinitionSerializationAdapter` sends codec — structurally identical (`string` + `float32` pairs), semantically distinct record types. Shape only.
|
|
57
|
-
- `engine/ecs/speaker/Voice.js` vs `engine/sound/sopra/runtime/Voice.js` (brief G) — pure name collision. The first is a speech-bubble ECS component (flags only, `Voice.serializable = false`); the second is a WebAudio `AudioBufferSourceNode` holder. No shared logic beyond the flag quad (finding 18). The `speaker/` package has no importers, but meep is a library — not a defect.
|
|
58
|
-
- `echogram_band_rt60.js:49` `10 * Math.log10(edc / total)` — **energy** dB, deliberately not `volume2dB`'s amplitude `20·log10`. Correct as written.
|
|
59
|
-
- Brief F `inline_lerp` in `probe_sdf_grid.js:118-121` — the innermost trilinear blend; folds into finding 14 anyway. `AcousticSimulator.js:453, :458` and `probe_detect_portals.js:94, :109` are 3-component point-on-segment evaluations, not scalar `lerp` calls.
|
|
60
|
-
- Brief F `inline_length3`, the remaining ~14 hits — hot per-ray / per-edge / per-candidate-pair loops (`OcclusionSolver.js:79` per ray, `bakeVisibility.js:106` per graph edge, `probe_visibility_augment.js:92,108,111` per 2-ring chord). Inlining is the right call there; only the cold `AcousticSimulator` site (finding 20) is worth changing.
|
|
61
|
-
- Brief F `epsilon_compare` `ProbeReverbRenderer.js:198-200` — a tunable re-bake hysteresis (`changeThreshold = 0.1 s`), not float-tolerance equality. `epsilonEquals` would be semantically wrong.
|
|
62
|
-
- Brief F `binary_search_inline` `probe_visibility_augment.js:171` — folded into finding 2.
|
|
63
|
-
- `EventInstance.js:47-92` `build3BandCrossover` / `set3BandGains` / `disconnect3Band` — already correctly factored and shared by BOTH the direct and corner-leak paths (`:457`, `:476`). No duplication; a model for finding 8.
|
|
64
|
-
- `ProbeFieldPathingSolution.js:62` correctly delegates to `csr_graph_build_distance_field`, and `AcousticProbeField.shortestPath:519` to `csr_graph_find_shortest_path`. **No Dijkstra / graph-search duplication in `simulation/`** — the probe pathing is a clean `core/graph` consumer.
|
|
65
|
-
- No hand-rolled biquad coefficient math, equal-power panning law, or crossfade curve anywhere in the domain — WebAudio nodes (`createBiquadFilter`, `PannerNode`, `linearRampToValueAtTime`) do all of it. The one filter-adjacent formula, the one-pole coefficient `1 − e^(−2πf/fs)`, is finding 6.
|
|
66
|
-
- The 3-band value classes (`AcousticMedium`, `AcousticMaterial`, `AcousticSolution`, `AcousticSourceState`) unroll their 3-element copies by hand in `equals`/`clone`/`hash`/`toJSON` — typed, explicit, per-field. Consistent with "typed over magic"; not duplication of *logic*.
|
|
67
|
-
|
|
68
|
-
## Notes — why duplication accumulates here
|
|
69
|
-
|
|
70
|
-
1. **Three generations coexist in one package.** `material/` (dead, superseded), `ecs/emitter/` (legacy, wired,
|
|
71
|
-
now a translator), and `sopra/` + `ecs/audio/` (new, unwired outside the engine). Each generation
|
|
72
|
-
re-solved the same problems — polymorphic type-tagged JSON, weighted random selection, composition
|
|
73
|
-
with per-child weights — because the previous one was not being deleted. The three JSON dispatchers
|
|
74
|
-
(finding 15) and the material-vs-clip parallel hierarchy are the visible residue.
|
|
75
|
-
|
|
76
|
-
2. **The migration seam stalled at the game boundary, not inside the engine.** Inside `engine/sound/`
|
|
77
|
-
the strangler is genuinely done — sopra is the single renderer and legacy is a translator. But
|
|
78
|
-
`makeMirEngineConfig.js` still registers the three legacy systems and nothing registers
|
|
79
|
-
`AudioEmitterSystem`, so the new path's code has no production caller keeping it honest. That is
|
|
80
|
-
what let finding 3 (two systems each owning the sopra tick) and finding 12 (two copies of the
|
|
81
|
-
play-options mapping) survive: only one of each pair actually runs.
|
|
82
|
-
|
|
83
|
-
3. **`core/graph/csr/` ships consumers but no producer.** It has Dijkstra, distance fields, and edge
|
|
84
|
-
lookup, but no `from_edge_list` builder — so the probe bake writes its own twice (finding 2), and the
|
|
85
|
-
companions it also needs (connected components, canonical edge key) got written locally 2–4× each
|
|
86
|
-
(findings 10, 21). One missing primitive generated four findings.
|
|
87
|
-
|
|
88
|
-
4. **Ray-scratch locality beats reuse.** Every module that casts a shadow ray declares its own
|
|
89
|
-
module-static `Ray3` and inlines the normalize-and-set sequence (finding 1). The pattern is
|
|
90
|
-
allocation-free by construction, which makes the copy feel cheap, but it means the "one shared
|
|
91
|
-
visibility test" the code documents does not actually exist.
|
|
92
|
-
|
|
93
|
-
5. **Per-sample DSP genuinely justifies inlining — but the justification got over-applied.** The IR
|
|
94
|
-
synthesis kernels (findings 6, 7) share far more than the inner loop: window sizing, filter
|
|
95
|
-
coefficients, and envelope shape are all per-*bake* work that could be shared without a per-sample
|
|
96
|
-
call. The `EventInstance` crossover helpers show the team already knows how to draw that line
|
|
97
|
-
correctly; the reverb bakers just predate it.
|
|
98
|
-
|
|
99
|
-
6. **The base class stops one level short.** `AbstractAudioClip` folds `gain`/`pitch` into `hash()`, and
|
|
100
|
-
`ContainerAudioClip` folds `children` into `equals()` — but not into `hash()`. Four subclasses filled
|
|
101
|
-
the gap by copy-paste, and one of them (Blend) has already drifted (finding 4). The same shape
|
|
102
|
-
appears in the container adapters, where the base clip fields are re-written per subclass.
|
|
1
|
+
# Domain F — engine/sound (legacy + sopra + acoustic simulation)
|
|
2
|
+
|
|
3
|
+
All paths relative to `H:/git/moh/app/src/mir-engine/meep/src/`. Every finding below was verified by
|
|
4
|
+
opening the cited files.
|
|
5
|
+
|
|
6
|
+
## Findings
|
|
7
|
+
|
|
8
|
+
| # | Severity | Concept | Sites | Evidence | Verdict | Proposed common path | Effort/Risk |
|
|
9
|
+
|---|---|---|---|---|---|---|---|
|
|
10
|
+
| 1 | HIGH | "Is this segment clear of occluders?" shadow-ray test written **3×**, each with its own module-static `Ray3` | `engine/sound/simulation/probe/probe_edge_visible.js:20` `probe_edge_visible`; `engine/sound/simulation/probe/probe_refine_connectivity.js:168` `segment_clear`; `engine/sound/simulation/probe/AcousticProbeField.js:328-345` (filter arrow in `nearestVisibleIndex`) | Brief C sim=0.75. All three do: zero-length ⇒ visible; `inv = 1/d`; `setOrigin/setDirection/tMax = d`; `!index.anyHit(RAY)`. `probe_refine_connectivity.js:167` comment literally says *"mirrors probe_edge_visible"*. `probe_edge_visible.js:8-9` claims it is *"the one visibility test shared by the offline bake and the runtime recheck, so they agree exactly"* — a claim the other two sites falsify | UNJUSTIFIED | `engine/sound/simulation/core/segment_clear.js` — `segment_clear(index, ax,ay,az, bx,by,bz)` with ONE scratch ray; `probe_edge_visible` becomes a 4-line probe-index wrapper; `nearestVisibleIndex`'s filter calls it directly | S / low |
|
|
11
|
+
| 2 | HIGH | Undirected edge-list → CSR (degree count → prefix sum → fill via write cursor) written twice; plus a hand-rolled sorted-row `isNeighbour` binary search | `engine/sound/simulation/probe/bakeVisibility.js:109-141` (weighted, → `CSRGraph.fromArrays`); `engine/sound/simulation/probe/probe_visibility_augment.js:50-70` (unweighted + per-row `.sort()`), with `isNeighbour` at `:167-182` | Both consume the same `{a,b}` edge arrays produced by stages A–D of the same bake. `core/graph/csr/` has `CSRGraph`, `csr_graph_find_edge`, `csr_graph_build_distance_field`, `csr_graph_find_shortest_path` — but **no builder**, so every caller re-derives one | PROMOTION | `core/graph/csr/csr_graph_from_undirected_edge_list.js(vertex_count, a, b, weights?, {sorted_rows})`; `isNeighbour` → `csr_graph_find_edge !== -1` (give the core one a sorted-row fast path) | M / low |
|
|
12
|
+
| 3 | HIGH | Listener resolution + `sopra.update(currentTime)` duplicated in two ECS systems that share ONE `SopraEngine` | `engine/sound/ecs/emitter/SoundEmitterSystem.js:137-158` `update`; `engine/sound/ecs/audio/AudioEmitterSystem.js:246-315` `update` | Both do `ecd.getAnyComponent(SoundListener)` → `getComponent(entity, Transform)` → `sopra.setListener(pos)` → `sopra.update(this.webAudioContext.currentTime)`. The engine is shared (`SoundEngine.obtainSopra`, `SoundEngine.js:82-88`), and BOTH class docs state coexistence is the design (`SoundEngine.js:76-78`, `AudioEmitterSystem.js:35-36`). Co-registering them double-runs `SopraEngine.update` (`sopra/SopraEngine.js:340-351`: snapshot + iterate all active instances + `#updateDuckers`). Harmless-but-wasted today because `EventInstance.#evaluate` guards on `entry.voice === null`; the copy-pasted "missing Transform" tolerance comment has already diverged (AudioEmitterSystem additionally skips `liveSet.refresh`) | UNJUSTIFIED | One owner for the tick: a `SopraTickSystem` (or fold listener-resolve + tick into `SoundListenerSystem`, which already owns the `SoundListener`+`Transform` pair). Emitter systems then only push emitter state | M / medium |
|
|
13
|
+
| 4 | HIGH | The container child-hash fold is copy-pasted into all 4 concrete containers instead of living on the base that owns `children` — and has already diverged | `engine/sound/sopra/definition/clip/SequenceContainerAudioClip.js:26-35`; `RandomContainerAudioClip.js:130-144`; `SwitchContainerAudioClip.js:83-96`; `BlendContainerAudioClip.js:126-139` (all `hash()`) | Brief A [70×2] + E. `ContainerAudioClip` owns `children` and DOES implement the parallel `equals()` fold (`ContainerAudioClip.js:80-99`) but has no `hash()`. Divergence already present: `BlendContainerAudioClip.hash()` folds `parameter`+`defaultValue` but **not** `blends`, though `equals()` compares them (`:117-121`) and the adapter serializes them — two blend containers differing only in curves hash identically. `EventDescription` polyphony buckets are keyed by event content, so clip hashes are load-bearing | UNJUSTIFIED | `ContainerAudioClip.hash()` = `super.hash()` folded with each child's `hash()`; each subclass folds only its own fields on top (and Blend adds `blends`) | S / low |
|
|
14
|
+
| 5 | MEDIUM | `AnimationCurve` binary keyframe codec hand-written twice | `engine/sound/sopra/definition/EventDescriptionSerializationAdapter.js:24-33` + `:55-64`; `engine/sound/sopra/definition/clip/BlendContainerAudioClipSerializationAdapter.js:20-33` + `:52-66` | Both carry the same excuse comment: *"AnimationCurve has no binary adapter of its own"*. Identical wire format (`uintVar` count, then `time/value/inTangent/outTangent` float32s). A payload change (wrap modes, tangent packing) must land in both or one silently mis-reads | PROMOTION | `engine/animation/curve/AnimationCurveSerializationAdapter.js` (or a `serialize_animation_curve` / `deserialize_animation_curve` pair beside `AnimationCurve.js`), registered so `this.objectAdapter` handles it | S / low |
|
|
15
|
+
| 6 | MEDIUM | 3-band decaying-noise IR synthesis kernel duplicated | `engine/sound/simulation/render/reverbImpulseResponse.js:26` `reverbImpulseResponse` (kernel `:33-72`); `engine/sound/simulation/render/foaReverbImpulseResponse.js:33` `foaReverbImpulseResponse` (kernel `:41-96`) | Brief E (4-line window). Identical: bin-length derivation from `decayBands × sampleRate`, one-pole coefficients `a = 1 − e^(−2πf/fs)`, the energy-preserving `low/mid/high` split, and the three `Math.pow(1 − i/len, decayPower)` envelopes. Only the write-out differs (omni sum vs direction-weighted B-format). Retuning the crossover or envelope must land in both | UNJUSTIFIED (the shared part is a per-buffer producer, not a per-sample call) | A `write_band_tails(outLow, outMid, outHigh, length, lenLow, lenMid, lenHigh, decayPower, sampleRate, random)` both consume; scalar sums, FOA weights by direction | M / medium (numeric output must stay bit-identical — the specs pin it) |
|
|
16
|
+
| 7 | MEDIUM | A **third** decaying-noise IR generator, unseeded | `engine/sound/sopra/definition/effect/ReverbEffect.js:45-66` `build` | Brief F `raw_math_random`: `data[i] = (Math.random() * 2 - 1) * Math.pow(1 - t, this.decayPower)` — the single-band case of finding 6. `reverbImpulseResponse.js:18` documents *"with three equal bands this reduces to the plain scalar tail"*, i.e. it already subsumes this. Divergence with teeth: the acoustic path is explicitly seeded (`ProbeReverbRenderer.js:97`, `FoaProbeReverbRenderer.js:161` → `seededRandom(1)`), so a sopra bus reverb is the only non-reproducible IR in the engine | UNJUSTIFIED | `ReverbEffect.build` → `reverbImpulseResponse(ctx, [d,d,d], this.decayPower, this.#random)` with a seeded RNG field | S / low |
|
|
17
|
+
| 8 | MEDIUM | Probe-reverb renderer scaffolding duplicated, with a divergent re-bake trigger | `engine/sound/simulation/render/ProbeReverbRenderer.js:108-136` (ctor) + `:192-230` `setBands`; `engine/sound/simulation/render/FoaProbeReverbRenderer.js:172-236` (ctor) + `:296-333` `#rebake` | Same double-buffered convolver bank, same `#active` flip, same `rampGain(..., FADE_EPSILON, crossfadeSeconds, ctx)` crossfade, same `max(bands) >= minDecay` audibility gate, same `setProbeField`/`input`/`output` surface. But the scalar re-bakes on per-band hysteresis (`changeThreshold`, `:198-208`) while the FOA re-bakes on nearest-probe-index change plus a `#dirty` force-flag (`:123-131`, `:257-260`) that the scalar **lacks** — a `setProbeField` swap between two fields whose bands agree at the listener leaves the scalar renderer's IR stale | UNJUSTIFIED for the crossfade/gate scaffolding (JUSTIFIED for the differing WebAudio graph shape) | Extract an N-channel `ConvolverCrossfadeBank` (build, `swapTo(buffers[])`, `mute()`) both renderers own; give both the same `#dirty` semantics | M / medium |
|
|
18
|
+
| 9 | MEDIUM | "Randomly orient a Fibonacci sample set" written twice, bypassing `random_point_on_sphere` | `engine/sound/simulation/core/OcclusionSolver.js:43-51` (in `solve`); `engine/sound/simulation/probe/bakeReverbBands.js:69-74` | Identical four lines: `z = 2·random()−1`, `azimuth = 2π·random()`, `ring = sqrt(max(0, 1−z²))`, then `v3_orthonormal_matrix_from_normal(ROTATION, 0, ring·cos, ring·sin, z)`. `core/geom/random/random_point_on_sphere.js` already produces the uniform direction | UNJUSTIFIED | `core/geom/random/random_orthonormal_basis(out9, out_offset, random)` beside `v3_orthonormal_matrix_from_normal`; at minimum call `random_point_on_sphere` for the direction | S / low |
|
|
19
|
+
| 10 | MEDIUM | Canonical undirected edge key `min·n + max` written **4×** (and decoded in a 5th place) | `engine/sound/simulation/probe/ProbeVisibilityRecheck.js:72-75` `#edgeId` (decode at `:112-113`); `engine/sound/simulation/probe/probe_densify_portals.js:141-143` `edgeKey`; `engine/sound/simulation/probe/probe_visibility_bridge_components.js:84-86` (inline `rlo·n + rhi`); `engine/sound/simulation/probe/probe_visibility_augment.js:147` (inline `u·n + v`, correctness relies on the caller's `u<v` canonicalisation at `:96`) | A bug-prone invariant (must use the probe count as the radix AND canonicalise the pair) replicated across four modules of one bake pipeline; changing the radix (e.g. for >65k probes packing) silently breaks the ones you miss | UNJUSTIFIED | `engine/sound/simulation/probe/probe_edge_key.js` — `probe_edge_key(a, b, n)` + `probe_edge_decode(key, n, out2)` | S / low |
|
|
20
|
+
| 11 | MEDIUM | `SoundControllerSystem` ≡ `AnimationControllerSystem` — the whole "component.rules → per-entity event-listener registration + teardown" ECS lifecycle | `engine/sound/ecs/SoundControllerSystem.js:49-101` `link` / `:103-113` `unlink`; `engine/graphics/ecs/animation/AnimationControllerSystem.js:18-62` `link` / `:64-72` `unlink` | Brief A [59×2] (`unlink` is token-identical) + E. Both hold `this.removers = []`, build `callbacks = this.removers[entity] = []`, register `{name, handler}` pairs for `rule.startEvent`/`rule.stopEvent` via `dataset.addEntityEventListener`, and tear down identically. Only the two handler bodies differ | UNJUSTIFIED (cross-domain, reported here per instructions) | A typed `EntityEventRuleBinding` value (owns the `{name, handler}` list, `bind(dataset, entity, name, handler)` / `release(dataset, entity)`) that both systems compose — no string-keyed registry involved | S / low |
|
|
21
|
+
| 11b | LOW | `randomFromArray` re-derived inline | `engine/sound/ecs/SoundControllerSystem.js:13-24` `pickTackFromRule` | Brief F `raw_math_random`: `Math.floor(Math.random() * tracks.length)`. `core/math/random/randomFromArray.js` exists and is used correctly two directories away in `engine/sound/material/concrete/RandomSoundMaterial.js:17` | UNJUSTIFIED | `randomFromArray(Math.random, rule.tracks)` | XS / none |
|
|
22
|
+
| 12 | MEDIUM | Emitter → `sopra.playEvent(...)` option mapping duplicated; drives a 9-positional-argument API | `engine/sound/ecs/audio/AudioEmitterSystem.js:209-233` `#play`; `engine/sound/ecs/audio/LiveEmitterSet.js:281-323` `#promote` (fed by `LiveEmitterSet.add` at `:158-163`, called with 9 positional args at `AudioEmitterSystem.js:139`) | Both derive `oneShot = !event.rootClip.loops()`, build the same `{busId, position, oneShot, acoustic, pathing, sourceRadius, orientation, directivity}` bag, then re-apply `setGainDb`. `LiveEmitterSet`'s record type (`:77`) is a hand-mirrored copy of that bag. Every new per-source acoustic property (`orientation`, `directivity` were the last two) must be threaded through the record, the 9-arg `add` signature, `#promote`, AND `#play` | UNJUSTIFIED | One typed `AudioEmitterPlaySpec` built from `(emitter, transform, acousticSim)`; `LiveEmitterSet.add(entity, spec)` stores it, `#promote` and `#play` both hand it to `playEvent` | M / low |
|
|
23
|
+
| 13 | MEDIUM | Three hand-written "dynamic BVH of handle-addressed objects" wrappers inside one domain | `engine/sound/simulation/core/AcousticOccluderIndex.js:40-122`; `engine/sound/simulation/core/AcousticRegionIndex.js:23-134`; `engine/sound/ecs/audio/SpatialAudioIndex.js:24-138` | Identical skeleton in all three: `#bvh = new BVH()`, a parallel `BvhClient` store, retire-a-slot-by-null, a `#fitLeaf(client, x)` that ends in `client.resize(x0…z1)`, a reused `#candidates` gather buffer, and a query that maps returned handles back to user objects. Only the leaf-bounds policy and the refit trigger (reactive `onChanged` / per-frame `refit()` / explicit `setTransform`) differ | UNJUSTIFIED | `core/bvh2/bvh3/BvhObjectIndex.js` — typed container holding `T[]` + clients, with `computeBounds(out, item)` supplied by the owner; the three sound indices become thin policy classes | M / medium |
|
|
24
|
+
| 13b | LOW | AABB union re-derived | `engine/sound/simulation/core/AcousticOccluderIndex.js:252-279` `computeWorldBounds` | Hand-written 6-component min/max fold; `core/geom/3d/aabb/aabb3_array_combine.js` does exactly this | UNJUSTIFIED | `aabb3_array_combine` | XS / none |
|
|
25
|
+
| 14 | MEDIUM | Trilinear sampling of a Float32Array 3D scalar grid re-implemented | `engine/sound/simulation/probe/probe_sdf_grid.js:104-122` `grid_sample_trilinear` (+ `clampInt` at `:181-183`) | `engine/graphics/texture/3d/scs3d_sample_linear.js:15` is the same function: same `z·(rx·ry) + y·rx + x` layout, same edge clamping, same trilinear blend. `SingleChannelSampler3D` is the matching container. `probe_sdf_grid.js:1` already imports `clamp` yet still hand-rolls `clampInt` | UNJUSTIFIED | Back `SdfGrid` with `SingleChannelSampler3D` and call `scs3d_sample_linear`; delete `clampInt` | S / low |
|
|
26
|
+
| 15 | LOW | `{type: ctor.typeName, data: x.toJSON()}` wrapper written 3× with two different `constructor` lookups | `engine/sound/material/concrete/json/serializeSoundMaterialToJSON.js:1-6` (`material.constructor.typeName`); `engine/sound/sopra/serialization/sopraJSON.js:22-27` `serializeAudioClipToJSON` and `:68-73` `serializeAudioEffectToJSON` (`Object.getPrototypeOf(x).constructor.typeName`) | Brief A [70×2], C sim=1. The **switch-based deserializers** (`deserializeSoundMaterialFromJSON.js:12`, `sopraJSON.js:33`, `sopraJSON.js:79`) are **JUSTIFIED** — the alternative in core (`core/json/abstractJSONDeserializer.js`) is precisely the string-keyed `Map<string, fn>` registry the house rules reject, and `sopraJSON.js:11-16` documents deliberate call-time class binding to survive an import cycle. Only the three one-line serializers are gratuitous, and they disagree on how to reach the constructor | UNJUSTIFIED (serializers only) | One `type_tagged_to_json(value)` helper (typed, no registry); pick one constructor lookup | XS / none |
|
|
27
|
+
| 16 | LOW | `dB2Volume` re-derived inline in a file that already imports it | `engine/sound/sopra/runtime/EventInstance.js:33-35` `dbTiltToGain` → `Math.pow(10, -excessDb / 20)`; `dB2Volume` imported at `:2` | Called 4× per acoustic instance per frame (`:542-543`, `:561-562`) — not per-sample, so no inlining justification | UNJUSTIFIED | `excessDb <= 0 ? 1 : dB2Volume(-excessDb)` | XS / none |
|
|
28
|
+
| 17 | LOW | `BusGraph` repeats the same lookup-or-throw preamble 6× | `engine/sound/sopra/runtime/BusGraph.js:143` `getInput`, `:159` `getOutput`, `:173` `getVolume`, `:187` `setVolume`, `:214` `rampVolume`, `:232` `approachVolume` | Six copies of `const bus = this.buses.get(id); if (bus === undefined) { throw new Error(\`sopra: no bus '${id}'\`); }` — ~30 lines | UNJUSTIFIED | A `#bus(id)` private that does the lookup + throw | XS / none |
|
|
29
|
+
| 18 | LOW / PROMOTION | The `setFlag`/`clearFlag`/`writeFlag`/`getFlag` quad, byte-identical in **35 files** | In this domain: `engine/sound/ecs/emitter/SoundEmitter.js:156-189`; `engine/sound/ecs/emitter/SoundTrack.js:138-171`; `engine/sound/asset/SoundAssetPlaybackSpec.js`; `engine/ecs/speaker/Voice.js:13-46` | Brief E (30-file window). `grep -l "clearFlag(flag) {"` returns 35 files spanning core/, engine/ecs, graphics, physics, sound, view | PROMOTION (codebase-wide, not sound-specific — flagged for the cross-domain roll-up, not for a sound-local fix) | A `FlagCarrier` base (or a shared `flags_set/clear/write/read` quartet on the flat `flags` word); JS single inheritance makes the base awkward where a class already extends, so the function quartet is the safer shape | L / medium |
|
|
30
|
+
| 19 | LOW | Well-conditioned perpendicular-axis construction re-derived | `engine/sound/simulation/probe/probe_detect_portals.js:170-184` `throatIsAperture` | Hand-rolls "cross with world-up, swap reference if `|ey| > 0.9`, normalize". `core/geom/vec3/v3_orthonormal_matrix_from_normal.js` builds exactly this stable basis and is already used twice in this same package (`OcclusionSolver.js:48`, `bakeReverbBands.js:74`) | UNJUSTIFIED | `v3_orthonormal_matrix_from_normal` and read the tangent column | XS / low |
|
|
31
|
+
| 20 | LOW | Vector normalize written by hand, inconsistently with the same file | `engine/sound/simulation/AcousticSimulator.js:598-621` `#directivityGainToward` (`Math.sqrt(dx*dx+dy*dy+dz*dz)` at `:605`) vs `:454, :455, :459, :460` in `#applyPath` (`Math.hypot`) | Brief F `inline_length3`. Cold path (once per non-omni source per frame). `v3_length` / `v3_normalize_array` exist; the file already imports `line3_compute_segment_closest_point_t` from the same family | UNJUSTIFIED (cold) | `v3_length`, or at least pick one of `Math.hypot`/`sqrt` within the file | XS / none |
|
|
32
|
+
| 21 | LOW / PROMOTION | Union-find connected components over probe connectivity written twice | `engine/sound/simulation/probe/probe_visibility_bridge_components.js:35-39` (from an `{a,b}` edge list); `engine/sound/simulation/probe/probe_refine_connectivity.js:50-56` (from a `CSRGraph`) | Both then use `uf_find(parent, i) === uf_find(parent, j)` as the same-component predicate. `core/graph/graph_compute_disconnected_clusters.js` exists but only for the object-graph `Graph`, not `CSRGraph` | PROMOTION | `core/graph/csr/csr_graph_connected_components(parent, graph)` beside `csr_graph_build_distance_field`; the edge-list variant folds into finding 2's builder | S / low |
|
|
33
|
+
|
|
34
|
+
## Strangler-migration debt
|
|
35
|
+
|
|
36
|
+
Context, not a defect list. Legacy-vs-sopra parallelism is the deliberate migration shape; what matters is
|
|
37
|
+
where the seam has stalled and where a fix would have to land twice.
|
|
38
|
+
|
|
39
|
+
| Legacy | sopra / native counterpart | Is legacy still wired? | Note |
|
|
40
|
+
|---|---|---|---|
|
|
41
|
+
| `ecs/emitter/SoundEmitterSystem` + `SoundEmitterComponentContext` | `ecs/audio/AudioEmitterSystem` + `AudioEmitter` + `LiveEmitterSet` | **YES** — `mir-engine/model/game/makeMirEngineConfig.js:182` | The seam is genuinely healthy here: legacy is a *translator* (`sopra/legacy/soundEmitterToEventDescription.js`) onto sopra, not a second renderer. Conversely `AudioEmitter*` has **zero** consumers outside `engine/sound/` — the new path is unused in the game |
|
|
42
|
+
| `ecs/SoundControllerSystem` | none — its own JSDoc names a *"forthcoming `AudioEventTrigger`"* (`SoundControllerSystem.js:27-30`) | **YES** — `makeMirEngineConfig.js:183` | `@deprecated` and still registered with no replacement in the tree. Findings 11/11b live here |
|
|
43
|
+
| `material/**` (`AbstractSoundMaterialDefinition`, `Single`/`Random`/`Composition`, `json/`) | sopra clips — `SwitchContainerAudioClip.js:11` *"Supersedes the dormant material/ surface-switch concept"*; `BlendContainerAudioClip.js:13` *"Supersedes the dormant material/ composition concept"* | **NO** — grep finds zero references outside `engine/sound/material/` (only a doc mention in `sopraJSON.js:13`) | Dead but public surface (meep is a library). Origin of the JSON copies in finding 15 |
|
|
44
|
+
| `SoundEmitterChannels` (`effects`/`music`/`ambient`) | sopra `SopraDefaultBus` + `BusDefinition` | **YES** — 7 app files import it (UnitMaker, StoryManager, WorldEventManager, …) | Already proxied 1:1: `SoundEmitterSystem.get/setChannelVolume` (`:91-102`) forwards to `sopra.get/setBusVolume` |
|
|
45
|
+
| `ecs/SoundListenerSystem` (WebAudio `AudioListener` pose) | `sopra.setListener(position)` inside the emitter systems | **YES** — `makeMirEngineConfig.js:184` | THREE readers of the same `SoundListener`→`Transform` relation. sopra's listener is position-only; the WebAudio listener also carries orientation. See finding 3 |
|
|
46
|
+
| `ProbeReverbRenderer` (scalar) | `FoaProbeReverbRenderer` (directional/B-format) | scalar is what `configureAcousticSimulation.js:31` documents; FOA is referenced only by `prototypeAcousticSolver.js:515` + its spec | Neither is deprecated; both live. Findings 6/8 mean an IR or crossfade fix must land in both |
|
|
47
|
+
| `ecs/emitter/SoundPanningModelType` | `sopra/definition/SopraPanningModel` | **YES** — translated by `soundEmitterToEventDescription.js:57-61` | Identical 2-value enums; translation is one ternary. Fine |
|
|
48
|
+
| `SoundAttenuationFunction` + `core/math/physics/irradiance/interpolate_irradiance_*` | authored `AnimationCurve` via `sopra/util/buildAttenuationCurve.js` | **YES** | This is the *good* seam: the legacy falloffs are REUSED to sample the new curve rather than reimplemented. Called out as a model, not debt |
|
|
49
|
+
|
|
50
|
+
## Dismissed leads
|
|
51
|
+
|
|
52
|
+
- Brief A/C `makePhysicsGroundResolver` / `recomputeMove` / `bindMoveKey` / `buildInputBindings` / `logHelp` (`engine/control/first-person/prototype_first_person_controller.js` ⟷ `engine/sound/simulation/prototypeAcousticSolver.js`) — both sites are `prototype*.js` harnesses, out of scope. **One line as instructed:** a real shared debug fly/walk camera *with* a physics ground resolver would be worth promoting to `engine/control/`, since it is now copied verbatim for every new subsystem demo.
|
|
53
|
+
- `DuckingRuleSerializationAdapter` / `CompressorEffectSerializationAdapter` / `ParameterDefinitionSerializationAdapter` `serialize`+`deserialize` (brief A [50×3] and [45×3]) — read all three: different field names and types (`writeUTF8String`+3×float32+float32 / 5×float32 / string+3×float32+uint8), zero shared logic. Exactly the per-class shape boilerplate the house rules bless.
|
|
54
|
+
- `SequenceContainerAudioClipSerializationAdapter.deserialize` ⟷ `SwitchContainerAudioClipSerializationAdapter.deserialize` (brief C sim=0.781) — same verdict; Switch additionally reads `parameter`/`defaultValue`.
|
|
55
|
+
- The container adapters' repeated `gain`/`pitch` writes and children loop (brief E) — wire order is per-class protocol; hoisting it into a base couples four independent binary formats. Deliberate.
|
|
56
|
+
- `MixerSnapshotSerializationAdapter` bus-entry codec vs `BusDefinitionSerializationAdapter` sends codec — structurally identical (`string` + `float32` pairs), semantically distinct record types. Shape only.
|
|
57
|
+
- `engine/ecs/speaker/Voice.js` vs `engine/sound/sopra/runtime/Voice.js` (brief G) — pure name collision. The first is a speech-bubble ECS component (flags only, `Voice.serializable = false`); the second is a WebAudio `AudioBufferSourceNode` holder. No shared logic beyond the flag quad (finding 18). The `speaker/` package has no importers, but meep is a library — not a defect.
|
|
58
|
+
- `echogram_band_rt60.js:49` `10 * Math.log10(edc / total)` — **energy** dB, deliberately not `volume2dB`'s amplitude `20·log10`. Correct as written.
|
|
59
|
+
- Brief F `inline_lerp` in `probe_sdf_grid.js:118-121` — the innermost trilinear blend; folds into finding 14 anyway. `AcousticSimulator.js:453, :458` and `probe_detect_portals.js:94, :109` are 3-component point-on-segment evaluations, not scalar `lerp` calls.
|
|
60
|
+
- Brief F `inline_length3`, the remaining ~14 hits — hot per-ray / per-edge / per-candidate-pair loops (`OcclusionSolver.js:79` per ray, `bakeVisibility.js:106` per graph edge, `probe_visibility_augment.js:92,108,111` per 2-ring chord). Inlining is the right call there; only the cold `AcousticSimulator` site (finding 20) is worth changing.
|
|
61
|
+
- Brief F `epsilon_compare` `ProbeReverbRenderer.js:198-200` — a tunable re-bake hysteresis (`changeThreshold = 0.1 s`), not float-tolerance equality. `epsilonEquals` would be semantically wrong.
|
|
62
|
+
- Brief F `binary_search_inline` `probe_visibility_augment.js:171` — folded into finding 2.
|
|
63
|
+
- `EventInstance.js:47-92` `build3BandCrossover` / `set3BandGains` / `disconnect3Band` — already correctly factored and shared by BOTH the direct and corner-leak paths (`:457`, `:476`). No duplication; a model for finding 8.
|
|
64
|
+
- `ProbeFieldPathingSolution.js:62` correctly delegates to `csr_graph_build_distance_field`, and `AcousticProbeField.shortestPath:519` to `csr_graph_find_shortest_path`. **No Dijkstra / graph-search duplication in `simulation/`** — the probe pathing is a clean `core/graph` consumer.
|
|
65
|
+
- No hand-rolled biquad coefficient math, equal-power panning law, or crossfade curve anywhere in the domain — WebAudio nodes (`createBiquadFilter`, `PannerNode`, `linearRampToValueAtTime`) do all of it. The one filter-adjacent formula, the one-pole coefficient `1 − e^(−2πf/fs)`, is finding 6.
|
|
66
|
+
- The 3-band value classes (`AcousticMedium`, `AcousticMaterial`, `AcousticSolution`, `AcousticSourceState`) unroll their 3-element copies by hand in `equals`/`clone`/`hash`/`toJSON` — typed, explicit, per-field. Consistent with "typed over magic"; not duplication of *logic*.
|
|
67
|
+
|
|
68
|
+
## Notes — why duplication accumulates here
|
|
69
|
+
|
|
70
|
+
1. **Three generations coexist in one package.** `material/` (dead, superseded), `ecs/emitter/` (legacy, wired,
|
|
71
|
+
now a translator), and `sopra/` + `ecs/audio/` (new, unwired outside the engine). Each generation
|
|
72
|
+
re-solved the same problems — polymorphic type-tagged JSON, weighted random selection, composition
|
|
73
|
+
with per-child weights — because the previous one was not being deleted. The three JSON dispatchers
|
|
74
|
+
(finding 15) and the material-vs-clip parallel hierarchy are the visible residue.
|
|
75
|
+
|
|
76
|
+
2. **The migration seam stalled at the game boundary, not inside the engine.** Inside `engine/sound/`
|
|
77
|
+
the strangler is genuinely done — sopra is the single renderer and legacy is a translator. But
|
|
78
|
+
`makeMirEngineConfig.js` still registers the three legacy systems and nothing registers
|
|
79
|
+
`AudioEmitterSystem`, so the new path's code has no production caller keeping it honest. That is
|
|
80
|
+
what let finding 3 (two systems each owning the sopra tick) and finding 12 (two copies of the
|
|
81
|
+
play-options mapping) survive: only one of each pair actually runs.
|
|
82
|
+
|
|
83
|
+
3. **`core/graph/csr/` ships consumers but no producer.** It has Dijkstra, distance fields, and edge
|
|
84
|
+
lookup, but no `from_edge_list` builder — so the probe bake writes its own twice (finding 2), and the
|
|
85
|
+
companions it also needs (connected components, canonical edge key) got written locally 2–4× each
|
|
86
|
+
(findings 10, 21). One missing primitive generated four findings.
|
|
87
|
+
|
|
88
|
+
4. **Ray-scratch locality beats reuse.** Every module that casts a shadow ray declares its own
|
|
89
|
+
module-static `Ray3` and inlines the normalize-and-set sequence (finding 1). The pattern is
|
|
90
|
+
allocation-free by construction, which makes the copy feel cheap, but it means the "one shared
|
|
91
|
+
visibility test" the code documents does not actually exist.
|
|
92
|
+
|
|
93
|
+
5. **Per-sample DSP genuinely justifies inlining — but the justification got over-applied.** The IR
|
|
94
|
+
synthesis kernels (findings 6, 7) share far more than the inner loop: window sizing, filter
|
|
95
|
+
coefficients, and envelope shape are all per-*bake* work that could be shared without a per-sample
|
|
96
|
+
call. The `EventInstance` crossover helpers show the team already knows how to draw that line
|
|
97
|
+
correctly; the reverb bakers just predate it.
|
|
98
|
+
|
|
99
|
+
6. **The base class stops one level short.** `AbstractAudioClip` folds `gain`/`pitch` into `hash()`, and
|
|
100
|
+
`ContainerAudioClip` folds `children` into `equals()` — but not into `hash()`. Four subclasses filled
|
|
101
|
+
the gap by copy-paste, and one of them (Blend) has already drifted (finding 4). The same shape
|
|
102
|
+
appears in the container adapters, where the base clip fields are re-written per subclass.
|