@woosh/meep-engine 3.24.0 → 3.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/engine/graphics3/PickingSystem.d.ts +3 -3
- package/src/engine/graphics3/PickingSystem.d.ts.map +1 -1
- package/src/engine/graphics3/PickingSystem.js +52 -55
- package/src/engine/graphics3/shader_picking_gather.d.ts +40 -0
- package/src/engine/graphics3/shader_picking_gather.d.ts.map +1 -0
- package/src/engine/graphics3/shader_picking_gather.js +120 -0
- package/src/engine/physics/cloth/MEASUREMENTS.md +1050 -0
- package/src/engine/physics/cloth/PLAN.md +411 -49
- package/src/engine/physics/cloth/ecs/ClothColliderSerializationAdapter.d.ts +52 -0
- package/src/engine/physics/cloth/ecs/ClothColliderSerializationAdapter.d.ts.map +1 -0
- package/src/engine/physics/cloth/ecs/ClothColliderSerializationAdapter.js +70 -0
- package/src/engine/physics/cloth/ecs/ClothDynamics.d.ts +12 -3
- package/src/engine/physics/cloth/ecs/ClothDynamics.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothDynamics.js +340 -331
- package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +36 -22
- package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothInstance.js +38 -23
- package/src/engine/physics/cloth/ecs/ClothSerializationAdapter.d.ts +84 -0
- package/src/engine/physics/cloth/ecs/ClothSerializationAdapter.d.ts.map +1 -0
- package/src/engine/physics/cloth/ecs/ClothSerializationAdapter.js +141 -0
- package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +51 -82
- package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothSystem.js +81 -869
- package/src/engine/physics/cloth/ecs/ClothWorkerCore.d.ts +48 -0
- package/src/engine/physics/cloth/ecs/ClothWorkerCore.d.ts.map +1 -0
- package/src/engine/physics/cloth/ecs/ClothWorkerCore.js +107 -0
- package/src/engine/physics/cloth/ecs/ClothWorld.d.ts +286 -0
- package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -0
- package/src/engine/physics/cloth/ecs/ClothWorld.js +1104 -0
- package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts +182 -0
- package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts.map +1 -0
- package/src/engine/physics/cloth/ecs/WorkerClothSystem.js +562 -0
- package/src/engine/physics/cloth/ecs/cloth.worker.d.ts +2 -0
- package/src/engine/physics/cloth/ecs/cloth.worker.d.ts.map +1 -0
- package/src/engine/physics/cloth/ecs/cloth.worker.js +12 -0
- package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_build_rows.js +2 -1
- package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts +58 -0
- package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_dynamics_map.js +341 -271
- package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts +20 -2
- package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +39 -15
- package/src/engine/physics/cloth/ecs/cloth_worker_protocol.d.ts +29 -0
- package/src/engine/physics/cloth/ecs/cloth_worker_protocol.d.ts.map +1 -0
- package/src/engine/physics/cloth/ecs/cloth_worker_protocol.js +78 -0
- package/src/engine/physics/cloth/ecs/makeClothWorker.d.ts +17 -0
- package/src/engine/physics/cloth/ecs/makeClothWorker.d.ts.map +1 -0
- package/src/engine/physics/cloth/ecs/makeClothWorker.js +18 -0
- package/src/engine/physics/cloth/playground/README.md +73 -2
- package/src/engine/physics/cloth/playground/wind.html +166 -0
- package/src/engine/physics/cloth/playground/wind_build.d.ts +81 -0
- package/src/engine/physics/cloth/playground/wind_build.d.ts.map +1 -0
- package/src/engine/physics/cloth/playground/wind_build.js +141 -0
- package/src/engine/physics/cloth/playground/wind_main.d.ts +2 -0
- package/src/engine/physics/cloth/playground/wind_main.d.ts.map +1 -0
- package/src/engine/physics/cloth/playground/wind_main.js +681 -0
- package/src/engine/physics/cloth/solver/ClothState.d.ts +134 -79
- package/src/engine/physics/cloth/solver/ClothState.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/ClothState.js +273 -12
- package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts +33 -0
- package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/cloth_solver_constants.js +34 -0
- package/src/engine/physics/cloth/solver/cloth_step.d.ts +21 -2
- package/src/engine/physics/cloth/solver/cloth_step.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/cloth_step.js +173 -4
- package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts +40 -0
- package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts.map +1 -0
- package/src/engine/physics/cloth/solver/cloth_step_instance.js +284 -0
- package/src/engine/physics/cloth/solver/cloth_wind_accelerate.d.ts +107 -0
- package/src/engine/physics/cloth/solver/cloth_wind_accelerate.d.ts.map +1 -0
- package/src/engine/physics/cloth/solver/cloth_wind_accelerate.js +263 -0
- package/src/engine/physics/cloth/wind/AbstractClothWind.d.ts +88 -0
- package/src/engine/physics/cloth/wind/AbstractClothWind.d.ts.map +1 -0
- package/src/engine/physics/cloth/wind/AbstractClothWind.js +100 -0
- package/src/engine/physics/cloth/wind/ClothAmbientWind.d.ts +85 -0
- package/src/engine/physics/cloth/wind/ClothAmbientWind.d.ts.map +1 -0
- package/src/engine/physics/cloth/wind/ClothAmbientWind.js +182 -0
- package/src/engine/physics/cloth/wind/ClothFluidWind.d.ts +79 -0
- package/src/engine/physics/cloth/wind/ClothFluidWind.d.ts.map +1 -0
- package/src/engine/physics/cloth/wind/ClothFluidWind.js +210 -0
- package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts +101 -0
- package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts.map +1 -0
- package/src/engine/physics/cloth/wind/cloth_sample_air.js +290 -0
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/shade/device/mock/texture_copy.d.ts.map +1 -1
- package/src/shade/device/mock/texture_copy.js +28 -0
- package/src/shade/playground/vgeo_runtime/README.md +94 -0
- package/src/shade/playground/vgeo_runtime/index.html +62 -0
- package/src/shade/playground/vgeo_runtime/main.d.ts +2 -0
- package/src/shade/playground/vgeo_runtime/main.d.ts.map +1 -0
- package/src/shade/playground/vgeo_runtime/main.js +399 -0
- package/src/shade/playground/vgeo_viewer/sample_asset.js +1 -1
- package/src/shade/renderer/camera/Camera.d.ts +14 -0
- package/src/shade/renderer/camera/Camera.d.ts.map +1 -1
- package/src/shade/renderer/camera/Camera.js +7 -1
- package/src/shade/renderer/geometry/GEOMETRY_METADATA_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/geometry/GEOMETRY_METADATA_STRUCT.js +16 -1
- package/src/shade/renderer/geometry/GPUGeometryManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/GPUGeometryManager.js +29 -9
- package/src/shade/renderer/geometry/GPUGeometryMetadata.d.ts +10 -0
- package/src/shade/renderer/geometry/GPUGeometryMetadata.d.ts.map +1 -1
- package/src/shade/renderer/geometry/GPUGeometryMetadata.js +13 -0
- package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +46 -18
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +70 -22
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +97 -19
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +12 -2
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +14 -3
- package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts +2 -2
- package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.js +124 -6
- package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.d.ts +22 -0
- package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.js +53 -0
- package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts +8 -1
- package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +8 -1
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_FEATURE_IGNORABLE_BOUNDED_PAGES.d.ts +16 -0
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_FEATURE_IGNORABLE_BOUNDED_PAGES.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_FEATURE_IGNORABLE_BOUNDED_PAGES.js +15 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_DEFAULT_PAGE_SIZE.d.ts +54 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_DEFAULT_PAGE_SIZE.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_DEFAULT_PAGE_SIZE.js +53 -0
- package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts +13 -2
- package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.js +14 -2
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts +100 -0
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.js +113 -0
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.js +5 -0
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js +108 -0
- package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +23 -0
- package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +40 -0
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts +228 -0
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.js +932 -0
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts +176 -0
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +425 -0
- package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +241 -0
- package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +574 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts +120 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.js +674 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts +287 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.js +976 -0
- package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts +42 -0
- package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.js +96 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_CULL.d.ts +16 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_CULL.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_CULL.js +15 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.d.ts +19 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.js +83 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_WORKGROUP_SIZE.d.ts +9 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_WORKGROUP_SIZE.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_WORKGROUP_SIZE.js +8 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts +19 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js +160 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.d.ts +3 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.js +255 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.d.ts +3 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.js +80 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts +3 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.js +144 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts +3 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +530 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts +39 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.js +54 -0
- package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_if_enabled.d.ts +41 -0
- package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_if_enabled.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_if_enabled.js +56 -0
- package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts +49 -0
- package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.js +328 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.d.ts +10 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.js +51 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts +13 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.js +12 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts +17 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.js +99 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.d.ts +9 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.js +8 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.d.ts +7 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.js +27 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_NONE.d.ts +14 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_NONE.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_NONE.js +13 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_OFFSET.d.ts +7 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_OFFSET.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_OFFSET.js +35 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_WORDS.d.ts +9 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_WORDS.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_WORDS.js +8 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_FLAG_SELECTABLE.d.ts +15 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_FLAG_SELECTABLE.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_FLAG_SELECTABLE.js +14 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.d.ts +7 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.js +36 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts +10 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.js +9 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_OFFSET.d.ts +11 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_OFFSET.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_OFFSET.js +54 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_WORDS.d.ts +9 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_WORDS.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_WORDS.js +8 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_LEAF_BIT.d.ts +11 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_LEAF_BIT.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_LEAF_BIT.js +10 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_OFFSET.d.ts +7 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_OFFSET.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_OFFSET.js +30 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_WORDS.d.ts +9 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_WORDS.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_WORDS.js +8 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NO_CHILD_PAGE.d.ts +13 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NO_CHILD_PAGE.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NO_CHILD_PAGE.js +12 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_NONE.d.ts +14 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_NONE.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_NONE.js +13 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.d.ts +11 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.js +61 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.d.ts +9 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.js +8 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_ITEM_WORDS.d.ts +13 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_ITEM_WORDS.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_ITEM_WORDS.js +12 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_GROUP_BIT.d.ts +9 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_GROUP_BIT.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_GROUP_BIT.js +8 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_WANT_WORDS.d.ts +20 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_WANT_WORDS.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_WANT_WORDS.js +19 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts +23 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.js +70 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts +18 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.js +25 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts +35 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.js +41 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts +32 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.js +138 -0
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +9 -0
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +12 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +12 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +10 -0
- package/src/shade/renderer/scene/GPUSceneContext.d.ts +18 -0
- package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
- package/src/shade/renderer/scene/GPUSceneContext.js +647 -594
- package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts.map +1 -1
- package/src/shade/renderer/scene/rows/GPUSceneRows.js +22 -0
- package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_singular_values.d.ts +35 -0
- package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_singular_values.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_singular_values.js +91 -0
- package/src/engine/graphics3/instantiate_scene_bundle.d.ts +0 -54
- package/src/engine/graphics3/instantiate_scene_bundle.d.ts.map +0 -1
- package/src/engine/graphics3/shade_node_to_entity_composition.d.ts +0 -39
- package/src/engine/graphics3/shade_node_to_entity_composition.d.ts.map +0 -1
- package/src/shade/renderer/animation/compute_skin_world_bounds.d.ts +0 -35
- package/src/shade/renderer/animation/compute_skin_world_bounds.d.ts.map +0 -1
- package/src/shade/renderer/animation/compute_skinned_mesh_clip_bounding_sphere.d.ts +0 -96
- package/src/shade/renderer/animation/compute_skinned_mesh_clip_bounding_sphere.d.ts.map +0 -1
- package/src/shade/renderer/animation/pose/pose_evaluate_world.d.ts +0 -20
- package/src/shade/renderer/animation/pose/pose_evaluate_world.d.ts.map +0 -1
- package/src/shade/renderer/animation/skin_bind_transform.d.ts +0 -25
- package/src/shade/renderer/animation/skin_bind_transform.d.ts.map +0 -1
- package/src/shade/renderer/animation/skin_frame_defect.d.ts +0 -34
- package/src/shade/renderer/animation/skin_frame_defect.d.ts.map +0 -1
- package/src/shade/renderer/animation/skin_joint_matrix.d.ts +0 -24
- package/src/shade/renderer/animation/skin_joint_matrix.d.ts.map +0 -1
- package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts +0 -70
- package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts.map +0 -1
- package/src/shade/renderer/buffer/table/GPUReadbackStagingRing.d.ts +0 -96
- package/src/shade/renderer/buffer/table/GPUReadbackStagingRing.d.ts.map +0 -1
- package/src/shade/renderer/scene/InstanceBatch.d.ts +0 -92
- package/src/shade/renderer/scene/InstanceBatch.d.ts.map +0 -1
- package/src/shade/renderer/scene/Mesh.d.ts +0 -55
- package/src/shade/renderer/scene/Mesh.d.ts.map +0 -1
- package/src/shade/renderer/scene/Node3D.d.ts +0 -159
- package/src/shade/renderer/scene/Node3D.d.ts.map +0 -1
- package/src/shade/renderer/scene/Node3DDirtyList.d.ts +0 -59
- package/src/shade/renderer/scene/Node3DDirtyList.d.ts.map +0 -1
- package/src/shade/renderer/scene/SceneNodeBinding.d.ts +0 -52
- package/src/shade/renderer/scene/SceneNodeBinding.d.ts.map +0 -1
- package/src/shade/renderer/scene/SkinnedMesh.d.ts +0 -27
- package/src/shade/renderer/scene/SkinnedMesh.d.ts.map +0 -1
- package/src/shade/renderer/scene/TransformAuthority.d.ts +0 -19
- package/src/shade/renderer/scene/TransformAuthority.d.ts.map +0 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Cloth — Design
|
|
2
2
|
|
|
3
|
-
> Design document. Written 2026-09-10; **M0
|
|
4
|
-
>
|
|
3
|
+
> Design document. Written 2026-09-10; **M0 through M5 are implemented** — the solver, chains,
|
|
4
|
+
> collision, the rigged path, self-collision and the worker — and every place they contradicted it has
|
|
5
|
+
> been corrected in place with the measurement named beside it.
|
|
5
6
|
> §17 says what is done and what is not; `MEASUREMENTS.md` is the evidence.
|
|
6
7
|
>
|
|
7
8
|
> Scope: a cloth and soft-chain simulator that lives inside `engine/physics`, collides against the
|
|
@@ -271,7 +272,8 @@ engine/physics/cloth/
|
|
|
271
272
|
cloth_dynamics_map.js normalised coordinates → row parameters; every constant measured
|
|
272
273
|
ClothCollider.js component: the RigidBody-analogue for collision
|
|
273
274
|
ClothColliderFlags.js
|
|
274
|
-
|
|
275
|
+
ClothWorld.js every cloth in a scene, and everything that is not arithmetic
|
|
276
|
+
ClothSystem.js main-thread system: a ClothWorld stepped inline
|
|
275
277
|
ClothInstance.js one live cloth: the binding between a subtree and a ClothState
|
|
276
278
|
cloth_seed_subtree.js §11.1's walk: entities → node_parent + animated_position
|
|
277
279
|
cloth_seed_rig.js §11.2's proxy: joints → node_parent + animated_position
|
|
@@ -280,14 +282,14 @@ engine/physics/cloth/
|
|
|
280
282
|
cloth_write_back.js that pose → Transform64 + TransformAttachment
|
|
281
283
|
cloth_write_back_rig.js that pose → Node3D.transform_local
|
|
282
284
|
cloth_gather_colliders.js the index's overlapping records, in the cloth's own frame
|
|
283
|
-
WorkerClothSystem.js drop-in worker variant
|
|
284
|
-
ClothWorkerCore.js
|
|
285
|
-
cloth.worker.js
|
|
286
|
-
makeClothWorker.js
|
|
287
|
-
cloth_worker_protocol.js
|
|
288
|
-
ClothSerializationAdapter.js
|
|
289
|
-
|
|
290
|
-
|
|
285
|
+
WorkerClothSystem.js drop-in worker variant: the same ClothWorld, stepped over there
|
|
286
|
+
ClothWorkerCore.js worker side: a ClothState view per registered cloth
|
|
287
|
+
cloth.worker.js the entry point, a real module worker
|
|
288
|
+
makeClothWorker.js the spawn expression, isolated because it needs import.meta
|
|
289
|
+
cloth_worker_protocol.js the command region: a count, then a block per cloth
|
|
290
|
+
ClothSerializationAdapter.js the component + its ClothDynamics, by value
|
|
291
|
+
ClothColliderSerializationAdapter.js five authored scalars, the same way
|
|
292
|
+
ClothRigSerializationAdapter.js NOT BUILT — §3.2's asset identity, and the proxy codec
|
|
291
293
|
collider/
|
|
292
294
|
ClothColliderIndex.js the index of live cloth colliders + their swept AABBs
|
|
293
295
|
ClothColliderRecord.js the packed layout: kind, params, both poses, bounds, interpolated pose
|
|
@@ -295,10 +297,17 @@ engine/physics/cloth/
|
|
|
295
297
|
cloth_collider_pack.js AbstractShape3D → packed record
|
|
296
298
|
cloth_collider_bounds.js the swept AABB, shared by the index and each cloth's own table
|
|
297
299
|
cloth_collider_sdf.js pose interpolation, localisation, and the field dispatch
|
|
300
|
+
wind/
|
|
301
|
+
AbstractClothWind.js §10's one interface: sample the air, and say whether it varies
|
|
302
|
+
ClothAmbientWind.js a constant plus a band-limited gust — the air where no field is
|
|
303
|
+
ClothFluidWind.js every live FluidComponent, with the ambient underneath it
|
|
304
|
+
cloth_sample_air.js the air into the cloth's own frame, on the main thread
|
|
298
305
|
solver/
|
|
299
306
|
ClothState.js SoA particle + topology + constraint storage over one buffer
|
|
307
|
+
cloth_step_instance.js one cloth, one step, over nothing but memory — what crosses
|
|
300
308
|
cloth_step.js the fixed step: predict, warm start, iterate, dual update, integrate
|
|
301
309
|
cloth_solve_vertex.js the per-vertex 3x3 block update
|
|
310
|
+
cloth_wind_accelerate.js §10's aerodynamic term, and the factor that makes a flag a flag
|
|
302
311
|
constraint/ one module per row type (§6)
|
|
303
312
|
cloth_color_particles.js graph colouring of the particle graph
|
|
304
313
|
constraint/cloth_leash_row.js §6's slack row and its tether row, which are one row model
|
|
@@ -340,10 +349,19 @@ Two systems:
|
|
|
340
349
|
- **`ClothSystem`** owns the simulation. Per fixed step: refresh the animated targets, gather the
|
|
341
350
|
overlapping colliders from the index, step, write back.
|
|
342
351
|
|
|
343
|
-
|
|
344
|
-
same
|
|
345
|
-
|
|
346
|
-
|
|
352
|
+
**M5 note: it owns it jointly with `WorkerClothSystem`, and "two systems" is now three.** The two
|
|
353
|
+
are interchangeable — same components, same declared access, same position in the derived order —
|
|
354
|
+
and they share a `ClothWorld`, which is the registry and every phase of a step that is not
|
|
355
|
+
arithmetic. `ClothSystem` calls `cloth_step_instance` between the two halves; `WorkerClothSystem`
|
|
356
|
+
puts a thread there. §12 has why that is what makes the bit-parity gate structural.
|
|
357
|
+
|
|
358
|
+
Cloth runs at the fixed rate and its poses are **held** between ticks rather than interpolated, which
|
|
359
|
+
on a display faster than the fixed step is visible and is not wrong. The first draft of this paragraph
|
|
360
|
+
said cloth reuses `PhysicsSystem`'s producer/`__interp_restore` contract; M5 measured that it cannot —
|
|
361
|
+
that contract is entity-keyed and a rigged garment's joints are not entities — and §13.1 and §18 carry
|
|
362
|
+
what the cloth-shaped version would be and what it would cost. What keeps a cloth-driven bone from
|
|
363
|
+
fighting an animation-driven one is not interpolation but `TransformAuthority`, which `cloth_seed_rig`
|
|
364
|
+
claims (§11.2, `MEASUREMENTS.md` §25).
|
|
347
365
|
|
|
348
366
|
### 4.1 Ordering, and why the answer is not to be ordered
|
|
349
367
|
|
|
@@ -874,6 +892,15 @@ which arrives with the component in M1 — so **damping is load-bearing for P5 r
|
|
|
874
892
|
control**, and the M1 spec has to gate on a soft cloth actually settling. `MEASUREMENTS.md` §6.
|
|
875
893
|
*Spec:* a settled cloth reaches sleep within a bounded number of steps and its positions are bit-identical
|
|
876
894
|
across the following 300.
|
|
895
|
+
**M6 note: a cloth also wakes on the air changing, and the test has one sample in it.** §10's "sleep
|
|
896
|
+
wakes on a wind change" is the same shape as the collider test beside it — ask a question three orders
|
|
897
|
+
of magnitude cheaper than the step it decides whether to skip — and the question is the air at the
|
|
898
|
+
cloth's own centre against the air the last *applied* step saw. The last applied rather than the last
|
|
899
|
+
sampled, so a gust that builds slowly accumulates instead of being reset by every step that declined
|
|
900
|
+
to act on it. *Its limit, stated:* one point. A gust that reaches only the hem of a sleeping banner
|
|
901
|
+
and leaves its middle still will not wake it. The collider test has no such gap because a collider's
|
|
902
|
+
pose is seven exact numbers and a field is a continuum.
|
|
903
|
+
|
|
877
904
|
*M1 landed both halves and measured them.* Every library fabric, on a 12-link chain set swinging at
|
|
878
905
|
3 m/s, sleeps: **220 steps for denim, 770 for silk**, everything else between, and the order is the
|
|
879
906
|
damping order. Sleep stops the **write-back** as well as the step, which is what makes "bit-identical"
|
|
@@ -1090,8 +1117,7 @@ the bodies in the scene.
|
|
|
1090
1117
|
|
|
1091
1118
|
So the design is: **a cloth overlapping a `FluidComponent` samples its velocity field per particle and
|
|
1092
1119
|
applies aerodynamic drag against it**, scaled by `ClothDynamics#drag`. The term is an external
|
|
1093
|
-
acceleration, `a_drag =
|
|
1094
|
-
the wind broadside and slice through it edge-on — the difference between a flag and a rope.
|
|
1120
|
+
acceleration, `a_drag = response · facing · (u_air − v)`.
|
|
1095
1121
|
|
|
1096
1122
|
The payoff is that a character running past a cape gets a wake, because the fluid solver already
|
|
1097
1123
|
produces one; a fire's convection lifts a banner; a door slamming pushes a curtain. None of that needs
|
|
@@ -1106,8 +1132,69 @@ Cost control: sampling is per particle per *step*, not per iteration, and a clot
|
|
|
1106
1132
|
field's bounds skips it entirely. Sleep (P5) wakes on a wind change so a settled banner does not stay
|
|
1107
1133
|
asleep through a gust.
|
|
1108
1134
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1135
|
+
**M6 built it and corrected this section in four places.**
|
|
1136
|
+
|
|
1137
|
+
**The factor above is not `|n·û|`, because half of what this system simulates has no `n`.** This
|
|
1138
|
+
paragraph originally wrote the term as `a_drag = drag · (u_air − v) · |n·û|`, "with the normal-facing
|
|
1139
|
+
factor making a sheet catch the wind broadside and slice through it edge-on — the difference between a
|
|
1140
|
+
flag and a rope". It is right about what it wants and wrong that a normal is available to want it
|
|
1141
|
+
with. A chain's particles span a **line** — every direction perpendicular to it is equally a normal,
|
|
1142
|
+
so picking one is the history-dependent fabrication §7 P4(a) exists to keep out — and a rope's
|
|
1143
|
+
aerodynamics are the *complement* of a sheet's anyway: it catches the flow across it and slices along
|
|
1144
|
+
it. Both are one quantity, **the part of the flow the particle's own incident rows cannot let past**:
|
|
1145
|
+
take the span `T` of the directions to its stretch-row neighbours in the current configuration, and
|
|
1146
|
+
`facing = ‖û − proj_T(û)‖`.
|
|
1147
|
+
|
|
1148
|
+
| rank of `T` | what the particle is | the factor |
|
|
1149
|
+
|---|---|---|
|
|
1150
|
+
| 2 | a point on a surface | `\|û·n\|` with `n = b₁×b₂` — this paragraph's own expression |
|
|
1151
|
+
| 1 | a link of a chain | `√(1 − (û·t)²)` — the cross-flow, which is the rope's answer |
|
|
1152
|
+
| 0 | an isolated particle | `1` — a bead has no preferred direction |
|
|
1153
|
+
|
|
1154
|
+
One expression, three topologies, no stored normal, no orientation, no sign, nothing carried between
|
|
1155
|
+
steps. `MEASUREMENTS.md` §47.
|
|
1156
|
+
|
|
1157
|
+
**And the factor's headline claim is about *laminar* air, which is a limit rather than a fault.** On
|
|
1158
|
+
the playground's banner, turning a uniform wind through ninety degrees takes the mean factor from
|
|
1159
|
+
0.221 to **0.000** and the aerodynamic acceleration from 43.2 m/s² to 0.08 — a flag becoming a rope,
|
|
1160
|
+
exactly as promised. Turning a **wake** through ninety degrees does nothing at all: 0.216 to 0.280. A
|
|
1161
|
+
wake has velocity in every direction, so no orientation of a surface is edge-on to one, and the factor
|
|
1162
|
+
is correctly reporting that there is nothing to slice through. That makes this section's argument for
|
|
1163
|
+
reading a simulation *stronger* than this section knew. `MEASUREMENTS.md` §51.
|
|
1164
|
+
|
|
1165
|
+
**`drag` is not what multiplies the flow, and the difference is a stability argument.** One external
|
|
1166
|
+
acceleration is held for the whole fixed step, so an explicit `rate · (u_air − v)` overshoots the air
|
|
1167
|
+
once `rate·dt > 1` and diverges past 2 — and §1's first commitment is "not at 5 fps", which is a `dt`
|
|
1168
|
+
of 0.2 s and a rate of 10. What the step multiplies by is the **per-step response**
|
|
1169
|
+
`(1 − e^(−rate·dt))/dt` (`cloth_drag_response`), the exact solution of the same linear ODE, which
|
|
1170
|
+
closes a fraction of the gap that is strictly below one for every rate and every step length. It is
|
|
1171
|
+
also the step `GlobalFluidEffector` already takes for the air itself, which is a symmetry rather than
|
|
1172
|
+
a coincidence: both are relaxations toward a velocity. `MEASUREMENTS.md` §49.
|
|
1173
|
+
|
|
1174
|
+
**The field answers in grid cells per second and cloth needs metres.** `FluidField#sampleVelocity`
|
|
1175
|
+
documents that it deliberately does not rescale, "on the assumption that callers (vegetation sway,
|
|
1176
|
+
dust drift) interpret the field as a direction × magnitude wind". Cloth is not such a caller: its row
|
|
1177
|
+
is a relaxation toward a velocity, so without the multiply by `cell_size` the same wind voxelised at
|
|
1178
|
+
0.25 m instead of 1 m blows a garment four times as hard. `ClothFluidWind` converts.
|
|
1179
|
+
`MEASUREMENTS.md` §46.
|
|
1180
|
+
|
|
1181
|
+
**And this section never said which thread samples, which M5 made a question.** It cannot be the
|
|
1182
|
+
worker's: a fluid field is a hundred thousand floats its own solver rewrites every step and may itself
|
|
1183
|
+
live on another worker, while the *answer* is three floats a particle. So `cloth_sample_air` runs on
|
|
1184
|
+
the **main thread** in both systems and writes into `ClothState#air_velocity` — a region of the
|
|
1185
|
+
cloth's own `SharedArrayBuffer`, so nothing is copied — and everything built from it runs in
|
|
1186
|
+
`cloth_step_instance` with the rest of the arithmetic, which is what keeps §12's parity structural.
|
|
1187
|
+
Measured: 58–62 ns a particle for a field sample, a twenty-fifth of that for the ambient (one sample
|
|
1188
|
+
for the whole cloth, which is what §10's "skips it entirely" is worth), and one per cent of a step for
|
|
1189
|
+
the term itself. `MEASUREMENTS.md` §48, §49.
|
|
1190
|
+
|
|
1191
|
+
**Where the air comes from is a scene's choice and there is no default.** `ClothWorld#wind` is `null`
|
|
1192
|
+
until something assigns a source, exactly as the collider index is: a scene without one gets M5's
|
|
1193
|
+
cloth rather than an error. Null rather than a still-air ambient, because a source answering zero
|
|
1194
|
+
everywhere is not "no wind" — the row is a relaxation toward the air, so still air still resists a
|
|
1195
|
+
*moving* cloth, anisotropically. That is better physics than `ClothDynamics#damping`'s isotropic decay
|
|
1196
|
+
and it is not free, so it is a thing a scene asks for. What a cloth does in air that is not moving is
|
|
1197
|
+
`damping`'s job (§7 P5, where it is load-bearing); what it does in air that *is* is this.
|
|
1111
1198
|
|
|
1112
1199
|
---
|
|
1113
1200
|
|
|
@@ -1299,12 +1386,19 @@ garments never override them.
|
|
|
1299
1386
|
|
|
1300
1387
|
## 12. Data layout, threading, the worker
|
|
1301
1388
|
|
|
1302
|
-
**One `SharedArrayBuffer`
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1389
|
+
**One `SharedArrayBuffer` per cloth**, and the command region names which cloths are live this step —
|
|
1390
|
+
so sleeping (P5) and culled (§12.1) cloths cost nothing but their absence from a list. `WorkerSystem`'s
|
|
1391
|
+
model is a single shared buffer, a single command region and a single atomic publishing one in-flight
|
|
1392
|
+
step, and `WorkerFluidSystem` shows how a variable set of simulated things fits it.
|
|
1393
|
+
|
|
1394
|
+
**M5 corrected "one buffer for the whole cloth world", and `reseed` is why.** A `ClothState` is sized
|
|
1395
|
+
at seed from *that cloth's* particle and row counts, and `ClothSystem#reseed` — a chain gaining a link,
|
|
1396
|
+
a `ClothDynamics` change the row tables depend on, a rigged garment whose model has just loaded —
|
|
1397
|
+
builds a different one while other cloths have steps in flight. A world buffer would therefore need an
|
|
1398
|
+
allocator with relocation, and a relocation invalidates every view of every region on both threads at
|
|
1399
|
+
once. One buffer per cloth costs one cold-plane message per seed (`add_cloth` / `remove_cloth`, the
|
|
1400
|
+
shape `WorkerFluidSystem` already has) and makes a re-seed a re-registration, which is the same code
|
|
1401
|
+
path as a first seed. `MEASUREMENTS.md` §39.
|
|
1308
1402
|
|
|
1309
1403
|
SoA throughout:
|
|
1310
1404
|
|
|
@@ -1315,13 +1409,40 @@ SoA throughout:
|
|
|
1315
1409
|
| topology | `Int32Array` | edge pairs, bend stencils, colour ranges, tether anchors |
|
|
1316
1410
|
| rest data | `Float32Array` | rest lengths, bend weights, `d_geo` |
|
|
1317
1411
|
| duals | `Float32Array` | `λ` and `k` per persistent constraint |
|
|
1318
|
-
| colliders | `Float32Array` | packed records for this step |
|
|
1412
|
+
| colliders | `Float32Array` | packed records for this step — `CLOTH_COLLIDER_SLOTS` of them |
|
|
1319
1413
|
| contacts | `Float32Array` | transient rows + their warm-start `λ`, keyed by (particle, collider slot) |
|
|
1320
1414
|
|
|
1321
|
-
|
|
1322
|
-
memory the worker already shares, cheaper than tracking dirtiness, and it keeps
|
|
1415
|
+
The **step's parameters** reach the worker through the command region, written every step — fourteen
|
|
1416
|
+
words into memory the worker already shares, cheaper than tracking dirtiness, and it keeps
|
|
1323
1417
|
`cloth.dynamics.slack = 0.2` working live, which is the point of a value object rather than a preset
|
|
1324
|
-
that has to be re-applied.
|
|
1418
|
+
that has to be re-applied. (Thirteen at M5; M6's §10 adds the drag response, and the air itself
|
|
1419
|
+
travels as a region of the same buffer rather than as words — `ClothState#air_velocity`, written by
|
|
1420
|
+
the main thread and read by the step, which is the one direction the buffer could always carry and
|
|
1421
|
+
nothing had used.)
|
|
1422
|
+
|
|
1423
|
+
**M5 corrected "`ClothDynamics` reaches the worker".** By the time the step can use them a cloth's
|
|
1424
|
+
dynamics have been through `cloth_dynamics_map.js` — a damping retention, a velocity clamp, a
|
|
1425
|
+
self-collision radius, gravity rotated into the local frame — and sending the raw component would mean
|
|
1426
|
+
running that mapping a second time on the far side, with no way to prove the two copies agreed. What
|
|
1427
|
+
crosses is `ClothStepParam`: `dt`, local gravity, substeps, iterations, retention, clamp, contact
|
|
1428
|
+
radius, self radius, collider count, and whether self-collision is on. `Float64` throughout, so the
|
|
1429
|
+
round trip is exact — an `f32` command region would round the clamp, and a clamp that rounds is a
|
|
1430
|
+
velocity that rounds. `MEASUREMENTS.md` §40.
|
|
1431
|
+
|
|
1432
|
+
The same block is what makes the parity gate below structural rather than aspirational:
|
|
1433
|
+
`ClothSystem` fills the words of a private scratch array and calls `cloth_step_instance`;
|
|
1434
|
+
`WorkerClothSystem` fills the same words of the shared command region and the worker calls the same
|
|
1435
|
+
function. Neither system computes a step parameter of its own, so neither can compute one differently.
|
|
1436
|
+
|
|
1437
|
+
**M6 found the one thing that is not a parameter and still differs: the clock.** A gust is a function
|
|
1438
|
+
of time, and a worker cloth is not advanced until its registration is acknowledged — so the two
|
|
1439
|
+
systems reach a cloth's *first step* at different tick counts, and a clock that counted ticks hands
|
|
1440
|
+
that cloth a different gust on the same step. Measured with the clock put back on ticks, the gate
|
|
1441
|
+
fails at step 1, byte 0, which is the step's own result word. `ClothWorld#time` therefore counts the
|
|
1442
|
+
steps the world has taken rather than the ticks it has been asked about: it advances at the first
|
|
1443
|
+
`advance` of a tick, so a tick in which nothing is advanced does not move the weather. That is also
|
|
1444
|
+
the right semantic on its own terms — a paused simulation should not have the wind move.
|
|
1445
|
+
`MEASUREMENTS.md` §50.
|
|
1325
1446
|
|
|
1326
1447
|
`Float32` for particle state, not `Float64`. The simulation runs in the cloth's **local frame**, so
|
|
1327
1448
|
coordinates are garment-sized rather than world-sized and f32 has ample precision; the payoff is half
|
|
@@ -1340,6 +1461,16 @@ fixed `CLOTH_CONTACT_SLOTS` per particle inside `ClothState`'s single buffer —
|
|
|
1340
1461
|
saturate on a twenty-collider scene and to average a tenth of one — which keeps §12's one-allocation
|
|
1341
1462
|
property and bounds the worst case at the four *most violated* colliders, which is the four that matter.
|
|
1342
1463
|
|
|
1464
|
+
**And M5 moved the per-step collider table itself into that buffer**, at a fixed
|
|
1465
|
+
`CLOTH_COLLIDER_SLOTS`, which is where the table above always put it. M2 kept it on `ClothInstance`
|
|
1466
|
+
and grew it by doubling, on the correct grounds that its size is a property of the world rather than of
|
|
1467
|
+
the cloth; what decides is that an array which can be *replaced* mid-run is memory the worker has to be
|
|
1468
|
+
told about again before it can name it. So the size stays a property of the world and the region gets a
|
|
1469
|
+
capacity. **Sixty-four**, because the measurement showed the bound is a *scene* rather than a garment —
|
|
1470
|
+
a cloth's table holds every collider on every character standing inside its query box, which is ten for
|
|
1471
|
+
one body and thirty for three — and because an overflow discards colliders in the index's own order
|
|
1472
|
+
rather than in order of how much they matter. It warns once. `MEASUREMENTS.md` §41.
|
|
1473
|
+
|
|
1343
1474
|
**The worker.** `WorkerClothSystem` is a `WorkerSystem` subclass and follows `WorkerFluidSystem`
|
|
1344
1475
|
exactly, because that pattern is already proven in this repo: `collect` packs colliders and animated
|
|
1345
1476
|
targets into shared memory and writes the command words, a single `Atomics.store` publishes the step,
|
|
@@ -1353,6 +1484,15 @@ identical order. That is what makes the worker a deployment choice rather than a
|
|
|
1353
1484
|
it is why the core is a pure function over typed arrays from milestone zero even though the worker does
|
|
1354
1485
|
not arrive until milestone five.
|
|
1355
1486
|
|
|
1487
|
+
**M5 measured it and the number is zero**, at every step of a run rather than at the end of one: the
|
|
1488
|
+
whole `ClothState` buffer — particles, velocities, every dual and penalty, the collider table, the
|
|
1489
|
+
warm-started contact multipliers, the self-collision pair list — compared byte for byte after each of
|
|
1490
|
+
420 steps across two scenes, one with a walking anchor and one with colliders and self-collision live.
|
|
1491
|
+
What the scene sees differs by exactly one tick: the pose written at tick `T` is step `T − 1`'s. That
|
|
1492
|
+
is §4.1's own sentence in a second setting — the cloth's *shape* is one fixed step stale and its
|
|
1493
|
+
*position* is not, because the attachment cascade carries the subtree rigidly with the anchor.
|
|
1494
|
+
`MEASUREMENTS.md` §40.
|
|
1495
|
+
|
|
1356
1496
|
Testing the bridge needs no worker: `SystemWorkerLoopback` gives a real `SystemWorkerHost`, a real
|
|
1357
1497
|
`SharedArrayBuffer` and real `Atomics` in-process, with message delivery and step execution as explicit
|
|
1358
1498
|
events a spec can order. Only the thread is faked.
|
|
@@ -1391,9 +1531,9 @@ What this design takes from the engine rather than building:
|
|
|
1391
1531
|
| Constraint-row doctrine | `physics/constraint/solve_constraints.js` |
|
|
1392
1532
|
| Subtree walk | `TransformAttachmentSystem#findChildrenOf` / `#countChildrenOf`, `TransformAttachment` |
|
|
1393
1533
|
| Animated targets | `mesh_apply_skeletal_vertex_skinning` (flat arrays, worker-safe) |
|
|
1394
|
-
| Wind / aerodynamics | `FluidField#sampleVelocity
|
|
1534
|
+
| Wind / aerodynamics | `FluidField#sampleVelocity` (in grid cells a second — §10 converts), `fluid_build_world_to_grid`, `GlobalFluidEffector` as the shape of the ambient *and* as the exact-ODE relaxation step `cloth_drag_response` copies |
|
|
1395
1535
|
| Worker offload | `ecs/async/WorkerSystem.js`, `SystemWorkerHost`, `SystemWorkerLoopback`, `WorkerFluidSystem` as the worked example |
|
|
1396
|
-
| Fixed step
|
|
1536
|
+
| Fixed step | `EntityManager`'s fixed-step loop, gate and `fixedStepTick` |
|
|
1397
1537
|
| Transforms | `Transform64`, `Node3D` |
|
|
1398
1538
|
| Particle → render-vertex mapping | `Skin` — joints + inverse-bind matrices + the four-weight palette; cloth writes joints and the existing pass does the rest |
|
|
1399
1539
|
| Reaching a model's joints from its entity | `SceneBundleInstance` (`skins`, `nodes` by name), `register_instance_animation` |
|
|
@@ -1404,6 +1544,24 @@ What this design takes from the engine rather than building:
|
|
|
1404
1544
|
|
|
1405
1545
|
---
|
|
1406
1546
|
|
|
1547
|
+
### 13.1 What is *not* reusable — render interpolation
|
|
1548
|
+
|
|
1549
|
+
`PhysicsSystem`'s producer/`__interp_restore` contract was in the ledger above and M5 took it out,
|
|
1550
|
+
because it cannot express a rigged garment. `Interpolated` is an **entity-keyed marker**: the
|
|
1551
|
+
`InterpolationSystem` files snapshots under a `key` it assigns from the entity id at link time. A
|
|
1552
|
+
rope's links are entities and a skinned garment's joints are not — `shade_node_to_entity_composition`
|
|
1553
|
+
does not expand a skinned node, "and that is a boundary rather than an omission" (§3) — so half the
|
|
1554
|
+
feature has nothing to put the component on.
|
|
1555
|
+
|
|
1556
|
+
Cloth also needs only half of what that contract does. `__interp_restore` exists to hand the
|
|
1557
|
+
simulation its authoritative pose back before it integrates, because the render blend writes into the
|
|
1558
|
+
components the simulation reads. Cloth's state is `ClothState.position` in the cloth's local frame and
|
|
1559
|
+
its write-back never touches the anchor it reads, so **cloth never reads back what it writes** and has
|
|
1560
|
+
nothing to restore. It needs the publish half, over a pose per node rather than a component per entity.
|
|
1561
|
+
§18 carries what that would cost. `MEASUREMENTS.md` §43.
|
|
1562
|
+
|
|
1563
|
+
---
|
|
1564
|
+
|
|
1407
1565
|
## 14. New shared code this justifies
|
|
1408
1566
|
|
|
1409
1567
|
Three pieces belong in `core`, not in `cloth`, because the cloth need exposes a gap the engine has
|
|
@@ -1623,13 +1781,36 @@ placement into a bounded quality question and leaves the scheduler alone.
|
|
|
1623
1781
|
and belongs with the collision rows that make a particle get *stuck* in the first place — a chain has
|
|
1624
1782
|
no configuration to be stuck in. Chebyshev acceleration, still gated on contacts that arrive at M2. `β`,
|
|
1625
1783
|
which M0 could not choose and M1 has no transient rows to choose it with either — every row M1 adds is
|
|
1626
|
-
persistent.
|
|
1627
|
-
|
|
1784
|
+
persistent. The serialization adapters: nothing in M1's scope list names them, and a codec written
|
|
1785
|
+
before `ClothRig` exists would be revised by M3 before it was ever loaded — **true of one of the three;
|
|
1786
|
+
the correction follows this block.**
|
|
1628
1787
|
Interpolation: cloth writes `Transform64` at the fixed rate and §12's producer/`__interp_restore`
|
|
1629
1788
|
contract is not wired, which shows as a cloth updating at the fixed rate rather than the frame rate
|
|
1630
1789
|
and is a visible-but-not-wrong gap. M5 is where it belongs, with the worker.
|
|
1631
1790
|
**This is the first shippable feature.**
|
|
1632
1791
|
|
|
1792
|
+
**Corrected 2026-09-13: the deferral above only ever fitted `ClothRig`'s adapter, and it was applied to
|
|
1793
|
+
all three of them from here to the end of the plan.** It fits `ClothRigSerializationAdapter`, whose
|
|
1794
|
+
component points at a built, shared, immutable asset and therefore needs the asset-identity story §3.2
|
|
1795
|
+
only gestures at; and it fits `ClothProxy`'s own codec, which genuinely does depend on what §11.3
|
|
1796
|
+
step 9 adds to a proxy. It never fitted `Cloth` or `ClothCollider`. Those two are scalars — `blend`,
|
|
1797
|
+
`layer`, `mask`, `flags` and a `ClothDynamics` value object of ten more; `inflation`,
|
|
1798
|
+
`friction_scale`, `layer`, `mask`, `flags` — and neither holds a proxy, a skin, or anything either
|
|
1799
|
+
geometry path introduces. The evidence that the revision this sentence feared was never coming is M3
|
|
1800
|
+
itself: it changed the design in four places (`MEASUREMENTS.md` §24–29) and not one of them was a
|
|
1801
|
+
field on either component.
|
|
1802
|
+
|
|
1803
|
+
What turned a sequencing call into a hole is that it was **re-deferred rather than re-examined**. M2
|
|
1804
|
+
deferred all three "per M1's own reasoning"; M3 deferred them again for M1's reason while, in the same
|
|
1805
|
+
list, deferring the unrigged path that the reason had just been made to wait on; and §11.3 step 9 is
|
|
1806
|
+
in no milestone at all. A wait with no scheduled end is not a sequencing decision, and what it cost is
|
|
1807
|
+
that M6 closed this plan with a subsystem a scene could not contain: a `Cloth` could be added from
|
|
1808
|
+
code and from nowhere else. `ClothSerializationAdapter` and `ClothColliderSerializationAdapter` are
|
|
1809
|
+
built and specced as of 2026-09-13, outside any milestone and with no `MEASUREMENTS.md` section,
|
|
1810
|
+
because there is no measurement behind them — a codec has nothing to measure, and that file's sections
|
|
1811
|
+
belong to milestones. `ClothRigSerializationAdapter` and the proxy codec stay deferred, on the reason
|
|
1812
|
+
as narrowed here.
|
|
1813
|
+
|
|
1633
1814
|
**M2 — collision. DONE, 2026-09-10.** `ClothCollider`, `ClothColliderFlags`, `ClothColliderSystem`,
|
|
1634
1815
|
`ClothColliderIndex`, the packed record and its swept bounds, analytic SDF gradients and
|
|
1635
1816
|
`PlaneShape3D` in `core` (§14.1, §14.3), the contact row and its friction row, the backstop row, and a
|
|
@@ -1659,8 +1840,10 @@ particle get stuck, and also measured the one stuck configuration whose *residua
|
|
|
1659
1840
|
the quantity the escape hatch was to watch (§7 P4). It belongs with the self-collision of M4, where a
|
|
1660
1841
|
particle can be pinched between two rows rather than merely held by one. The backstop row is built,
|
|
1661
1842
|
specced and driven by the playground, but nothing **authors** one: its plane comes from a garment's own
|
|
1662
|
-
skinned surface and a skinned garment is M3. Two-way coupling, per §2.
|
|
1663
|
-
|
|
1843
|
+
skinned surface and a skinned garment is M3. Two-way coupling, per §2. The serialization adapters, per
|
|
1844
|
+
M1's own reasoning — which, as M1 now records, reached only `ClothRig`'s. The other two were not
|
|
1845
|
+
blocked by anything M2 had in hand or M3 was about to add, and this line is the first of the two
|
|
1846
|
+
restatements that carried them anyway.
|
|
1664
1847
|
|
|
1665
1848
|
**M3 — skinned garments, the rigged path. DONE, 2026-09-10.** `ClothRig`, `ClothProxy`,
|
|
1666
1849
|
`cloth_proxy_from_joints` (§11.3 steps 5–8), the rig seeder, and joint write-back into
|
|
@@ -1695,7 +1878,13 @@ budget. §11.3 step 10's bind-pose collision validation, which `cloth_proxy_from
|
|
|
1695
1878
|
because it has a joint tree and no collider set — and which `MEASUREMENTS.md` §29 makes the argument
|
|
1696
1879
|
for, because a garment authored inside its own body reads as a *tether* failure and costs an hour to
|
|
1697
1880
|
diagnose. `ClothProxy`'s binary codec and `ClothRigSerializationAdapter`, for M1's own reason: a
|
|
1698
|
-
codec written before the unrigged path exists would be revised before it was ever loaded.
|
|
1881
|
+
codec written before the unrigged path exists would be revised before it was ever loaded. **Those two
|
|
1882
|
+
only, corrected 2026-09-13.** As written this sentence carried `Cloth`'s and `ClothCollider`'s
|
|
1883
|
+
adapters with them, over a set of three, for a reason that held for one — and it did so in the same
|
|
1884
|
+
list that defers the unrigged path the reason had just been made to wait on, which is what closed the
|
|
1885
|
+
loop: the dependency and the thing depending on it were deferred by the same paragraph. Neither scalar
|
|
1886
|
+
component holds a proxy, so §3.2's asset-identity question — the real content of M1's reason — was
|
|
1887
|
+
never theirs to answer. Both adapters were built on 2026-09-13; M1 carries the full correction.
|
|
1699
1888
|
|
|
1700
1889
|
**M4 — self-collision. DONE, 2026-09-11.** `core/geom/3d/hash-grid/PointHashGrid3.js`, the
|
|
1701
1890
|
particle-particle row (`solver/constraint/cloth_self_row.js`), the pair search
|
|
@@ -1762,13 +1951,110 @@ realisations, a self-tangled sheet unties itself every time the gust stops, so M
|
|
|
1762
1951
|
configuration to escape from. The case P4(d) names is a particle wedged between a collider *and* a
|
|
1763
1952
|
fold, which is collision and self-collision together, and M4 has not measured it.
|
|
1764
1953
|
|
|
1765
|
-
**M5 — the worker.** `WorkerClothSystem`, `ClothWorkerCore`, `cloth.worker.js`,
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1954
|
+
**M5 — the worker. DONE, 2026-09-12.** `WorkerClothSystem`, `ClothWorkerCore`, `cloth.worker.js`,
|
|
1955
|
+
`makeClothWorker` and the protocol — plus the two pieces that had to exist for the parity gate to be
|
|
1956
|
+
structural rather than hopeful: `ClothWorld`, which is every cloth in a scene and everything about
|
|
1957
|
+
stepping one that is not arithmetic, owned by *both* systems; and `cloth_step_instance`, which is the
|
|
1958
|
+
arithmetic, driven by a `Float64` parameter block that is also the command region's per-cloth record.
|
|
1959
|
+
`ClothState` gains shared-memory backing, a descriptor, the collider table and a result region.
|
|
1960
|
+
*Proofs, all green:* bit-parity over the **whole `ClothState` buffer at every step** of two runs — a
|
|
1961
|
+
walking anchor for 240 steps, and colliders plus self-collision for 180 — with the write-back's
|
|
1962
|
+
one-tick lag pinned tick by tick rather than asserted at the end; the state a worker steps being the
|
|
1963
|
+
main thread's memory rather than a copy; a cloth being neither stepped **nor advanced** until its
|
|
1964
|
+
registration is acknowledged, which is what keeps the two systems' step *sequences* identical rather
|
|
1965
|
+
than merely similar; a re-seed retiring its registration and waiting again; a despawn between dispatch
|
|
1966
|
+
and step not failing the bridge; sleep removing a cloth from the dispatch and `wake` putting it back;
|
|
1967
|
+
shutdown settling the step in flight, releasing the gate and dropping the worker; and the derived
|
|
1968
|
+
execution order landing `WorkerClothSystem` exactly where `ClothSystem` lands. **10 tests in one new
|
|
1969
|
+
spec file**, with the M0–M4 suites unchanged at 183. The engine suite is green at 18,579.
|
|
1970
|
+
|
|
1971
|
+
**M5 changed the design in four places**, each with the measurement behind it in `MEASUREMENTS.md`
|
|
1972
|
+
§38–44 and each folded back above. In one line each: §12's one buffer for the whole cloth world is one
|
|
1973
|
+
buffer per cloth, because `reseed` resizes one while others are in flight; §12's collider table does
|
|
1974
|
+
belong in that buffer and therefore needs a capacity rather than a growth policy, and the capacity is
|
|
1975
|
+
bounded by the *scene* — thirty records for three characters — rather than by the garment; §12's
|
|
1976
|
+
"`ClothDynamics` reaches the worker through the command region" is the *mapped* step parameters rather
|
|
1977
|
+
than the component, because mapping them twice is two copies that cannot be proved equal; and §13's
|
|
1978
|
+
reuse of `PhysicsSystem`'s interpolation contract does not hold, because that contract is entity-keyed
|
|
1979
|
+
and a rigged garment's joints are not entities.
|
|
1980
|
+
|
|
1981
|
+
**And the strongest result is again a number rather than a correction.** §12 asks the two systems for
|
|
1982
|
+
bit-identical results. Over 420 steps across two scenes, comparing the entire state buffer after every
|
|
1983
|
+
one of them, the number of differing bytes is **zero** — and it is zero by construction rather than by
|
|
1984
|
+
luck, because there is one `ClothWorld`, one `cloth_step_instance`, and one parameter block that both
|
|
1985
|
+
systems fill and neither system interprets. The main thread's share of a thousand-particle garment goes
|
|
1986
|
+
from 3.62 ms to 0.18 ms; across the library's shapes, 92% to 96% of it crosses. `MEASUREMENTS.md` §40,
|
|
1987
|
+
§41.
|
|
1988
|
+
|
|
1989
|
+
**Deliberately not in M5:** **render interpolation**, which §18 said would land here and which M5
|
|
1990
|
+
decided against with three findings rather than a deferral — the named contract is entity-keyed and
|
|
1991
|
+
cannot reach a rigged garment's joints, cloth needs only the half of it that has no name, and the
|
|
1992
|
+
worker removes the cheap implementation by owning `ClothState` outside the fixed pass. What it needs
|
|
1993
|
+
instead is a published pose slot, and §18 now carries what that costs (about 0.12 ms a frame on the
|
|
1994
|
+
§17 workload, which roughly triples the main thread's share of cloth and leaves it at a seventh of
|
|
1995
|
+
its pre-M5 cost) and what it buys (half a link length a step at a walk, a whole one at a sprint).
|
|
1996
|
+
`MEASUREMENTS.md` §43. **A playground page**, for a reason about the dev server rather than the cloth:
|
|
1997
|
+
`SharedArrayBuffer` needs a cross-origin isolated page, and adding `COOP`/`COEP` to
|
|
1998
|
+
`vite.config.mjs` changes how every other playground page in the package loads its subresources —
|
|
1999
|
+
to demonstrate a bridge `SystemWorkerLoopback` already tests more precisely than a page could.
|
|
2000
|
+
**Culling (§12.1)**, which is a policy over the same dispatch list sleep already uses and belongs with
|
|
2001
|
+
a camera. And **a deeper pipeline**: `WorkerSystem` allows one step in flight and the fixed-step gate
|
|
2002
|
+
means a cloth could not use a second.
|
|
2003
|
+
|
|
2004
|
+
**M6 — wind. DONE, 2026-09-13.** §10 end to end: `wind/AbstractClothWind.js` and the two sources
|
|
2005
|
+
behind it (`ClothAmbientWind`, `ClothFluidWind`), `wind/cloth_sample_air.js`,
|
|
2006
|
+
`solver/cloth_wind_accelerate.js`, `ClothState`'s air and external-acceleration regions,
|
|
2007
|
+
`cloth_drag_response`, the parameter block's fourteenth word, and `ClothWorld`'s wind source, clock
|
|
2008
|
+
and wake test.
|
|
2009
|
+
*Proofs, all green:* the normal-facing factor on both topologies, which is the whole of what M6 adds
|
|
2010
|
+
to the physics — a sheet reading 1.000 broadside and 0.000 edge-on, a chain reading the reverse, and
|
|
2011
|
+
neither a skip row nor a strain-limit row able to fabricate a plane out of a line; the two sources
|
|
2012
|
+
through the interface, including a field answering in metres rather than grid cells and `varies()`
|
|
2013
|
+
being a promise the sampler acts on; the ECS path end to end — a garment blown downwind, one inside a
|
|
2014
|
+
`FluidComponent` reading the simulation while one ten metres away reads the weather, a settled banner
|
|
2015
|
+
sleeping in a steady wind and waking on a gust, and a scene with no source producing the cloth M5
|
|
2016
|
+
shipped **byte for byte** across 120 steps; the drag response closing a fraction of the gap to the
|
|
2017
|
+
air that is strictly below one at every step length from 1/240 s to 1 s; and §12's parity gate
|
|
2018
|
+
extended to a third run with wind live. **25 tests across three new spec files** — the term's 9, the
|
|
2019
|
+
sources' 10, the ECS path's 6 — plus the parity spec's eleventh, for 26 in all, with the M0–M5
|
|
2020
|
+
suites unchanged at 193. The engine suite is green at **18,638**, and the same suite run against M5's
|
|
2021
|
+
own commit in a detached worktree reads 18,612 on this machine, so the difference is exactly M6's
|
|
2022
|
+
twenty-six. A fifth playground page, `playground/wind.html`, with a banner hanging in a real
|
|
2023
|
+
`FluidSimulator`'s wake.
|
|
2024
|
+
|
|
2025
|
+
**M6 changed the design in five places**, each with the measurement behind it in `MEASUREMENTS.md`
|
|
2026
|
+
§45–§52 and each folded back above. In one line each: §10's `|n·û|` cannot be written for a chain,
|
|
2027
|
+
which has no normal and wants the complementary factor anyway, so both are one expression over the
|
|
2028
|
+
span of the incident rows; §10's "samples its velocity field" needs a unit conversion, because
|
|
2029
|
+
`FluidField` answers in grid cells a second and a garment blowing at a speed that depends on the
|
|
2030
|
+
voxel size is not a garment; §10 never said which thread samples and it cannot be the worker's, so
|
|
2031
|
+
the field stays where it is and three floats a particle cross in the cloth's own buffer; `drag`
|
|
2032
|
+
cannot multiply the flow directly, because one acceleration held for a whole fixed step overshoots
|
|
2033
|
+
the air once `rate·dt > 1`; and §12's thirteen-word block is fourteen, with a clock that has to count
|
|
2034
|
+
steps rather than ticks or the two systems' weather diverges at step 1.
|
|
2035
|
+
|
|
2036
|
+
**And the strongest result is again a number rather than a correction.** §10 offers the normal-facing
|
|
2037
|
+
factor as the difference between a flag and a rope. In uniform air it is: turning the wind ninety
|
|
2038
|
+
degrees at the same speed takes the banner's aerodynamic acceleration from **43.2 m/s² to 0.08**, a
|
|
2039
|
+
factor of five hundred. In a **wake** it does nothing at all — 0.216 against 0.280 — because a wake
|
|
2040
|
+
has velocity in every direction and no orientation of a surface is edge-on to one. That is not a
|
|
2041
|
+
failure of the factor; it is the measurement saying §10's argument for reading a simulation is
|
|
2042
|
+
stronger than §10 knew, and that the flag-versus-rope claim is a statement about laminar air.
|
|
2043
|
+
`MEASUREMENTS.md` §51.
|
|
2044
|
+
|
|
2045
|
+
**Deliberately not in M6:** **a spatially varying ambient.** The gust is uniform in space, so a long
|
|
2046
|
+
banner does not see it arrive at one end before the other — and that is what makes the no-simulation
|
|
2047
|
+
case cost one sample for a whole garment instead of one a particle (§48). Spatial structure is what a
|
|
2048
|
+
fluid field is *for*, and a second, worse one inside the fallback is the duplication §10 exists to
|
|
2049
|
+
avoid. **A blend at a field's edge**: a point inside a volume reads the volume and a point outside
|
|
2050
|
+
reads the ambient, so a garment straddling the boundary sees a step rather than a ramp. Where the
|
|
2051
|
+
ambient is the far field the volume relaxes toward — `GlobalFluidEffector`'s own `wind` — the step is
|
|
2052
|
+
small and is mostly the disturbance the volume exists to carry; where it is not, the answer is a
|
|
2053
|
+
volume large enough to hold what moves through it. **Two-way coupling into the fluid**, per §2's
|
|
2054
|
+
one-way rule: cloth reads the air and does not disturb it, and anything that should has
|
|
2055
|
+
`FluidObstacle`. And **per-substep sampling**, because §10 says per step and §48 measures the sample
|
|
2056
|
+
at 58 ns a particle — the one part of this that is not free is the one part per-substep sampling
|
|
2057
|
+
would double.
|
|
1772
2058
|
|
|
1773
2059
|
**Performance budget — measured at M0, re-measured at M1, and it still does not hold on its own.** One
|
|
1774
2060
|
character's full cloth complement — 1,024 particles at 2 substeps × 4 iterations, on a triangulated
|
|
@@ -1818,7 +2104,31 @@ attributing cost by deletion applies unchanged. What it licenses is that the col
|
|
|
1818
2104
|
between nothing and a quarter of a frame and does not scale with the collider count over the range a
|
|
1819
2105
|
character occupies. Every library entry's own budget is untouched. `MEASUREMENTS.md` §23.
|
|
1820
2106
|
|
|
1821
|
-
Re-
|
|
2107
|
+
**Re-measured at M5, and no entry is re-sized — but not for the reason the table reads.** The absolute
|
|
2108
|
+
column came back 15% to 25% above M4's, on a machine carrying nineteen other node processes at 30–38%
|
|
2109
|
+
background CPU, which is exactly the swing M4's own lesson predicts. What licenses leaving the library
|
|
2110
|
+
alone is a *paired* measurement instead: `cloth_library_bench.mjs` never touches
|
|
2111
|
+
`WorkerClothSystem` — it drives `cloth_step` over a `ClothState` directly — so the only thing M5
|
|
2112
|
+
changed that it can see is the buffer layout, and an interleaved A/B of the two layouts in one process
|
|
2113
|
+
reads 0.95 to 0.98 with the two states staying bit-identical for 1,600 steps. §33's ordering stands
|
|
2114
|
+
entry for entry. `MEASUREMENTS.md` §39, §42.
|
|
2115
|
+
|
|
2116
|
+
**Re-measured at M6, and no entry is re-sized — now with a measured reason rather than only a
|
|
2117
|
+
disclaimer.** §10's aerodynamic term, priced in the bench's own §33 scenario, moves each entry by
|
|
2118
|
+
−2.7% to +9.3% across three runs, with the sign not monotone in `drag`; and with the *physics* held fixed so the two
|
|
2119
|
+
sides simulate the same cloth, the whole feature is **one per cent of a step**. The rest of that
|
|
2120
|
+
column is a different cloth — a rope blown horizontal costs 3.24× a rope hanging straight down, which
|
|
2121
|
+
is §7's warning arriving in a new setting and is about the rope. Three runs of the same bench within
|
|
2122
|
+
an hour put the §17 baseline row at 3.49, 3.83 and 4.26 ms, so the absolute column is still the
|
|
2123
|
+
machine: M5's advice to re-measure it quietly stands, and M6 adds that a process count is a proxy for
|
|
2124
|
+
load rather than a measure of it. `MEASUREMENTS.md` §49, §52.
|
|
2125
|
+
|
|
2126
|
+
**And the budget now means something slightly different.** The worker moves the cost rather than
|
|
2127
|
+
reducing it, so 2 ms is still 2 ms — but it is 2 ms on a thread that is not drawing the frame, and the
|
|
2128
|
+
question it answers has become "does all the cloth in the scene fit in one worker tick" rather than
|
|
2129
|
+
"does one garment fit in a frame". The main thread's share of the §17 workload is 0.18 ms.
|
|
2130
|
+
`MEASUREMENTS.md` §41. Re-measure the absolute column on a quiet machine, and check the process count
|
|
2131
|
+
first.
|
|
1822
2132
|
|
|
1823
2133
|
---
|
|
1824
2134
|
|
|
@@ -1942,6 +2252,33 @@ set.
|
|
|
1942
2252
|
garment or a runtime hierarchy. Culling (§12.1) covers the off-screen case; the far-but-visible case is
|
|
1943
2253
|
unaddressed.
|
|
1944
2254
|
|
|
2255
|
+
**Open — a wake has no direction, and the aerodynamic model has only one term. — MEASURED.** §10's
|
|
2256
|
+
normal-facing factor is the whole of M6's aerodynamics, and in uniform air it does exactly what §10
|
|
2257
|
+
promises: ninety degrees of flow angle is a factor of five hundred in the force a banner feels. In a
|
|
2258
|
+
fluid wake it does nothing — 0.216 against 0.280 — because a wake has velocity in every direction.
|
|
2259
|
+
What that leaves open is whether a cloth *in* a wake wants something the linear drag row does not
|
|
2260
|
+
carry: the fluttering a real flag does in one comes from a pressure difference across the sheet and a
|
|
2261
|
+
shed vortex, not from a relaxation toward a local velocity, and this design has no term for either.
|
|
2262
|
+
*Mitigation:* none is needed for what M6 claims — the cloth reads the air the simulation produces and
|
|
2263
|
+
is moved by it. It is named because "a banner in a wake looks alive" is what a reader will expect from
|
|
2264
|
+
§10 and is not what a drag row alone produces. `MEASUREMENTS.md` §51.
|
|
2265
|
+
|
|
2266
|
+
**Open — the ambient is uniform in space and the field boundary is a step.** Two limits of §10's
|
|
2267
|
+
fallback, both deliberate and both cheap to see: a gust lifts a whole banner at once rather than
|
|
2268
|
+
travelling along it, and a garment carried out of a fluid volume crosses a discontinuity rather than a
|
|
2269
|
+
ramp. The first is what makes the no-simulation case cost one sample rather than one a particle; the
|
|
2270
|
+
second is small where the ambient is the far field the volume relaxes toward and visible where it is
|
|
2271
|
+
not. A blend would want a falloff distance, which is an authored quantity of exactly the kind §3.4
|
|
2272
|
+
spent a section removing.
|
|
2273
|
+
|
|
2274
|
+
**Open — a fluid volume's wake is steady at a grid a frame can afford. — MEASURED, and it is the
|
|
2275
|
+
fluid system's rather than cloth's.** At 18, 26 and 34 cells across, with the simulator's damping and
|
|
2276
|
+
vorticity confinement at both ends of their range, the air a banner's width behind a solid post varies
|
|
2277
|
+
by under 0.03 m/s: a velocity deficit, not a shedding street. The deficit is real and is a third of
|
|
2278
|
+
the free stream, which is what the cloth reads; what is not there is the unsteadiness. Resolving a
|
|
2279
|
+
shedding shear layer costs 15 ms a step at 26 cells and 35 at 34, against 5 ms at 18.
|
|
2280
|
+
`MEASUREMENTS.md` §51.
|
|
2281
|
+
|
|
1945
2282
|
**Open — `stretch` is a weak control, and it is not obvious what would make it a strong one.** M1
|
|
1946
2283
|
measured the strain-limit row removing about a third of the peak over-stretch and never reaching its
|
|
1947
2284
|
own allowance, because it is a second hard row competing with the first for the same trust region and
|
|
@@ -1953,11 +2290,36 @@ penalty band, making `k_max/(m/h²)` a material property rather than a global (w
|
|
|
1953
2290
|
§4 shows trades stretchiness against settling); or accepting that near-inextensibility is what fabric
|
|
1954
2291
|
*is* and demoting `stretch` to a documented weak knob. M2's contacts do not bear on it either way.
|
|
1955
2292
|
|
|
1956
|
-
**Open — the write-back has no interpolation
|
|
1957
|
-
`
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
2293
|
+
**Open — the write-back has no interpolation, and M5 decided it does not land with the worker.** Cloth
|
|
2294
|
+
writes `Transform64` at the fixed rate and nothing blends between ticks, so a cloth updates at 60 Hz on
|
|
2295
|
+
a 165 Hz display — 105 of every 165 frames repeat a pose. That is visible and it is not wrong: the
|
|
2296
|
+
poses are authoritative, they are simply held. **How** visible is now measured, on M3's cape geometry:
|
|
2297
|
+
a node covers 0.47 link lengths in a fixed step at a walk and 1.33 at a sprint (`MEASUREMENTS.md` §43).
|
|
2298
|
+
|
|
2299
|
+
What M5 changed is the shape of the remaining work, in three findings that between them delete the
|
|
2300
|
+
sentence this note used to end with.
|
|
2301
|
+
|
|
2302
|
+
- **`PhysicsSystem`'s producer / `__interp_restore` contract cannot express half the feature.**
|
|
2303
|
+
`Interpolated` is an entity-keyed marker and a rigged garment's joints are `Node3D`s, not entities
|
|
2304
|
+
(§13.1). There is nothing to put the component on and no key to file a snapshot under.
|
|
2305
|
+
- **Cloth needs only the publish half.** `__interp_restore` undoes render-time writes before the
|
|
2306
|
+
simulation reads them; cloth's state is its particle array and its write-back never touches the
|
|
2307
|
+
anchor it reads, so there is nothing to restore. The restore it *does* depend on — an anchor on a
|
|
2308
|
+
physics-driven character — it already gets from `PhysicsSystem`.
|
|
2309
|
+
- **The worker removes the cheap implementation.** A render-time pass cannot read
|
|
2310
|
+
`ClothState.position`, because the worker owns it from dispatch until the next tick's join, which is
|
|
2311
|
+
essentially all of the time outside the fixed pass. What is needed is a **published pose slot**: the
|
|
2312
|
+
last two steps' node poses, copied out at join and blended at render time — the same double-buffered
|
|
2313
|
+
publish slot `WorkerFluidSystem` names as the unbuilt prerequisite for its own render-time reads.
|
|
2314
|
+
|
|
2315
|
+
The slot is nearly free to fill, because `cloth_write_back` already computes every node's local TRS
|
|
2316
|
+
and would only have to keep it; what costs is the per-frame pass. For the §17 workload that is about
|
|
2317
|
+
0.12 ms a frame — 20.5 ms a second at 165 Hz, against the 10.9 ms a second M5 leaves on the main
|
|
2318
|
+
thread and the 217 ms a second it cost before. **Interpolating a cloth would roughly triple the main
|
|
2319
|
+
thread's share of it and leave it at a seventh of what it was**, which is the honest shape of the
|
|
2320
|
+
trade: the feature is affordable because M5 happened, and it is not free. The cloth-shaped version is
|
|
2321
|
+
*simpler* than the engine's, not harder: no restore pass, no serialization adapters, no log, and it
|
|
2322
|
+
covers the rigged and entity paths identically because both end in a pose per node.
|
|
1961
2323
|
|
|
1962
2324
|
**Open — the subtree is walked once.** A rope's links do not come and go, so `cloth_seed_subtree` runs
|
|
1963
2325
|
on first step and `ClothSystem#reseed` is how a caller says the structure changed. That is the same
|