@woosh/meep-engine 3.60.4 → 3.60.6
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 +2621 -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/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/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/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/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/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- 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 +11 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +50 -6
- 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 +28 -45
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +111 -119
- package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts +22 -3
- package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts.map +1 -1
- package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.js +84 -3
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.d.ts +19 -0
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.js +25 -0
- 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/VirtualGeometry.d.ts +15 -0
- package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.js +38 -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/geometry/virtual/sdf/VGeoSDFProxies.d.ts +28 -59
- package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.js +84 -789
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +1 -8
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts +7 -6
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.js +12 -11
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.d.ts +2 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.js +2 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +1 -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 +1 -10
- 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/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/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/material/GPUMaterialManager.d.ts +11 -1
- package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
- package/src/shade/renderer/material/GPUMaterialManager.js +16 -8
- 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/gtao/GTAO.d.ts +7 -3
- package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/GTAO.js +26 -13
- package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts +14 -0
- package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.js +196 -68
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js +42 -28
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js +429 -414
- package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.d.ts +16 -0
- package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.js +156 -0
- package/src/shade/renderer/scene/GPUSceneContext.d.ts +2 -2
- package/src/shade/renderer/scene/GPUSceneContext.js +5 -5
- 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/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/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 -481
- 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 -529
- package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts +0 -47
- package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.js +0 -80
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts +0 -16
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.js +0 -67
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts +0 -17
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.js +0 -145
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts +0 -12
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.js +0 -105
- 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 -4306
- 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/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 -280
- 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 -332
- 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 -391
- package/src/shade/renderer/shader/chunk/color/tonemap/ACES_TONE_MAPPING_BRIEF_2026_10_17.md +0 -153
- 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 -927
- 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 -3
- 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
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
# `WorkerSystem` (Design A) — implementation plan
|
|
2
|
-
|
|
3
|
-
Companion to [`../ASYNC_SYSTEMS_PROPOSAL.md`](../ASYNC_SYSTEMS_PROPOSAL.md). This is the
|
|
4
|
-
SharedArrayBuffer + Atomics variant with a **synchronous hot path**: the `System`
|
|
5
|
-
tick stays synchronous, the worker owns the data between ticks, and a fixed-step
|
|
6
|
-
gate in `EntityManager` keeps exactly one step in flight.
|
|
7
|
-
|
|
8
|
-
> **Status: implemented.** Where the built code diverged from this plan, the plan
|
|
9
|
-
> has been updated to match — see §6.
|
|
10
|
-
|
|
11
|
-
First consumer: `WorkerFluidSystem`, a drop-in alternative to `FluidSystem`.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## 1. Design in one page
|
|
16
|
-
|
|
17
|
-
Per fixed tick `T`, a `WorkerSystem` does exactly two things:
|
|
18
|
-
|
|
19
|
-
1. **Join** the outstanding step — the gate has already guaranteed its `DONE`, so
|
|
20
|
-
this is a load and a hook call, never a wait.
|
|
21
|
-
2. **Dispatch** step `T` — write the command words, `Atomics.store(REQ, T)`,
|
|
22
|
-
`Atomics.notify`.
|
|
23
|
-
|
|
24
|
-
Between dispatch and the next join the **worker owns the shared state**. The main
|
|
25
|
-
thread must not touch it. The gate makes that safe for the simulation:
|
|
26
|
-
`EntityManager` refuses to begin a tick while a step is outstanding, so every
|
|
27
|
-
system's `fixedUpdate` runs with the worker idle.
|
|
28
|
-
|
|
29
|
-
**Exactly one step is in flight**, which is structural rather than a tuning knob.
|
|
30
|
-
The worker mutates shared state in place, so the main thread has nowhere to build
|
|
31
|
-
a second step's input while the first runs; and `REQ` holds a single tick, so a
|
|
32
|
-
second dispatch before the worker consumed the first would overwrite it and
|
|
33
|
-
silently drop a step. A deeper pipeline needs double-buffered payloads and a
|
|
34
|
-
request queue — a different design, not a parameter on this one.
|
|
35
|
-
|
|
36
|
-
```
|
|
37
|
-
fixed tick: T T+1 T+2
|
|
38
|
-
main thread: join(T-1) join(T) join(T+1)
|
|
39
|
-
dispatch(T) dispatch(T+1) dispatch(T+2)
|
|
40
|
-
...other systems, ...other systems,
|
|
41
|
-
render, idle... render, idle...
|
|
42
|
-
worker: └── steps T ─────────┘ └── steps T+1 ────────┘
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Two planes:
|
|
46
|
-
|
|
47
|
-
- **Hot plane** — `Atomics` over a `SharedArrayBuffer`. Per-tick, zero-copy,
|
|
48
|
-
zero allocation, no message queue.
|
|
49
|
-
- **Cold plane** — `postMessage`. Structural changes only (register a new field,
|
|
50
|
-
boot payloads). Rare, and never on the critical path.
|
|
51
|
-
|
|
52
|
-
Because a futex-parked worker (`Atomics.wait`) does not turn its event loop, cold
|
|
53
|
-
messages would never be delivered while the worker is parked or busy. A `MSG_SEQ`
|
|
54
|
-
counter + `notify` wakes the pump, which yields to the event loop, lets the
|
|
55
|
-
message land, and re-enters via `setTimeout(0)`. The main thread does **not**
|
|
56
|
-
stall on this: newly registered work is simply excluded from dispatch until the
|
|
57
|
-
worker acks it via `ACK_SEQ`.
|
|
58
|
-
|
|
59
|
-
### Control block (`Int32Array` header at the head of one SAB)
|
|
60
|
-
|
|
61
|
-
| slot | name | writer | meaning |
|
|
62
|
-
|---|---|---|---|
|
|
63
|
-
| 0 | `REQ` | main | latest dispatched tick |
|
|
64
|
-
| 1 | `DONE` | worker | latest completed tick |
|
|
65
|
-
| 2 | `ERR` | worker | 0 = ok, else the step threw |
|
|
66
|
-
| 3 | `QUIT` | main | 1 = terminate the pump |
|
|
67
|
-
| 4 | `MSG_SEQ` | main | bumped when a cold message is posted |
|
|
68
|
-
| 5 | `ACK_SEQ` | worker | echoes `MSG_SEQ` once handled |
|
|
69
|
-
|
|
70
|
-
Publication is protocol-ordered, not per-word-atomic: the worker finishes all
|
|
71
|
-
plain writes to the shared payload, *then* seq-cst-stores `DONE`. Main seq-cst-loads
|
|
72
|
-
`DONE ≥ T` *before* reading. That pair is the happens-before edge, so the payload
|
|
73
|
-
itself needs no atomics.
|
|
74
|
-
|
|
75
|
-
Followed by a `Float64Array` **command region** — per-tick parameters, written by
|
|
76
|
-
main before the `REQ` store, read by the worker after the `REQ` load.
|
|
77
|
-
|
|
78
|
-
### Deadlock safety
|
|
79
|
-
|
|
80
|
-
The gate is the one new way to stall the simulation, so every path must be
|
|
81
|
-
provably non-blocking:
|
|
82
|
-
|
|
83
|
-
| situation | outcome |
|
|
84
|
-
|---|---|
|
|
85
|
-
| worker computing | `DONE` lands → gate opens |
|
|
86
|
-
| step threw | host sets `ERR` **and** `DONE` → gate opens, main disables the system |
|
|
87
|
-
| worker `onerror` / dead | `#failed = true` → gate returns `true` unconditionally |
|
|
88
|
-
| worker never booted | gate is registered *after* the ready handshake; `startup` never resolves, so `fixedUpdate` never runs |
|
|
89
|
-
| **worker hangs or is killed silently** | no `DONE`, no `error` event — caught by a wall-clock deadline (`step_deadline_ms`) that fails the bridge, which opens the gate |
|
|
90
|
-
| shutdown | gate unregistered before terminate |
|
|
91
|
-
| never dispatched (priming, `collect` declined, detached dataset) | nothing in flight → gate returns `true` |
|
|
92
|
-
| `collect` / `apply` throws | `EntityManager` logs it; no dispatch happens that tick, so the gate stays open |
|
|
93
|
-
|
|
94
|
-
A worker that is merely *slow* costs the bounded spin, then the loop breaks with
|
|
95
|
-
the accumulator loaded.
|
|
96
|
-
|
|
97
|
-
**A gated system runs at most one fixed step per `simulate()` call.** Once it has
|
|
98
|
-
dispatched, the gate closes for the rest of that cycle, so the fixed rate is
|
|
99
|
-
capped at the rate `simulate()` is called — normally the frame rate. Unlike an
|
|
100
|
-
over-budget main-thread system, which can still run several steps inside the
|
|
101
|
-
15 ms catch-up budget, a gated one cannot repay accumulated debt at all: if the
|
|
102
|
-
worker cannot sustain the fixed step rate, the simulation runs slower than real
|
|
103
|
-
time indefinitely and `__fixedStepAccumulator` grows without bound. This is
|
|
104
|
-
reported once via `console.warn` after `BLOCKED_CYCLES_BEFORE_WARNING`
|
|
105
|
-
consecutive blocked cycles, because the failure is otherwise invisible — the
|
|
106
|
-
fixed loop's own runaway warning sits downstream of the gate's `break` and never
|
|
107
|
-
fires. Fixed step size and per-step worker cost have to be chosen with that
|
|
108
|
-
ceiling in mind.
|
|
109
|
-
|
|
110
|
-
---
|
|
111
|
-
|
|
112
|
-
## 2. Files
|
|
113
|
-
|
|
114
|
-
### Framework — `engine/ecs/async/`
|
|
115
|
-
|
|
116
|
-
| file | contents |
|
|
117
|
-
|---|---|
|
|
118
|
-
| `WorkerSystemProtocol.js` | control-slot enum, sizing, `worker_system_allocate_shared`, spin helper |
|
|
119
|
-
| `WorkerSystem.js` | `WorkerSystem extends System` — lifecycle, hot path, cold path, gate |
|
|
120
|
-
| `SystemWorkerHost.js` | worker-side pump: wait → step → publish, message yield, error capture |
|
|
121
|
-
| `SystemWorkerLoopback.js` | in-process stand-in for a `Worker`, so bridges are testable without a thread |
|
|
122
|
-
| `WorkerSystem.spec.js` | main-thread lifecycle + gate, real SABs, real atomics |
|
|
123
|
-
| `SystemWorkerHost.spec.js` | worker-side pump including the real `Atomics.wait` park |
|
|
124
|
-
|
|
125
|
-
### Core hook — `engine/ecs/EntityManager.js`
|
|
126
|
-
|
|
127
|
-
`registerFixedStepGate(gate)` / `unregisterFixedStepGate(gate)` plus one check in
|
|
128
|
-
the fixed loop before `fixedStepTick++`. No other semantics change; when no gate
|
|
129
|
-
is registered the added cost is one `length !== 0` test per step.
|
|
130
|
-
|
|
131
|
-
### Fluid — `engine/physics/fluid/`
|
|
132
|
-
|
|
133
|
-
| file | contents |
|
|
134
|
-
|---|---|
|
|
135
|
-
| `FluidField.js` | **modified**: `shared_memory` flag → single-SAB allocation, `getSharedDescriptor()` / `fromSharedDescriptor()` |
|
|
136
|
-
| `ecs/fluid_reanchor_field.js`, `ecs/fluid_build_world_to_grid.js`, `ecs/fluid_gather_visible_effectors.js`, `ecs/fluid_sync_effectors_from_transform.js` | **extracted** from `FluidSystem` so both systems share one implementation rather than two that can drift |
|
|
137
|
-
| `ecs/fluid_worker_protocol.js` | command word layout + enum packing, shared by both sides |
|
|
138
|
-
| `ecs/FluidWorkerCore.js` | worker-side logic — pure, no worker globals, node-testable |
|
|
139
|
-
| `ecs/WorkerFluidSystem.js` | the system |
|
|
140
|
-
| `ecs/fluid.worker.js` | worker entry: `SystemWorkerHost.serve(new FluidWorkerCore())` |
|
|
141
|
-
| `ecs/makeFluidWorker.js` | `new Worker(new URL(...))` factory — **never imported by specs** (`import.meta` is a Jest parse error) |
|
|
142
|
-
| `ecs/WorkerFluidSystem.spec.js` | parity vs `FluidSystem`, ownership, lifecycle |
|
|
143
|
-
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
## 3. `WorkerFluidSystem` specifics
|
|
147
|
-
|
|
148
|
-
### Work split
|
|
149
|
-
|
|
150
|
-
`FluidSimulator.step()` phase 1 is "apply effectors"; phases 2–6 are the solve
|
|
151
|
-
(~80% of which is the pressure Poisson). So:
|
|
152
|
-
|
|
153
|
-
- **main thread**: reanchor/shift, effector `syncFromTransform`, AABB broad-phase,
|
|
154
|
-
and `effector.apply(field, dt, world_to_grid)` — arbitrary user-authored
|
|
155
|
-
effector classes keep running on the main thread, unserialized.
|
|
156
|
-
- **worker**: `sim.step(field, dt, [], IDENTITY)` — the *same* method with an empty
|
|
157
|
-
effector list, which is exactly phases 2–6.
|
|
158
|
-
|
|
159
|
-
This needs **zero changes to `FluidSimulator`**, and makes parity exact by
|
|
160
|
-
construction: identical code, identical order, identical float data.
|
|
161
|
-
|
|
162
|
-
### Memory
|
|
163
|
-
|
|
164
|
-
`FluidField` gains one opt-in flag. When `shared_memory = true`, `build()` allocates
|
|
165
|
-
a **single** `SharedArrayBuffer` and sub-views every buffer into it (f32 lattices
|
|
166
|
-
first, then the u8 masks, each offset aligned to its element size), so the whole
|
|
167
|
-
field travels as one object on the cold plane. Layout is derived deterministically
|
|
168
|
-
from `(resolution, scalar names, half_precision_pressure)`, so
|
|
169
|
-
`fromSharedDescriptor` recomputes identical offsets rather than shipping a table.
|
|
170
|
-
|
|
171
|
-
`makeArrayBuffer` is deliberately **not** used: it reads the bare global
|
|
172
|
-
`crossOriginIsolated` (a `ReferenceError` under node) and silently falls back to a
|
|
173
|
-
non-shared `ArrayBuffer`, which for a worker bridge is a silent correctness
|
|
174
|
-
disaster — main and worker would each mutate their own copy. `WorkerFluidSystem`
|
|
175
|
-
instead throws at link time when a field isn't SAB-backed.
|
|
176
|
-
|
|
177
|
-
### Drop-in scope
|
|
178
|
-
|
|
179
|
-
Drop-in at the system level: `FluidComponent`, effectors, `FluidObstacleSystem`,
|
|
180
|
-
and the sampling API are untouched. Two things the caller must do:
|
|
181
|
-
|
|
182
|
-
1. `field.shared_memory = true` before `field.build()` (throws with instructions
|
|
183
|
-
otherwise).
|
|
184
|
-
2. Supply a worker factory: `new WorkerFluidSystem({ worker_factory: makeFluidWorker })`.
|
|
185
|
-
|
|
186
|
-
### Ownership contract (the one real semantic difference)
|
|
187
|
-
|
|
188
|
-
`FluidSystem` leaves the field consistent at all times. `WorkerFluidSystem` leaves
|
|
189
|
-
it worker-owned between dispatch and the next join. Precisely:
|
|
190
|
-
|
|
191
|
-
- Systems that touch fluid fields must run **earlier in the fixed pass** than
|
|
192
|
-
`WorkerFluidSystem`. Note this is *not* controlled by registration order: the
|
|
193
|
-
order is derived from `components_used` (`orderSystemsByDeclaredAccess` — a
|
|
194
|
-
writer runs before a same-pass reader, and the score picks among free systems
|
|
195
|
-
and decides which member of a cycle runs first). Two systems touch the field
|
|
196
|
-
from outside the fluid step. Each is on a cycle with `WorkerFluidSystem`, so
|
|
197
|
-
the **score** puts it first, and a spec holds each there in both registration
|
|
198
|
-
orders as a standing assertion — because if the scheduler ever reorders them,
|
|
199
|
-
the symptom is a silent data race rather than a visible failure:
|
|
200
|
-
- `FluidObstacleSystem` writes solid masks. It and `WorkerFluidSystem` both read
|
|
201
|
-
and write `FluidComponent` (20 against 11 in a game-like composition);
|
|
202
|
-
`WorkerFluidSystem.spec.js` holds it first.
|
|
203
|
-
- Cloth (`ClothSystem`, `WorkerClothSystem`) reads the velocity field through
|
|
204
|
-
`ClothFluidWind`, once a tick in `ClothWorld#advance`, and declares that
|
|
205
|
-
read. It also writes the `Transform64` every fluid system reads, so the edges
|
|
206
|
-
run both ways (cloth 49 against `WorkerFluidSystem`'s 15);
|
|
207
|
-
`cloth_wind.spec.js` holds cloth first. Before the read was declared, the
|
|
208
|
-
`Transform64` edge alone ran it first: measured 2026-10-16 over all four
|
|
209
|
-
cloth × fluid pairs and a census of every `System` subclass, and declaring
|
|
210
|
-
it flipped no cloth–fluid pair. There, step `T − 1` has landed (the gate)
|
|
211
|
-
and step `T` is not yet dispatched, so cloth reads a complete field — under
|
|
212
|
-
either fluid system the one step `T − 1` left.
|
|
213
|
-
- `update()`-phase and render-phase readers see a step in progress. **Today there
|
|
214
|
-
are no such readers** — cloth is the one consumer outside the fluid package, and
|
|
215
|
-
it reads inside the fixed pass, above — so the exposure is zero, but it is a real
|
|
216
|
-
constraint and is documented on the class. The escape hatch, if a reader ever
|
|
217
|
-
appears, is a double-buffered publish slot (2× memory, one worker-side copy per
|
|
218
|
-
step); not built now because it would erase most of the zero-copy win for a
|
|
219
|
-
hypothetical.
|
|
220
|
-
|
|
221
|
-
### Latency
|
|
222
|
-
|
|
223
|
-
One tick (`K = 1`) of visible latency: at tick `T` the field shows the result of
|
|
224
|
-
step `T − 1`. The *sequence* of states is identical to `FluidSystem`'s, only
|
|
225
|
-
observed one tick later.
|
|
226
|
-
|
|
227
|
-
---
|
|
228
|
-
|
|
229
|
-
## 4. Testing
|
|
230
|
-
|
|
231
|
-
Real `SharedArrayBuffer`, real `Atomics`, real `FluidSimulator`, real
|
|
232
|
-
`EntityManager`/`EntityComponentDataset`/`Entity`. The only stand-in is the browser
|
|
233
|
-
`Worker` itself — a genuine platform boundary that cannot exist in node, faked by a
|
|
234
|
-
`LoopbackWorker` that implements the same message interface and lets the spec drive
|
|
235
|
-
the real `SystemWorkerHost`/`FluidWorkerCore` pump by hand. All protocol logic,
|
|
236
|
-
atomics, and solver code under test is production code.
|
|
237
|
-
|
|
238
|
-
Cases:
|
|
239
|
-
|
|
240
|
-
1. **Protocol** — dispatch/join round trip; `DONE` ordering; `ERR` capture keeps
|
|
241
|
-
`DONE` advancing; cold message yield + ack.
|
|
242
|
-
2. **Gate** — blocks advancement until the result lands; opens for a failed worker;
|
|
243
|
-
opens when nothing was dispatched; accumulator retains time on block.
|
|
244
|
-
3. **Parity** — `FluidSystem` and `WorkerFluidSystem` over an identical scene
|
|
245
|
-
(moving transform, effectors, obstacles) for N ticks produce **bit-identical**
|
|
246
|
-
fields.
|
|
247
|
-
4. **Ownership** — the field is not mutated by the main thread between dispatch
|
|
248
|
-
and join.
|
|
249
|
-
5. **Lifecycle** — link/unlink registers/removes fields; non-shared field throws;
|
|
250
|
-
shutdown settles and terminates.
|
|
251
|
-
|
|
252
|
-
---
|
|
253
|
-
|
|
254
|
-
## 5. Order of work
|
|
255
|
-
|
|
256
|
-
1. `WorkerSystemProtocol.js` + `SystemWorkerHost.js` + `WorkerSystem.js`
|
|
257
|
-
2. `EntityManager` gate + spec
|
|
258
|
-
3. `WorkerSystem.spec.js`
|
|
259
|
-
4. `FluidField` shared allocation + spec additions
|
|
260
|
-
5. `fluid_worker_protocol.js`, `FluidWorkerCore.js`, `WorkerFluidSystem.js`, entry + factory
|
|
261
|
-
6. `WorkerFluidSystem.spec.js` (parity is the gate)
|
|
262
|
-
7. Adversarial review, then commit
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
# IK — trees, goals on bones, joints and contacts from physics
|
|
2
|
-
|
|
3
|
-
> Revision 5, 2026-10-06. **S1–S4 are implemented** (2026-10-06); S5 is later. What implementing
|
|
4
|
-
> it measured or decided beyond this text is in §9. The solver (§2) was accepted at revision 2. Revisions 3–5 settle the components, limits, contacts and clip-driven bones. Each
|
|
5
|
-
> revision went through an adversarial review. Numbers come from one-off probes outside the repo,
|
|
6
|
-
> and every stage re-pins what it relies on with a failing spec first.
|
|
7
|
-
|
|
8
|
-
## 1. Where it stands (master `c01c0ebbd`)
|
|
9
|
-
|
|
10
|
-
- `physics/inverse_kinematics/fabrik/` solves one chain, and nothing calls it.
|
|
11
|
-
- The ECS IK (`2BIK`, `1BSA`) reads name-keyed `IKConstraint` lists on an `InverseKinematics`
|
|
12
|
-
component, and probes `Terrain` directly.
|
|
13
|
-
- **Holds** (`gpu_authority_hold`): a CPU writer takes a joint's `TransformAttachment`. The
|
|
14
|
-
animation manager mutes every clip binding that names the joint, and `t64_evaluate_world` /
|
|
15
|
-
`T64WorldCache` read the held offset. Cloth uses this.
|
|
16
|
-
- `two_joint_ik` takes the knee's hinge. The CPU pose twin matches the GPU resolve.
|
|
17
|
-
|
|
18
|
-
## 2. Solver: frame FABRIK over a tree
|
|
19
|
-
|
|
20
|
-
**Unchanged.**
|
|
21
|
-
- Every solved joint is a frame: rigid offsets to its children, with its rotation fitted inward
|
|
22
|
-
(from its old position) and outward. With one child this is the paper's pull.
|
|
23
|
-
- Fits start from the carried rotation, so nothing drifts.
|
|
24
|
-
- A goal's path runs from its effector to its `chain_root`. A link carries the weight of the goals
|
|
25
|
-
whose path holds both ends, and a `chain_root` takes rotation, never position.
|
|
26
|
-
- The target is `lerp(carried, goal, w)`. Weight 0 gives the same pose as no goal, and the result is
|
|
27
|
-
continuous in `w`.
|
|
28
|
-
- Two-bone paths use `two_joint_ik`. Twist is held from the animated pose, and the effector's
|
|
29
|
-
rotation is slerped toward the goal after the solve.
|
|
30
|
-
- Worst error at 16 iterations is 0.005–1.34 mm across the probe cases. The paper's point branches
|
|
31
|
-
miss by 8–49 mm once written back.
|
|
32
|
-
|
|
33
|
-
## 3. Components: on the bones, limits from physics
|
|
34
|
-
|
|
35
|
-
Data lives on the entity it describes, and the tree is the `TransformAttachment` hierarchy. Meep's
|
|
36
|
-
own rig setup puts these components on the bones when the instance is created.
|
|
37
|
-
|
|
38
|
-
**`IKGoal`, on the effector bone.**
|
|
39
|
-
|
|
40
|
-
| field | meaning |
|
|
41
|
-
|---|---|
|
|
42
|
-
| `target` | entity id. Its world pose is the goal. |
|
|
43
|
-
| `chain_root` | entity id, an ancestor-or-self of the effector: the highest bone this goal rotates |
|
|
44
|
-
| `position_weight`, `rotation_weight` | 0..1 |
|
|
45
|
-
|
|
46
|
-
- References are fixed while attached.
|
|
47
|
-
- The goal removes itself when its target or any joint on its path is destroyed, as
|
|
48
|
-
`TransformAttachment` does when its parent dies.
|
|
49
|
-
- A rejected goal holds nothing. Rejection is a real branch plus an assert, because asserts are
|
|
50
|
-
stripped in production. A goal is rejected for:
|
|
51
|
-
- a `chain_root` that is not an ancestor-or-self of the effector;
|
|
52
|
-
- a target hanging below its own path;
|
|
53
|
-
- a two-bone path whose knee has no bend side.
|
|
54
|
-
- A target hanging below *another* goal's joints in the same tree (a grip on a weapon held by the
|
|
55
|
-
other hand) is read from the solver's current frames on every iteration. Convergence is measured
|
|
56
|
-
in S4.
|
|
57
|
-
- Across groups, dependencies order the solves, and a cycle rejects both groups.
|
|
58
|
-
|
|
59
|
-
**Joint limits are the physics `Joint` on the bone.** Physics already has this as a 6-DOF
|
|
60
|
-
description (PhysX D6): per-axis `DofMode`, limits, `localBasisA/B`, and two entity ids.
|
|
61
|
-
- On a bone, `entityA` is the parent bone and `entityB` is the bone itself.
|
|
62
|
-
- Asserted at link:
|
|
63
|
-
- `entityA` equals `TransformAttachment.parent`;
|
|
64
|
-
- `localBasisA = rest_local · localBasisB`;
|
|
65
|
-
- `swingTwist` is on. Without it, physics measures angles as 2 sin(θ/2), so limits above 114.6°
|
|
66
|
-
never engage and physics and IK would disagree.
|
|
67
|
-
- **Hinge** (`asHinge`) gives the knee's `mid_axis`. Its bend side comes from asymmetric limits.
|
|
68
|
-
- **Cone-twist** gives twist on X and separate Y/Z swing, and IK clamps to the same box physics
|
|
69
|
-
allows.
|
|
70
|
-
- IK ignores anchors, linear DOFs, SPRING and MOTOR.
|
|
71
|
-
- **Shared maths.** `swing_twist_error*` moves from `physics/constraint/solve_constraints.js` to
|
|
72
|
-
`core/geom/3d/quaternion/`; the clamp is new.
|
|
73
|
-
- **Contract.** Physics simulates a `Joint` only when both sides have a body (or `JOINT_WORLD`).
|
|
74
|
-
Otherwise it is a kinematic limit. Nothing observes `Joint` today.
|
|
75
|
-
|
|
76
|
-
**Solver settings** (`max_iterations`, `tolerance`) belong to the system.
|
|
77
|
-
|
|
78
|
-
## 4. Contacts come from physics
|
|
79
|
-
|
|
80
|
-
A foot is a joint, the ground is a collider, and contact points are physics' job. IK probes
|
|
81
|
-
nothing: today's terrain prober goes, and nothing here touches `Terrain` or `ClingToTerrain`.
|
|
82
|
-
|
|
83
|
-
- **A goal that should touch a surface** gets its target from a physics query: a ray or shape cast
|
|
84
|
-
(`PhysicsSystem.raycast`, `shapeCast`) from a reference the rig provides, returning a
|
|
85
|
-
`PhysicsSurfacePoint` (position, normal, entity).
|
|
86
|
-
- **The component lives in the physics package.** It is a generic contact-probe component that
|
|
87
|
-
places its entity at the hit and aligns it to the normal. It knows nothing about IK or feet, so
|
|
88
|
-
footsteps, decals and AI can use it too. Physics has no query component today;
|
|
89
|
-
`PhysicsSurfacePoint` was written with "a ground sensor" in mind.
|
|
90
|
-
- **Its contract:**
|
|
91
|
-
- it exact-evaluates a GPU-composed reference through `T64WorldCache`;
|
|
92
|
-
- it writes its own attachment offset;
|
|
93
|
-
- it skips the character's own colliders (same `ParentEntity` root);
|
|
94
|
-
- it runs in `update`, before IK;
|
|
95
|
-
- its reference must not be held by IK, or the target would chase IK's output (asserted by the
|
|
96
|
-
IK side, §3).
|
|
97
|
-
- Pelvis lowering comes later.
|
|
98
|
-
|
|
99
|
-
## 5. Bones a clip drives: IK holds them
|
|
100
|
-
|
|
101
|
-
Downstream code (`t64_evaluate_world`, `T64WorldCache`, sockets, cloth anchors, the GPU hierarchy)
|
|
102
|
-
reads the held offset exactly as it reads cloth's, and never learns that IK exists.
|
|
103
|
-
|
|
104
|
-
- **Link:** hold `TransformAttachment` on every joint of the path, capture the offsets, and write the
|
|
105
|
-
evaluated base pose at once, so no frame shows the bind pose.
|
|
106
|
-
- **Every tick:** write each held joint's full local: translation, rotation and scale, since a hold
|
|
107
|
-
mutes every channel. The base comes from `pose_evaluate_local`, never from a held offset.
|
|
108
|
-
- **Unlink:** write the captured offset back, then unhold, guarded by `entityExists`.
|
|
109
|
-
- **One holder per joint:** IK on a cloth-held joint trips the existing assert.
|
|
110
|
-
- **Cost:** 37 / 127 / 531 µs per character per tick at 2 / 8 / 30 registered clips (34 levels),
|
|
111
|
-
because every level scans every channel of every clip.
|
|
112
|
-
- **Ordering:** IK declares what it reads, `Animation` and `AnimationGraphController`. The drivers
|
|
113
|
-
declare that they write them. With the scheduler fixed (below), IK then runs after the drivers.
|
|
114
|
-
Cloth needs no order against IK: it runs in `fixedUpdate` on the previous update's consistent
|
|
115
|
-
state.
|
|
116
|
-
|
|
117
|
-
## 6. Prerequisites (separate tasks)
|
|
118
|
-
|
|
119
|
-
1. **Holds: LANDED** (`84f2f7267`, `c01c0ebbd`). A clip that binds during a hold takes the rest the holder
|
|
120
|
-
leaves when the hold ends, and that offset reaches the row.
|
|
121
|
-
2. **Pose evaluator:** index channels by target (the cost row above). Sockets and cloth gain too.
|
|
122
|
-
3. **Scheduler:** a system that reads X and writes Y sorts before the writer of X. Measured with
|
|
123
|
-
synthetic components: Producer (X) and Consumer (X → Y) order as `Consumer > Producer` in both
|
|
124
|
-
registration orders. `system_execution_order.spec.js` covers only a pure reader. The fix stays
|
|
125
|
-
inside derived ordering.
|
|
126
|
-
|
|
127
|
-
## 7. Stages
|
|
128
|
-
|
|
129
|
-
Each stage is red-then-green.
|
|
130
|
-
|
|
131
|
-
1. **S1 Frame FABRIK:** a pure solver over typed arrays.
|
|
132
|
-
2. **S2 Two-bone paths:** `two_joint_ik` with the hinge read from the `Joint`.
|
|
133
|
-
3. **S3 Limits:** the shared swing-twist maths, the clamp, and `Joint` frames inside the passes.
|
|
134
|
-
4. **S4 ECS, landing together:**
|
|
135
|
-
- `IKGoal` and its system (groups, holds, base evaluation, target reads, declarations);
|
|
136
|
-
- the physics contact-probe component;
|
|
137
|
-
- in the same change, `InverseKinematics`, `IKConstraint`, `2BIK`, `1BSA`, the terrain prober
|
|
138
|
-
and the adapter go (hard break).
|
|
139
|
-
5. **S5 Later:** pelvis lowering, twist distribution, goal priority, pole goals.
|
|
140
|
-
|
|
141
|
-
## 8. Sources
|
|
142
|
-
|
|
143
|
-
- [A] Aristidou, Chrysanthou, Lasenby, *Extending FABRIK with model constraints*, CAVW 2016
|
|
144
|
-
- [C] Aristidou, Lasenby, *FABRIK*, Graphical Models 73, 2011
|
|
145
|
-
- [D] Baerlocher, Boulic, *Parametrization and range of motion of the ball-and-socket joint*, 2001
|
|
146
|
-
- [G] Unreal Engine FullBodyIK (PBIK) and Foot Placement documentation
|
|
147
|
-
- [N] ozz-animation `ik_two_bone_job.h`, `samples/foot_ik/sample_foot_ik.cc`
|
|
148
|
-
|
|
149
|
-
## 9. After implementation (2026-10-06)
|
|
150
|
-
|
|
151
|
-
Measured while implementing S1–S4, and decided with the owner where it changed anything.
|
|
152
|
-
|
|
153
|
-
- **The fit** is 8 steps of power iteration warm-started from the carried rotation: 110–130 ns a fit
|
|
154
|
-
against 610–790 ns for the exact Jacobi fit, which also turns the chest about its shoulder line
|
|
155
|
-
from rounding (a 40–175° bend at the chest joint, converged, where the warm start bends 0–54°).
|
|
156
|
-
- **The 0/0** of this mechanism is a target on the effector's parent, at any bone lengths: the parent
|
|
157
|
-
is fitted toward a child sitting on its old position. A target at a limb's top with equal bones is
|
|
158
|
-
finite and reached. A straight arm turned only at the shoulder, its target exactly on the elbow,
|
|
159
|
-
has no side to fold to and stays (finite); 1e-12 m off, it reaches.
|
|
160
|
-
- **An effector inside another goal's path** (a goal on the elbow under a goal on the hand) is one
|
|
161
|
-
more proposal in its joint's mean, weighted by its goal's weight (owner's decision; pinning it at
|
|
162
|
-
its target moved the pose 161 mm at weight 0). Weight 0 is no goal, 0.001 moves it 0.05 mm, and two
|
|
163
|
-
equal goals that disagree meet half way.
|
|
164
|
-
- **`FrameFabrikProblem` takes `base_local_rotation`**: a goal's root is limited against its
|
|
165
|
-
parent's fixed frame, which world rotations alone do not give.
|
|
166
|
-
- **A hinge alone on a longer path can lock**: the arc fits never twist the shoulder to bring an
|
|
167
|
-
elbow's hinge plane round, so a target off it left the arm straight, 350 / 334 mm short; a
|
|
168
|
-
cone-twist on the shoulder unlocks it (1.4 mm at 16 iterations, 0 at 64). Hinges also slow a grip
|
|
169
|
-
pair down: 65 / 220 mm at 16 iterations, 0 at 64. Two-bone paths are analytic and unaffected.
|
|
170
|
-
Stated, not fixed.
|
|
171
|
-
- **"Below its own path"** means the deepest posed bone the target hangs from is a bone of the goal's
|
|
172
|
-
own path. A grip on a weapon in the other hand hangs below the gripping hand's own chest too, but
|
|
173
|
-
the other arm's bones carry it, so it is read from the solver's frames. Known only with every goal,
|
|
174
|
-
so it is found when the groups are formed; a target below the effector itself is refused at link.
|
|
175
|
-
- **The contact probe** casts a sphere of `radius`, a ray at 0. It and IK both read and write
|
|
176
|
-
`TransformAttachment`, so declared access leaves them a cycle and the scheduler keeps their
|
|
177
|
-
registration order: the probe is registered first (owner's decision). That its reference must not
|
|
178
|
-
be held by a CPU writer is documented on `ContactProbe`, not asserted (owner's decision): IK cannot
|
|
179
|
-
check it without knowing the probe's component.
|