@woosh/meep-engine 3.60.3 → 3.60.5
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/CHANGELOG.md +2602 -0
- package/CONTRIBUTING.md +7 -0
- package/MIGRATION_2_TO_3.md +136 -0
- package/SECURITY.md +4 -0
- package/editor/EditorCameraNavigation.d.ts +2 -1
- package/editor/EditorCameraNavigation.d.ts.map +1 -1
- package/editor/EditorCameraNavigation.js +51 -57
- package/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts +3 -0
- package/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts.map +1 -1
- package/editor/ecs/component/editors/ecs/Trail3DEditor.js +6 -0
- package/package.json +6 -2
- package/src/engine/EngineHarness.d.ts +3 -9
- package/src/engine/EngineHarness.d.ts.map +1 -1
- package/src/engine/EngineHarness.js +6 -8
- package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts +9 -6
- package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts.map +1 -1
- package/src/engine/graphics/camera/makeOrbitalCameraController.js +44 -71
- package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts +64 -0
- package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts.map +1 -0
- package/src/engine/graphics/camera/orbital/MomentumAxis.js +187 -0
- package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts +86 -0
- package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts.map +1 -0
- package/src/engine/graphics/camera/orbital/OrbitalCameraRig.js +319 -0
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts +34 -10
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +116 -30
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts +1 -7
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +1 -37
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +1 -2
- package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.d.ts +14 -0
- package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.d.ts.map +1 -0
- package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.js +22 -0
- package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts +19 -3
- package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail3d/Trail3D.js +31 -4
- package/src/engine/graphics/ecs/trail3d/advance_trail.js +1 -1
- package/src/engine/graphics/ecs/water/Water.d.ts +8 -0
- package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
- package/src/engine/graphics/ecs/water/Water.js +8 -0
- package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts +5 -5
- package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts.map +1 -1
- package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js +6 -6
- package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/WaterSystem.js +731 -734
- package/src/engine/graphics3/decal/DecalTileShader.d.ts +61 -0
- package/src/engine/graphics3/decal/DecalTileShader.d.ts.map +1 -0
- package/src/engine/graphics3/decal/DecalTileShader.js +138 -0
- package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts +10 -4
- package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts.map +1 -1
- package/src/engine/graphics3/decal/GPUDecalRenderer.js +149 -44
- package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts +25 -0
- package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts.map +1 -0
- package/src/engine/graphics3/decal/chunk_decal_tile_vertex.js +55 -0
- package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts +22 -0
- package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts.map +1 -0
- package/src/engine/graphics3/decal/graph_gather_decal_tiles.js +70 -0
- package/src/engine/graphics3/decal/shader_decal_composite.d.ts +7 -5
- package/src/engine/graphics3/decal/shader_decal_composite.d.ts.map +1 -1
- package/src/engine/graphics3/decal/shader_decal_composite.js +48 -44
- package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts +12 -0
- package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts.map +1 -0
- package/src/engine/graphics3/decal/shader_decal_tile_copy.js +78 -0
- package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts +11 -0
- package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts.map +1 -0
- package/src/engine/graphics3/decal/shader_gather_decal_tiles.js +80 -0
- package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts +10 -0
- package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts.map +1 -1
- package/src/engine/graphics3/trail/trail_tube_write_geometry.js +13 -1
- package/src/engine/input/devices/mouse/MouseEventButtons.d.ts +13 -0
- package/src/engine/input/devices/mouse/MouseEventButtons.d.ts.map +1 -0
- package/src/engine/input/devices/mouse/MouseEventButtons.js +23 -0
- package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts +4 -0
- package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts.map +1 -1
- package/src/engine/input/ecs/controllers/KeyboardCameraController.js +9 -45
- package/src/engine/physics/cloth/ecs/cloth_geodesic.d.ts +2 -2
- package/src/engine/physics/cloth/ecs/cloth_geodesic.js +2 -2
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/shade/playground/probe_clipmap/README.md +1 -0
- package/src/shade/playground/probe_clipmap/index.html +5 -0
- package/src/shade/playground/probe_clipmap/main.d.ts.map +1 -1
- package/src/shade/playground/probe_clipmap/main.js +16 -2
- package/src/shade/playground/vgeo_scene/README.md +1 -1
- package/src/shade/playground/water_refraction/README.md +5 -6
- package/src/shade/playground/water_refraction/index.html +1 -1
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +7 -5
- package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts +4 -6
- package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts.map +1 -1
- package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.js +34 -36
- package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts +1 -1
- package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts.map +1 -1
- package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.js +162 -163
- package/src/shade/renderer/atmosphere/compute_mie_absorption.d.ts +1 -1
- package/src/shade/renderer/atmosphere/compute_mie_absorption.js +1 -1
- package/src/shade/renderer/atmosphere/hillaire/ATMOSPHERE_EARTH.js +1 -1
- package/src/shade/renderer/atmosphere/hillaire/ATMOSPHERE_PARAMETERS_STRUCT.js +1 -1
- package/src/shade/renderer/atmosphere/shader_multiscatter_lut.js +164 -164
- package/src/shade/renderer/atmosphere/shader_sky_irradiance_lut.js +1 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts +40 -80
- package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts.map +1 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraController.js +197 -147
- package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
- package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
- package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
- package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
- package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +4 -4
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +4 -4
- package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts +2 -1
- package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts.map +1 -1
- package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.js +18 -1
- package/src/shade/renderer/geometry/sdf/cards/mesh_cards_raster_pass_descriptor.js +2 -2
- package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_histogram.d.ts +1 -1
- package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_histogram.js +1 -1
- package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.d.ts +4 -5
- package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.d.ts.map +1 -1
- package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.js +6 -7
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts +31 -0
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.js +343 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts +17 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.js +29 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.js +7 -13
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +2 -8
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts +5 -5
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.js +10 -10
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts +12 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.js +22 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +4 -4
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +50 -13
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +6 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +7 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.d.ts +13 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.js +13 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts +17 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +20 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts +5 -15
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.js +6 -34
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.d.ts +18 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.js +42 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.js +5 -4
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts +4 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.js +13 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.d.ts +52 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.js +250 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +1 -7
- package/src/shade/renderer/hiz/HZBFootprint.d.ts +14 -0
- package/src/shade/renderer/hiz/HZBFootprint.d.ts.map +1 -0
- package/src/shade/renderer/hiz/HZBFootprint.js +16 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts +10 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts.map +1 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.js +11 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts +10 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts.map +1 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.js +11 -0
- package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +0 -7
- package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
- package/src/shade/renderer/hiz/HierarchicalZBuffer.js +405 -350
- package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts +12 -0
- package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts.map +1 -0
- package/src/shade/renderer/hiz/chunk_hzb_footprint_x.js +27 -0
- package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts +12 -0
- package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts.map +1 -0
- package/src/shade/renderer/hiz/chunk_hzb_footprint_y.js +27 -0
- package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts +14 -0
- package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts.map +1 -0
- package/src/shade/renderer/hiz/hzb_footprint_texels.js +34 -0
- package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts +0 -5
- package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts.map +1 -1
- package/src/shade/renderer/hiz/shader_hzb_reduce_any.js +37 -57
- package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts +11 -0
- package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts.map +1 -0
- package/src/shade/renderer/hiz/{shader_hzb_reduce_mip0_clip.js → shader_hzb_reduce_mip0.js} +120 -111
- package/src/shade/renderer/light/environment/environment_level_1_blur_plan.d.ts +39 -0
- package/src/shade/renderer/light/environment/environment_level_1_blur_plan.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/environment_level_1_blur_plan.js +119 -0
- package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts +15 -7
- package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/execute_filter_environment_map.js +66 -83
- package/src/shade/renderer/light/environment/graph_filter_environment_map.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/graph_filter_environment_map.js +15 -6
- package/src/shade/renderer/light/environment/shader_blur_environment_map.d.ts +3 -0
- package/src/shade/renderer/light/environment/shader_blur_environment_map.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/shader_blur_environment_map.js +111 -0
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.d.ts +3 -0
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.js +128 -0
- package/src/shade/renderer/light/environment/shader_downsample_environment_map.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/shader_downsample_environment_map.js +0 -1
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/material/GPUMaterialManager.d.ts +7 -0
- package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
- package/src/shade/renderer/material/GPUMaterialManager.js +11 -0
- package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +12 -12
- package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
- package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +17 -118
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -3
- package/src/shade/renderer/postprocess/nss/NSS.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.js +1 -3
- package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.d.ts +5 -6
- package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.js +9 -10
- package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.js +48 -49
- package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.js +12 -16
- package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.js +246 -246
- package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts +1 -2
- package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.js +34 -35
- package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.d.ts +2 -2
- package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.js +2 -2
- package/src/shade/renderer/shader/chunk/atmosphere/chunk_compute_extinction.js +1 -1
- package/src/shade/renderer/shader/chunk/atmosphere/hillaire/DENSITY_PROFILE_LAYER_STRUCT.d.ts +1 -1
- package/src/shade/renderer/shader/chunk/atmosphere/hillaire/DENSITY_PROFILE_LAYER_STRUCT.js +1 -1
- package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_Schlick.d.ts +3 -3
- package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_Schlick.js +3 -3
- package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.d.ts +9 -10
- package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.js +9 -10
- package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_tonemap_ue_filmic.d.ts +4 -4
- package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_tonemap_ue_filmic.js +5 -5
- package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.d.ts +1 -2
- package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.js +1 -2
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.d.ts +14 -1
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.js +19 -6
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.d.ts +10 -1
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.js +14 -6
- package/src/shade/renderer/shader/chunk/random/murmur/chunk_murmur3_vec3u.d.ts +1 -1
- package/src/shade/renderer/shader/chunk/random/murmur/chunk_murmur3_vec3u.js +1 -1
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +7 -2
- package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.d.ts +2 -3
- package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.js +7 -8
- package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_LOAD_FACTOR.d.ts +1 -1
- package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_LOAD_FACTOR.js +12 -12
- package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts +2 -2
- package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/VSMPageManager.js +3 -4
- package/src/shade/renderer/water/GPUWaterRenderer.d.ts +0 -6
- package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
- package/src/shade/renderer/water/GPUWaterRenderer.js +12 -17
- package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts +35 -0
- package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts.map +1 -0
- package/src/shade/renderer/water/medium/graph_water_meshlets.js +89 -0
- package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
- package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
- package/src/shade/renderer/water/shader_water.js +22 -13
- package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -1
- package/src/shade/renderer/water/shader_water_avboit.js +7 -7
- package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +1 -1
- package/src/shade/renderer/water/surface/graph_water_tessellation.js +1 -1
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +9 -1
- package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts +0 -19
- package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts.map +1 -1
- package/src/view/elements/progress/segmented/SegmentedResourceBarView.js +2 -52
- package/editor/REWORK_PLAN_2026_08.md +0 -692
- package/src/CODEC_LAYOUT_VERIFICATION.md +0 -419
- package/src/CORRECTNESS_HUNT_2026_07_24.md +0 -142
- package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +0 -738
- package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +0 -79
- package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +0 -93
- package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +0 -80
- package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +0 -164
- package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +0 -69
- package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +0 -110
- package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +0 -129
- package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +0 -72
- package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +0 -74
- package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +0 -169
- package/src/DUPLICATION_AUDIT_2026_08_07.md +0 -254
- package/src/DUPLICATION_AUDIT_REMEDIATION.md +0 -250
- package/src/DUPLICATION_REVIEW_2026_10_24.md +0 -1072
- package/src/REVIEW_2026_08_06/adv-core.md +0 -511
- package/src/REVIEW_2026_08_06/adv-gfx.md +0 -480
- package/src/REVIEW_2026_08_06/adv-view.md +0 -446
- package/src/REVIEW_2026_08_06/api-core.md +0 -296
- package/src/REVIEW_2026_08_06/api-engine.md +0 -195
- package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +0 -507
- package/src/REVIEW_2026_08_06/core-binary.md +0 -348
- package/src/REVIEW_2026_08_06/core-collection.md +0 -376
- package/src/REVIEW_2026_08_06/core-math.md +0 -221
- package/src/REVIEW_2026_08_06/core-process.md +0 -266
- package/src/REVIEW_2026_08_06/core-science.md +0 -223
- package/src/REVIEW_2026_08_06/engine-ai-nav.md +0 -279
- package/src/REVIEW_2026_08_06/engine-animation.md +0 -167
- package/src/REVIEW_2026_08_06/engine-asset.md +0 -166
- package/src/REVIEW_2026_08_06/engine-ecs.md +0 -920
- package/src/REVIEW_2026_08_06/engine-input.md +0 -197
- package/src/REVIEW_2026_08_06/engine-misc.md +0 -248
- package/src/REVIEW_2026_08_06/engine-terrain.md +0 -52
- package/src/REVIEW_2026_08_06/generation.md +0 -176
- package/src/REVIEW_2026_08_06/geom-2d.md +0 -434
- package/src/REVIEW_2026_08_06/geom-3d.md +0 -117
- package/src/REVIEW_2026_08_06/gfx-gi.md +0 -143
- package/src/REVIEW_2026_08_06/gfx-render.md +0 -740
- package/src/REVIEW_2026_08_06/gfx-texture.md +0 -690
- package/src/REVIEW_2026_08_06/prior-criticals.md +0 -49
- package/src/REVIEW_2026_08_06/prior-highs.md +0 -138
- package/src/REVIEW_2026_08_06/test-gaps.md +0 -364
- package/src/REVIEW_2026_08_06/view.md +0 -667
- package/src/REVIEW_2026_08_06.md +0 -610
- package/src/core/binary/lz4/LZ4_HIGH_COMPRESSION_PLAN_2026_09_14.md +0 -542
- package/src/core/bvh2/bvh3/NOTES.md +0 -2
- package/src/core/bvh8/NOTES.md +0 -63
- package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +0 -243
- package/src/core/color/COLOR_FIX_PLAN_2026_08_28.md +0 -417
- package/src/core/color/COLOR_REVIEW_2026_08_28.md +0 -779
- package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +0 -433
- package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +0 -486
- package/src/core/geom/3d/atlas/BFF_PLAN.md +0 -174
- package/src/core/geom/3d/atlas/REVIEW_02.md +0 -392
- package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +0 -146
- package/src/core/geom/3d/gjk/NOTES.md +0 -8
- package/src/core/geom/3d/topology/TOPO_MESH_RETIREMENT_PLAN.md +0 -135
- package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +0 -381
- package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +0 -684
- package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +0 -554
- package/src/core/model/reactive/evaluation/PREDICATE_APPLICABILITY_PROPOSAL.md +0 -446
- package/src/engine/REVIEW_2026_07_03.md +0 -211
- package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +0 -815
- package/src/engine/control/first-person/DESIGN.md +0 -658
- package/src/engine/control/first-person/DESIGN_COLLISION.md +0 -382
- package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +0 -563
- package/src/engine/control/first-person/TODO.md +0 -160
- package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +0 -531
- package/src/engine/ecs/HIERARCHY_REVIEW.md +0 -717
- package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +0 -262
- package/src/engine/ecs/ik/PLAN.md +0 -179
- package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +0 -272
- package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +0 -199
- package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +0 -521
- package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +0 -463
- package/src/engine/graphics/ecs/camera/pp/NOTES.md +0 -48
- package/src/engine/graphics/render/frame_graph/CORRECTNESS_FIXES_2026_09_04.md +0 -659
- package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +0 -235
- package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +0 -490
- package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +0 -466
- package/src/engine/input/GAMEPAD_API_PROPOSAL.md +0 -717
- package/src/engine/input/ecs/ism/ISM_DESIGN.md +0 -1523
- package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +0 -627
- package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +0 -661
- package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +0 -229
- package/src/engine/network/CONGESTION_CONTROL.md +0 -87
- package/src/engine/network/MESSAGE_TRANSPORT.md +0 -364
- package/src/engine/network/SMOOTHNESS_DESIGN.md +0 -400
- package/src/engine/physics/cloth/MEASUREMENTS.md +0 -5304
- package/src/engine/physics/cloth/PLAN.md +0 -3056
- package/src/engine/physics/fluid/GPU_FLUID_CASCADES_PROPOSAL_2026_10_16.md +0 -1382
- package/src/engine/physics/fluid/REVIEW_02_PLAN.md +0 -243
- package/src/engine/physics/fluid/solver/NOTES.md +0 -46
- package/src/engine/sound/SOUND_EMITTER_RETIREMENT_PLAN.md +0 -536
- package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +0 -432
- package/src/engine/sound/simulation/PROBE_PLACEMENT.md +0 -250
- package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +0 -322
- package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +0 -261
- package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +0 -121
- package/src/engine/sound/sopra/IMPLEMENTATION_PLAN.md +0 -999
- package/src/format/image/avif/DECISIONS.md +0 -545
- package/src/format/image/jpeg/NOTES.md +0 -3
- package/src/format/scene/gltf/MESHOPT_COMPRESSION_PLAN.md +0 -814
- package/src/generation/COORDINATES.md +0 -54
- package/src/generation/REVIEW_01_ACTION_PLAN.md +0 -628
- package/src/generation/foliage/ANNUAL_SHOOTS_BRIEF_2026_10_25.md +0 -220
- package/src/generation/foliage/CROWN_FILLING_BRIEF_2026_10_26.md +0 -172
- package/src/generation/foliage/CROWN_FULLNESS_BRIEF_2026_10_26.md +0 -86
- package/src/generation/foliage/FOLIAGE_DESIGN.md +0 -547
- package/src/generation/foliage/FOLIAGE_PLAN.md +0 -198
- package/src/generation/foliage/LEAF_ARCHITECTURE_BRIEF_2026_10_26.md +0 -158
- package/src/generation/foliage/LOD_REWORK_BRIEF_2026_10_27.md +0 -521
- package/src/generation/foliage/REAL_TIME_TREES_BRIEF_2026_10_27.md +0 -86
- package/src/generation/foliage/SELF_ORGANIZING_FRAMEWORK_BRIEF_2026_10_26.md +0 -347
- package/src/generation/foliage/SHOOT_SYSTEMS_ALL_SPECIES_BRIEF_2026_10_26.md +0 -248
- package/src/generation/foliage/SPEEDTREE_COMPARISON_2026_10_24.md +0 -694
- package/src/generation/foliage/TWO_FRONT_ENDS_BRIEF_2026_10_28.md +0 -552
- package/src/shade/RENDERER_CONTRACT.md +0 -697
- package/src/shade/SHADER_F16_PROPOSAL_2026_10_24.md +0 -263
- package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +0 -569
- package/src/shade/device/PASS_ENCODER_PROPOSAL_2026_08_31.md +0 -755
- package/src/shade/device/mock/EXTENSION_PROPOSAL_2026_08_22.md +0 -458
- package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +0 -255
- package/src/shade/device/mock/REVIEW_2026_08_22.md +0 -527
- package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +0 -894
- package/src/shade/device/timing/profile/SGPT_FORMAT.md +0 -345
- package/src/shade/renderer/NOTES.md +0 -78
- package/src/shade/renderer/PLAN.md +0 -8
- package/src/shade/renderer/animation/ADDITIVE_BLENDING_PLAN.md +0 -145
- package/src/shade/renderer/animation/SKINNING_FRAME_PLAN.md +0 -357
- package/src/shade/renderer/atmosphere/NOTES.md +0 -6
- package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts +0 -76
- package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts.map +0 -1
- package/src/shade/renderer/camera/orbital/MomentumAxis.js +0 -164
- package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts +0 -45
- package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts.map +0 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.js +0 -66
- package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts +0 -98
- package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts.map +0 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraInput.js +0 -184
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +0 -175
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +0 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +0 -411
- package/src/shade/renderer/camera/orbital/README.md +0 -81
- package/src/shade/renderer/camera/orbital/index.d.ts +0 -6
- package/src/shade/renderer/camera/orbital/index.d.ts.map +0 -1
- package/src/shade/renderer/camera/orbital/index.js +0 -9
- package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +0 -964
- package/src/shade/renderer/fluid/GPU_FLUID_OCCUPANCY_AND_SOTA_REVIEW_2026_10_26.md +0 -361
- package/src/shade/renderer/geometry/bvh/BLAS_REFIT_BATCH_PLAN.md +0 -619
- package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +0 -674
- package/src/shade/renderer/geometry/sdf/NOTES.md +0 -6
- package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +0 -540
- package/src/shade/renderer/geometry/virtual/MICRON_COMPARISON_2026_08_20.md +0 -305
- package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +0 -913
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +0 -787
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +0 -483
- package/src/shade/renderer/geometry/virtual/VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md +0 -301
- package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +0 -448
- package/src/shade/renderer/geometry/virtual/runtime/CULL_PLAN_2026_09_16.md +0 -385
- package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +0 -656
- package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +0 -308
- package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +0 -657
- package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +0 -551
- package/src/shade/renderer/geometry/virtual/runtime/SHADOW_PLAN_2026_09_28.md +0 -536
- package/src/shade/renderer/global_illumination/lpv/NOTES.md +0 -3
- package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +0 -4225
- package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_RAYS_VARIABLE_AND_SORTED_SCOPE_2026_10_30.md +0 -410
- package/src/shade/renderer/global_illumination/sharc/NOTES.md +0 -8
- package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +0 -1369
- package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +0 -202
- package/src/shade/renderer/gpu_primitive/indirect/NOTES.md +0 -94
- package/src/shade/renderer/hiz/NOTES.md +0 -3
- package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts +0 -8
- package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts.map +0 -1
- package/src/shade/renderer/light/cluster/NOTES.md +0 -11
- package/src/shade/renderer/light/cluster/PROPOSAL_ALGORITHM_2026_10_16.md +0 -524
- package/src/shade/renderer/light/cluster/PROPOSAL_HIERARCHICAL_2026_10_16.md +0 -1101
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts +0 -13
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts.map +0 -1
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.js +0 -198
- package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts +0 -24
- package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts.map +0 -1
- package/src/shade/renderer/material/resident/shader_resident_texture_means.js +0 -120
- package/src/shade/renderer/particles/DESIGN.md +0 -1011
- package/src/shade/renderer/particles/EMITTER_GLOBAL_STATE_PLAN_2026_10_29.md +0 -283
- package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +0 -453
- package/src/shade/renderer/path_tracer/PATH_TRACED_MEDIA_PLAN_2026_10_02.md +0 -520
- package/src/shade/renderer/postprocess/bloom/NOTES.md +0 -6
- package/src/shade/renderer/postprocess/fsr/NOTES.md +0 -4
- package/src/shade/renderer/postprocess/gtao/NOTES.md +0 -6
- package/src/shade/renderer/postprocess/nss/NOTES.md +0 -339
- package/src/shade/renderer/postprocess/perspective/NOTES.md +0 -3
- package/src/shade/renderer/postprocess/sscs/NOTES.md +0 -5
- package/src/shade/renderer/postprocess/ssr/NOTES.md +0 -14
- package/src/shade/renderer/postprocess/ssr/REPROJECTION_PROPOSAL.md +0 -286
- package/src/shade/renderer/postprocess/ssr/prefilter/NOTES.md +0 -39
- package/src/shade/renderer/postprocess/taa/NOTES.md +0 -11
- package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +0 -78
- package/src/shade/renderer/postprocess/vrs/NOTES.md +0 -4
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +0 -1218
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +0 -600
- package/src/shade/renderer/restir/reservoir/NOTES.md +0 -6
- package/src/shade/renderer/shader/chunk/atmosphere/NOTES.md +0 -6
- package/src/shade/renderer/shader/chunk/brdf/FRESNEL_BRIEF_2026_10_16.md +0 -394
- package/src/shade/renderer/shader/chunk/color/tonemap/ACES_TONE_MAPPING_BRIEF_2026_10_17.md +0 -160
- package/src/shade/renderer/shader/chunk/color/tonemap/HDR_TONE_MAPPING_FORMS_BRIEF_2026_10_16.md +0 -322
- package/src/shade/renderer/shader/chunk/color/tonemap/SDR_TONE_MAPPING_BRIEF_2026_10_16.md +0 -247
- package/src/shade/renderer/shader/chunk/fsr/NOTES.md +0 -3
- package/src/shade/renderer/shader/chunk/fsr/spd/NOTES.md +0 -4
- package/src/shade/renderer/shader/chunk/geometry/ray/NOTES.md +0 -4
- package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +0 -159
- package/src/shade/renderer/shader/chunk/mboit/NOTES.md +0 -38
- package/src/shade/renderer/shader/chunk/spherical_harmonics/l1i/NOTES.md +0 -3
- package/src/shade/renderer/shader/chunk/spherical_harmonics/sampling/NOTES.md +0 -8
- package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +0 -932
- package/src/shade/renderer/shadow/contact/FINDINGS.md +0 -413
- package/src/shade/renderer/shadow/map/csm/NOTES.md +0 -5
- package/src/shade/renderer/shadow/ray/NOTES.md +0 -31
- package/src/shade/renderer/shadow/sdf/NOTES.md +0 -9
- package/src/shade/renderer/shadow/sdf/brixelizer/NOTES.md +0 -5
- package/src/shade/renderer/shadow/sdf/cascade/NOTES.md +0 -9
- package/src/shade/renderer/shadow/vsm/NOTES.md +0 -12
- package/src/shade/renderer/shadow/vsm/PLAN.md +0 -442
- package/src/shade/renderer/shadow/vsm/REVIEW_LEDGER.md +0 -43
- package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +0 -2444
- package/src/shade/renderer/texture/mipmaps/NOTES.md +0 -4
- package/src/shade/renderer/volumetrics/NOTES.md +0 -1329
- package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +0 -336
- package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +0 -23
- package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +0 -1
- package/src/shade/renderer/water/medium/GPUWaterBodies.js +0 -75
- package/src/shade/wgsl/NOTES.md +0 -4
- package/src/view/elements/progress/segmented/segment_frame_transform.d.ts +0 -15
- package/src/view/elements/progress/segmented/segment_frame_transform.d.ts.map +0 -1
- package/src/view/elements/progress/segmented/segment_frame_transform.js +0 -69
|
@@ -1,1072 +0,0 @@
|
|
|
1
|
-
# Meep Engine Code Duplication & Logical Error Review (2026-10-24)
|
|
2
|
-
|
|
3
|
-
- **Date**: 2026-10-24
|
|
4
|
-
- **Scope**: `packages/meep-engine/src` (all **7,256 production source files** across `core/`, `engine/`, `format/`, `generation/`, `shade/`, and `view/`, excluding `*.spec.js` and `prototype*`/`test*` harnesses)
|
|
5
|
-
- **Focus**:
|
|
6
|
-
1. **Meaningful Code Duplication — Existing Function Reuse (`DUP-REUSE-*`)**: Places where an existing, tested engine function is re-implemented inline (often with subtle bugs, lower performance, or missing edge-case guards).
|
|
7
|
-
2. **Meaningful Code Duplication — Outlining New Logical Abstractions (`DUP-OUTLINE-*`)**: Places where substantial multi-step algorithms, shaders, or subsystem pipelines are duplicated across files and have begun to drift.
|
|
8
|
-
3. **Evaluated & Justified Hot-Path Inlining (`INLINE-*`)**: Intentional inlining and monomorphic specialization where abstraction is **not** recommended due to zero-allocation, register-pressure, or hot-loop constraints.
|
|
9
|
-
4. **Major Logical Errors & Correctness Bugs (`BUG-*`)**: High-impact functional and mathematical bugs discovered during the full-source review.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Summary of Findings
|
|
14
|
-
|
|
15
|
-
| Category | `HIGH` Confidence / Value | `MEDIUM` Confidence / Value | `LOW` Confidence / Value | Total |
|
|
16
|
-
| :--- | :---: | :---: | :---: | :---: |
|
|
17
|
-
| **1. Existing Function Reuse (`DUP-REUSE-*`)** | 11 | 9 | 3 | **23** |
|
|
18
|
-
| **2. Outlining New Logical Functions (`DUP-OUTLINE-*`)** | 11 | 12 | 4 | **27** |
|
|
19
|
-
| **3. Evaluated & Justified Inlining (`INLINE-*`)** | — | — | — | **8** *(Reference)* |
|
|
20
|
-
| **4. Major Logical Errors (`BUG-*`)** | 24 | 11 | 0 | **35** |
|
|
21
|
-
| **Total Actionable Findings** | **46** | **32** | **7** | **85** |
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## 1. Meaningful Code Duplication — Existing Function Reuse (`DUP-REUSE-*`)
|
|
26
|
-
|
|
27
|
-
### `HIGH` Confidence / Value
|
|
28
|
-
|
|
29
|
-
#### `[DUP-REUSE-01]` [HIGH] Broken UTF-8 Byte Length Re-implementation Instead of `utf8_encoded_length`
|
|
30
|
-
- **Duplicated In**: `core/primitives/strings/string_compute_byte_size.js:6-21`
|
|
31
|
-
- **Existing Function**: `core/binary/utf8/utf8_encoded_length.js:13-36` (`utf8_encoded_length`)
|
|
32
|
-
- **Analysis**:
|
|
33
|
-
`string_compute_byte_size(value)` is documented as `@returns {number} number of bytes required to store the string in UTF-8 encoding` (used by `core/function/FunctionCompiler.js`), but its implementation shifts the UTF-16 code unit by 8 bits (`while (c > 0xff) { p++; c >>= 8; } p++;`).
|
|
34
|
-
- For code points in `U+0080..U+00FF` (e.g. `'é'`, `0xE9`), `c > 0xff` is `false`, returning `1` instead of `2`.
|
|
35
|
-
- For 3-byte UTF-8 BMP code points in `U+0800..U+FFFF` (e.g. `'€'`, `0x20AC`), it shifts once and returns `2` instead of `3`.
|
|
36
|
-
`utf8_encoded_length(text)` in `core/binary/utf8/utf8_encoded_length.js` already computes the exact UTF-8 byte length in a single allocation-free pass (including surrogate pairs).
|
|
37
|
-
- **Hot-Path Assessment**: `utf8_encoded_length` is allocation-free and faster than the inner `while (c > 0xff)` loop.
|
|
38
|
-
- **Recommendation**: Delegate `string_compute_byte_size(value)` directly to `utf8_encoded_length(value)` (or replace callers with `utf8_encoded_length`).
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
#### `[DUP-REUSE-02]` [HIGH] N-D Hermite Spline Nearest-Point Solver Re-implements Monomial Conversion, Residual Accumulation & Root Finding
|
|
43
|
-
- **Duplicated In**: `core/math/spline/spline3_hermite_nearest_point.js:10-18, 73-167` (`spline3_hermite_nearest_point_array`, `polyval`)
|
|
44
|
-
- **Existing Functions**:
|
|
45
|
-
- `core/math/spline/spline3_hermite_to_monomial.js:32-35` (`spline3_hermite_to_monomial`)
|
|
46
|
-
- `core/math/linalg/cubic_residual_times_derivative_accumulate.js:25-41` (`cubic_residual_times_derivative_accumulate`)
|
|
47
|
-
- `core/math/linalg/polynomial_real_roots_in_interval.js:36-96` (`polynomial_real_roots_in_interval`)
|
|
48
|
-
- `core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.js:231-276` (`nearest_t_to_fixed_point`)
|
|
49
|
-
- **Analysis**:
|
|
50
|
-
`spline3_hermite_nearest_point_array` manually converts Hermite parameters to monomial coefficients (duplicating `spline3_hermite_to_monomial`), manually expands the quintic stationarity polynomial $\sum (p(t) - \text{ref}) p'(t)$ into a `Float32Array(6)` (duplicating `cubic_residual_times_derivative_accumulate` while truncating precision to `Float32`), defines a local `polyval` (duplicating `polynomial_evaluate`), and runs only 3 unguarded Newton steps (`NEWTON_STEPS = 3`) from 6 fixed seeds (`0, 0.2, 0.4, 0.6, 0.8, 1.0`), which can overshoot `[0, 1]` or miss roots between seeds.
|
|
51
|
-
Meanwhile, `nearest_t_to_fixed_point` in `spline3_hermite_intersection_spline3_hermite_nd.js:231-276` solves the exact same problem in `Float64` by composing `spline3_hermite_to_monomial`, `cubic_residual_times_derivative_accumulate`, and `polynomial_real_roots_in_interval`.
|
|
52
|
-
- **Hot-Path Assessment**: `polynomial_real_roots_in_interval` uses preallocated module-level `Float64Array` scratch buffers with zero heap allocations.
|
|
53
|
-
- **Recommendation**: Extract ` nearest_t_to_fixed_point` into a shared helper in `core/math/spline/` and use it in both `spline3_hermite_nearest_point.js` and `spline3_hermite_intersection_spline3_hermite_nd.js`.
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
#### `[DUP-REUSE-03]` [HIGH] Disk-Cycle Edge Insertion (`bt_disk_edge_add`) Copy-Pasted Into `bt_vertex_replace.js` and `bt_edge_create.js`
|
|
58
|
-
- **Duplicated In**:
|
|
59
|
-
- `core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_replace.js:148-192`
|
|
60
|
-
- `core/geom/3d/topology/struct/binary/io/edge/bt_edge_create.js:10-61, 80-113`
|
|
61
|
-
- **Existing Function**: `core/geom/3d/topology/struct/binary/io/bt_disk_edge_add.js:10-43` (`bt_disk_edge_add`)
|
|
62
|
-
- **Analysis**:
|
|
63
|
-
- `bt_vertex_replace.js` **already imports `bt_disk_edge_add` on line 2** and calls `bt_disk_edge_add(mesh, e, replacement)` on lines `118` and `136`, and then on lines `148-192` copy-pastes the entire 44-line implementation of `bt_disk_edge_add(mesh, e, replacement)` inline.
|
|
64
|
-
- `bt_edge_create.js` defines 50 lines of local helpers (`edge_link_disk_forward`, `edge_link_disk_back`, `edge_link_disk`) and 34 lines of endpoint branching (`80-113`) that are 100% equivalent to:
|
|
65
|
-
```js
|
|
66
|
-
bt_disk_edge_add(mesh, edge_id, v0);
|
|
67
|
-
if (v0 !== v1) {
|
|
68
|
-
bt_disk_edge_add(mesh, edge_id, v1);
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
- **Hot-Path Assessment**: Topology mutation operations are not inner tight loops where avoiding a direct call to `bt_disk_edge_add` is needed, and `bt_vertex_replace.js` already calls `bt_disk_edge_add` in the same loop.
|
|
72
|
-
- **Recommendation**: Replace `bt_vertex_replace.js:148-192` and `bt_edge_create.js:10-61, 80-113` with calls to `bt_disk_edge_add`.
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
#### `[DUP-REUSE-04]` [HIGH] Binary Topology Helpers (`bt_face_kill`, `bt_face_get_centroid`, `bt_face_third_vertex`, `bt_face_read_triangle`, `bt_edge_other_face`, `bt_mesh_build_face_bvh`) Re-implemented Across `core/geom/3d/topology/`
|
|
77
|
-
- **Duplicated In & Existing Functions**:
|
|
78
|
-
1. **Face deletion**: `bt_vertex_fuse_duplicate_edges.js:129-137` manually walks the face loop cycle calling `bt_radial_loop_remove`, `mesh.loops.release`, and `mesh.faces.release` instead of calling `bt_face_kill(mesh, face)` (`io/face/bt_face_kill.js:11-26`).
|
|
79
|
-
2. **Face centroid**: `bt_face_get_incenter.js:58-84` re-implements `bt_face_get_centroid(output, output_offset, mesh, face_id)` (`query/bt_face_get_centroid.js:34-74`) for its non-triangle fallback.
|
|
80
|
-
3. **Third triangle vertex (`bt_face_third_vertex`)**: `query/bt_face_third_vertex.js:27-55` is re-implemented in `bt_mesh_build_boundary_distance_field.js:125-139`, `bt_mesh_resolve_t_junctions.js:170-182`, and `bt_mesh_face_find_path.js:98-112` (`third_vertex`).
|
|
81
|
-
4. **Triangle vertex/coordinate read (`bt_face_read_triangle_vertices` / `bt_face_read_triangle_coordinates`)**: `query/bt_face_read_triangle.js:19-64` is re-implemented inline in `bt_mesh_compute_face_normals.js:29-45`, `bt_mesh_compute_vertex_quadrics.js:98-118`, `bt_mesh_compute_wedge_attribute_quadrics.js:74-89`, `bt_mesh_simplify.js:164-171`, and `bt_mesh_segment_penetrates_surface.js:115-125`.
|
|
82
|
-
5. **Opposite face across manifold edge (`bt_edge_other_face`)**: `query/bt_edge_other_face.js:33-59` is re-implemented as `neighbour_across` in `bt_mesh_face_find_path_polyanya.js:130-147` and `loop_opposite_face` in `bt_mesh_face_find_path.js:241-255`.
|
|
83
|
-
6. **Face BVH construction (`bt_mesh_build_face_bvh`)**: `query/bt_mesh_surface_ray_parity.js:40-80` (`bt_mesh_build_surface_triangle_bvh`) duplicates `query/bt_mesh_build_face_bvh.js:15-55`.
|
|
84
|
-
- **Recommendation**: Replace the inline re-implementations with the canonical `bt_*` primitives in `core/geom/3d/topology/struct/binary/`.
|
|
85
|
-
|
|
86
|
-
---
|
|
87
|
-
|
|
88
|
-
#### `[DUP-REUSE-05]` [HIGH] `MeshShape3D` and `ConvexHullShape3D` Re-implement `tetrahedron_contains_point` (With Winding Drift) and `polygon3_compute_normal`
|
|
89
|
-
- **Duplicated In**:
|
|
90
|
-
- `core/geom/3d/shape/MeshShape3D.js:186-225` (`tet_contains_point`)
|
|
91
|
-
- `core/geom/3d/shape/ConvexHullShape3D.js:599-615` (`loop_planes` Newell normal loop)
|
|
92
|
-
- **Existing Functions**:
|
|
93
|
-
- `core/geom/3d/tetrahedra/tetrahedron_contains_point.js:19-37` (`tetrahedron_contains_point`)
|
|
94
|
-
- `core/geom/3d/polygon/polygon3_compute_normal.js:26-60` (`polygon3_compute_normal`)
|
|
95
|
-
- **Analysis**:
|
|
96
|
-
- `MeshShape3D.js:186-225` defines a private `tet_contains_point` that is an older copy of `tetrahedron_contains_point.js`. `tetrahedron_contains_point.js:29-30` was fixed to be winding-agnostic (accepting either all-non-positive or all-non-negative face orientation determinants), whereas `MeshShape3D.js` still assumes a single fixed winding (`d0 <= 1e-8 && ...`).
|
|
97
|
-
- `ConvexHullShape3D.js:599-615` manually implements Newell's polygon normal loop + normalization instead of calling `polygon3_compute_normal`.
|
|
98
|
-
- **Recommendation**: Call `tetrahedron_contains_point` in `MeshShape3D.js` and `polygon3_compute_normal` in `ConvexHullShape3D.js`.
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
#### `[DUP-REUSE-06]` [HIGH] `prepare_meshlet_draw_commands_by_material.js` and `graph_rasterize_meshes_alpha_tested.js` Re-implement `graph_extract_meshes_from_bucket` and `material_draw_indirect`
|
|
103
|
-
- **Duplicated In**:
|
|
104
|
-
- `shade/renderer/rasterize/meshlet/prepare_meshlet_draw_commands_by_material.js:73-119`
|
|
105
|
-
- `shade/renderer/rasterize/pass/graph_rasterize_meshes_alpha_tested.js:213-240`
|
|
106
|
-
- **Existing Functions**:
|
|
107
|
-
- `shade/renderer/rasterize/expand/bucket/graph_extract_meshes_from_bucket.js:22-79` (`graph_extract_meshes_from_bucket`)
|
|
108
|
-
- `shade/renderer/rasterize/material_draw_indirect.js:31-75` (`material_draw_indirect`)
|
|
109
|
-
- **Analysis**:
|
|
110
|
-
- `prepare_meshlet_draw_commands_by_material.js:73-119` manually creates the `bucket_elements_buffer` storage buffer, `compute_extract_bucket` compute pass, bind group descriptors, and indirect dispatch that `graph_extract_meshes_from_bucket` already encapsulates (and which `graph_rasterize_meshes_opaque.js:165`, `graph_rasterize_meshes_alpha_tested.js:161`, `graph_rasterize_meshes_transmission.js:158`, `graph_rasterize_meshes_transparent_oit.js:159`, and `graph_rasterize_meshes_transparent_avboit.js:218` all call).
|
|
111
|
-
- `graph_rasterize_meshes_alpha_tested.js:213-240` manually sets up the render pipeline, bind groups 0/1/2/3, vertex/index buffers, and `drawIndexedIndirect` call instead of calling `material_draw_indirect(...)`, which every other rasterize pass (`opaque`, `transmission`, `transparent_oit`, `transparent_avboit`) uses.
|
|
112
|
-
- **Hot-Path Assessment**: Frame-graph recording on the CPU once per material bucket per frame; zero GPU or hot-path reason to inline.
|
|
113
|
-
- **Recommendation**: Call `graph_extract_meshes_from_bucket` in `prepare_meshlet_draw_commands_by_material.js` and `material_draw_indirect` in `graph_rasterize_meshes_alpha_tested.js`.
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
|
-
#### `[DUP-REUSE-07]` [HIGH] `StorageAchievementGateway` Re-implements `StorageBackedSet` (With Read/Write Race and `null` Crash)
|
|
118
|
-
- **Duplicated In**: `engine/achievements/gateway/StorageAchievementGateway.js:8-91`
|
|
119
|
-
- **Existing Class**: `engine/save/StorageBackedList.js:1-145` (`StorageBackedSet`)
|
|
120
|
-
- **Analysis**:
|
|
121
|
-
`StorageAchievementGateway` manually manages a JSON array of unique string IDs in a `Storage` backend using `storage.get` + `JSON.parse` + `indexOf` + `storage.set` + `JSON.stringify`. `StorageBackedSet` in `engine/save/StorageBackedList.js` already implements this exact abstraction with an in-memory `Set` cache, serialized `load()` promise deduplication, and defensive `Array.isArray` validation.
|
|
122
|
-
Because `StorageAchievementGateway` re-implemented this ad hoc without caching or locking:
|
|
123
|
-
1. Concurrent `unlock('a')` and `unlock('b')` calls both read the same initial state before either `storage.set` finishes, and the second write silently overwrites the first unlocked achievement.
|
|
124
|
-
2. If storage contains `"null"` or non-array JSON, `JSON.parse(rawData)` returns `null` (which passes `rawData !== undefined && rawData !== null`) and `data.indexOf(id)` throws `TypeError`.
|
|
125
|
-
- **Recommendation**: Back `StorageAchievementGateway` with `new StorageBackedSet(this.storage, this.key)`.
|
|
126
|
-
|
|
127
|
-
---
|
|
128
|
-
|
|
129
|
-
#### `[DUP-REUSE-08]` [HIGH] Acoustic Probe Ray-Visibility Loop Duplicated in 3 Files Instead of Calling `probe_edge_visible`
|
|
130
|
-
- **Duplicated In**:
|
|
131
|
-
- `engine/sound/simulation/baking/probe_refine_connectivity.js:168-181`
|
|
132
|
-
- `engine/sound/simulation/probe/AcousticProbeField.js:329-344` (`# mutual_visibility`)
|
|
133
|
-
- **Existing Function**: `engine/sound/simulation/baking/probe_edge_visible.js:20-41` (`probe_edge_visible`)
|
|
134
|
-
- **Analysis**:
|
|
135
|
-
`probe_edge_visible(scene, ax, ay, az, bx, by, bz, radius)` computes the segment vector, checks length against `1e-5` and `radius`, normalizes the direction, and calls `!scene.any_hit(...)` with `RAY_T_MIN = 1e-3` and `dist - RAY_T_MIN`. Both `probe_refine_connectivity.js` and `AcousticProbeField.js` re-implement this exact ray setup and `scene.any_hit` check inline.
|
|
136
|
-
- **Hot-Path Assessment**: Offline/background acoustic probe baking and infrequent listener-to-probe visibility checks where `scene.any_hit` (BVH traversal) dominates runtime.
|
|
137
|
-
- **Recommendation**: Call `probe_edge_visible` in both `probe_refine_connectivity.js` and `AcousticProbeField.js`.
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
#### `[DUP-REUSE-09]` [HIGH] Fluid MacCormack Scalar Advection & `FluidObstacleSystem` Miss `v3_grid_limit8_at_indices` and `FluidField.prototype.setVelocityAt`
|
|
142
|
-
- **Duplicated In**:
|
|
143
|
-
- `engine/physics/fluid/solver/v3_grid_advect_maccormack_scalar.js:129-139`
|
|
144
|
-
- `engine/physics/fluid/ecs/FluidObstacleSystem.js:345-353` (`#stamp_wall_velocity`)
|
|
145
|
-
- **Existing Functions**:
|
|
146
|
-
- `engine/physics/fluid/solver/v3_grid_limit8_at_indices.js:1-38` (`v3_grid_limit8_at_indices`)
|
|
147
|
-
- `engine/physics/fluid/FluidField.js:559-573` (`FluidField.prototype.setVelocityAt`)
|
|
148
|
-
- **Analysis**:
|
|
149
|
-
1. `v3_grid_limit8_at_indices` was specifically extracted to hold the 8-corner trilinear stencil min/max clamp for MacCormack advection and is called in `v3_grid_advect_maccormack_velocity.js:159-161` and `v3_mac_advect_maccormack_velocity.js:332-338`, but `v3_grid_advect_maccormack_scalar.js:129-139` still has the 11-line min/max clamp inlined verbatim.
|
|
150
|
-
2. `FluidObstacleSystem.#stamp_wall_velocity` (`FluidObstacleSystem.js:345-353`) manually computes the 6 staggered MAC face indices (`u`, `u + 1`, `v`, `v + rx`, `w`, `w + slice`) and writes `(gvx, gvy, gvz)` to all 6 faces — which is the exact implementation of `field.setVelocityAt(x, y, z, gvx, gvy, gvz)` (`FluidField.js:559-573`).
|
|
151
|
-
- **Recommendation**: Call `v3_grid_limit8_at_indices` in `v3_grid_advect_maccormack_scalar.js` and `field.setVelocityAt` in `FluidObstacleSystem.#stamp_wall_velocity`.
|
|
152
|
-
|
|
153
|
-
---
|
|
154
|
-
|
|
155
|
-
#### `[DUP-REUSE-10]` [HIGH] Codec & Transcoder Helpers Re-implemented (`quantise.js` vs `reconstruct.js`, `etc1s_to_bc7.js` vs `fit_endpoints.js`, `uastc_decode_block.js` vs `astc_block.js`, `loop_restoration.js`)
|
|
156
|
-
- **Duplicated In & Existing Functions**:
|
|
157
|
-
1. **AV1 quantizer helpers**: `format/image/av1/quantise.js:176-212` redefines `dequant_denominator`, `dc_delta`, and `ac_delta`, which are verbatim copies of the functions in `format/image/av1/reconstruct.js:123-129, 222-241` — a module `quantise.js` **already imports on line 18** (`import { dc_q, ac_q } from "./reconstruct.js"`).
|
|
158
|
-
2. **AV1 loop restoration**: `format/image/av1/Av1FrameContext.js:575-577` (`count_units`) and `format/image/av1/encoder/choose_restoration.js:593-603` (`expand`) are verbatim duplicates of `count_units_in_frame` (`format/image/av1/loop_restoration.js:52-55`) and `expand_wiener` (`loop_restoration.js:76-89`).
|
|
159
|
-
3. **Basis ETC1S -> BC7 weight table lookup**: `format/texture/basis/etc1s_to_bc7.js:476-496` (`closest_weight`) is a verbatim duplicate of `nearest_weight` in `format/texture/basis/fit_endpoints.js:237-260`.
|
|
160
|
-
4. **ASTC endpoint interpolation**: `format/texture/basis/uastc_decode_block.js:89-94` (`interpolate`) is a verbatim duplicate of `astc_interpolate` in `format/texture/astc/astc_block.js:75-80`.
|
|
161
|
-
- **Recommendation**: Export and import the existing functions from `reconstruct.js`, `loop_restoration.js`, `fit_endpoints.js`, and `astc_block.js`.
|
|
162
|
-
|
|
163
|
-
---
|
|
164
|
-
|
|
165
|
-
#### `[DUP-REUSE-11]` [HIGH] Equality Helpers Re-implemented Instead of `objectsEqual` (Causing `null` Dereference Bug in `NodeInstance.setParameterValue`)
|
|
166
|
-
- **Duplicated In**:
|
|
167
|
-
- `core/model/node-graph/node/NodeInstance.js:218-229` (`setParameterValue`)
|
|
168
|
-
- `core/collection/list/List.js:3, 956-988` (`List.prototype.equals`)
|
|
169
|
-
- `core/collection/array/isArrayEqual.js:36-53`
|
|
170
|
-
- `core/model/object/optional_equality.js:7-27`
|
|
171
|
-
- **Existing Function**: `core/function/objectsEqual.js:7-16` (`objectsEqual`)
|
|
172
|
-
- **Analysis**:
|
|
173
|
-
- `List.js` imports `objectsEqual` on line 3, leaves it unused, and writes out a 25-line manual equality check in `List.prototype.equals`.
|
|
174
|
-
- `NodeInstance.prototype.setParameterValue` (`NodeInstance.js:218-229`) hand-rolls an `.equals` check that **omits `value !== null && old_value !== null`**:
|
|
175
|
-
```js
|
|
176
|
-
if (
|
|
177
|
-
value !== undefined
|
|
178
|
-
&& old_value !== undefined
|
|
179
|
-
&& typeof value === "object"
|
|
180
|
-
&& typeof value.equals === "function"
|
|
181
|
-
&& value.equals(old_value)
|
|
182
|
-
)
|
|
183
|
-
```
|
|
184
|
-
Because `typeof null === "object"`, calling `node.setParameterValue(id, null)` when `old_value !== undefined` throws `TypeError: Cannot read properties of null (reading 'equals')`, and calling `node.setParameterValue(id, obj)` when `old_value === null` calls `obj.equals(null)`.
|
|
185
|
-
- **Recommendation**: Use `objectsEqual(a, b)` in `NodeInstance.prototype.setParameterValue`, `List.prototype.equals`, `isArrayEqual`, and `optional_equality`.
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
|
-
### `MEDIUM` Confidence / Value
|
|
190
|
-
|
|
191
|
-
#### `[DUP-REUSE-12]` [MEDIUM] `RowFirstTable` Copies and Compares `ArrayBuffer` Byte-by-Byte via `DataView` Instead of `array_buffer_copy` and `is_array_buffer_equals`
|
|
192
|
-
- **Duplicated In**: `core/collection/table/RowFirstTable.js:213-227, 459-463, 646-658`
|
|
193
|
-
- **Existing Functions**:
|
|
194
|
-
- `core/binary/operations/array_buffer_copy.js:13-53` (`array_buffer_copy`)
|
|
195
|
-
- `core/collection/array/typed/is_array_buffer_equals.js:11-39` (`is_array_buffer_equals`)
|
|
196
|
-
- **Analysis**:
|
|
197
|
-
`RowFirstTable.prototype.setCapacity` (lines `213-227`) and `copy` (`459-463`) copy `ArrayBuffer` contents 1 byte at a time via `DataView.getUint8`/`setUint8`, and `equals` (`646-658`) compares buffers 1 byte at a time via `DataView.getUint8`. `array_buffer_copy` and `is_array_buffer_equals` already exist in `core/` and operate on 32-bit words (`Uint32Array`), running ~10–50x faster.
|
|
198
|
-
- **Recommendation**: Replace the `DataView` byte loops in `RowFirstTable.js` with `array_buffer_copy` and `is_array_buffer_equals`.
|
|
199
|
-
|
|
200
|
-
---
|
|
201
|
-
|
|
202
|
-
#### `[DUP-REUSE-13]` [MEDIUM] `qt_collect_by_point` Is a Verbatim Specialization of `qt_collect_by_box`
|
|
203
|
-
- **Duplicated In**: `core/geom/2d/quad-tree/qt_collect_by_point.js:19-85`
|
|
204
|
-
- **Existing Function**: `core/geom/2d/quad-tree/qt_collect_by_box.js:22-90` (`qt_collect_by_box`)
|
|
205
|
-
- **Analysis**:
|
|
206
|
-
`qt_collect_by_point(result, result_offset, tree, x, y)` is identical to `qt_collect_by_box(result, result_offset, tree, x, y, x, y)` (since `aabb2_intersects_aabb2(node.x0, node.y0, node.x1, node.y1, x, y, x, y)` is algebraically identical to `aabb2_contains_point(node.x0, node.y0, node.x1, node.y1, x, y)`, and the quadrant comparisons `hx >= x`, `hx <= x`, `hy >= y`, `hy <= y` are identical).
|
|
207
|
-
- **Recommendation**: Delegate `qt_collect_by_point` to `return qt_collect_by_box(result, result_offset, tree, x, y, x, y);`.
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
#### `[DUP-REUSE-14]` [MEDIUM] `line3`, `ray3`, `plane3`, `aabb3`, `frustum3`, `packing`, and `ConicRay` Re-implement Existing `core/geom` Primitives
|
|
212
|
-
- **Duplicated In & Existing Functions**:
|
|
213
|
-
1. `core/geom/3d/line/line3_compute_segment_point_distance_sqr.js:3, 20-54`: Imports `line3_compute_segment_closest_point_t` on line 3 (unused!) and copy-pastes its 28-line body on lines `25-46`.
|
|
214
|
-
2. `core/geom/3d/ray/ray3_compute_closest_point.js:17-43`: Re-implements `ray3_compute_point_projection_t` (`ray3_compute_point_projection_t.js:16-31`) instead of calling `Math.max(0, ray3_compute_point_projection_t(...))` + `ray3_at(...)`.
|
|
215
|
-
3. `core/geom/3d/plane/plane3_compute_intersection_objects.js:6, 13-82` & `plane3_slerp.js:37-82`: Re-implement 2-plane intersection line finding already provided by `plane3_compute_intersection` (`plane3_compute_intersection.js:20-76`; note the `TODO consolidate` on line 6 of `plane3_compute_intersection_objects.js`).
|
|
216
|
-
4. `core/geom/3d/triangle/tri3_ray_intersection.js:193-354` (`tri3_ray_intersection_triangle_array`): Copy-pastes all 145 lines of `tri3_ray_intersection` (`25-171`) in the same file instead of unpacking the 9 triangle coordinates and calling `tri3_ray_intersection`.
|
|
217
|
-
5. `core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.js:114-164, 201-227`: Re-implements AABB signed plane distance extrema twice instead of calling `aabb3_compute_distance_above_plane_min` / `aabb3_compute_distance_above_plane_max` (`core/geom/3d/aabb/aabb3_compute_distance_above_plane.js:17-61`).
|
|
218
|
-
6. `core/geom/2d/hash-grid/shg_query_elements_circle.js:3, 21-42, 102-108`: Imports `aabb2_distance_sqr_to_point` on line 3, then defines a 22-line `unit_square_intersects_circle` that re-implements `aabb2_distance_sqr_to_point(0, 0, 1, 1, cx, cy) <= r * r`.
|
|
219
|
-
7. `core/geom/3d/aabb/aabb3_score_boxes_SAH.js:19-32`: Re-implements `2 * aabb3_union_half_surface_area(...)` (`aabb3_union_half_surface_area.js:21-33`).
|
|
220
|
-
8. `core/geom/3d/aabb/aabb3_build_frustum.js:17-46` vs `core/geom/3d/frustum/frustum3_from_aabb.js:12-30`: Both build the same 6 frustum planes from an AABB.
|
|
221
|
-
9. `core/geom/2d/packing/MaxRectanglesPacker.js:35-51` (`add`) vs `core/geom/2d/packing/pack_one_box.js:11-36`: Identical `findPositionForNewNodeBestAreaFit` + `placeRect` logic.
|
|
222
|
-
10. `core/geom/ConicRay.js:56-103` (`sampleRandomDirection`): Re-implements `v3_uniform_sample_cone` (`core/geom/random/v3_uniform_sample_cone.js:15-42`).
|
|
223
|
-
- **Recommendation**: Delegate each wrapper to the existing primitive.
|
|
224
|
-
|
|
225
|
-
---
|
|
226
|
-
|
|
227
|
-
#### `[DUP-REUSE-15]` [MEDIUM] Atlas Parameterization & Chart Merging Re-implement `tri3_flatten_isometric_indexed`, `union_find`, and `choose_placement`
|
|
228
|
-
- **Duplicated In**:
|
|
229
|
-
- `core/geom/3d/atlas/param/atlas_chart_parameterize_bff.js:48-50` (`project_triangle`)
|
|
230
|
-
- `core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.js:65-67` (`project_triangle`)
|
|
231
|
-
- `core/geom/3d/atlas/chart/atlas_merge_charts.js:51-65`
|
|
232
|
-
- `core/geom/3d/atlas/pack/atlas_pack_charts.js:181-196`
|
|
233
|
-
- **Existing Functions**:
|
|
234
|
-
- `core/geom/3d/triangle/tri3_flatten_isometric.js:66-79` (`tri3_flatten_isometric_indexed`)
|
|
235
|
-
- `core/collection/union-find/union_find.js` (`uf_init`, `uf_find`)
|
|
236
|
-
- `core/geom/3d/atlas/pack/atlas_pack_charts.js:138-152` (`choose_placement`)
|
|
237
|
-
- **Analysis**:
|
|
238
|
-
- Both `atlas_chart_parameterize_bff.js` and `atlas_chart_parameterize_lscm.js` define local `project_triangle(pos, i0, i1, i2, out)` helpers that duplicate `tri3_flatten_isometric_indexed`.
|
|
239
|
-
- `atlas_merge_charts.js:51-65` hand-rolls a union-find structure with path halving instead of using `uf_init` / `uf_find`.
|
|
240
|
-
- `atlas_pack_charts.js:181-196` inlines a copy of its own helper `choose_placement` (`138-152`).
|
|
241
|
-
- **Recommendation**: Use `tri3_flatten_isometric_indexed`, `uf_init`/`uf_find`, and `choose_placement`.
|
|
242
|
-
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
#### `[DUP-REUSE-16]` [MEDIUM] `prefab_skin_frame.js` Re-implements `m4_linear_determinant`
|
|
246
|
-
- **Duplicated In**: `shade/renderer/loader/prefab_skin_frame.js:110-116` (`upper3x3_determinant`)
|
|
247
|
-
- **Existing Function**: `core/geom/3d/mat4/m4_linear_determinant.js:9-21` (`m4_linear_determinant`)
|
|
248
|
-
- **Analysis**:
|
|
249
|
-
`upper3x3_determinant(m)` in `prefab_skin_frame.js` computes the exact same $3\times 3$ cofactor expansion of a 16-element column-major matrix as `m4_linear_determinant(m)`.
|
|
250
|
-
- **Recommendation**: Import and call `m4_linear_determinant` in `prefab_skin_frame.js`.
|
|
251
|
-
|
|
252
|
-
---
|
|
253
|
-
|
|
254
|
-
#### `[DUP-REUSE-17]` [MEDIUM] `load_gltf.js` and `load_usd.js` Re-implement `SceneNode.from`, `array_starts_with`, and `utf8_decode`
|
|
255
|
-
- **Duplicated In**:
|
|
256
|
-
- `format/scene/gltf/load_gltf.js:718-749` (`build_scene_node`)
|
|
257
|
-
- `format/scene/usd/load_usd.js:198-225` (`starts_with_bytes`, `ascii_slice`)
|
|
258
|
-
- **Existing Functions**:
|
|
259
|
-
- `format/scene/SceneNode.js:71-107` (`SceneNode.from`)
|
|
260
|
-
- `core/collection/array/array_starts_with.js:9-31` (`array_starts_with`)
|
|
261
|
-
- `core/binary/utf8/utf8_decode.js:18-93` (`utf8_decode`)
|
|
262
|
-
- **Analysis**:
|
|
263
|
-
- `load_gltf.js:718-749` manually instantiates `new SceneNode()`, copies translation/rotation/scale/weights/children, and calls `update_local_matrix()` — which is already implemented step-for-step by `SceneNode.from(...)` (`SceneNode.js:71-107`).
|
|
264
|
-
- `load_usd.js:198-225` defines `starts_with_bytes` and `ascii_slice`, which duplicate `array_starts_with` and `utf8_decode`.
|
|
265
|
-
- **Recommendation**: Use `SceneNode.from` in `load_gltf.js` and `array_starts_with` / `utf8_decode` in `load_usd.js`.
|
|
266
|
-
|
|
267
|
-
---
|
|
268
|
-
|
|
269
|
-
#### `[DUP-REUSE-18]` [MEDIUM] `ao_reproject_shader.js` and `graph_atorus_denoise_luma.js` Re-implement Existing WGSL Chunks `chunk_taa_velocity_confidence` and `shader_denoise_luminance_spatial`
|
|
270
|
-
- **Duplicated In**:
|
|
271
|
-
- `shade/renderer/postprocess/gtao/ao_reproject_shader.js:73-79`
|
|
272
|
-
- `shade/renderer/global_illumination/screen_space/graph_atorus_denoise_luma.js:82-104`
|
|
273
|
-
- **Existing Shader Chunks / Shaders**:
|
|
274
|
-
- `shade/renderer/postprocess/taa/shader_taa.js:34-43` (`chunk_taa_velocity_confidence`)
|
|
275
|
-
- `shade/renderer/postprocess/gtao/fragment_shader_denoise_ao.js:144-155` (`shader_denoise_luminance_spatial`)
|
|
276
|
-
- **Analysis**:
|
|
277
|
-
- `ao_reproject_shader.js:73-79` inlines the exact 5-line `velocity_confidence` formula already exported as `chunk_taa_velocity_confidence` in `shader_taa.js`.
|
|
278
|
-
- `graph_atorus_denoise_luma.js:82-104` defines an inline WGSL shader that is 100% identical to `shader_denoise_luminance_spatial` exported from `fragment_shader_denoise_ao.js`.
|
|
279
|
-
- **Recommendation**: Reuse `chunk_taa_velocity_confidence` and `shader_denoise_luminance_spatial`.
|
|
280
|
-
|
|
281
|
-
---
|
|
282
|
-
|
|
283
|
-
#### `[DUP-REUSE-19]` [MEDIUM] `criticallyDampedSpringStep` Re-implements `dampedSpringStep` (`zeta = 1.0`), and `cloth_proxy_from_joints.js` Re-implements `cloth_tree_graph`
|
|
284
|
-
- **Duplicated In**:
|
|
285
|
-
- `engine/control/first-person/math/criticallyDampedSpring.js:19-34` (`criticallyDampedSpringStep`)
|
|
286
|
-
- `engine/physics/cloth/build/cloth_proxy_from_joints.js:305-320`
|
|
287
|
-
- `engine/physics/narrowphase/convex_decomposition.js:240-247, 278-286`
|
|
288
|
-
- **Existing Functions**:
|
|
289
|
-
- `engine/control/first-person/math/dampedSpringStep.js:64-71` (`dampedSpringStep`)
|
|
290
|
-
- `engine/physics/cloth/ecs/cloth_build_rows.js:108-135` (`cloth_tree_graph`)
|
|
291
|
-
- `core/geom/vec3/v3_orthonormal_vectors_from_normal.js` and `core/geom/3d/aabb/aabb3_from_v3_buffer.js`
|
|
292
|
-
- **Analysis**:
|
|
293
|
-
- `criticallyDampedSpringStep` is identical to the `zeta === 1.0` branch of `dampedSpringStep`.
|
|
294
|
-
- `cloth_build_rows.js:108-135` extracted `cloth_tree_graph(parent, n)` to build `{ edge, skip }` (`parent -> i` and `grandparent -> i` index pairs), while `cloth_proxy_from_joints.js:305-320` still builds the same `struct` and `bend` arrays inline.
|
|
295
|
-
- `convex_decomposition.js:240-247` and `278-286` inline tangent basis construction and vertex-buffer AABB bounds instead of calling `v3_orthonormal_vectors_from_normal` and `aabb3_from_v3_buffer`.
|
|
296
|
-
- **Recommendation**: Delegate to `dampedSpringStep`, `cloth_tree_graph`, `v3_orthonormal_vectors_from_normal`, and `aabb3_from_v3_buffer`.
|
|
297
|
-
|
|
298
|
-
---
|
|
299
|
-
|
|
300
|
-
#### `[DUP-REUSE-20]` [MEDIUM] `Signal.prototype.remove`, `EntityComponentDataset` Signal Removal, `NodeGraph.prototype.createConnection`, and `OffsetAllocator.prototype.allocate` Duplicate Internal Helpers
|
|
301
|
-
- **Duplicated In & Existing Functions**:
|
|
302
|
-
1. `core/events/signal/Signal.js:231-272` (`Signal.prototype.remove`): Re-implements `signal_handler_list_find(this.handlers, fn, context)` + `this.#remove_handler_internal(handler)` (`Signal.js:291-336`).
|
|
303
|
-
2. `engine/ecs/EntityComponentDataset.js:1895-1901, 1985-1991`: Branches on `if (thisArg === undefined) findSignalHandlerIndexByHandle(...) else findSignalHandlerIndexByHandleAndContext(...)`, even though `findSignalHandlerIndexByHandle(handlers, handle, context)` (`Signal.js:24-46`) already accepts `context = undefined`!
|
|
304
|
-
3. `core/model/node-graph/NodeGraph.js:620-654` (`createConnection`): Duplicates all of `addConnection` (`664-690`) instead of delegating to `this.addConnection(connection)` (like `createNode` delegates to `addNode` at `457-469`).
|
|
305
|
-
4. `core/binary/allocator/OffsetAllocator.js:348-383` (`allocate`): Duplicates the head-of-bin node pop and `usedBins`/`usedBinsTop` bitmask clearing already implemented in `this.#removeNodeFromBin(binIndex, nodeIndex)` (`895-922`).
|
|
306
|
-
- **Recommendation**: Delegate each method to its existing helper.
|
|
307
|
-
|
|
308
|
-
---
|
|
309
|
-
|
|
310
|
-
### `LOW` Confidence / Value
|
|
311
|
-
|
|
312
|
-
#### `[DUP-REUSE-21]` [LOW] Playground Harnesses Re-implement `half_to_float_uint16` (5x) and `install_profile_hotkey` (3x)
|
|
313
|
-
- **Duplicated In**:
|
|
314
|
-
- `shade/playground/environment_Helper.js:27-39`, `shade/playground/water/playground.js:116-127`, `shade/playground/terrain/playground.js:154-165`, `shade/playground/cloud_shadow/playground.js:151-162`, `shade/playground/restir_pt/playground.js:172-183` (`float16_to_float32` / `f16_to_f32`)
|
|
315
|
-
- `shade/playground/foliage/playground.js:141-190`, `shade/playground/fluid/playground.js:315-365`, `shade/playground/particles/playground.js:110-160`
|
|
316
|
-
- **Existing Functions**:
|
|
317
|
-
- `core/binary/half_to_float_uint16.js:1-38` (`half_to_float_uint16`)
|
|
318
|
-
- `shade/playground/profile_hotkey.js:1-60` (`install_profile_hotkey`)
|
|
319
|
-
- **Recommendation**: Import `half_to_float_uint16` and `install_profile_hotkey` in the playground scripts.
|
|
320
|
-
|
|
321
|
-
---
|
|
322
|
-
|
|
323
|
-
#### `[DUP-REUSE-22]` [LOW] `RemainingTimeEstimator` Re-implements `StatTracker` Ring Buffer
|
|
324
|
-
- **Duplicated In**: `core/process/task/RemainingTimeEstimator.js:1-50`
|
|
325
|
-
- **Existing Class**: `core/debug/stats/StatTracker.js:1-87` (`StatTracker`)
|
|
326
|
-
- **Analysis**: `RemainingTimeEstimator` re-implements `StatTracker`'s fixed-size `Float64Array` circular buffer rolling average.
|
|
327
|
-
- **Recommendation**: Compose `StatTracker` inside `RemainingTimeEstimator`.
|
|
328
|
-
|
|
329
|
-
---
|
|
330
|
-
|
|
331
|
-
#### `[DUP-REUSE-23]` [LOW] `sh3_convolve_with_cosine_kernel` Is a 1-Line Specialization of `sh3_convolve_with_zh`
|
|
332
|
-
- **Duplicated In**: `core/geom/3d/sphere/harmonics/sh3_convolve_with_cosine_kernel.js:13-25`
|
|
333
|
-
- **Existing Function**: `core/geom/3d/sphere/harmonics/sh3_convolve_with_zh.js:22-53` (`sh3_convolve_with_zh`)
|
|
334
|
-
- **Recommendation**: Delegate `sh3_convolve_with_cosine_kernel(out, out_offset, input, input_offset)` to `sh3_convolve_with_zh(out, out_offset, input, input_offset, CosineA0, CosineA1, CosineA2, 1)`.
|
|
335
|
-
|
|
336
|
-
---
|
|
337
|
-
|
|
338
|
-
## 2. Meaningful Code Duplication — New Logical Abstractions & Consolidations (`DUP-OUTLINE-*`)
|
|
339
|
-
|
|
340
|
-
### `HIGH` Confidence / Value
|
|
341
|
-
|
|
342
|
-
#### `[DUP-OUTLINE-01]` [HIGH] `ebvh_optimize_treelet.js` vs `ebvh_update_hierarchy_bounds.js` — Duplicated Stackless Post-Order Traversal State Machine (With Shared Out-of-Bounds Root Read & Double Root Optimization)
|
|
343
|
-
- **Locations**:
|
|
344
|
-
- `core/bvh2/bvh3/ebvh_optimize_treelet.js:139-145, 269-436`
|
|
345
|
-
- `core/bvh2/bvh3/ebvh_update_hierarchy_bounds.js:4-147`
|
|
346
|
-
- `core/bvh2/bvh3/ebvh_build_hierarchy_radix.js:169-200` (`refit_radix_tree`)
|
|
347
|
-
- **Analysis**:
|
|
348
|
-
`ebvh_update_hierarchy_bounds` is a copy-paste of `ebvh_optimize_treelet`'s ~130-line stackless bottom-up post-order traversal state machine (`CAME_FROM_TYPE_PARENT`, `CAME_FROM_TYPE_CHILD_1`, `CAME_FROM_TYPE_CHILD_2`), down to the comment `// only add to treelet when traversing up, not down` at `ebvh_update_hierarchy_bounds.js:56`. Both copies share an out-of-bounds read bug at `root` (`bvh.node_get_child1(parent)` when `parent = NULL_NODE = 0xFFFFFFFF` before checking `if (node === root) break;`; see `[BUG-11]`), and `ebvh_optimize_treelet.js` optimizes `root` twice (`338` and `435`). Meanwhile `refit_radix_tree` (`ebvh_build_hierarchy_radix.js:169-200`) implements a third bottom-up bounds/height refit pass.
|
|
349
|
-
- **Hot-Path Assessment**: Offline/background BVH construction and treelet optimization.
|
|
350
|
-
- **Recommendation**: Fix the `if (node === root) break;` ordering in both files, remove the duplicate post-loop `optimize_at_node(bvh, root, treelet_size)` in `ebvh_optimize_treelet.js:435`, use `aabb3_surface_area` at `ebvh_optimize_treelet.js:139-145`, and reuse `ebvh_update_hierarchy_bounds` in `ebvh_build_hierarchy_radix.js`.
|
|
351
|
-
|
|
352
|
-
---
|
|
353
|
-
|
|
354
|
-
#### `[DUP-OUTLINE-02]` [HIGH] `BVH8Converter.js` vs `bvh8_from_proxy.js` — Duplicated 8-Child Octant Assignment & Quantized Node Encoding (Plus 8x Redundant `node_get_aabb` in `BVH8Converter`)
|
|
355
|
-
- **Locations**:
|
|
356
|
-
- `core/bvh8/build/bvh8_from_proxy.js:13-90, 103-256` (`bvh8_sort_children`, `bvh8_populate_node_from_proxy`)
|
|
357
|
-
- `core/bvh8/build/BVH8Converter.js:283-363, 409-565` (`order_children`, `#collapse_node`)
|
|
358
|
-
- **Analysis**:
|
|
359
|
-
1. **Greedy 8-slot octant child ordering**: `bvh8_sort_children` and `BVH8Converter.prototype.order_children` implement the exact same ~80-line algorithm: computing the parent AABB centroid, populating the $8 \times 8$ octant direction dot-product cost matrix, and running an 8-iteration greedy minimum-cost slot assignment. In `BVH8Converter.prototype.order_children` (`BVH8Converter.js:304-316`), `this.bvh2.node_get_aabb(child_id, child_aabb)` is called inside the inner `for (let s = 0; s < 8; s++)` loop — recomputing the child's AABB and centroid 8 times per child (64 times per node instead of 8).
|
|
360
|
-
2. **Parent frame `(p, e)` and quantized child AABB encoding**: `bvh8_populate_node_from_proxy` (`112-139, 202-219`) and `BVH8Converter.prototype.#collapse_node` (`434-461, 494-506`) duplicate the exact IEEE-754 exponent extraction (`encode_bounds_e`, `reinterpret_float32_as_uint32`, `u_ex >> 23`) and `Math.floor` / `Math.ceil` 8-bit child AABB quantization into `bvh8`.
|
|
361
|
-
- **Recommendation**: Outline `bvh8_encode_node_frame(bvh8, node_id, parent_aabb, out_inv_e)` and `bvh8_encode_quantized_child_aabb(bvh8, node_id, slot, child_aabb, parent_aabb, inv_e)` in `core/bvh8/build/`, and hoist `node_get_aabb` out of the `s` loop in `BVH8Converter.prototype.order_children`.
|
|
362
|
-
|
|
363
|
-
---
|
|
364
|
-
|
|
365
|
-
#### `[DUP-OUTLINE-03]` [HIGH] `gjk.js` vs `gjk_epa_penetration.js` — Two Separate 3D GJK Simplex Solvers With Collinear Fallback Drift
|
|
366
|
-
- **Locations**:
|
|
367
|
-
- `core/geom/3d/gjk/gjk.js:47-445`
|
|
368
|
-
- `core/geom/3d/gjk/gjk_epa_penetration.js:147-358`
|
|
369
|
-
- **Analysis**:
|
|
370
|
-
`gjk.js` and `gjk_epa_penetration.js` each contain a complete, independently written 3D GJK simplex solver (`line`, `triangle`, and `tetrahedron` Voronoi region reduction + support loop) operating on `minkowski_support`.
|
|
371
|
-
`gjk.js` was hardened with `perpendicular_to_edge(dir, ex, ey, ez)` (`gjk.js:207-225`) used in both `update_simplex_line` (`244`) and all three edge/face cases of `update_simplex_triangle` (`283-285, 304-306, 334-345`) when `ex_to_o` is collinear with an edge or the origin lies on the triangle face. `gjk_epa_penetration.js` only inlines the fallback in `line_case()` (`213-228`) and lacks it in `triangle_case()` (`237-300`), leaving `gjk_epa_penetration` vulnerable to zero search directions `(0, 0, 0)` when the origin hits a triangle edge or face during GJK phase.
|
|
372
|
-
- **Hot-Path Assessment**: Sharing a single zero-allocation GJK simplex step / solver operating on flat `Float64Array` buffers preserves 100% of hot-path performance while eliminating a 210-line duplicate GJK implementation.
|
|
373
|
-
- **Recommendation**: Unify the GJK simplex reduction routines between `gjk.js` and `gjk_epa_penetration.js`.
|
|
374
|
-
|
|
375
|
-
---
|
|
376
|
-
|
|
377
|
-
#### `[DUP-OUTLINE-04]` [HIGH] `CircleLayout.js` vs `BoxLayouter.js` — Cloned Graph Layout Engines That Drifted After Bug Fixes
|
|
378
|
-
- **Locations**:
|
|
379
|
-
- `core/graph/layout/CircleLayout.js:75-472, 584-778, 807-823`
|
|
380
|
-
- `core/graph/layout/box/BoxLayouter.js:105-415`
|
|
381
|
-
- **Analysis**:
|
|
382
|
-
`CircleLayout.js` and `BoxLayouter.js` share the same topological-layer / clustered / node-swap / force-directed layout pipeline (`graph_peel_topological_layer.js` was previously extracted from both, but the rest of the pipeline remained duplicated). Fixes applied to `CircleLayout.js` were never ported to `BoxLayouter.js`:
|
|
383
|
-
1. **Locked nodes moved**: `CircleLayout.js` guards `if (!c.locked)` in `applyClusteredLayoutStep` (`113-116`), `centerCircleCollectionOn` (`232-235`), and `applyPullToCircles` (`282-298`). `BoxLayouter.js` moves locked boxes unconditionally in `applyPull` (`187-188`), `applyClusteredLayoutStep` (`350`), and `centerAABB2CollectionOn` (`412`).
|
|
384
|
-
2. **Missing `allowedSteps` argument**: `CircleLayout.js:103` calls `applyNodeSwaps(circles, 3)`, whereas `BoxLayouter.js:325` calls `applyNodeSwaps(boxes)` with 1 argument (`--allowedSteps` is `NaN`).
|
|
385
|
-
3. **`trySwapBoxes` swaps dimensions of boxes**: `BoxLayouter.js:114-125` swaps `box0` and `box1` via `box0.copy(box1); box1.copy(temp);`, which swaps the **widths and heights** of the two boxes instead of just swapping their centers `(centerX, centerY)`, and uses `if (costAfter > costBefore)` instead of `>=` (accepting zero-gain swaps).
|
|
386
|
-
4. **Internal duplication in `CircleLayout.js:584-647` vs `655-778`**: `circleOverlapsConnection` and `resolveCircleLineOverlap` duplicate ~60 lines of ray-circle quadratic intersection math.
|
|
387
|
-
- **Recommendation**: Outline the shared node-swap / clustered-layout orchestration (or port the center-only swap, `costAfter >= costBefore`, `allowedSteps = 3`, and `locked` guards to `BoxLayouter.js`) and extract `segmentCircleIntersection` in `CircleLayout.js`.
|
|
388
|
-
|
|
389
|
-
---
|
|
390
|
-
|
|
391
|
-
#### `[DUP-OUTLINE-05]` [HIGH] `LockRot*`-Masked Local Inverse Inertia Diagonal (`locked_inverse_inertia`) Duplicated in 4 Physics Files (and Missed in `RaycastVehicle`)
|
|
392
|
-
- **Locations**:
|
|
393
|
-
- `engine/physics/integration/integrate_velocity.js:19-28` (`locked_inverse_inertia`)
|
|
394
|
-
- `engine/physics/ccd/linear_sweep.js:1151-1157` (`masked_inverse_inertia`)
|
|
395
|
-
- `engine/physics/body/SolverBodyState.js:191-195` (`SolverBodyState.prototype.gather`)
|
|
396
|
-
- `engine/physics/ecs/PhysicsSystem.js:1262-1266` (`PhysicsSystem.prototype.applyImpulseAt`)
|
|
397
|
-
- Missed in `engine/physics/vehicle/RaycastVehicle.js:50-63` (`effective_mass` — causing `[BUG-04]`)
|
|
398
|
-
- **Analysis**:
|
|
399
|
-
Five places in `engine/physics` need a `RigidBody`'s local inverse inertia diagonal with axes locked by `RigidBodyFlags.LockRotX / Y / Z` zeroed out. Four implement it separately (`integrate_velocity.js` and `linear_sweep.js` each define their own file-private helper), and the fifth (`RaycastVehicle.js:50-63`) forgot to mask it at all.
|
|
400
|
-
- **Hot-Path Assessment**: Writing 3 numbers into a `Float64Array(3)` out-parameter (`locked_inverse_inertia(out, rb)`) is allocation-free and trivially inlined by V8.
|
|
401
|
-
- **Recommendation**: Export `locked_inverse_inertia(out, rb)` from `engine/physics/inertia/locked_inverse_inertia.js` and call it across all 5 sites.
|
|
402
|
-
|
|
403
|
-
---
|
|
404
|
-
|
|
405
|
-
#### `[DUP-OUTLINE-06]` [HIGH] Concave Triangle Enumeration (`decompose_to_triangles` Grow-and-Retry) & Rigid `TransformedShape3D` Unwrapping Duplicated Across 4 Physics Modules
|
|
406
|
-
- **Locations**:
|
|
407
|
-
- `engine/physics/narrowphase/narrowphase_step.js:2201-2230` (`narrowphase_concave_vs_convex`), `3184-3221` vs `3382-3411` (compound sub-pair pretest & feature-ID salting)
|
|
408
|
-
- `engine/physics/narrowphase/compute_penetration.js:287-349` (`unwrap_transformed`, `penetration_vs_concave`)
|
|
409
|
-
- `engine/physics/queries/convex_overlaps_shape.js:101-152` (`unwrap_rigid_transformed`, `overlaps_concave`)
|
|
410
|
-
- `engine/physics/queries/shape_cast.js:1667-1689` (`cast_convex_vs_concave`), `2216-2225` (`unwrap_rigid_transformed`)
|
|
411
|
-
- **Analysis**:
|
|
412
|
-
1. **Concave triangle enumeration**: All 4 modules transform a world-space query AABB into the concave shape's local space via `aabb3_transform_inverse`, call `concave.decompose_to_triangles(local_aabb, tri_buf, 0, tri_cap)`, check `if (tri_count > tri_cap && tri_cap < MAX_TRIANGLES_PER_PAIR)`, reallocate `tri_buf = new Float64Array(tri_cap * 9)` up to `MAX_TRIANGLES_PER_PAIR = 4096`, retry `decompose_to_triangles`, and clamp `tri_count`.
|
|
413
|
-
2. **Rigid `TransformedShape3D` unwrapping**: `compute_penetration.js`, `convex_overlaps_shape.js`, and `shape_cast.js` each define near-identical `unwrap_transformed` / `unwrap_rigid_transformed` helpers that compose unit-scale `TransformedShape3D` child poses via `v3_quaternion_apply` and `quat_multiply`.
|
|
414
|
-
3. **Compound sub-pair loop in `narrowphase_step.js`**: `narrowphase_step` (`3184-3221`) and `redetect_pair_geometry` (`3382-3411`) in the same file duplicate the compound collider sub-pair AABB pretest, child pose composition, narrowphase dispatch, and feature-ID salting (`(((ia + 1) * 0x9E3779B1) ^ ((ib + 1) * 0x85EBCA6B)) >>> 0`).
|
|
415
|
-
- **Recommendation**: Outline `decompose_concave_world_aabb(concave, pose_matrix, world_aabb)` and `unwrap_rigid_transformed(shape, pose)` in `engine/physics/narrowphase/`, and share a single compound sub-pair helper in `narrowphase_step.js`.
|
|
416
|
-
|
|
417
|
-
---
|
|
418
|
-
|
|
419
|
-
#### `[DUP-OUTLINE-07]` [HIGH] Cube vs Tetrahedron Point-Light Shadowmap Classes, Draw Contexts, and Octahedral Remap Shaders
|
|
420
|
-
- **Locations**:
|
|
421
|
-
- `shade/renderer/shadow/PointLightShadowmap.js:1-147` vs `shade/renderer/shadow/TetrahedronPointLightShadowmap.js:1-146`
|
|
422
|
-
- `shade/renderer/shadow/GPUSceneShadowmapContext.js:855-971` (`#draw_point`) vs `980-1102` (`#draw_point_tetrahedron`)
|
|
423
|
-
- `shade/renderer/shadow/shader_shadowmap_remap_cube_to_octahedral.js:1-177` vs `shade/renderer/shadow/shader_shadowmap_remap_tetrahedron_to_octahedral.js:1-244`
|
|
424
|
-
- **Analysis**:
|
|
425
|
-
- `PointLightShadowmap` (6 cube faces) and `TetrahedronPointLightShadowmap` (4 tetrahedron faces) are ~90% identical: both allocate `atlas_Regions` and `cameras`, compute `far = compute_point_light_radius(...)`, position face cameras at `light.position`, and update view-projection matrices when `dirty`.
|
|
426
|
-
- In `GPUSceneShadowmapContext.js`, `#draw_point` (116 lines) and `#draw_point_tetrahedron` (122 lines) are ~95% identical: both iterate over point lights, check the shadowmap cache, allocate atlas slots, iterate over N faces (`6` vs `4`) to frustum-cull and record rasterize passes into `face_depth_textures`, and dispatch the octahedral remap pass.
|
|
427
|
-
- `shader_shadowmap_remap_cube_to_octahedral.js` and `shader_shadowmap_remap_tetrahedron_to_octahedral.js` share the exact same 110-line octahedral border-wrapping and ray-direction outer shell, differing only in how `ray_dir` is projected into a face UV + depth sample.
|
|
428
|
-
- **Recommendation**: Parameterize `PointLightShadowmap` (or extract a shared base class) over face count and face camera setup, unify `#draw_point` and `#draw_point_tetrahedron` into `#draw_omnidirectional_point(light_index, shadowmap, face_count, remap_shader)`, and extract the shared octahedral remap shell.
|
|
429
|
-
|
|
430
|
-
---
|
|
431
|
-
|
|
432
|
-
#### `[DUP-OUTLINE-08]` [HIGH] Depth-of-Field Dual Pipeline (`DepthOfField` vs `DepthOfFieldU`, `compute_dof_uniform` vs `compute_dof2_uniform`, `graph_postprocess_dof` vs `graph_postprocess_dof2`, `raymarch` vs `slice` Shaders)
|
|
433
|
-
- **Locations**:
|
|
434
|
-
- `shade/renderer/postprocess/dof/DepthOfField.js:1-162` vs `shade/renderer/postprocess/dof/DepthOfFieldU.js:1-180`
|
|
435
|
-
- `shade/renderer/postprocess/dof/compute_dof_uniform.js:1-106` vs `shade/renderer/postprocess/dof/compute_dof2_uniform.js:1-125`
|
|
436
|
-
- `shade/renderer/postprocess/dof/graph_postprocess_dof.js:1-319` vs `shade/renderer/postprocess/dof/graph_postprocess_dof2.js:1-385`
|
|
437
|
-
- `shade/renderer/postprocess/dof/shader_dof_raymarch_compute.js:1-294` vs `shade/renderer/postprocess/dof/shader_dof_slice_compute.js:1-334`
|
|
438
|
-
- **Analysis**:
|
|
439
|
-
The `dof/` directory contains two nearly identical copies of the entire physical Depth-of-Field stack:
|
|
440
|
-
- `DepthOfField.js` and `DepthOfFieldU.js` share ~145 lines of identical lens getters/setters (`focus_distance`, `focal_length`, `f_stop`, `blades`, `sensor_size`, `max_coc_radius_pixels`, etc.).
|
|
441
|
-
- `compute_dof_uniform.js` and `compute_dof2_uniform.js` compute the exact same thin-lens optics equations (`focal_length_m`, `aperture_diameter_m`, `coc_scale`, `near_start`, `far_start`) and write identical byte offsets `0..63`, with `dof2` only appending 3 extra fields at bytes `64..79`.
|
|
442
|
-
- `graph_postprocess_dof.js` and `graph_postprocess_dof2.js` duplicate ~260 lines of CoC tile generation, tile neighborhood dilation, bilateral prefilter, temporal stabilization, and final composite passes.
|
|
443
|
-
- `shader_dof_raymarch_compute.js` and `shader_dof_slice_compute.js` duplicate ~160 lines of WGSL helper functions (`dof_luma`, `dof_karis_weight`, `dof_dir_for_blade`, `dof_regular_polygon_radius_dir`, `dof_hash12`, `dof_hash22`, `dof_concentric_disk`, `dof_bokeh_tap_offset`, `dof_update_Partition`, `dof_eval_Partition`).
|
|
444
|
-
- **Recommendation**: Extract the shared WGSL bokeh/partition functions into `chunk_dof_common.js`, have `compute_dof2_uniform` extend `compute_dof_uniform`, and unify `DepthOfField` / `DepthOfFieldU` via a mode option (`mode: 'raymarch' | 'slice'`).
|
|
445
|
-
|
|
446
|
-
---
|
|
447
|
-
|
|
448
|
-
#### `[DUP-OUTLINE-09]` [HIGH] `GPUAnimationManager.js` — Duplicated Clip vs Foliage Target Authority Claim & Release (`#take_targets` vs `#take_foliage_targets`, `#release_targets` vs `#release_foliage_targets`)
|
|
449
|
-
- **Locations**:
|
|
450
|
-
- `shade/renderer/animation/GPUAnimationManager.js:1666-1688` (`#take_targets`) vs `2175-2197` (`#take_foliage_targets`)
|
|
451
|
-
- `shade/renderer/animation/GPUAnimationManager.js:1695-1748` (`#release_targets`) vs `2204-2257` (`#release_foliage_targets`)
|
|
452
|
-
- **Analysis**:
|
|
453
|
-
`#take_targets` / `#release_targets` (for skeletal clip animations) and `#take_foliage_targets` / `#release_foliage_targets` (for procedural foliage wind animations) execute the exact same GPU authority claim, descendant traversal, reference counting (`#claimed_counts` vs `#foliage_claimed_counts`), and CPU matrix writeback on release (`#write_entity_world_to_transform64` + `HierarchyControlSystem.syncSubtreeFromWorld`).
|
|
454
|
-
Because they were duplicated instead of parameterized over the ref-count map, `#take_foliage_targets` (`line 2178`) received the fix `gpu_authority_declare(dataset, target)` before `gpu_authority_grant(dataset, target)`, while `#take_targets` (`line 1670`) **missed the fix** (causing `[BUG-09]`).
|
|
455
|
-
- **Recommendation**: Consolidate into `#take_authority_targets(targets, claimed_counts_map)` and `#release_authority_targets(targets, claimed_counts_map)`.
|
|
456
|
-
|
|
457
|
-
---
|
|
458
|
-
|
|
459
|
-
#### `[DUP-OUTLINE-10]` [HIGH] `gpu_table_partial_upload_shader.js` vs `gpu_table_gather_shader.js` vs `gpu_table_element_upload_shader.js` — Three 98%-Identical 116-Line Compute Shaders
|
|
460
|
-
- **Locations**:
|
|
461
|
-
- `shade/renderer/buffer/table/gpu_table_partial_upload_shader.js:1-116`
|
|
462
|
-
- `shade/renderer/buffer/table/gpu_table_gather_shader.js:1-116`
|
|
463
|
-
- `shade/renderer/buffer/table/single/gpu_table_element_upload_shader.js:1-106`
|
|
464
|
-
- **Analysis**:
|
|
465
|
-
`gpu_table_partial_upload_shader.js` and `gpu_table_gather_shader.js` are line-for-line identical across all 116 lines except for a single assignment direction on line 100 (`target_words[dst_word_offset + word_in_row] = source_words[src_word_offset + word_in_row]` vs `source_words[...] = target_words[...]`). `gpu_table_element_upload_shader.js` is the same kernel specialized to 1 page (`target_words` instead of `page_0..page_N`).
|
|
466
|
-
- **Recommendation**: Parameterize a single shader generator `build_gpu_table_transfer_shader(page_count, direction)` in `shade/renderer/buffer/table/`.
|
|
467
|
-
|
|
468
|
-
---
|
|
469
|
-
|
|
470
|
-
#### `[DUP-OUTLINE-11]` [HIGH] Tree Generation — Duplicated Hermite Segment Evaluators, Phyllotaxis Angle Switches, and 4-Slot Skin-Weight Interpolation
|
|
471
|
-
- **Locations**:
|
|
472
|
-
1. `generation/foliage/skeleton/TreeSkeleton.js:869-901` (`evaluate_segment_position`) vs `911-943` (`evaluate_segment_derivative`)
|
|
473
|
-
2. `generation/foliage/skeleton/tree_skeleton_grow.js:597-643` vs `generation/foliage/mesh/foliage_collect_attachments.js:169-179, 226-251`
|
|
474
|
-
3. `generation/foliage/mesh/tree_mesh_build_bark.js:374-422` vs `generation/foliage/mesh/foliage_collect_attachments.js:293-348`
|
|
475
|
-
- **Analysis**:
|
|
476
|
-
1. `TreeSkeleton.evaluate_segment_position` and `evaluate_segment_derivative` have identical 20-line endpoint/transform/tangent extraction prologues, differing only in calling `spline3_hermite` vs `spline3_hermite_derivative`.
|
|
477
|
-
2. `tree_skeleton_grow.js` and `foliage_collect_attachments.js` both implement the 6-way `switch (phyllotaxis_mode)` (`AlternateDistichous`, `Tristichous`, `Opposite`, `OppositeDecussate`, `Whorled`, `SpiralGolden`) and longitudinal `u` pairing/whorl index math.
|
|
478
|
-
3. `tree_mesh_build_bark.js:374-422` and `foliage_collect_attachments.js:293-348` both implement the 50-line 4-slot joint/weight linear interpolation between two parent bark rings `ra` and `rb` at fraction `xi`, deduplicating up to 8 joint IDs, sorting descending by weight, and normalizing the top 4 weights.
|
|
479
|
-
- **Recommendation**: Outline `phyllotaxis_sample_angle(mode, index, whorl_count, base_phi)` in `generation/foliage/species/PhyllotaxisMode.js` and `interpolate_ring_skin_weights4(out_joints, out_j_offset, out_weights, out_w_offset, ra, rb, xi)` in `generation/foliage/mesh/`.
|
|
480
|
-
|
|
481
|
-
---
|
|
482
|
-
|
|
483
|
-
### `MEDIUM` Confidence / Value
|
|
484
|
-
|
|
485
|
-
#### `[DUP-OUTLINE-12]` [MEDIUM] `ReactiveUnaryExpression` vs `ReactiveBinaryExpression` & 14 Operator Subclasses (`clone`, `equals`, `from`, `hash`, and `dummySignal`)
|
|
486
|
-
- **Locations**:
|
|
487
|
-
- `core/model/reactive/model/ReactiveUnaryExpression.js:24-28, 36-52, 72-85`
|
|
488
|
-
- `core/model/reactive/model/ReactiveBinaryExpression.js:62-68, 80-96, 118-151`
|
|
489
|
-
- All 12 binary subclasses (`core/model/reactive/model/{arithmetic,comparative,logic}/*.js`) and 2 unary subclasses (`ReactiveNegate.js`, `ReactiveNot.js`)
|
|
490
|
-
- `core/model/reactive/model/terminal/ReactiveLiteral{Boolean,Number,String}.js`
|
|
491
|
-
- **Analysis**:
|
|
492
|
-
1. All 14 operator subclasses duplicate identical `clone()`, `equals(other)`, and `static from(...)` methods that can be defined once on `ReactiveBinaryExpression` and `ReactiveUnaryExpression` using `this.constructor` / `new this()`.
|
|
493
|
-
2. `ReactiveUnaryExpression.prototype.hash()` (`83-85`) ignores `this.source.hash()`, and `ReactiveExpression.prototype.hash()` ignores `this.constructor.name` (so `a + b`, `a - b`, `a * b`, `a / b` all hash identically).
|
|
494
|
-
3. `ReactiveLiteralBoolean`, `ReactiveLiteralNumber`, and `ReactiveLiteralString` each create a module-level `const dummySignal = new Signal()` assigned to `this.onChanged`, so connecting any expression to a literal leaf registers `this.update` onto the module-global `dummySignal` and leaks the expression unless `.disconnect()` is called.
|
|
495
|
-
- **Recommendation**: Hoist `clone`, `equals`, and `static from` to `ReactiveUnaryExpression` / `ReactiveBinaryExpression`, include operator identity and `this.source.hash()` in `hash()`, and skip `onChanged.add` when `child.isLiteral === true`.
|
|
496
|
-
|
|
497
|
-
---
|
|
498
|
-
|
|
499
|
-
#### `[DUP-OUTLINE-13]` [MEDIUM] `IndexedFloatMinHeap.js` vs `IndexedFloatMaxHeap.js` and `Uint32Heap.js` vs `Uint32Heap4.js`
|
|
500
|
-
- **Locations**:
|
|
501
|
-
- `core/collection/heap/IndexedFloatMinHeap.js:1-278` vs `core/collection/heap/IndexedFloatMaxHeap.js:1-261`
|
|
502
|
-
- `core/collection/heap/Uint32Heap.js:1-476` vs `core/collection/heap/Uint32Heap4.js:1-491`
|
|
503
|
-
- **Analysis**:
|
|
504
|
-
- `IndexedFloatMinHeap` and `IndexedFloatMaxHeap` are 98% identical. `IndexedFloatMinHeap` received `assert.lessThan(id, this.__slot_of_id.length, 'id < id_capacity')` in `contains`, `change_priority`, `upsert`, and `remove` (with a detailed comment at lines `59-64` explaining how `id >= id_capacity` corrupts heap state because `undefined !== -1`), whereas `IndexedFloatMaxHeap` never received those assertions.
|
|
505
|
-
- `Uint32Heap` and `Uint32Heap4` share ~380 lines of identical buffer allocation, `insert`, `pop_min`, `remove`, `find_index`, and `validate` code, differing only in `heap_up` and `heap_down`.
|
|
506
|
-
- **Hot-Path Assessment**: ` __sift_up` / `__sift_down` and `heap_up` / `heap_down` are hot-path methods and should remain specialized per class, while non-hot container methods and assertions should be shared or synchronized.
|
|
507
|
-
- **Recommendation**: Port the `id < id_capacity` assertions to `IndexedFloatMaxHeap` and share a base class for `Uint32Heap` / `Uint32Heap4` non-sift methods.
|
|
508
|
-
|
|
509
|
-
---
|
|
510
|
-
|
|
511
|
-
#### `[DUP-OUTLINE-14]` [MEDIUM] `spline3_hermite_bounds.js` vs `spline3_hermite_bounds_t.js` (Called Back-to-Back in `spline3_hermite_intersection_spline3_hermite_1d.js`)
|
|
512
|
-
- **Locations**:
|
|
513
|
-
- `core/math/spline/spline3_hermite_bounds.js:23-109`
|
|
514
|
-
- `core/math/spline/spline3_hermite_bounds_t.js:26-115`
|
|
515
|
-
- `core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.js:72-111`
|
|
516
|
-
- **Analysis**:
|
|
517
|
-
`spline3_hermite_bounds` and `spline3_hermite_bounds_t` are 95% identical (~85 lines of quadratic derivative root-finding and endpoint comparison), differing only in whether `[min_v, max_v]` or `[min_t, max_t]` is written to `result`. In `spline3_hermite_intersection_spline3_hermite_1d.js:72-111`, both functions are called back-to-back on the exact same Hermite curve, solving the quadratic derivative twice.
|
|
518
|
-
- **Recommendation**: Outline `spline3_hermite_bounds_with_t(out_v, out_v_offset, out_t, out_t_offset, p0, p1, m0, m1)` and delegate both existing functions (and `spline3_hermite_intersection_spline3_hermite_1d.js`) to it.
|
|
519
|
-
|
|
520
|
-
---
|
|
521
|
-
|
|
522
|
-
#### `[DUP-OUTLINE-15]` [MEDIUM] 3x3 Rotation Matrix to Quaternion Conversion (`Shepperd's Method`) Duplicated in 3 Files
|
|
523
|
-
- **Locations**:
|
|
524
|
-
- `core/geom/Quaternion.js:179-227` (`Quaternion.prototype.fromRotationMatrix`)
|
|
525
|
-
- `core/geom/3d/mat4/m4_decompose_array.js:89-141`
|
|
526
|
-
- `core/geom/3d/quaternion/quat3_look_rotation.js:110-162`
|
|
527
|
-
- **Analysis**: All 3 files inline the 50-line 4-branch trace/diagonal conversion from 9 scalar rotation matrix entries `(m00..m22)` to a quaternion `(x, y, z, w)`.
|
|
528
|
-
- **Recommendation**: Outline `quat3_from_mat3_scalars(result, result_offset, m00, m01, m02, m10, m11, m12, m20, m21, m22)` in `core/geom/3d/quaternion/`.
|
|
529
|
-
|
|
530
|
-
---
|
|
531
|
-
|
|
532
|
-
#### `[DUP-OUTLINE-16]` [MEDIUM] 3-Ray Surface Parity Majority-Vote Inside/Outside Test Duplicated Between `bt_mesh_sample_interior_grid_points.js` and `tetrahedral_mesh_carve_outside_surface.js`
|
|
533
|
-
- **Locations**:
|
|
534
|
-
- `core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.js:52-71, 130-182`
|
|
535
|
-
- `core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_carve_outside_surface.js:274-322, 340-360`
|
|
536
|
-
- `core/geom/3d/topology/struct/binary/query/bt_mesh_surface_ray_parity.js:1-145`
|
|
537
|
-
- **Analysis**:
|
|
538
|
-
Both files define the same 3 skewed irrational ray directions (`SURFACE_PARITY_RAY_DIRECTIONS`), traverse a face BVH (`query_bvh_ray_intersections`), run watertight/Möller-Trumbore triangle intersection with `hit_faces` deduplication, and take a 2-of-3 odd-parity majority vote.
|
|
539
|
-
- **Recommendation**: Move the 3-ray majority vote point-in-mesh query into `bt_mesh_surface_ray_parity.js` and call it from both files.
|
|
540
|
-
|
|
541
|
-
---
|
|
542
|
-
|
|
543
|
-
#### `[DUP-OUTLINE-17]` [MEDIUM] BVH AABB Query Grow-and-Retry Pattern (`bvh_query_user_data_intersects_aabb`) & Query `seen_stamp` Body Deduplication
|
|
544
|
-
- **Locations**:
|
|
545
|
-
- **BVH query grow-and-retry**: `engine/physics/broadphase/generate_pairs.js:83-95, 114-122`, `engine/physics/ecs/PhysicsSystem.js:834-840`, `engine/physics/queries/overlap_shape.js:143-149`, `engine/physics/queries/shape_cast.js:2085-2092`, `engine/physics/cloth/collider/ClothColliderIndex.js:815-825`
|
|
546
|
-
- **Per-query `scratch_seen` + `seen_stamp`**: `engine/physics/queries/overlap_shape.js:26-27, 159-181`, `engine/physics/queries/raycast.js:24-25, 168-176, 341-345`, `engine/physics/queries/shape_cast.js:1873-1874, 2127-2145`
|
|
547
|
-
- **CCD `body_extent`**: `engine/physics/ccd/linear_sweep.js:1113-1134` vs `engine/physics/ecs/PhysicsSystem.js:2504-2529`
|
|
548
|
-
- **Recommendation**:
|
|
549
|
-
- Provide a growable scratch wrapper around `bvh_query_user_data_intersects_aabb`.
|
|
550
|
-
- Extract a shared `StampSet` in `engine/physics/queries/` for body deduplication across `overlap_shape`, `raycast`, and `shape_cast`.
|
|
551
|
-
- Export `body_extent` from `linear_sweep.js` and pass the precomputed extent from `PhysicsSystem.fixedUpdate` into `ccd_clamp_step` so it is not computed twice per CCD body per substep.
|
|
552
|
-
|
|
553
|
-
---
|
|
554
|
-
|
|
555
|
-
#### `[DUP-OUTLINE-18]` [MEDIUM] 8 Tonemap Compute Shaders (`shader_tonemap_*.js`) and 8 Texture Block-Compression Shaders (`shader_texture_encode_*.js`)
|
|
556
|
-
- **Locations**:
|
|
557
|
-
- `shade/renderer/deferred/tonemap/shader_tonemap_{aces,agx,gt,khronos_pbr_neutral,linear,lottes,reinhard2,uchimura}.js` (8 files)
|
|
558
|
-
- `shade/renderer/texture/encode/shader_texture_encode_{bc1,bc3,bc4,bc5,bc6h,bc7,etc1s,astc}.js` (8 files) and `chunk_pca_axis_{rgb,rgba,tangent}.js` (3 files)
|
|
559
|
-
- **Analysis**:
|
|
560
|
-
- All 8 tonemap compute shaders share an identical ~70-line wrapper (bindings for `input_texture`, `bloom_texture`, `exposure`, `output_texture`, `settings`, bloom mixing, exposure scaling, `chunk_sample_vignette`, `chunk_linear_to_srgb`, `chunk_interleaved_gradient_noise` dithering, and `textureStore`), differing only in the 1-line curve call (`tonemap_aces(color)`, `tonemap_agx(color)`, etc.).
|
|
561
|
-
- All 8 GPU texture compression shaders share the same `encode_uniforms` struct (`width`, `height`, `blocks_x`, `srgb`, `block_offset`), 2D workgroup dispatch bounds check, and $4\times 4$ texel block gather + edge clamping loop (`load_block_rgba` / `load_block_rgb`), and `chunk_pca_axis_{rgb,rgba,tangent}.js` duplicate the 6-iteration power-iteration covariance eigenvector solver.
|
|
562
|
-
- **Recommendation**: Replace the 8 tonemap shader files with a `build_tonemap_shader(curve_chunk, curve_fn_name)` factory, and extract `chunk_encode_load_block_4x4.js` + a shared power-iteration helper in `shade/renderer/texture/encode/`.
|
|
563
|
-
|
|
564
|
-
---
|
|
565
|
-
|
|
566
|
-
#### `[DUP-OUTLINE-19]` [MEDIUM] Transparent Rasterization Passes (`graph_rasterize_meshes_transparent_oit.js` vs `graph_rasterize_meshes_transparent_avboit.js`) & 5x Duplicated Scene/Light Bind Group Descriptors
|
|
567
|
-
- **Locations**:
|
|
568
|
-
- `shade/renderer/rasterize/pass/graph_rasterize_meshes_transparent_oit.js:103-173` vs `graph_rasterize_meshes_transparent_avboit.js:162-232`
|
|
569
|
-
- `shade/renderer/rasterize/pass/graph_rasterize_meshes_{opaque,alpha_tested,transmission,transparent_oit,transparent_avboit}.js` and `shade/renderer/deferred/graph_shade_deferred_meshes.js`
|
|
570
|
-
- **Analysis**:
|
|
571
|
-
- `graph_rasterize_meshes_transparent_oit.js` and `graph_rasterize_meshes_transparent_avboit.js` duplicate ~70 lines of material bucket iteration, `get_transparent_material_bucket_keys`, `material_db.get_material_from_key`, double-sided pipeline state setup, and `graph_extract_meshes_from_bucket`.
|
|
572
|
-
- All 5 forward/transparent rasterize passes and `graph_shade_deferred_meshes.js` construct the exact same 40-line `bg_1_descriptor` (and 80-line `bg_2_descriptor` lighting/LPV/shadow/environment bind group layout).
|
|
573
|
-
- **Recommendation**: Extract `build_rasterize_scene_bind_group_descriptor(...)` and `build_rasterize_lighting_bind_group_descriptor(...)` in `shade/renderer/rasterize/pass/`.
|
|
574
|
-
|
|
575
|
-
---
|
|
576
|
-
|
|
577
|
-
#### `[DUP-OUTLINE-20]` [MEDIUM] `shader_volumetrics_build_sun_shadow_volume.js` (`outermost` vs `seeded` 100-Line Compute Kernels)
|
|
578
|
-
- **Location**: `shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.js:48-153` vs `155-261`
|
|
579
|
-
- **Analysis**:
|
|
580
|
-
`shader_volumetrics_build_sun_shadow_volume_outermost` and `shader_volumetrics_build_sun_shadow_volume_seeded` are two 105-line WGSL compute shaders in the same file that share ~95 lines of identical bindings, `csm_cascade_uv_z` helper, `sample_transmittance` helper, and froxel z-slice raymarching loop, differing only in whether initial `optical_depth` starts at `0.0` or is read from `outer_shadow_volume`.
|
|
581
|
-
- **Recommendation**: Factor the common WGSL helpers and raymarch loop into a template `build_sun_shadow_volume_shader({ seeded })`.
|
|
582
|
-
|
|
583
|
-
---
|
|
584
|
-
|
|
585
|
-
#### `[DUP-OUTLINE-21]` [MEDIUM] `make_box_geometry.js` vs `make_plane_geometry.js` (`buildPlane` 89-Line Inner Function) and `usd_build_scene.js` (`expand_by_indices` vs `expand_indexed_to_flat`)
|
|
586
|
-
- **Locations**:
|
|
587
|
-
- `shade/renderer/geometry/primitive/make_box_geometry.js:49-137` vs `shade/renderer/geometry/primitive/make_plane_geometry.js:23-98`
|
|
588
|
-
- `format/scene/usd/usd_build_scene.js:588-604` (`expand_by_indices`) vs `609-625` (`expand_indexed_to_flat`)
|
|
589
|
-
- **Analysis**:
|
|
590
|
-
- `make_plane_geometry.js` was created by copy-pasting the 89-line inner `buildPlane` function from `make_box_geometry.js` (even keeping the `-vector[2]` normal negation that `make_box_geometry` used for its side faces!).
|
|
591
|
-
- In `usd_build_scene.js`, `expand_by_indices(values, indices, stride)` (`588-604`) and `expand_indexed_to_flat(src, indices, tuple_size)` (`609-625`) are written right next to each other in the same file and do the exact same strided `Float32Array` index expansion.
|
|
592
|
-
- **Recommendation**: Have `make_box_geometry` and `make_plane_geometry` share a `build_grid_plane_patch` helper, and delete `expand_indexed_to_flat` in `usd_build_scene.js` in favor of `expand_by_indices`.
|
|
593
|
-
|
|
594
|
-
---
|
|
595
|
-
|
|
596
|
-
#### `[DUP-OUTLINE-22]` [MEDIUM] Network Entity Serialization, Snapshot Compaction & Monotonic Sequence Comparison (`engine/network/*`)
|
|
597
|
-
- **Locations**:
|
|
598
|
-
- `engine/network/autoritative/ServerAuthoritativeServer.js:466-491, 909-990`
|
|
599
|
-
- `engine/network/replication/Replicator.js:354-370, 520-580`
|
|
600
|
-
- `engine/network/replication/Snapshotter.js:492-510`
|
|
601
|
-
- `engine/network/DeterministicLockstepPeer.js:615-645`
|
|
602
|
-
- `engine/network/SimActionExecutor.js:110-145` vs `engine/network/NetworkSession.js:220-255`
|
|
603
|
-
- **Analysis**:
|
|
604
|
-
- `ServerAuthoritativeServer.js`, `Replicator.js`, and `Snapshotter.js` duplicate the `compact_and_collect_Snapshot_records` bitset-compaction loop and component-mask binary serialization framing.
|
|
605
|
-
- `SimActionExecutor.js` and `NetworkSession.js` duplicate action envelope binary serialization/deserialization (`tick`, `player_id`, `action_id`, `payload`).
|
|
606
|
-
- **Recommendation**: Extract shared network framing and snapshot record compaction helpers in `engine/network/protocol/`.
|
|
607
|
-
|
|
608
|
-
---
|
|
609
|
-
|
|
610
|
-
#### `[DUP-OUTLINE-23]` [MEDIUM] `ImageView.js` vs `VideoView.js` (`HTMLElementCacheKey` Pooling & Missing `null` Guard in `VideoView`) and Editor `Vector2Control` / `Vector3Control` / `NumericIntervalControl`
|
|
611
|
-
- **Locations**:
|
|
612
|
-
- `view/elements/image/ImageView.js:12-142` vs `view/elements/video/VideoView.js:14-160`
|
|
613
|
-
- `view/controller/controls/Vector2Control.js:19-55`, `Vector3Control.js:19-62`, `NumericIntervalControl.js:20-56`
|
|
614
|
-
- **Analysis**:
|
|
615
|
-
- `ImageView` and `VideoView` are ~90% identical wrappers around `HTMLElementCacheKey` and DOM element pooling (`<img>` vs `<video>`). `ImageView` was updated to use `ImmutableObjectPool` (`ImageView.js:24-27`) and to guard `if (url === null || url === undefined) src = '';` (`62-63`), whereas `VideoView` still uses a manual `Cache<HTMLElementCacheKey, HTMLElement[]>` (`VideoView.js:14-57, 124-138`) and leaves `src = undefined` when `url === null` (setting `src="undefined"` on the `<video>` element).
|
|
616
|
-
- `Vector2Control`, `Vector3Control`, and `NumericIntervalControl` repeat the same `DatGuiController` surrogate object + `locked` re-entrancy guard boilerplate.
|
|
617
|
-
- **Recommendation**: Share a base `MediaElementView` (using `ImmutableObjectPool` and the `null`/`undefined` URL guard) for `ImageView` and `VideoView`.
|
|
618
|
-
|
|
619
|
-
---
|
|
620
|
-
|
|
621
|
-
### `LOW` Confidence / Value
|
|
622
|
-
|
|
623
|
-
#### `[DUP-OUTLINE-24]` [LOW] `SoftwareGPU{Compute,Render}PassEncoder.js`, `ShadeGPU{Compute,Render}PassEncoder.js`, and `{Compute,Render}PipelineManager.js`
|
|
624
|
-
- **Locations**:
|
|
625
|
-
- `shade/device/software/SoftwareGPUComputePassEncoder.js:35-88` vs `SoftwareGPURenderPassEncoder.js:60-115`
|
|
626
|
-
- `shade/device/Pass/ShadeGPUComputePassEncoder.js:1-145` vs `ShadeGPURenderPassEncoder.js:1-210`
|
|
627
|
-
- `shade/device/pipeline/ComputePipelineManager.js:1-125` vs `RenderPipelineManager.js:1-135`
|
|
628
|
-
- **Analysis**: Each Compute/Render pair duplicates bind group slot tracking (`#bind_groups`), `resolve_bindings(layout)`, and LRU pipeline cache lookup/creation.
|
|
629
|
-
- **Recommendation**: Extract a shared mixin/base helper for pass bind-group state and pipeline cache lookup.
|
|
630
|
-
|
|
631
|
-
---
|
|
632
|
-
|
|
633
|
-
#### `[DUP-OUTLINE-25]` [LOW] Fluid Effector GPU Record Serialization (`ImpulseFluidEffector`, `VortexFluidEffector`, `WakeFluidEffector`)
|
|
634
|
-
- **Locations**: `engine/physics/fluid/effector/{ImpulseFluidEffector,VortexFluidEffector,WakeFluidEffector}.js`
|
|
635
|
-
- **Analysis**: All three fluid effector classes implement the same 64-byte (`16 x float32`) GPU record layout packing (`write_gpu_record`).
|
|
636
|
-
- **Recommendation**: Extract a shared `write_fluid_effector_gpu_record` helper in `engine/physics/fluid/effector/`.
|
|
637
|
-
|
|
638
|
-
---
|
|
639
|
-
|
|
640
|
-
#### `[DUP-OUTLINE-26]` [LOW] Grid 0->1 Serialization Upgraders Duplicate v1 Serialization Adapters
|
|
641
|
-
- **Locations**:
|
|
642
|
-
- `engine/grid/grid2transform/GridPosition2TransformSerializationAdapter.js:19-41` vs `GridPosition2TransformSerializationUpgrader_0_1.js:13-35`
|
|
643
|
-
- `engine/grid/position/GridPositionSerializationAdapter.js:18-32` vs `GridPositionSerializationUpdater_0_1.js:11-30`
|
|
644
|
-
- **Recommendation**: Expose static `writeFields` helpers on the v1 adapters and call them from the 0->1 upgraders.
|
|
645
|
-
|
|
646
|
-
---
|
|
647
|
-
|
|
648
|
-
#### `[DUP-OUTLINE-27]` [LOW] Bitflag Accessor Quartet (`getFlag`, `setFlag`, `clearFlag`, `writeFlag`) Duplicated Across 35+ Classes With Single-Bit vs Multi-Bit Mask Drift
|
|
649
|
-
- **Locations**: 35+ root classes across `core/`, `engine/`, and `generation/` (e.g. `engine/ecs/EntityManager.js:181-231`, `engine/navigation/ecs/components/PathFollower.js:138-183`, `engine/sound/ecs/SoundEmitter.js:559-609`, `engine/graphics/trail/RibbonX.js:244-292`, `core/math/lookup/ParameterLookupTable.js:407-455`)
|
|
650
|
-
- **Analysis**:
|
|
651
|
-
35+ classes copy-paste the 30-line `getFlag` / `setFlag` / `clearFlag` / `writeFlag` quartet on `this.flags`, with subtle drift between `(this.flags & flag) === flag` (22 classes — multi-bit mask semantics) and `(this.flags & flag) !== 0` (13 classes — any-bit semantics).
|
|
652
|
-
- **Recommendation**: Standardize on `(this.flags & flag) === flag` or provide a lightweight `BitFlagged` base/mixin for non-TypedArray classes.
|
|
653
|
-
|
|
654
|
-
---
|
|
655
|
-
|
|
656
|
-
## 3. Evaluated & Justified Hot-Path Inlining / Specialization (`INLINE-*`)
|
|
657
|
-
|
|
658
|
-
The following patterns were evaluated during the whole-engine audit and determined to be **justified inlining or intentional monomorphic specialization**. They should **not** be abstracted behind generic higher-order functions or polymorphic dispatch:
|
|
659
|
-
|
|
660
|
-
- **`[INLINE-01]` Scalar vs Array/TypedArray Math Kernels (`core/geom/vec2/*`, `core/geom/vec3/*`, `core/geom/vec4/*`, `core/geom/3d/mat4/*`, `core/geom/3d/quaternion/*`)**:
|
|
661
|
-
Keeping separate scalar (`v3_dot`), array (`v3_dot_array`), and offset (`v3_Buffer_*`) functions avoids temporary array allocations and keeps arguments in V8 registers on physics and rendering hot paths.
|
|
662
|
-
- **`[INLINE-02]` Unrolled 8-Wide Ray-BVH8 Slab Intersection (`core/bvh8/traversal/bvh8_traverse_ray_intersect_watertight.js:334-441`)**:
|
|
663
|
-
The 8 child slab tests (`child0_tx0..child7_tz1`) are explicitly unrolled into local scalar variables so V8's TurboFan compiler keeps the 8-wide octant test in registers without per-ray array allocations.
|
|
664
|
-
- **`[INLINE-03]` 3D `BVH.js` vs 2D `BVH2D.js` Inner AABB Loops (`core/bvh2/bvh3/BVH.js` vs `core/geom/2d/bvh/BVH2D.js`)**:
|
|
665
|
-
While non-hot topology methods and bug fixes (`bubble_up_refit` height bug; see `[BUG-12]`) must stay synchronized, inlining the 6-float (`ELEMENT_WORD_COUNT = 10`) vs 4-float (`ELEMENT_WORD_COUNT = 8`) SAH and AABB union math directly over `this.__data_float32` in `insert_leaf` / `bubble_up_update` is justified for broadphase performance.
|
|
666
|
-
- **`[INLINE-04]` Physics PGS / TGS Contact & Joint Constraint Solvers (`engine/physics/solver/solve_contacts.js`, `engine/physics/cloth/solver/*`)**:
|
|
667
|
-
The inner Gauss-Seidel velocity/position iteration loops read and write flat `Float64Array` solver body states (`SolverBodyState`) inline. Outlining per-row closures or polymorphic calls inside the `iterations x manifolds x contacts` loop would regress solver throughput.
|
|
668
|
-
- **`[INLINE-05]` 3D MAC Grid Trilinear Stencils in Fluid Advection (`engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.js`, `v3_grid_advect_semi_lagrangian.js`)**:
|
|
669
|
-
Staggered u/v/w face interpolation in the `O(N^3)` voxel loops is intentionally unrolled per axis to avoid per-voxel callback overhead.
|
|
670
|
-
- **`[INLINE-06]` `Signal.prototype.send0 / send1 / send2 / send3 / send4` (`core/events/signal/Signal.js:76-205`)**:
|
|
671
|
-
Arity-specialized dispatch methods avoid `...args` rest-array allocation and `.apply()` overhead on high-frequency ECS and transform events.
|
|
672
|
-
- **`[INLINE-07]` `Transform64` Stride-32 `Float64Array` View Accessors (`engine/ecs/transform/Transform64.js`)**:
|
|
673
|
-
Direct indexed reads/writes on the 32-double backing buffer avoid wrapper object allocations during scene-graph hierarchy propagation.
|
|
674
|
-
- **`[INLINE-08]` Basis `etc1s_to_astc.js` vs `fit_endpoints.js` Endpoint Solvers (`format/texture/basis/etc1s_to_astc.js:118-246` vs `fit_endpoints.js:112-235`)**:
|
|
675
|
-
Although identically named (`fit_endpoints`), `etc1s_to_astc.js` solves a texel-frequency-weighted 16-texel least-squares system scoring `astc_interpolate`, whereas `fit_endpoints.js` solves an unweighted 4-selector span table for BC7. They have distinct mathematical contracts and should remain separate.
|
|
676
|
-
|
|
677
|
-
---
|
|
678
|
-
|
|
679
|
-
## 4. Major Logical Errors & Correctness Bugs (`BUG-*`)
|
|
680
|
-
|
|
681
|
-
### `HIGH` Severity / Confidence
|
|
682
|
-
|
|
683
|
-
#### `[BUG-01]` [HIGH] `WaterSystem.prototype.splash` Places Side-Effecting `m4_invert` Inside `assert.ok(...)` (Stripped in Production Builds)
|
|
684
|
-
- **Location**: `engine/graphics3/WaterSystem.js:271-275`
|
|
685
|
-
- **Problem**:
|
|
686
|
-
```js
|
|
687
|
-
const inverse = SPLASH_INVERSE;
|
|
688
|
-
assert.ok(m4_invert(inverse, entry.transform), 'water transform is singular');
|
|
689
|
-
const x = inverse[0] * position[0] + inverse[4] * position[1] + inverse[8] * position[2] + inverse[12];
|
|
690
|
-
const z = inverse[2] * position[0] + inverse[6] * position[1] + inverse[10] * position[2] + inverse[14];
|
|
691
|
-
```
|
|
692
|
-
Rollup's production build strips `assert.*` calls (`src/rollup.config.js`). In production builds, `m4_invert(inverse, entry.transform)` is completely removed, leaving `SPLASH_INVERSE` filled with all zeros, so every splash collapses to `x = 0, z = 0`.
|
|
693
|
-
- **Fix**:
|
|
694
|
-
```js
|
|
695
|
-
const inverted = m4_invert(inverse, entry.transform);
|
|
696
|
-
assert.ok(inverted, 'water transform is singular');
|
|
697
|
-
```
|
|
698
|
-
|
|
699
|
-
---
|
|
700
|
-
|
|
701
|
-
#### `[BUG-02]` [HIGH] `base58_encode` Never Zeroes Module-Level `scratch` Buffer, Corrupting All Subsequent Calls
|
|
702
|
-
- **Location**: `core/binary/base58/base58_encode.js:5, 41-67`
|
|
703
|
-
- **Problem**:
|
|
704
|
-
Line 5 allocates `const scratch = new Uint8Array(256);`, and line 42 selects `const b58 = (size <= scratch.length) ? scratch : new Uint8Array(size);` without zeroing `b58` (`b58.fill(0, 0, size)` is missing).
|
|
705
|
-
In the inner base-conversion loop (`lines 51-61`), whenever `i === length` and `carry !== 0` extends `length` into a new digit at `b58[it1]`, line 57 reads `carry += (256 * b58[it1]) >>> 0;` where `b58[it1]` still contains non-zero bytes from the previous call to `base58_encode`. On the second and subsequent calls, `carry` accumulates stale bytes from `scratch`, either throwing `AssertionError: carry must be zero` at line 63 in dev/test or returning a corrupted Base58 string in production.
|
|
706
|
-
- **Fix**: Add `b58.fill(0, 0, size);` immediately after line 42.
|
|
707
|
-
|
|
708
|
-
---
|
|
709
|
-
|
|
710
|
-
#### `[BUG-03]` [HIGH] `BinaryObjectSerializationAdapter` Corrupts Outer Buffer When Deserializing Nested Migrated Objects
|
|
711
|
-
- **Location**: `engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter.js:22-30, 106-125`
|
|
712
|
-
- **Compare With**: `engine/ecs/storage/binary/object-v2/BinaryObjectDeSerializer.js:304-309`
|
|
713
|
-
- **Problem**:
|
|
714
|
-
`BinaryObjectSerializationAdapter` stores two shared scratch buffers on the instance (`this.__upgradeBuffer0` and `this.__upgradeBuffer1`). In `deserialize(buffer)` (`lines 106-125`), after running an upgrader chain into `b0` (`this.__upgradeBuffer0` or `this.__upgradeBuffer1`), it calls `adapter.deserialize(b0, value)`.
|
|
715
|
-
If that outer object's `adapter.deserialize(b0, value)` deserializes a nested child object via `this.objectAdapter.deserialize(b0)` that **also** requires version migration, the nested call resets `this.__upgradeBuffer0.position = 0` and overwrites `this.__upgradeBuffer0` while the outer `adapter.deserialize` is still reading from it. (This exact bug was fixed in `object-v2/BinaryObjectDeSerializer.js:304-309` by allocating fresh `new BinaryBuffer()` instances per upgrade invocation, but was never ported to `BinaryObjectSerializationAdapter.js`.)
|
|
716
|
-
- **Fix**: Allocate local `BinaryBuffer` instances inside the `version !== targetVersion` branch of `BinaryObjectSerializationAdapter.prototype.deserialize`.
|
|
717
|
-
|
|
718
|
-
---
|
|
719
|
-
|
|
720
|
-
#### `[BUG-04]` [HIGH] `RaycastVehicle` `effective_mass` Ignores `RigidBodyFlags.LockRotX / Y / Z` and `addWheel` Caches `__drivenCount` Prematurely
|
|
721
|
-
- **Location**: `engine/physics/vehicle/RaycastVehicle.js:50-63, 170-211, 240, 330-358`
|
|
722
|
-
- **Problem**:
|
|
723
|
-
1. **`LockRot*` ignored in `effective_mass` (`lines 50-63`)**: `effective_mass` passes `rb.inverseInertiaLocal` (`ii[0], ii[1], ii[2]`) directly to `angular_effective_mass_raw(...)` without zeroing axes locked by `RigidBodyFlags.LockRotX / Y / Z`. However, `this.system.applyImpulseAt` (`line 358` -> `PhysicsSystem.js:1262-1266`) masks out locked rotational axes. When a chassis has locked roll/pitch (`LockRotX | LockRotZ`), `effective_mass` overestimates `denomSide` and `denomFwd`, systematically under-computing lateral grip and braking impulses every substep.
|
|
724
|
-
2. **`__drivenCount` cached before caller configures returned `Wheel` (`lines 170-211, 240`)**: The JSDoc on `addWheel` states *"Returns the created `Wheel` (also pushed to `wheels`); set any further config fields on it before stepping"*, but line 209 increments `if (w.driven) this.__drivenCount++;` before returning `w`. Setting `w.driven = true` on the returned `Wheel` leaves `this.__drivenCount = 0` and `drivePer = 0`.
|
|
725
|
-
- **Fix**: Mask `ii` using `locked_inverse_inertia` (`[DUP-OUTLINE-05]`) in `effective_mass`, and count `w.driven` across `this.wheels` at the start of `update(dt)`.
|
|
726
|
-
|
|
727
|
-
---
|
|
728
|
-
|
|
729
|
-
#### `[BUG-05]` [HIGH] LPV WGSL Type Mismatch (`SH2` 4-Vec3 Probe Indexed as `SH3` 9-Vec3 Array) and Unchecked `0.0 / 0.0 = NaN` / `INVALID_TET` Out-of-Bounds Reads
|
|
730
|
-
- **Locations**:
|
|
731
|
-
- `shade/renderer/shader/chunk/light/lpv/chunk_lpv_probe_get_data.js:7`
|
|
732
|
-
- `shade/renderer/shader/chunk/light/lpv/chunk_lpv_mesh_interpolate_probe.js:25-45`
|
|
733
|
-
- `shade/renderer/shader/chunk/light/lpv/chunk_lpv_mesh_interpolate_probe_irradiance.js:23-31`
|
|
734
|
-
- `shade/renderer/shader/chunk/light/lpv/chunk_lpv_interpolate_probe_irradiance8.js:20-29`
|
|
735
|
-
- `shade/renderer/global_illumination/lpv/graph_bake_irradiance_lookup_2d.js:85-95`
|
|
736
|
-
- `shade/renderer/shader/chunk/light/lpv/chunk_lpv_mesh_interpolate_radiance_via_atlas.js:35-38`
|
|
737
|
-
- `shade/renderer/shader/chunk/light/lpv/chunk_lpv_sample_light.js:42-81`
|
|
738
|
-
- **Problem**:
|
|
739
|
-
1. **SH2 vs SH3 WGSL compilation error**: `LIGHT_PROBE_STRUCT` stores 12 floats (`SH2`), and `lpv_probe_get_data` returns `array<vec3<f32>, 4>`. However, `chunk_lpv_mesh_interpolate_probe.js`, `chunk_lpv_mesh_interpolate_probe_irradiance.js`, and `chunk_lpv_interpolate_probe_irradiance8.js` declare `var probe: array<vec3<f32>, 9>` and index `p0[4..8]` on the return value of `lpv_probe_get_data`, which fails WGSL compilation whenever included (including `graph_bake_irradiance_lookup_2d.js:91-92`, which links `chunk_lpv_sample_sh3_at`).
|
|
740
|
-
2. **Unchecked `0.0 / 0.0 = NaN` and `INVALID_TET` out-of-bounds read**: In `chunk_lpv_mesh_interpolate_radiance_via_atlas.js:38` and `chunk_lpv_sample_light.js:57`, `c_weights /= dot(c_weights, vec4(1.0));` divides by `0.0` whenever `coord.barycentric == vec4(0.0)` or all 4 probe weights are zero. Furthermore, `lpv_sample_light` (`chunk_lpv_sample_light.js:42-81`) never checks `if (coord.cell == 0x3FFFFFFFu)` (`INVALID_TET`) after `lpv_2d_cache_sample`, indexing `lpv_mesh[0x3FFFFFFFu]` out of bounds and producing `NaN` specular GI on every cache miss.
|
|
741
|
-
- **Fix**: Update the three legacy SH3 LPV interpolation chunks to `array<vec3<f32>, 4>` (and remove the unused `chunk_lpv_sample_sh3_at` import from `graph_bake_irradiance_lookup_2d.js`), guard `if (coord.cell == 0x3FFFFFFFu) { return vec3(0.0); }` in `chunk_lpv_sample_light.js`, and guard `let w_sum = dot(c_weights, vec4(1.0)); if (w_sum <= 0.0) { return vec3(0.0); } c_weights /= w_sum;`.
|
|
742
|
-
|
|
743
|
-
---
|
|
744
|
-
|
|
745
|
-
#### `[BUG-06]` [HIGH] `meshopt_clusterizer.js` 3x Triangle Buffer Over-Read (`used_triangles * 3`) and Redundant JS `meshopt_optimizeMeshlet` Pass
|
|
746
|
-
- **Locations**:
|
|
747
|
-
- `shade/renderer/geometry/meshlet/build/meshoptimizer/meshopt_clusterizer.js:83, 115-141`
|
|
748
|
-
- `shade/renderer/geometry/meshlet/build/geometry_to_meshlets_meshoptimizer.js:76-81`
|
|
749
|
-
- **Problem**:
|
|
750
|
-
1. **3x buffer over-read**: In `meshopt_clusterizer.js:135`, `used_triangles = last_triangle_offset + last_triangle_count * 3` is already a **byte/index count** (`meshlet_trianglesp` was allocated with `sbrk(indices.length)` bytes at line 83). Yet line 140 allocates `new Uint8Array(heap.buffer, meshlet_trianglesp, used_triangles * 3).slice()`, multiplying by 3 a second time! This reads `3x` past `meshlet_trianglesp` into adjacent WASM heap allocations (`indicesp` / `verticesp`) or throws `RangeError: Invalid typed array length` when near the end of WASM memory. Additionally, when `count === 0`, `last_meshlet_offset = (0 - 1) * 4 = -4` reads `meshlets[-4..-1]`.
|
|
751
|
-
2. **Redundant optimization pass**: `meshopt_clusterizer.js:115-127` already runs WASM `instance.exports.meshopt_optimizeMeshlet` on every meshlet, and `geometry_to_meshlets_meshoptimizer.js:76-81` immediately runs the 110-line JS port `meshopt_optimizeMeshlet` a second time on every meshlet.
|
|
752
|
-
- **Fix**: Change `used_triangles * 3` at `meshopt_clusterizer.js:140` to `used_triangles` (with a `count > 0` guard), and remove the redundant second `meshopt_optimizeMeshlet` loop in `geometry_to_meshlets_meshoptimizer.js:76-81`.
|
|
753
|
-
|
|
754
|
-
---
|
|
755
|
-
|
|
756
|
-
#### `[BUG-07]` [HIGH] Meshlet Bucket Overflow Clamping Missing in `shader_meshlets_extract_bucket.js`
|
|
757
|
-
- **Location**: `shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.js:45-61`
|
|
758
|
-
- **Compare With**: `shade/renderer/rasterize/expand/bucket/shader_read_out_bucket.js:43-46`
|
|
759
|
-
- **Problem**:
|
|
760
|
-
`shader_read_out_bucket.js:43-46` was fixed to clamp `bucket_size` against `available = select(0u, input_capacity - bucket_header.offset, input_capacity > bucket_header.offset)` so that when a bucket overflows `bucket_data.elements`, `output.count` only reflects elements actually present in `bucket_data`.
|
|
761
|
-
Its twin `shader_meshlets_extract_bucket.js:45-61` only clamps `bucket_size` to `arrayLength(&output.elements)` and writes that unclamped count to `output.count` (line 49), while threads with `input_index >= arrayLength(&bucket_data.elements)` return early at line 58 without writing `output.elements[thread_id]`, causing downstream meshlet draw passes to read uninitialized `output.elements` entries.
|
|
762
|
-
- **Fix**: Apply the same `available` clamp from `shader_read_out_bucket.js:43-46` to `shader_meshlets_extract_bucket.js:45`.
|
|
763
|
-
|
|
764
|
-
---
|
|
765
|
-
|
|
766
|
-
#### `[BUG-08]` [HIGH] `GPUTypedTable.prototype.unmap` Has an Empty Pool-Release Block (Leaking Every Page Buffer) and `GPUSingleTypeTable.prototype.set` Reads Stale Recycled Upload Memory
|
|
767
|
-
- **Locations**:
|
|
768
|
-
- `shade/renderer/buffer/table/GPUTypedTable.js:1027-1031`
|
|
769
|
-
- `shade/renderer/buffer/table/single/GPUSingleTypeTable.js:206-244`
|
|
770
|
-
- **Problem**:
|
|
771
|
-
1. In `GPUTypedTable.prototype.unmap()` (`lines 1027-1031`):
|
|
772
|
-
```js
|
|
773
|
-
const pool = this.#page_uint32_array_pool;
|
|
774
|
-
if (pool !== undefined && page.cpu_data_buffer instanceof ArrayBuffer) {
|
|
775
|
-
}
|
|
776
|
-
```
|
|
777
|
-
The `if` body is completely empty (contrast with `GPUDatabase.js:598`: `pool.release(new Uint32Array(page.cpu_data_buffer));`), leaking every pooled page `ArrayBuffer` on `unmap()`.
|
|
778
|
-
2. In `GPUSingleTypeTable.prototype.set(id, value)` (`lines 236-240`), `write_wgsl_type_value` writes directly into a recycled `upload_buffer` acquired from `this.#allocator` without zeroing the element slot or copying from a shadow buffer (unlike `GPUTypedTable.js:1131-1161`), so any omitted struct fields or alignment padding bytes retain stale data from previous frames' uploads.
|
|
779
|
-
- **Fix**: Call `pool.release(new Uint32Array(page.cpu_data_buffer))` in `GPUTypedTable.prototype.unmap()`, and zero the destination slice `new Uint8Array(upload_buffer, dst_byte_offset, element_byte_size).fill(0)` before `write_wgsl_type_value` in `GPUSingleTypeTable.prototype.set`.
|
|
780
|
-
|
|
781
|
-
---
|
|
782
|
-
|
|
783
|
-
#### `[BUG-09]` [HIGH] `GPUAnimationManager.#take_targets` Omits `gpu_authority_declare(dataset, target)` Before `gpu_authority_grant`
|
|
784
|
-
- **Location**: `shade/renderer/animation/GPUAnimationManager.js:1670` (vs `#take_foliage_targets` at `line 2178`)
|
|
785
|
-
- **Problem**:
|
|
786
|
-
`#take_foliage_targets` (`line 2178`) calls `gpu_authority_declare(dataset, target)` before `gpu_authority_grant(dataset, target, ...)`, whereas `#take_targets` (`line 1670`) only calls `gpu_authority_declare` on descendants (`line 1683`) and omits it on `target` itself. Playing a GPU animation clip targeting any entity whose `GPUAuthorityComponent` was not pre-attached by the glTF prefab compiler fails in `gpu_authority_grant`.
|
|
787
|
-
- **Fix**: Add `gpu_authority_declare(dataset, target);` before `gpu_authority_grant` in `#take_targets` (or unify `#take_targets` and `#take_foliage_targets` per `[DUP-OUTLINE-09]`).
|
|
788
|
-
|
|
789
|
-
---
|
|
790
|
-
|
|
791
|
-
#### `[BUG-10]` [HIGH] `GTAO.prototype.render` Returns Pre-Write `moments_output` Resource Handle Instead of Post-Pass `moments` (Plus Bias Drift in `fragment_shader_gtao_with_irradiance.js`)
|
|
792
|
-
- **Locations**:
|
|
793
|
-
- `shade/renderer/postprocess/gtao/GTAO.js:97, 137-169`
|
|
794
|
-
- `shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js:137, 336`
|
|
795
|
-
- **Problem**:
|
|
796
|
-
1. In `GTAO.prototype.render` (`GTAO.js:97`), `const moments_output = graph.import_resource(...)` imports the temporal moments texture, and `ao_reproject_shader` (`line 137`) writes the updated moments into a new resource handle `const { ao, moments } = ...`. However, line 168 returns `visibility: moments_output` (the pre-pass handle at version 0) instead of `moments`, breaking frame-graph dependency edges for any pass consuming `visibility`.
|
|
797
|
-
2. In `fragment_shader_gtao_with_irradiance.js:137`, `var visibility = saturate((10 - screenspaceRadius)/100)*0.5;` initializes `visibility` to a positive bias before the slice loop and divides it by `SLICE_COUNT` at line 336 (this was fixed to `var visibility = 0.0;` in `fragment_shader_gtao.js:163`, but left unfixed in `fragment_shader_gtao_with_irradiance.js`).
|
|
798
|
-
- **Fix**: Return `visibility: moments` in `GTAO.js:168` and initialize `var visibility = 0.0;` in `fragment_shader_gtao_with_irradiance.js:137`.
|
|
799
|
-
|
|
800
|
-
---
|
|
801
|
-
|
|
802
|
-
#### `[BUG-11]` [HIGH] `ebvh_optimize_treelet.js` and `ebvh_update_hierarchy_bounds.js` Read Out-of-Bounds `NULL_NODE` Parent at Root, and `ebvh_optimize_treelet` Optimizes Root Twice
|
|
803
|
-
- **Locations**:
|
|
804
|
-
- `core/bvh2/bvh3/ebvh_optimize_treelet.js:338, 363-374, 435`
|
|
805
|
-
- `core/bvh2/bvh3/ebvh_update_hierarchy_bounds.js:77-88`
|
|
806
|
-
- **Problem**:
|
|
807
|
-
1. When `came_from_type === CAME_FROM_TYPE_CHILD_2` and `node === root`, both functions call `const parent = bvh.node_get_parent(node);` (`NULL_NODE = 0xFFFFFFFF`) and `bvh.node_get_child1(parent)` (reading `this.__data_uint32[10 * 0xFFFFFFFF + 7] -> undefined`) **before** checking `if (node === root) { break; }`.
|
|
808
|
-
2. When `node === root` and `came_from_type === CAME_FROM_TYPE_CHILD_2`, `ebvh_optimize_treelet.js` calls `optimize_at_node(bvh, node, treelet_size)` at line 338, breaks out of the loop at line 373, and immediately calls `optimize_at_node(bvh, root, treelet_size)` a second time at line 435.
|
|
809
|
-
- **Fix**: Check `if (node === root) { break; }` before reading `bvh.node_get_parent(node)` in both files, and remove the duplicate `optimize_at_node(bvh, root, treelet_size)` at line 435 of `ebvh_optimize_treelet.js`.
|
|
810
|
-
|
|
811
|
-
---
|
|
812
|
-
|
|
813
|
-
#### `[BUG-12]` [HIGH] `BVH2D.prototype.bubble_up_refit` Calls `this.balance(index)` Without Updating Ancestor Heights, and `StaticR2Tree.prototype.sort_morton` Produces `NaN` on Zero-Span Axes
|
|
814
|
-
- **Locations**:
|
|
815
|
-
- `core/geom/2d/bvh/BVH2D.js:675-698, 808-809`
|
|
816
|
-
- `core/bvh2/bvh3/BVH.js:918-919`
|
|
817
|
-
- `core/geom/2d/r-tree/StaticR2Tree.js:37-38, 239-243`
|
|
818
|
-
- **Problem**:
|
|
819
|
-
1. `BVH2D.prototype.bubble_up_refit` (`BVH2D.js:675-698`) is documented as `Only updates bounds / NOTE: Does not update "height"`, yet line 681 calls `index = this.balance(index);`, which performs AVL tree rotations based on node heights and recomputes heights only for the rotated pair while leaving ancestor heights stale (`BVH.js:779-800` removed `this.balance(index)` from `bubble_up_refit`, but `BVH2D.js` did not). Also both `BVH.js:918-919` and `BVH2D.js:808-809` assert `assert.notEqual(iA, iB)` twice instead of checking `iC` on the second line.
|
|
820
|
-
2. `StaticR2Tree.prototype.sort_morton` (`StaticR2Tree.js:242-243`) computes `const bounds_multiplier_x = 4095 / bounds_span_x;`. When all leaves have the same X or Y centroid (e.g. a 1-leaf tree or collinear axis-aligned items), `bounds_span_x === 0`, `bounds_multiplier_x === Infinity`, and `(cx - bounds_x0) * bounds_multiplier_x` evaluates to `0 * Infinity = NaN`.
|
|
821
|
-
- **Fix**: Remove `index = this.balance(index);` from `BVH2D.prototype.bubble_up_refit`, fix the second assertion in `BVH.js:919` / `BVH2D.js:809` to `assert.notEqual(iA, iC)`, and guard `bounds_span_x > 0 ? 4095 / bounds_span_x : 0` in `StaticR2Tree.js:242-243`.
|
|
822
|
-
|
|
823
|
-
---
|
|
824
|
-
|
|
825
|
-
#### `[BUG-13]` [HIGH] `TransformedShape3D` Returns Unrotated Local-Space SDF Gradient & `NaN` Distance on Zero Gradient; `UnionShape3D` Ignores `result_offset`; `shape_to_type` Rejects `BoxShape3D`
|
|
826
|
-
- **Locations**:
|
|
827
|
-
- `core/geom/3d/shape/TransformedShape3D.js:206-265`
|
|
828
|
-
- `core/geom/3d/shape/UnionShape3D.js:270-322`
|
|
829
|
-
- `core/geom/3d/shape/json/shape_to_type.js:17-18`
|
|
830
|
-
- **Problem**:
|
|
831
|
-
1. **`TransformedShape3D.prototype.signed_distance_gradient_at_point` (`lines 206-265`)**:
|
|
832
|
-
- Scales the local-space gradient `subject_point` by `(scale_x, scale_y, scale_z)` and writes it directly to `result` **without rotating the gradient from local space back to world space** (via the upper-left $3\times 3$ cofactor / inverse-transpose matrix). For any rotated `TransformedShape3D`, the returned gradient points in the wrong direction.
|
|
833
|
-
- Line 240 computes `const gd = distance / v3_length(subject_point[0], subject_point[1], subject_point[2]);` unguarded. When `distance === 0` and the local gradient is `(0, 0, 0)`, `gd = 0 / 0 = NaN`, making `scaled_distance = NaN` (which also breaks `signed_distance_at_point` at line 199).
|
|
834
|
-
2. **`UnionShape3D.prototype.sample_random_point_in_volume` (`lines 270-322`)**:
|
|
835
|
-
- Does not lazily initialize `if (this.__volume_sum === -1) this.__compute_volume_variables();`.
|
|
836
|
-
- Line 287 writes the candidate sample into `result` at `result_offset`, but line 303 calls `child_1.contains_point(result)`, which always reads `result[0], result[1], result[2]` instead of `result[result_offset .. result_offset + 2]`.
|
|
837
|
-
3. **`shape_to_type.js:17-18`**: Checks `else if (shape instanceof UnitCubeShape3D)` instead of `BoxShape3D`, even though `type_adapters.js:14-41` (`'cube'`) serializes and deserializes arbitrary `BoxShape3D` `half_extents`.
|
|
838
|
-
- **Fix**: Transform the gradient by the world rotation/inverse-transpose in `TransformedShape3D` and guard `v3_length > 0` before dividing; lazily initialize `__volume_sum` and pass a 3-element scratch point at offset 0 to `child_1.contains_point` in `UnionShape3D`; and check `instanceof BoxShape3D` in `shape_to_type.js`.
|
|
839
|
-
|
|
840
|
-
---
|
|
841
|
-
|
|
842
|
-
#### `[BUG-14]` [HIGH] `shg_query_elements_line` Fails to Clamp Grid Coordinates, Causing Row Wrapping and Infinite Loops on Upper Grid Boundary
|
|
843
|
-
- **Location**: `core/geom/2d/hash-grid/shg_query_elements_line.js:35-76`
|
|
844
|
-
- **Problem**:
|
|
845
|
-
Lines 35-38 allow coordinates up to `x <= grid.size_x * grid.scale` and `y <= grid.size_y * grid.scale`, for which `Math.floor(x * scale_inverse)` produces `igrid_x = grid.size_x` or `igrid_y = grid.size_y`. Unlike `SpatialHashGrid.prototype.element_insert` (`SpatialHashGrid.js:189-193`) and `shg_query_elements_aabb` (`shg_query_elements_aabb.js:24-27`), `shg_query_elements_line` never clamps `igrid_x0, igrid_y0, igrid_x1, igrid_y1` to `[0, grid.size_x - 1]` and `[0, grid.size_y - 1]`.
|
|
846
|
-
- When `x === grid.size_x`, `cell_position_to_index(x, y)` wraps to `(0, y + 1)`.
|
|
847
|
-
- When `y === grid.size_y`, `grid.cell_get_first_node(cell_index)` indexes past `this.__cells` and returns `undefined`; then `while (node !== NULL_POINTER)` (`undefined !== 0x3FFFFFFF`) at line 73 **loops forever** because `grid.node_get_next(undefined)` returns `undefined`.
|
|
848
|
-
- **Fix**: Clamp `igrid_x0, igrid_x1` to `[0, grid.size_x - 1]` and `igrid_y0, igrid_y1` to `[0, grid.size_y - 1]`.
|
|
849
|
-
|
|
850
|
-
---
|
|
851
|
-
|
|
852
|
-
#### `[BUG-15]` [HIGH] Spherical Harmonics Sign Convention Mismatch Between `sh3_basis_at` and `SH3_COEFFICIENTS` / `sh3_sample_by_direction`
|
|
853
|
-
- **Locations**:
|
|
854
|
-
- `core/geom/3d/sphere/harmonics/SH3_COEFFICIENTS.js:5-17`
|
|
855
|
-
- `core/geom/3d/sphere/harmonics/sh3_sample_by_direction.js:17-53`
|
|
856
|
-
- `core/geom/3d/sphere/harmonics/sh3_basis_at.js:13-42`
|
|
857
|
-
- `core/geom/3d/sphere/harmonics/sh3_sample_irradiance_by_direction.js:9-20`
|
|
858
|
-
- **Problem**:
|
|
859
|
-
As documented in `sh3_sample_irradiance_by_direction.js:9-20`, the engine's SH convention is the all-positive real basis with no Condon-Shortley phase (`sh3_basis_at.js:28-41`). However, `SH3_COEFFICIENTS.js:8, 10, 13, 15` still carries negative signs (`-0.4886025`, `-1.0925484`) on the odd-$m$ bands (`1: Y1,-1`, `3: Y1,1`, `5: Y2,-1`, `7: Y2,1`), and `sh3_sample_by_direction.js` multiplies by `SH3_COEFFICIENTS`. Consequently, projecting any directional signal with `sh3_basis_at` and reconstructing it with `sh3_sample_by_direction` flips the X and Y directional axes.
|
|
860
|
-
- **Fix**: Remove the negative signs on indices 1, 3, 5, and 7 in `SH3_COEFFICIENTS.js` (or evaluate the basis via `sh3_basis_at` inside `sh3_sample_by_direction.js`).
|
|
861
|
-
|
|
862
|
-
---
|
|
863
|
-
|
|
864
|
-
#### `[BUG-16]` [HIGH] `TetrahedralMesh.prototype.clear()` Only Zeroes `1/8`th of the Backing Buffer and `allocate()` Leaves Stale Neighbours on Recycled Tets
|
|
865
|
-
- **Location**: `core/geom/3d/tetrahedra/TetrahedralMesh.js:196-207, 430-455`
|
|
866
|
-
- **Problem**:
|
|
867
|
-
1. `clear()` (`line 199`) calls `this.__data_uint32.fill(0, 0, this.__used_end);`, where `this.__used_end` is the number of tetrahedra, but each tetrahedron occupies `LAYOUT_TETRA_WORD_COUNT = 8` `Uint32` words. Thus `clear()` only zeroes the first `1/8`th of the used buffer.
|
|
868
|
-
2. `allocate()` (`lines 431-434`) returns a recycled tetrahedron from `this.__free` without resetting its 4 neighbour slots to `INVALID_NEIGHBOUR` (unlike fresh allocations at lines `446-448`).
|
|
869
|
-
- **Fix**: Change line 199 to `this.__data_uint32.fill(0, 0, this.__used_end * LAYOUT_TETRA_WORD_COUNT);` and initialize all 4 neighbour slots to `INVALID_NEIGHBOUR` before returning a popped index from `this.__free`.
|
|
870
|
-
|
|
871
|
-
---
|
|
872
|
-
|
|
873
|
-
#### `[BUG-17]` [HIGH] `array_buffer_hash`, `uint16_array_hash`, and `uint8_array_hash` Produce Alignment-Dependent Hashes, Swapped Endianness, and Sign-Extended `Int8`/`Int16` Corruption
|
|
874
|
-
- **Locations**:
|
|
875
|
-
- `core/collection/array/typed/array_buffer_hash.js:12-38`
|
|
876
|
-
- `core/collection/array/typed/uint16_array_hash.js:8-29`
|
|
877
|
-
- `core/collection/array/typed/uint8_array_hash.js:8-36`
|
|
878
|
-
- `core/collection/array/typed/uint32_array_hash.js:15-31`
|
|
879
|
-
- **Problem**:
|
|
880
|
-
`array_buffer_hash(buffer, offset, length)` dispatches to `uint32_array_hash`, `uint16_array_hash`, or `uint8_array_hash` based on `(offset | length)` alignment, and `uint16_array_hash` / `uint8_array_hash` pack elements into 32-bit `word`s to match `uint32_array_hash`, but fail in 4 ways:
|
|
881
|
-
1. **Different initial seeds**: `uint32_array_hash` seeds `hash = length >>> 2`, `uint16_array_hash` seeds `hash = length >>> 1`, and `uint8_array_hash` seeds `hash = length`.
|
|
882
|
-
2. **Swapped endianness in `uint16_array_hash.js:22`**: `const word = (v0 << 16) | v1;` puts `v0` in the high 16 bits, whereas little-endian `Uint32Array` and `uint8_array_hash.js:26-29` (`v0 | (v1 << 8) | (v2 << 16) | (v3 << 24)`) put the first element in the low bits (`(v0 & 0xFFFF) | ((v1 & 0xFFFF) << 16)`).
|
|
883
|
-
3. **Sign-extension on `Int8Array` / `Int16Array`**: `uint8_array_hash` and `uint16_array_hash` accept `Int8Array` and `Int16Array`, but do not mask `v0 & 0xFF` or `v0 & 0xFFFF`, so negative elements sign-extend to `0xFFFFFFFF` and clobber all higher lanes of `word`.
|
|
884
|
-
4. **Missing `| 0` 32-bit truncation**: `uint8_array_hash`, `uint16_array_hash`, `uint32_array_hash`, and `float32_array_hash` omit `| 0` on `hash = ((hash << 5) - hash) + word`.
|
|
885
|
-
- **Fix**: Fix the word packing in `uint16_array_hash` to `(v0 & 0xFFFF) | ((v1 & 0xFFFF) << 16)`, mask bytes with `& 0xFF` in `uint8_array_hash`, add `| 0` on every hash accumulation step, and normalize the initial seed in `array_buffer_hash`.
|
|
886
|
-
|
|
887
|
-
---
|
|
888
|
-
|
|
889
|
-
#### `[BUG-18]` [HIGH] `UintArrayForCount` Uses Off-By-One Power-of-Two Thresholds (`256` and `65536`), Overflowing `HashMap` Bins at 255 Entries
|
|
890
|
-
- **Locations**:
|
|
891
|
-
- `core/collection/array/typed/uint_array_for_count.js:8-20`
|
|
892
|
-
- `core/collection/map/HashMap.js:290`
|
|
893
|
-
- **Problem**:
|
|
894
|
-
`UintArrayForCount(max_value)` is documented as `@param {number} max_value maximum value to be contained in the array`, and `HashMap.prototype.rehash` (`HashMap.js:290`) calls `UintArrayForCount(this.__entries_allocated_count + ENTRY_BASE)` where `ENTRY_BASE = 1`.
|
|
895
|
-
However, `UintArrayForCount` checks `if (max_value <= 256) return Uint8Array; else if (max_value <= 65536) return Uint16Array;`. Because `Uint8Array` can only store `0..255` and `Uint16Array` can only store `0..65535`, when `max_value === 256` (or `65536`), storing `256` into `Uint8Array` silently wraps to `0` (`EMPTY_BIN`), corrupting `HashMap` bin chains when `entries_allocated_count === 255`.
|
|
896
|
-
- **Fix**: Change `uint_array_for_count.js:11` to `if (max_value <= 255)` and line 13 to `else if (max_value <= 65535)`.
|
|
897
|
-
|
|
898
|
-
---
|
|
899
|
-
|
|
900
|
-
#### `[BUG-19]` [HIGH] `CuckooFilter` `computeBucketsNeeded` Passes Fractional Float to Bitwise `ceilPowerOfTwo`, Rounding Bucket Count Down by 2x
|
|
901
|
-
- **Location**: `core/collection/CuckooFilter.js:34-36`
|
|
902
|
-
- **Problem**:
|
|
903
|
-
`computeBucketsNeeded(max_keys, bucket_size)` returns `ceilPowerOfTwo(max_keys / bucket_size)`. Because `ceilPowerOfTwo(v)` begins with `let x = v - 1; x |= x >> 1;`, passing any non-integer float in `(2^k, 2^k + 1)` (such as `maxKeys = 5, 6, 7` with `bucket_size = 4` -> `v = 1.25, 1.5, 1.75`, or `maxKeys = 9, 10, 11` -> `v = 2.25, 2.5, 2.75`) truncates `v - 1` to `2^k - 1` and returns `2^k` instead of `2^(k+1)`.
|
|
904
|
-
- **Fix**: Change `CuckooFilter.js:35` to `return ceilPowerOfTwo(Math.ceil(max_keys / bucket_size));`.
|
|
905
|
-
|
|
906
|
-
---
|
|
907
|
-
|
|
908
|
-
#### `[BUG-20]` [HIGH] `BitSet.prototype.shift`, `MultiPredicateEvaluator.prototype.computeMaxParentScore`, `write_property`, `DescribeAs.prototype.describeTo`, `TypeBuilder.js`, `NodeParameterDescription.js`, and `LocalizationEngine.prototype.resolve`
|
|
909
|
-
- **Locations & Problems**:
|
|
910
|
-
1. **`core/binary/BitSet.js:658-664` (`shift`)**: Both `if (distance > 0)` and `else` call `this.shift_right(...)` instead of calling `this.shift_left(-distance, start_index, end_index)` when `distance < 0`.
|
|
911
|
-
2. **`core/model/reactive/evaluation/MultiPredicateEvaluator.js:62-82` (`computeMaxParentScore`)**: Line 75 calls `result = max2(parent.computeMaxParentScore(scoringFunction));` with **1 argument** instead of `max2(result, ...)`, which always returns `undefined` (`b`). Also line 69 returns early without setting `this.__max_parent_score` when `n === 0`.
|
|
912
|
-
3. **`core/model/object/write_property.js:12-17`**: Calls `read_property(root, parts, 0, last_index, true)` instead of `read_property(root, parts, part_offset, part_count - 1, true)`, ignoring `part_offset` whenever `part_offset > 0`.
|
|
913
|
-
4. **`core/debug/matchers/DescribeAs.js:39-59`**: Iterates `this.template.matchAll(RX)` via `for (let i = 0; i < matches.length; i++)` (`matches.length` is `undefined` on a `RegExpStringIterator`), reads `match.groups[1]` on an unnamed capture group (`match[1]`), and sets `textStart = match[0].length` instead of `match.index + match[0].length`.
|
|
914
|
-
5. **`core/binary/type/TypeBuilder.js:76` & `core/model/node-graph/node/parameter/NodeParameterDescription.js:69`**: Reference undeclared identifier `type` (instead of `structure` and `this.type`), throwing `ReferenceError: type is not defined`.
|
|
915
|
-
6. **`core/localization/LocalizationEngine.js:65-68`**: Indexes `locale[fragment_key]` directly on a `LocaleDataset` instance (which stores entries in private `this.#data = new Map()`) instead of calling `locale.get(fragment_key)`, so `resolve()` always misses.
|
|
916
|
-
- **Fix**: Apply the direct 1-line fixes at each site above.
|
|
917
|
-
|
|
918
|
-
---
|
|
919
|
-
|
|
920
|
-
#### `[BUG-21]` [HIGH] `StateNode.prototype.pickBestMoves` Crashes With `TypeError` When Any Child `MoveEdge` Is Unmaterialized, and `PathFollowingSystem` Poisons `position` With `NaN` on Zero-Length Looping Paths
|
|
921
|
-
- **Locations**:
|
|
922
|
-
- `engine/intelligence/mcts/StateNode.js:27-37, 291-329`
|
|
923
|
-
- `engine/navigation/ecs/path_following/PathFollowingSystem.js:31-70, 183-186`
|
|
924
|
-
- `engine/navigation/ecs/components/Path.js:101-103`
|
|
925
|
-
- **Problem**:
|
|
926
|
-
1. **`StateNode.prototype.pickBestMoves`**: Unlike `aggregateHeuristicScore` (`line 170`) and `traverse` (`line 356`), `pickBestMoves` iterates `this.moves` without checking `if (!move.isTargetMaterialized()) continue;`. Calling `pickBestScore(move, player)` on any unvisited `MoveEdge` dereferences `move.target.playouts` on `null` and throws `TypeError`.
|
|
927
|
-
2. **`PathFollowingSystem`**: When a `Path` has 1 point (or coincident points), `path.isEmpty()` is `false` and `path.length === 0`. If `PathFollowerFlags.Loop` is set, line 58 computes `next_offset % 0 = NaN` and line 69 writes `pathFollower.position = NaN`, permanently poisoning the follower. Additionally, `Path.prototype.clear()` (`Path.js:101-103`) fails to reset cached `this.__length = -1`.
|
|
928
|
-
- **Fix**: Skip `!move.isTargetMaterialized()` edges in `StateNode.prototype.pickBestMoves()`; guard `if (path_length <= 0)` in `PathFollowingSystem.js:performStep`; and reset `this.__length = -1` in `Path.prototype.clear()`.
|
|
929
|
-
|
|
930
|
-
---
|
|
931
|
-
|
|
932
|
-
#### `[BUG-22]` [HIGH] Behavior Tree Binary Serialization Crashes on `RepeatBehavior` (`limit = Infinity`), `InvertStatusBehavior`, and `ParallelBehavior`
|
|
933
|
-
- **Locations**:
|
|
934
|
-
- `engine/intelligence/behavior/decorator/RepeatBehaviorSerializationAdapter.js:15-32`
|
|
935
|
-
- `engine/intelligence/behavior/decorator/RepeatBehavior.js:30-43`
|
|
936
|
-
- `engine/intelligence/behavior/decorator/InvertStatusBehavior.js:16-20`
|
|
937
|
-
- `engine/intelligence/behavior/composite/ParallelBehavior.js:46-50`
|
|
938
|
-
- **Problem**:
|
|
939
|
-
1. `RepeatBehavior` defaults `limit` to `Infinity` (`RepeatBehavior.js:30`), which `RepeatBehaviorSerializationAdapter.prototype.serialize` passes to `buffer.writeUintVar(value.limit)`. `BinaryBuffer.prototype.writeUintVar` (`BinaryBuffer.js:1030`) throws `RangeError` because `(Infinity | 0) !== Infinity`.
|
|
940
|
-
2. `BinaryObjectSerializationAdapter.prototype.deserialize` (`line 120`) instantiates `new Klass()` with zero arguments before calling `adapter.deserialize(buffer, value)`. However, `new RepeatBehavior()` and `new InvertStatusBehavior()` unconditionally call `this.setSource(undefined)`, which throws `TypeError: Cannot read properties of undefined (reading 'isBehavior')`, and `new ParallelBehavior()` asserts `assert.enum(undefined, ParallelBehaviorPolicy)`.
|
|
941
|
-
- **Fix**: Guard `if (source !== undefined) this.setSource(source);` in `RepeatBehavior` and `InvertStatusBehavior`, provide default policies in `ParallelBehavior`'s constructor, and encode `Infinity` in `RepeatBehaviorSerializationAdapter`.
|
|
942
|
-
|
|
943
|
-
---
|
|
944
|
-
|
|
945
|
-
#### `[BUG-23]` [HIGH] `PriorityFetch` Causes Unhandled Promise Rejections on Every Failed Request and Stalls Queue Progression
|
|
946
|
-
- **Location**: `engine/network/PriorityFetch.js:131-180`
|
|
947
|
-
- **Problem**:
|
|
948
|
-
In `__dispatch(req)` (`lines 152-172`), `const promise = fetch(req.input, req.init);` attaches `promise.then(req.resolve, req.reject)` and separately attaches `promise.finally(() => { this.__pending_set.delete(req); this.__prod(); })` without chaining `.catch(...)` onto the `finally` branch. Because `promise.finally(...)` returns a new Promise that rejects whenever `promise` rejects, every failed `fetch` triggers an `UnhandledPromiseRejection` in the runtime even when the caller awaits/catches `priorityFetch.fetch(...)`. Additionally, `set concurrency(v)` (`lines 131-137`) does not call `this.__prod()`, and `__prod()` (`lines 174-180`) uses `if` instead of `while`.
|
|
949
|
-
- **Fix**: Chain `.finally(...)` onto `promise.then(req.resolve, req.reject)`, change `if` to `while` in `__prod()`, and call `this.__prod()` in `set concurrency(v)`.
|
|
950
|
-
|
|
951
|
-
---
|
|
952
|
-
|
|
953
|
-
#### `[BUG-24]` [HIGH] Missing `GPUShaderStage` Imports, `wglsl_tree_sitter_to_ast` Missing `cursor` Argument, `TextureResourceDescriptor` Domain Mismatch, and Lightmap Shader Bugs
|
|
954
|
-
- **Locations**:
|
|
955
|
-
1. `shade/descriptor/BindGroupLayoutDescriptor.js:50, 60` and `shade/renderer/lightmap/shaders/shader_lightmap_to_viz_buffer.js:154, 181, 208`: Reference `GPUShaderStage` without importing `{ GPUShaderStage } from "../../device/GPUConstants.js"`, throwing `ReferenceError` in software/headless WebGPU environments where `globalThis.GPUShaderStage` is undefined.
|
|
956
|
-
2. `shade/wgsl/wglsl_tree_sitter_to_ast.js:30, 57, 62`: Calls `advance_to_node_type('ident')` instead of `advance_to_node_type(cursor, 'ident')` at lines 57 and 62, making both calls silent no-ops (`'ident'.nodeType !== undefined` is `false`).
|
|
957
|
-
3. `shade/device/graph/TextureResourceDescriptor.js:36, 70-127, 137, 168`: `TextureResourceDescriptor.from()` leaves `resolution_domain = TextureResolutionDomain.PresentationRelative` (`1`), while `fromTexture()` sets `resolution_domain = TextureResolutionDomain.Absolute` (`0`), so `equals()` and `hash()` never match between descriptors created by the two factories.
|
|
958
|
-
4. `shade/renderer/lightmap/shaders/shader_lightmap_viz_to_sh.js:204`: Clamps `max(vec2<f32>(0.0), sh_irradiance.CoCg)` on signed YCoCg chrominance channels `Co` and `Cg` (which are centered at `0.0` and negative for cyan/blue/purple/magenta hues), destroying all negative chrominance in baked lightmaps.
|
|
959
|
-
5. `shade/renderer/lightmap/shaders/shader_lightmap_debug_texels.js:116` and `shader_lightmap_debug_charts.js:111`: Add `geometry.meshlets_address` twice (`let meshlet_id = geometry.meshlets_address + instance_index; let meshlet = get_meshlet_by_index(geometry.meshlets_address + meshlet_id);`).
|
|
960
|
-
6. `shade/renderer/postprocess/lens/chunk_sample_with_chromatic_aberration.js:11, 17`: `sample_with_chromatic_aberration(coord, input, strenght: f32)` ignores its `strenght` parameter and reads an undeclared global `settings.chromatic_aberration`.
|
|
961
|
-
7. `shade/renderer/geometry/sdf/shader_sdf_visualize.js:110-136`: Runs a 170-iteration `sdf_mesh_nearest_point` loop (`lines 110-125`), then unconditionally executes `return vec4(vec3(sdf_intersection_t)/60, 1.0);` at line 130 before lines 133-136 (`var sdf_normal = ...; return vec4(sdf_normal, 1.0);`).
|
|
962
|
-
- **Fix**: Add the missing `GPUShaderStage` imports, pass `cursor` in `wglsl_tree_sitter_to_ast.js`, set `resolution_domain = TextureResolutionDomain.Absolute` in `TextureResourceDescriptor.from()`, remove the `max(vec2(0.0), ...)` clamp on `CoCg`, pass `meshlet_id` directly to `get_meshlet_by_index`, pass `strength` to `ffx_lens_get_rg_mag(strength)`, and remove the leftover debug `return` at `shader_sdf_visualize.js:130`.
|
|
963
|
-
|
|
964
|
-
---
|
|
965
|
-
|
|
966
|
-
### `MEDIUM` Severity / Confidence
|
|
967
|
-
|
|
968
|
-
#### `[BUG-25]` [MEDIUM] Legacy `OrbitalCameraController` Never Calls `updateMatrix()` After `lookAt()` and Suffers First-Frame `dt` Spike
|
|
969
|
-
- **Location**: `shade/renderer/camera/OrbitalCameraController.js:68, 306-360` (vs `shade/renderer/camera/orbital/OrbitalCameraController.js`)
|
|
970
|
-
- **Problem**:
|
|
971
|
-
In `shade/renderer/camera/OrbitalCameraController.js`, `#update_camera()` calls `scratch_transform.lookAt(this.target)` (`Transform64.js:417`), which updates the quaternion `[16..19]` but does **not** call `scratch_transform.updateMatrix()`. Because `pan()` and `#process_keyboard_state()` transform camera movement vectors by the $4\times 4$ matrix `[0..15]`, camera panning and WASD movement always use a stale identity/previous matrix. Additionally, `#last_update_time = 0` causes a huge `time_delta` on the first frame.
|
|
972
|
-
A replacement class fixing both bugs was written at `shade/renderer/camera/orbital/OrbitalCameraController.js`, but all 16 playgrounds still import `shade/renderer/camera/OrbitalCameraController.js`.
|
|
973
|
-
- **Fix**: Re-export `shade/renderer/camera/orbital/OrbitalCameraController.js` from `shade/renderer/camera/OrbitalCameraController.js` (or call `scratch_transform.updateMatrix()` after `lookAt`).
|
|
974
|
-
|
|
975
|
-
---
|
|
976
|
-
|
|
977
|
-
#### `[BUG-26]` [MEDIUM] `SVGAssetLoader` Hangs Forever on Non-200 HTTP Responses, and `InputControllerSystem` / `InputSystem` Have Unlink & Splice Bugs
|
|
978
|
-
- **Locations**:
|
|
979
|
-
- `engine/asset/loaders/SVGAssetLoader.js:9-21`
|
|
980
|
-
- `engine/input/ecs/systems/InputControllerSystem.js:58-77, 128-139`
|
|
981
|
-
- `engine/input/ecs/systems/InputSystem.js:147-158`
|
|
982
|
-
- **Problem**:
|
|
983
|
-
1. `SVGAssetLoader.prototype.load` (`lines 9-21`): The `xhr` `'load'` listener checks `if (xhr.readyState === xhr.DONE && xhr.status === 200)` with no `else { failure(xhr); }`, so HTTP 404/500 responses never settle the asset request.
|
|
984
|
-
2. `InputControllerSystem`: `remove(mapping)` (`line 72`) calls ` list.splice(list.indexOf(listener), 1)` without checking `indexOf !== -1` (splicing the last element when not found); `__process` (`lines 128-139`) iterates a live listener array that can be mutated by `unlink()` mid-dispatch; and `InputSystem.prototype.unlink` (`InputSystem.js:152`) logs `console.error` when `componentData === undefined` without returning before `componentData.cleanup()`.
|
|
985
|
-
- **Fix**: Call `failure(xhr)` in the `else` branch of `SVGAssetLoader`, guard `indexOf !== -1` and snapshot/guard listeners in `InputControllerSystem`, and `return;` when `componentData === undefined` in `InputSystem.unlink`.
|
|
986
|
-
|
|
987
|
-
---
|
|
988
|
-
|
|
989
|
-
#### `[BUG-27]` [MEDIUM] `EntityComponentDataset.prototype.promiseComponent` Leaks Event Listeners, and `Ticker` Background Suspend Conflicts With Manual `pause()`
|
|
990
|
-
- **Locations**:
|
|
991
|
-
- `engine/ecs/EntityComponentDataset.js:302-336`
|
|
992
|
-
- `engine/simulation/Ticker.js:145-183, 347-388`
|
|
993
|
-
- **Problem**:
|
|
994
|
-
1. `promiseComponent` registers both `componentAdded` and `entityRemoved` listeners using `addComponentEventListener` / `addEntityEventListener`, but when either fires, it only removes that one listener (and uses `removeEntityEventListener` instead of `removeComponentEventListener` on line 319!), leaking the other listener on the dataset forever.
|
|
995
|
-
2. `Ticker`: When a manually paused `Ticker` (`this.isPaused === true`) loses and regains browser tab visibility, `#onVisibilityChange` (`lines 347-388`) calls `this.clock.start()`, resuming the underlying clock while `Ticker` is still paused.
|
|
996
|
-
- **Fix**: Remove both listeners using the matching `removeComponentEventListener` and `removeEntityEventListener` calls in `promiseComponent`, and only restart `this.clock` on visibility restore if `!this.isPaused`.
|
|
997
|
-
|
|
998
|
-
---
|
|
999
|
-
|
|
1000
|
-
#### `[BUG-28]` [MEDIUM] `FoaProbeReverbRenderer` Retains Stale `#scratchBands` on Unbind and Skips Rebake When Only Source Direction Changes
|
|
1001
|
-
- **Location**: `engine/sound/simulation/render/FoaProbeReverbRenderer.js:396-462`
|
|
1002
|
-
- **Problem**:
|
|
1003
|
-
When the probe field is unbound or out of range, `#rebake` returns early without zeroing `#scratchBands`, leaving the last valid reverb impulse response playing indefinitely. Additionally, `#lastSourceX/Y/Z` dirty checking ignores source orientation changes for directional emitters.
|
|
1004
|
-
- **Fix**: Zero `#scratchBands` when unbinding/invalidating, and include emitter forward direction in the rebake dirty check.
|
|
1005
|
-
|
|
1006
|
-
---
|
|
1007
|
-
|
|
1008
|
-
#### `[BUG-29]` [MEDIUM] `GridPosition2TransformSystem` Overwrites Entity Translation With `(0, 0, 0)` When `Terrain` Is Not Yet Present
|
|
1009
|
-
- **Location**: `engine/grid/grid2transform/GridPosition2TransformSystem.js:23-28, 115-137`
|
|
1010
|
-
- **Compare With**: `engine/grid/transform2grid/Transform2GridPositionSystem.js:40-50`
|
|
1011
|
-
- **Problem**:
|
|
1012
|
-
When `obtainTerrain(dataset)` returns `null`, `this.mapPoint(x, y, v3)` does nothing, leaving module-scoped `v3` at `(0, 0, 0)` (or the previous entity's world position), and `setTransformFromGrid` proceeds to overwrite `transform.setTranslation(v3.x, transform.translation_y, v3.z)`.
|
|
1013
|
-
- **Fix**: Return `false` from `this.mapPoint` when `terrain === null` and early-return in `setTransformFromGrid`.
|
|
1014
|
-
|
|
1015
|
-
---
|
|
1016
|
-
|
|
1017
|
-
#### `[BUG-30]` [MEDIUM] `MLS_MPMSolver` P2G Scatter Rejects Last Grid Row/Column (`grid_x >= n`), Whereas G2P Gather Includes `grid_width = n + 1`
|
|
1018
|
-
- **Location**: `engine/physics/mls-mpm/MLS_MPM.js:192, 203` (vs `lines 341, 352`)
|
|
1019
|
-
- **Problem**:
|
|
1020
|
-
The grid has `grid_width = n + 1` by `grid_height = n + 1` nodes (`lines 19-26, 97-98`). The G2P gather loop (`lines 341, 352`) checks `grid_y >= grid_height` and `grid_x >= grid_width`, whereas the P2G scatter loop (`lines 192, 203`) checks `grid_y >= n` and `grid_x >= n`, discarding momentum and mass scattered to node index `n`.
|
|
1021
|
-
- **Fix**: Change `grid_y >= n` (`line 192`) to `grid_y >= grid_height` and `grid_x >= n` (`line 203`) to `grid_x >= grid_width`.
|
|
1022
|
-
|
|
1023
|
-
---
|
|
1024
|
-
|
|
1025
|
-
#### `[BUG-31]` [MEDIUM] `ConditionalBehavior`, `RotationBehavior`, `BlackboardStack`, and `ResourceAllocationSolver` Bugs in `engine/intelligence`
|
|
1026
|
-
- **Locations & Problems**:
|
|
1027
|
-
1. `engine/intelligence/behavior/util/ConditionalBehavior.js:26-59`: `tick()` calls `condition.initialize(this.context)` unconditionally on every tick (resetting multi-tick conditions every frame) and calls `condition.finalize()` both in `tick()` and again in `finalize()`.
|
|
1028
|
-
2. `engine/intelligence/behavior/util/RotationBehavior.js:54-75`: Mutates `Transform64` rotation and calls `transform.updateMatrix()`, but never calls `t64_announce_change(this.ecd, this.entity)`.
|
|
1029
|
-
3. `engine/intelligence/blackboard/BlackboardStack.js:7-51`: Extends `AbstractBlackboard` without implementing `release(name)` or `getKeys()`, so inherited `incrementNumber()` and `make_blackboard_proxy()` throw `Error: Not implemented`.
|
|
1030
|
-
4. `engine/intelligence/resource/ResourceAllocationSolver.js:64-74`: `addResource(resource)` stores the first `Resource` reference without `.clone()` and mutates `existingResource.amount += resource.amount` in place on the caller's object when subsequent resources of the same type are added.
|
|
1031
|
-
- **Fix**: Initialize `condition` only when entering `ConditionalBehavior`; call `t64_announce_change` in `RotationBehavior`; implement `release` and `getKeys` in `BlackboardStack`; and store `resource.clone()` in `ResourceAllocationSolver.prototype.addResource`.
|
|
1032
|
-
|
|
1033
|
-
---
|
|
1034
|
-
|
|
1035
|
-
#### `[BUG-32]` [MEDIUM] `sampler2d_channel_compute_min_indices` Writes `0` Instead of `channel`, and `sort_triangles_by_distance_to_point` Passes `count - 1` Instead of `offset + count - 1`
|
|
1036
|
-
- **Locations**:
|
|
1037
|
-
- `engine/graphics/texture/sampler/sampler2d_channel_compute_min_indices.js:36-50`
|
|
1038
|
-
- `engine/graphics/sh3/path_tracer/geometry/sort_triangles_by_distance_to_point.js:29-46`
|
|
1039
|
-
- **Problem**:
|
|
1040
|
-
1. `sampler2d_channel_compute_min_indices.js:38` initializes `result[result_offset] = 0;` instead of `channel` (whereas the loop at line 42 starts at `i = channel + itemSize` and writes `result[...] = i`). Whenever `channel > 0` and the first pixel holds the minimum value, index `0` (wrong channel) is returned instead of `channel`.
|
|
1041
|
-
2. `sort_triangles_by_distance_to_point.js:46` passes `offset, count - 1` as the `(left, right)` inclusive indices to `array_quick_sort_by_comparator`, which immediately exits whenever `offset > 0` and `offset >= count - 1` instead of sorting `[offset, offset + count - 1]`.
|
|
1042
|
-
- **Fix**: Initialize `result[result_offset] = channel;` in `sampler2d_channel_compute_min_indices.js:38`, and pass `offset + count - 1` as the `right` bound in `sort_triangles_by_distance_to_point.js:46`.
|
|
1043
|
-
|
|
1044
|
-
---
|
|
1045
|
-
|
|
1046
|
-
#### `[BUG-33]` [MEDIUM] `GridTaskGenerateRoads` Never Initializes `this.traversable`, and `GridCellActionPlaceMarkerGroup` Never Initializes Child Actions
|
|
1047
|
-
- **Locations**:
|
|
1048
|
-
- `generation/grid/generation/road/GridTaskGenerateRoads.js:520-615`
|
|
1049
|
-
- `generation/markers/GridCellActionPlaceMarkerGroup.js:12-86`
|
|
1050
|
-
- **Problem**:
|
|
1051
|
-
1. `GridTaskGenerateRoads.prototype.build` initializes `this.actions` (`lines 539-543`), but never calls `this.traversable.initialize(grid, seed)` before `tBuildDistanceField`, `tBuildPaths`, and `tDrawPaths` evaluate `this.traversable`.
|
|
1052
|
-
2. `GridCellActionPlaceMarkerGroup` overrides `testNodeCollisions`, `computeWrites`, and `execute` to iterate `this.children`, but omits `initialize(data, seed)`, leaving child `GridCellActionPlaceMarker` RNGs and transformers uninitialized (`random = null`).
|
|
1053
|
-
- **Fix**: Call `traversable.initialize(grid, seed)` inside `tInitializeActions` (and add `tInitializeActions` as a dependency of `tBuildDistanceField` and `tBuildPaths`) in `GridTaskGenerateRoads.js`, and override `initialize(data, seed)` in `GridCellActionPlaceMarkerGroup.js` to initialize all children.
|
|
1054
|
-
|
|
1055
|
-
---
|
|
1056
|
-
|
|
1057
|
-
#### `[BUG-34]` [MEDIUM] View Layer Bugs in `SvgImageView`, `MinimapTerrainView`, `Vector1Control`, and `ListController`
|
|
1058
|
-
- **Locations & Problems**:
|
|
1059
|
-
1. `view/elements/image/SvgImageView.js:65-69`: `update()` calls `resize(self.size.x, self.size.y)`, where both `resize` and `self` are local to `constructor` and out of scope in `update()`, throwing `ReferenceError: resize is not defined` whenever `link()` (line 77) or `update()` is called.
|
|
1060
|
-
2. `view/minimap/dom/MinimapTerrainView.js:127, 134-144`: `__setTerrain` is registered without `this` context (`this.terrain.onChanged.add(this.__setTerrain)`), does not guard `if (terrain !== null)`, and references `updatePreview` which is scoped inside `constructor`, throwing `ReferenceError: updatePreview is not defined`.
|
|
1061
|
-
3. `view/controller/controls/Vector1Control.js:67, 80`: `subscribe(v1)` and `unsubscribe(v1)` register `v1.onChanged.add(writeBack)` instead of `syncForward`, so model changes on `v1` write `v1`'s value back into `v1` instead of updating the UI `NumberController` (`cNumber`).
|
|
1062
|
-
4. `view/controller/controls/ListController.js:46, 62, 67-74`: `handleItemAdded` adds `vElementContainer` to `vContainer` (`vContainer.addChild(vElementContainer)`), but `handleItemRemoved` calls `vContainer.removeChild(guiControl)` instead of `vContainer.removeChild(vElementContainer)`, leaving removed item containers in the DOM.
|
|
1063
|
-
- **Fix**: Bind `resize` / `updatePreview` on `this`, register `syncForward` on `v1.onChanged` in `Vector1Control`, and store/remove `vElementContainer` in `ListController`.
|
|
1064
|
-
|
|
1065
|
-
---
|
|
1066
|
-
|
|
1067
|
-
#### `[BUG-35]` [MEDIUM] `ConcurrentExecutor` Hangs `TaskGroup` When Child Task Is Cancelled, `ParameterLookupTable.addValue` Leaves `valueMin === valueMax === 0`, and `DebouncedObservedBoolean` Uses Mismatched Timeout Field Names
|
|
1068
|
-
- **Locations & Problems**:
|
|
1069
|
-
1. `core/process/executor/ConcurrentExecutor.js:141-150, 212, 692-705`: `runGroup` listens to `child.on.completed` and `child.on.failed`, but not `child.on.cancelled` (so cancelling a child task via `removeTask` hangs the `TaskGroup` forever), and `tryResolve` does not handle `TaskState.CANCELLED`.
|
|
1070
|
-
2. `core/math/lookup/ParameterLookupTable.js:310-345, 370-386`: `addValue()` mutates `this.positions` and `this.data` without updating `this.valueMin` and `this.valueMax` (which default to `0`). In `hash()` (`line 373`), `if (valueMin === valueMax)` is `true` (`0 === 0`), so `hash()` returns `computeHashFloat(0)` (`0`) for every table populated via `addValue()`.
|
|
1071
|
-
3. `core/model/DebouncedObservedBoolean.js:38, 59-62, 106`: Constructor initializes `this.__timeout_ms = null`, whereas `__clearTimeout` and `__handleSourceChange` read/write `this.__timeout`.
|
|
1072
|
-
- **Fix**: Handle `TaskState.CANCELLED` and `child.on.cancelled` in `ConcurrentExecutor`, update `valueMin`/`valueMax` in `ParameterLookupTable.prototype.addValue`, and rename `__timeout_ms` to `__timeout` in `DebouncedObservedBoolean`.
|