@woosh/meep-engine 3.22.0 → 3.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/package.json +1 -1
  2. package/src/core/geom/3d/hash-grid/PointHashGrid3.d.ts +118 -0
  3. package/src/core/geom/3d/hash-grid/PointHashGrid3.d.ts.map +1 -0
  4. package/src/core/geom/3d/hash-grid/PointHashGrid3.js +283 -0
  5. package/src/core/geom/3d/shape/BoxShape3D.d.ts.map +1 -1
  6. package/src/core/geom/3d/shape/BoxShape3D.js +6 -17
  7. package/src/core/geom/3d/shape/CapsuleShape3D.d.ts.map +1 -1
  8. package/src/core/geom/3d/shape/CapsuleShape3D.js +3 -14
  9. package/src/core/geom/3d/shape/CylinderShape3D.d.ts.map +1 -1
  10. package/src/core/geom/3d/shape/CylinderShape3D.js +3 -21
  11. package/src/core/geom/3d/shape/PlaneShape3D.d.ts +105 -0
  12. package/src/core/geom/3d/shape/PlaneShape3D.d.ts.map +1 -0
  13. package/src/core/geom/3d/shape/PlaneShape3D.js +251 -0
  14. package/src/core/geom/3d/shape/SphereShape3D.js +3 -3
  15. package/src/core/geom/3d/shape/json/shape_to_type.d.ts.map +1 -1
  16. package/src/core/geom/3d/shape/json/shape_to_type.js +3 -0
  17. package/src/core/geom/3d/shape/json/type_adapters.d.ts +14 -0
  18. package/src/core/geom/3d/shape/json/type_adapters.d.ts.map +1 -1
  19. package/src/core/geom/3d/shape/json/type_adapters.js +15 -0
  20. package/src/core/geom/3d/shape/sdf/sdf3_box.d.ts +51 -0
  21. package/src/core/geom/3d/shape/sdf/sdf3_box.d.ts.map +1 -0
  22. package/src/core/geom/3d/shape/sdf/sdf3_box.js +113 -0
  23. package/src/core/geom/3d/shape/sdf/sdf3_capsule.d.ts +46 -0
  24. package/src/core/geom/3d/shape/sdf/sdf3_capsule.d.ts.map +1 -0
  25. package/src/core/geom/3d/shape/sdf/sdf3_capsule.js +82 -0
  26. package/src/core/geom/3d/shape/sdf/sdf3_cylinder.d.ts +60 -0
  27. package/src/core/geom/3d/shape/sdf/sdf3_cylinder.d.ts.map +1 -0
  28. package/src/core/geom/3d/shape/sdf/sdf3_cylinder.js +126 -0
  29. package/src/core/geom/3d/shape/sdf/sdf3_degenerate_normal.d.ts +50 -0
  30. package/src/core/geom/3d/shape/sdf/sdf3_degenerate_normal.d.ts.map +1 -0
  31. package/src/core/geom/3d/shape/sdf/sdf3_degenerate_normal.js +53 -0
  32. package/src/core/geom/3d/shape/sdf/sdf3_plane.d.ts +52 -0
  33. package/src/core/geom/3d/shape/sdf/sdf3_plane.d.ts.map +1 -0
  34. package/src/core/geom/3d/shape/sdf/sdf3_plane.js +61 -0
  35. package/src/core/geom/3d/shape/sdf/sdf3_sphere.d.ts +41 -0
  36. package/src/core/geom/3d/shape/sdf/sdf3_sphere.d.ts.map +1 -0
  37. package/src/core/geom/3d/shape/sdf/sdf3_sphere.js +71 -0
  38. package/src/core/process/ConcurrencyGate.d.ts +103 -0
  39. package/src/core/process/ConcurrencyGate.d.ts.map +1 -0
  40. package/src/core/process/ConcurrencyGate.js +207 -0
  41. package/src/engine/asset/AssetManager.d.ts +10 -0
  42. package/src/engine/asset/AssetManager.d.ts.map +1 -1
  43. package/src/engine/asset/AssetManager.js +10 -0
  44. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +30 -13
  45. package/src/engine/graphics3/pose/collect_entity_playbacks.d.ts +26 -0
  46. package/src/engine/graphics3/pose/collect_entity_playbacks.d.ts.map +1 -0
  47. package/src/engine/graphics3/pose/collect_entity_playbacks.js +40 -0
  48. package/src/engine/graphics3/pose/query_entity_node_world_pose.d.ts.map +1 -1
  49. package/src/engine/graphics3/pose/query_entity_node_world_pose.js +74 -101
  50. package/src/engine/physics/cloth/MEASUREMENTS.md +1287 -4
  51. package/src/engine/physics/cloth/PLAN.md +1967 -1581
  52. package/src/engine/physics/cloth/build/ClothProxy.d.ts +147 -0
  53. package/src/engine/physics/cloth/build/ClothProxy.d.ts.map +1 -0
  54. package/src/engine/physics/cloth/build/ClothProxy.js +207 -0
  55. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.d.ts +70 -0
  56. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.d.ts.map +1 -0
  57. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.js +256 -0
  58. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts +176 -0
  59. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts.map +1 -0
  60. package/src/engine/physics/cloth/collider/ClothColliderIndex.js +435 -0
  61. package/src/engine/physics/cloth/collider/ClothColliderKind.d.ts +12 -0
  62. package/src/engine/physics/cloth/collider/ClothColliderKind.d.ts.map +1 -0
  63. package/src/engine/physics/cloth/collider/ClothColliderKind.js +38 -0
  64. package/src/engine/physics/cloth/collider/ClothColliderRecord.d.ts +120 -0
  65. package/src/engine/physics/cloth/collider/ClothColliderRecord.d.ts.map +1 -0
  66. package/src/engine/physics/cloth/collider/ClothColliderRecord.js +137 -0
  67. package/src/engine/physics/cloth/collider/cloth_collider_bounds.d.ts +25 -0
  68. package/src/engine/physics/cloth/collider/cloth_collider_bounds.d.ts.map +1 -0
  69. package/src/engine/physics/cloth/collider/cloth_collider_bounds.js +190 -0
  70. package/src/engine/physics/cloth/collider/cloth_collider_pack.d.ts +44 -0
  71. package/src/engine/physics/cloth/collider/cloth_collider_pack.d.ts.map +1 -0
  72. package/src/engine/physics/cloth/collider/cloth_collider_pack.js +173 -0
  73. package/src/engine/physics/cloth/collider/cloth_collider_sdf.d.ts +94 -0
  74. package/src/engine/physics/cloth/collider/cloth_collider_sdf.d.ts.map +1 -0
  75. package/src/engine/physics/cloth/collider/cloth_collider_sdf.js +275 -0
  76. package/src/engine/physics/cloth/ecs/ClothCollider.d.ts +126 -0
  77. package/src/engine/physics/cloth/ecs/ClothCollider.d.ts.map +1 -0
  78. package/src/engine/physics/cloth/ecs/ClothCollider.js +199 -0
  79. package/src/engine/physics/cloth/ecs/ClothColliderFlags.d.ts +9 -0
  80. package/src/engine/physics/cloth/ecs/ClothColliderFlags.d.ts.map +1 -0
  81. package/src/engine/physics/cloth/ecs/ClothColliderFlags.js +32 -0
  82. package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts +114 -0
  83. package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts.map +1 -0
  84. package/src/engine/physics/cloth/ecs/ClothColliderSystem.js +243 -0
  85. package/src/engine/physics/cloth/ecs/ClothDynamicsFlags.js +66 -57
  86. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +109 -1
  87. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
  88. package/src/engine/physics/cloth/ecs/ClothInstance.js +479 -363
  89. package/src/engine/physics/cloth/ecs/ClothRig.d.ts +95 -0
  90. package/src/engine/physics/cloth/ecs/ClothRig.d.ts.map +1 -0
  91. package/src/engine/physics/cloth/ecs/ClothRig.js +140 -0
  92. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +30 -1
  93. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
  94. package/src/engine/physics/cloth/ecs/ClothSystem.js +1021 -773
  95. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts +90 -0
  96. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts.map +1 -0
  97. package/src/engine/physics/cloth/ecs/cloth_build_rows.js +374 -0
  98. package/src/engine/physics/cloth/ecs/cloth_dynamics_library.d.ts +32 -3
  99. package/src/engine/physics/cloth/ecs/cloth_dynamics_library.d.ts.map +1 -1
  100. package/src/engine/physics/cloth/ecs/cloth_dynamics_library.js +283 -254
  101. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts +59 -0
  102. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts.map +1 -0
  103. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +328 -0
  104. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.d.ts +13 -0
  105. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.d.ts.map +1 -0
  106. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.js +288 -0
  107. package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts +58 -0
  108. package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts.map +1 -0
  109. package/src/engine/physics/cloth/ecs/cloth_seed_rig.js +199 -0
  110. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts +8 -63
  111. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts.map +1 -1
  112. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.js +317 -645
  113. package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts +61 -0
  114. package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts.map +1 -1
  115. package/src/engine/physics/cloth/ecs/cloth_write_back.js +212 -449
  116. package/src/engine/physics/cloth/ecs/cloth_write_back_rig.d.ts +49 -0
  117. package/src/engine/physics/cloth/ecs/cloth_write_back_rig.d.ts.map +1 -0
  118. package/src/engine/physics/cloth/ecs/cloth_write_back_rig.js +115 -0
  119. package/src/engine/physics/cloth/playground/README.md +155 -1
  120. package/src/engine/physics/cloth/playground/collide.html +178 -0
  121. package/src/engine/physics/cloth/playground/collide_build.d.ts +95 -0
  122. package/src/engine/physics/cloth/playground/collide_build.d.ts.map +1 -0
  123. package/src/engine/physics/cloth/playground/collide_build.js +315 -0
  124. package/src/engine/physics/cloth/playground/collide_main.d.ts +2 -0
  125. package/src/engine/physics/cloth/playground/collide_main.d.ts.map +1 -0
  126. package/src/engine/physics/cloth/playground/collide_main.js +720 -0
  127. package/src/engine/physics/cloth/playground/garment.html +182 -0
  128. package/src/engine/physics/cloth/playground/garment_build.d.ts +78 -0
  129. package/src/engine/physics/cloth/playground/garment_build.d.ts.map +1 -0
  130. package/src/engine/physics/cloth/playground/garment_build.js +359 -0
  131. package/src/engine/physics/cloth/playground/garment_main.d.ts +2 -0
  132. package/src/engine/physics/cloth/playground/garment_main.d.ts.map +1 -0
  133. package/src/engine/physics/cloth/playground/garment_main.js +633 -0
  134. package/src/engine/physics/cloth/solver/ClothState.d.ts +208 -5
  135. package/src/engine/physics/cloth/solver/ClothState.d.ts.map +1 -1
  136. package/src/engine/physics/cloth/solver/ClothState.js +302 -8
  137. package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts +87 -0
  138. package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts.map +1 -0
  139. package/src/engine/physics/cloth/solver/cloth_contact_find.js +343 -0
  140. package/src/engine/physics/cloth/solver/cloth_self_find.d.ts +112 -0
  141. package/src/engine/physics/cloth/solver/cloth_self_find.d.ts.map +1 -0
  142. package/src/engine/physics/cloth/solver/cloth_self_find.js +448 -0
  143. package/src/engine/physics/cloth/solver/cloth_solve_vertex.d.ts +4 -1
  144. package/src/engine/physics/cloth/solver/cloth_solve_vertex.d.ts.map +1 -1
  145. package/src/engine/physics/cloth/solver/cloth_solve_vertex.js +16 -1
  146. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts +112 -5
  147. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts.map +1 -1
  148. package/src/engine/physics/cloth/solver/cloth_solver_constants.js +117 -6
  149. package/src/engine/physics/cloth/solver/cloth_step.d.ts +40 -9
  150. package/src/engine/physics/cloth/solver/cloth_step.d.ts.map +1 -1
  151. package/src/engine/physics/cloth/solver/cloth_step.js +122 -10
  152. package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.d.ts +95 -0
  153. package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.d.ts.map +1 -0
  154. package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.js +285 -0
  155. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.d.ts +129 -0
  156. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.d.ts.map +1 -0
  157. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.js +501 -0
  158. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.d.ts +75 -0
  159. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.d.ts.map +1 -0
  160. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.js +202 -0
  161. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  162. package/src/shade/playground/vgeo_viewer/README.md +18 -0
  163. package/src/shade/playground/vgeo_viewer/main.js +87 -50
  164. package/src/shade/renderer/buffer/table/GPUDatabase.d.ts.map +1 -1
  165. package/src/shade/renderer/buffer/table/GPUDatabase.js +191 -62
  166. package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts +94 -2
  167. package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
  168. package/src/shade/renderer/buffer/table/GPUTypedTable.js +414 -2
  169. package/src/shade/renderer/buffer/table/GPU_TABLE_PARTIAL_UPLOAD_DESCRIPTOR_WORDS.d.ts +14 -0
  170. package/src/shade/renderer/buffer/table/GPU_TABLE_PARTIAL_UPLOAD_DESCRIPTOR_WORDS.d.ts.map +1 -0
  171. package/src/shade/renderer/buffer/table/GPU_TABLE_PARTIAL_UPLOAD_DESCRIPTOR_WORDS.js +13 -0
  172. package/src/shade/renderer/buffer/table/gpu_table_partial_upload_shader.d.ts +17 -0
  173. package/src/shade/renderer/buffer/table/gpu_table_partial_upload_shader.d.ts.map +1 -0
  174. package/src/shade/renderer/buffer/table/gpu_table_partial_upload_shader.js +116 -0
  175. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts +9 -4
  176. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts.map +1 -1
  177. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.js +843 -734
  178. package/src/shade/renderer/geometry/bvh/GPUGeometryMeshletsBVH.d.ts +12 -0
  179. package/src/shade/renderer/geometry/bvh/GPUGeometryMeshletsBVH.d.ts.map +1 -1
  180. package/src/shade/renderer/geometry/bvh/GPUGeometryMeshletsBVH.js +13 -0
  181. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts +17 -0
  182. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
  183. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +185 -30
  184. package/src/shade/renderer/geometry/virtual/MICRON_COMPARISON_2026_08_20.md +17 -0
  185. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +141 -24
  186. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +204 -64
  187. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +110 -10
  188. package/src/shade/renderer/geometry/virtual/VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md +19 -0
  189. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts +19 -6
  190. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts.map +1 -1
  191. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.js +26 -7
  192. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts +173 -14
  193. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts.map +1 -1
  194. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.js +458 -51
  195. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts +49 -13
  196. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts.map +1 -1
  197. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.js +52 -14
  198. package/src/shade/renderer/geometry/virtual/format/read/tool/VGEO_MAX_AUDIT_BYTES.d.ts +20 -0
  199. package/src/shade/renderer/geometry/virtual/format/read/tool/VGEO_MAX_AUDIT_BYTES.d.ts.map +1 -0
  200. package/src/shade/renderer/geometry/virtual/format/read/tool/VGEO_MAX_AUDIT_BYTES.js +19 -0
  201. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_audit_container.d.ts +27 -0
  202. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_audit_container.d.ts.map +1 -0
  203. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_audit_container.js +48 -0
  204. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_install_all_pages.d.ts +29 -0
  205. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_install_all_pages.d.ts.map +1 -0
  206. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_install_all_pages.js +60 -0
  207. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts +8 -1
  208. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts.map +1 -1
  209. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.js +32 -2
  210. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts +258 -0
  211. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts.map +1 -0
  212. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.js +723 -0
  213. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts +49 -0
  214. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts.map +1 -0
  215. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.js +50 -0
  216. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  217. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  218. package/src/shade/renderer/texture/bindless/GPUBindlessTextureManager.d.ts +3 -0
  219. package/src/shade/renderer/texture/bindless/GPUBindlessTextureManager.d.ts.map +1 -1
  220. package/src/shade/renderer/texture/bindless/GPUBindlessTextureManager.js +121 -21
  221. package/src/shade/renderer/texture/virtual/README.md +6 -4
  222. package/src/shade/renderer/texture/virtual/VirtualTextureManager.d.ts.map +1 -1
  223. package/src/shade/renderer/texture/virtual/VirtualTextureManager.js +7 -0
  224. package/src/shade/renderer/texture/virtual/source/VTSourceTiled.d.ts +3 -3
  225. package/src/shade/renderer/texture/virtual/source/VTSourceTiled.js +3 -3
  226. package/src/engine/ecs/system/SystemExecutionPhase.d.ts +0 -9
  227. package/src/engine/ecs/system/SystemExecutionPhase.d.ts.map +0 -1
  228. package/src/engine/graphics/texture/virtual/NOTES.md +0 -128
  229. package/src/engine/graphics/texture/virtual/VT_DEFAULT_PAGE_RESOLUTION.d.ts +0 -6
  230. package/src/engine/graphics/texture/virtual/VT_DEFAULT_PAGE_RESOLUTION.d.ts.map +0 -1
  231. package/src/engine/graphics/texture/virtual/VT_DEFAULT_PAGE_RESOLUTION.js +0 -5
  232. package/src/engine/graphics/texture/virtual/VirtualTextureTileLoader.d.ts +0 -49
  233. package/src/engine/graphics/texture/virtual/VirtualTextureTileLoader.d.ts.map +0 -1
  234. package/src/engine/graphics/texture/virtual/VirtualTextureTileLoader.js +0 -264
  235. package/src/engine/graphics/texture/virtual/VirtualTextureUsage.d.ts +0 -44
  236. package/src/engine/graphics/texture/virtual/VirtualTextureUsage.d.ts.map +0 -1
  237. package/src/engine/graphics/texture/virtual/VirtualTextureUsage.js +0 -228
  238. package/src/engine/graphics/texture/virtual/VirtualTextureUsageShader.d.ts +0 -3
  239. package/src/engine/graphics/texture/virtual/VirtualTextureUsageShader.d.ts.map +0 -1
  240. package/src/engine/graphics/texture/virtual/VirtualTextureUsageShader.js +0 -73
  241. package/src/engine/graphics/texture/virtual/debug/ResidencyDebugView.d.ts +0 -14
  242. package/src/engine/graphics/texture/virtual/debug/ResidencyDebugView.d.ts.map +0 -1
  243. package/src/engine/graphics/texture/virtual/debug/ResidencyDebugView.js +0 -85
  244. package/src/engine/graphics/texture/virtual/debug/UsageDebugView.d.ts +0 -12
  245. package/src/engine/graphics/texture/virtual/debug/UsageDebugView.d.ts.map +0 -1
  246. package/src/engine/graphics/texture/virtual/debug/UsageDebugView.js +0 -63
  247. package/src/engine/graphics/texture/virtual/debug/UsagePyramidDebugView.d.ts +0 -12
  248. package/src/engine/graphics/texture/virtual/debug/UsagePyramidDebugView.d.ts.map +0 -1
  249. package/src/engine/graphics/texture/virtual/debug/UsagePyramidDebugView.js +0 -243
  250. package/src/engine/graphics/texture/virtual/tile/VirtualTextureTile.d.ts +0 -30
  251. package/src/engine/graphics/texture/virtual/tile/VirtualTextureTile.d.ts.map +0 -1
  252. package/src/engine/graphics/texture/virtual/tile/VirtualTextureTile.js +0 -39
  253. package/src/engine/graphics/texture/virtual/tile/compose_finger_print.d.ts +0 -9
  254. package/src/engine/graphics/texture/virtual/tile/compose_finger_print.d.ts.map +0 -1
  255. package/src/engine/graphics/texture/virtual/tile/compose_finger_print.js +0 -24
  256. package/src/engine/graphics/texture/virtual/tile/compose_tile_address.d.ts +0 -9
  257. package/src/engine/graphics/texture/virtual/tile/compose_tile_address.d.ts.map +0 -1
  258. package/src/engine/graphics/texture/virtual/tile/compose_tile_address.js +0 -26
  259. package/src/engine/graphics/texture/virtual/tile/decompose_finger_print.d.ts +0 -11
  260. package/src/engine/graphics/texture/virtual/tile/decompose_finger_print.d.ts.map +0 -1
  261. package/src/engine/graphics/texture/virtual/tile/decompose_finger_print.js +0 -12
  262. package/src/engine/graphics/texture/virtual/tile/finger_print_to_tile_address.d.ts +0 -7
  263. package/src/engine/graphics/texture/virtual/tile/finger_print_to_tile_address.d.ts.map +0 -1
  264. package/src/engine/graphics/texture/virtual/tile/finger_print_to_tile_address.js +0 -16
  265. package/src/engine/graphics/texture/virtual/tile/tile_address_to_finger_print.d.ts +0 -7
  266. package/src/engine/graphics/texture/virtual/tile/tile_address_to_finger_print.d.ts.map +0 -1
  267. package/src/engine/graphics/texture/virtual/tile/tile_address_to_finger_print.js +0 -35
@@ -49,10 +49,26 @@
49
49
  * for **transient** rows — a contact that should arrive soft and stiffen rather
50
50
  * than shock the mesh on its first frame — and M0 has none.
51
51
  *
52
- * **`β` could not be chosen from M0 data.** Above about 1e4 every M0 scenario
53
- * gives an identical answer, and with the band collapsed it has no effect at
54
- * all. The value below is a placeholder that M2 must replace with a
55
- * measurement against real contacts.
52
+ * **`β` is M2's, and it is 1e3 rather than M0's 1e5 placeholder.** M0 could not
53
+ * choose it: above about 1e4 every M0 scenario gave an identical answer, and
54
+ * with the band collapsed it had no effect at all. M2's contacts are the first
55
+ * transient rows in the system and the ramp is finally live. Measured on a
56
+ * 400-particle sheet landing on the ground, both ends of the range are worse
57
+ * than the middle: **no ramp at all** leaves a fast arrival's penalty at the
58
+ * floor and the multiplier alone to carry it, which settles at 1.95 J of
59
+ * residual kinetic energy against 0.12 J at 1e3; a **fast** ramp reaches the
60
+ * ceiling on violations of a millimetre and gives up most of what the low floor
61
+ * bought, settling three orders of magnitude worse on a gentle landing
62
+ * (6.4e-5 J against 3.2e-8 J). `MEASUREMENTS.md` §17.
63
+ *
64
+ * The scale-dependence the paragraph above warns of is real and is stated
65
+ * rather than removed: `β` has units of N/m², so it is a stiffness *per metre
66
+ * of violation*, and 1e3 against a ceiling of `8·(m/h²)` means the ramp is
67
+ * inert for a millimetre of penetration and reaches the ceiling within a step
68
+ * for a decimetre of it. That shape — gentle where the contact is grazing,
69
+ * immediate where it is a real interpenetration — is what the measurement
70
+ * picked, and a ratio-valued `β` that removed the dependence would have to
71
+ * choose the reference length that shape is built out of.
56
72
  *
57
73
  * @author Alex Goldring
58
74
  * @copyright Company Named Limited (c) 2026
@@ -65,10 +81,11 @@
65
81
  export const CLOTH_ALPHA = 0.25;
66
82
 
67
83
  /**
68
- * Penalty ramp.
84
+ * Penalty ramp, N/m². See the module docblock — measured at M2, against the
85
+ * first transient rows the system has.
69
86
  * @type {number}
70
87
  */
71
- export const CLOTH_BETA = 1e5;
88
+ export const CLOTH_BETA = 1e3;
72
89
 
73
90
  /**
74
91
  * Dual decay, `[0, 1)`.
@@ -88,6 +105,100 @@ export const CLOTH_K_MIN_RATIO = 8;
88
105
  */
89
106
  export const CLOTH_K_MAX_RATIO = 8;
90
107
 
108
+ /**
109
+ * Penalty floor for a **contact** row, as a multiple of the row's `m/h²`.
110
+ *
111
+ * Separate from {@link CLOTH_K_MIN_RATIO}, which is pinned to the ceiling,
112
+ * because a contact is the one row kind that *arrives*: a persistent row has
113
+ * been converging since the cloth was seeded and wants the stiffest penalty the
114
+ * step is stable at, and a contact that starts there shocks the mesh on its
115
+ * first frame.
116
+ *
117
+ * **A thirty-second of the ceiling**, measured. The floor is what decides how
118
+ * hard a new contact hits, and the measurement is unambiguous about the
119
+ * direction: on a sheet landing at 3 m/s, the peak contact force falls from
120
+ * 18.9 N at the ceiling to 0.16 N at an eighth of it and the settled kinetic
121
+ * energy falls by six orders of magnitude, while the penetration *improves*
122
+ * from 1.6 cm to 1.1 cm. Below about an eighth the numbers stop moving — every
123
+ * floor from ×1/8 down to ×1/128 gives the same penetration to three figures —
124
+ * so the value is a margin decision the same way `α·γ` was, and ×1/32 sits in
125
+ * the middle of the flat region with two decades of ramp above it.
126
+ *
127
+ * The reason the floor matters so little to the *residual* is the augmented
128
+ * Lagrangian working as designed: the multiplier carries the load and the
129
+ * penalty only has to be large enough to generate one. See MEASUREMENTS §17.
130
+ * @type {number}
131
+ */
132
+ export const CLOTH_CONTACT_K_MIN_RATIO = 0.25;
133
+
134
+ /**
135
+ * Penalty stiffness for a **self-collision** row, as a multiple of the row's
136
+ * own `m_eff/h²`.
137
+ *
138
+ * It is a plain penalty rather than a hard row because the load it carries is
139
+ * cloth against cloth. `MEASUREMENTS.md` §18 measured why a *collider* contact
140
+ * cannot be soft — 376 N for a sheet arriving at 8 m/s against the 4.6 N a soft
141
+ * row inside M0's dissipative band can deliver — and a self-collision row never
142
+ * sees anything of the kind: what it holds apart weighs grams and is moving at
143
+ * the speed cloth moves relative to itself.
144
+ *
145
+ * **Eight, and the dissipative band is not the reason.** Eight is where M0's
146
+ * band tops out and it would be natural to read this as the same ceiling, but
147
+ * `MEASUREMENTS.md` §32 swept the ratio from 1 to 64 and found no energy growth
148
+ * anywhere: the band was measured on rows that are *on every step*, and a
149
+ * one-sided row that vanishes the instant it is satisfied has no standing spring
150
+ * to pump. What picks eight is the crossing count — it is the joint minimum of
151
+ * the mean, and a stiffer row makes the *worst* case steadily worse by throwing
152
+ * a particle hard enough to put the sheet through itself elsewhere.
153
+ *
154
+ * The consequence is a row with no multiplier, no warm start and no identity
155
+ * across steps, which is what makes rebuilding the pair list from scratch every
156
+ * step affordable. `MEASUREMENTS.md` §33.
157
+ * @type {number}
158
+ */
159
+ export const CLOTH_SELF_RATIO = 8;
160
+
161
+ /**
162
+ * Smallest radius a self-collision row is written at, as a multiple of the
163
+ * cloth's shortest row — so the row's **diameter** is one particle spacing.
164
+ *
165
+ * `ClothState#self_radius` carries why the radius is a property of the proxy
166
+ * rather than of the fabric, and `MEASUREMENTS.md` §31 is the sweep: at the
167
+ * fabric's own thickness the row removes 21% of a folding sheet's
168
+ * self-intersections and here it removes 82%.
169
+ * @type {number}
170
+ */
171
+ export const CLOTH_SELF_RADIUS_MIN_RATIO = 0.5;
172
+
173
+ /**
174
+ * Largest radius a self-collision row is written at, as a fraction of the
175
+ * closest two particles get **with no row between them** in the bind pose —
176
+ * `ClothInstance#closest_free_pair`. Two fifths of it, so the row's diameter is
177
+ * four fifths of the nearest thing it could touch and a cloth at rest is never
178
+ * in contact with itself.
179
+ *
180
+ * **A ceiling is needed because the floor is not always reachable.** A thick
181
+ * fabric on a coarse proxy takes its thickness rather than
182
+ * {@link CLOTH_SELF_RADIUS_MIN_RATIO}, and a diameter that reaches the nearest
183
+ * ungoverned pair puts a cloth that is not folded at all in permanent contact
184
+ * with itself — leather, 20 mm thick on a 20 mm proxy, found 1,398 pairs a step
185
+ * across 576 particles standing still. `MEASUREMENTS.md` §36.
186
+ *
187
+ * **And it is measured rather than derived, because a garment is not a sheet.**
188
+ * On a triangulated sheet the closest ungoverned pair is exactly two hops, so
189
+ * this comes out at four fifths of the shortest row and the analytic answer
190
+ * would have done — checked at two spacings, to the precision the proxy's
191
+ * Float32 positions carry. A rigged
192
+ * garment's proxy is *strips* with nothing joining one to the next, so every
193
+ * pair across the gap is ungoverned at whatever distance the artist put the
194
+ * joints, which the shortest row knows nothing about: the playground's cape has
195
+ * 8 cm links and 8.5 cm gaps, half what a sheet would imply. Derived from the
196
+ * link length the ceiling left that cape with three pairs permanently in
197
+ * contact at rest; measured, it has none. `MEASUREMENTS.md` §37.
198
+ * @type {number}
199
+ */
200
+ export const CLOTH_SELF_RADIUS_MAX_RATIO = 0.4;
201
+
91
202
  /**
92
203
  * The five constants as one frozen object, which is what {@link cloth_step}
93
204
  * takes. A sweep passes its own; nothing else should.
@@ -15,10 +15,29 @@
15
15
  * k ← min(k + β·|C|, min(k_max, stiffness))
16
16
  * integrate v = (x − x_prev)/h
17
17
  *
18
- * The three row kinds — stretch, bend and leash — each contribute a warm start,
19
- * an accumulate and a dual update, and this function knows nothing else about
20
- * them. A new row kind is a new module and three more calls; §6 is the whole of
21
- * what they have in common.
18
+ * Six row kinds — stretch, bend, leash, backstop, contact and self-collision —
19
+ * each contribute a warm start, an accumulate and a dual update, and this
20
+ * function knows nothing else about them. A new row kind is a new module and
21
+ * three more calls; §6 is the whole of what they have in common.
22
+ *
23
+ * Self-collision is the one that does not fit that shape, and §5.4 is why: it is
24
+ * a **pair**, so it is accumulated into a per-particle scratch once per
25
+ * iteration and read by the vertex updates rather than visited from them. It has
26
+ * no warm start and no dual update because it is a plain penalty
27
+ * (`cloth_self_row.js`).
28
+ *
29
+ * ## The colliders move within the step
30
+ *
31
+ * §8.2's swept collision is one call per substep: the packed collider table is
32
+ * re-posed at the substep's fraction of the way from the previous step's pose to
33
+ * this one's, and every contact row then evaluates against where the collider
34
+ * *is at that moment* rather than where it ended up. The contact **set** is
35
+ * fixed for the step (`cloth_contact_find.js` and its speculative margin); only
36
+ * the poses move.
37
+ *
38
+ * The fraction is the end of each substep rather than its start — with one
39
+ * substep that is the collider's final pose, which is the one a particle must
40
+ * not be inside when the step is over.
22
41
  *
23
42
  * ## What each piece is for
24
43
  *
@@ -50,9 +69,17 @@
50
69
  *
51
70
  * ## What is deliberately not here
52
71
  *
53
- * *Chebyshev acceleration* (§5.2) is off by default and gated on a particle
54
- * not being in contact this step; contacts arrive at M2 and there is nothing
55
- * in M0 for it to be measured against, so it lands with them.
72
+ * *Chebyshev acceleration* (§5.2), which M2 built, measured and removed. It is
73
+ * gated on a particle not being in contact, so M2 was the first milestone that
74
+ * could evaluate it at all — and it lost on every axis: on a sheet landing at
75
+ * 3 m/s it left the penetration inside the run-to-run spread and made the
76
+ * settled kinetic energy 1.5× worse; on a hanging cape it traded a third off the
77
+ * penetration for ten times the residual energy; and past `ρ = 0.9` it
78
+ * diverges, with `ρ = 0.99` reaching a non-positive-definite Hessian inside a
79
+ * second. A mechanism that costs 2–5%, does not help, and detonates at the top
80
+ * of its own parameter range is worse than its absence. `MEASUREMENTS.md` §21
81
+ * carries the grid, so a future attempt starts from the numbers rather than
82
+ * from the idea.
56
83
  *
57
84
  * *The velocity clamp* named in §7 P1 is derived from `ClothDynamics#thickness`
58
85
  * and belongs with the component that carries it — `ClothSystem` applies it
@@ -79,13 +106,17 @@
79
106
  * @param {number} acceleration_z
80
107
  * @param {number} substeps
81
108
  * @param {number} iterations descent iterations per substep
82
- * @param {{alpha: number, beta: number, gamma: number, k_min_ratio: number, k_max_ratio: number}} [tuning]
109
+ * @param {Float32Array|Float64Array|null} [collider_table] packed colliders in the
110
+ * cloth's own frame, with their contact rows already found
111
+ * @param {number} [collider_count]
112
+ * @param {{alpha: number, beta: number, gamma: number, k_min_ratio: number, k_max_ratio: number, self_ratio?: number}} [tuning]
83
113
  */
84
- export function cloth_step(state: ClothState, dt: number, acceleration_x: number, acceleration_y: number, acceleration_z: number, substeps: number, iterations: number, tuning?: {
114
+ export function cloth_step(state: ClothState, dt: number, acceleration_x: number, acceleration_y: number, acceleration_z: number, substeps: number, iterations: number, collider_table?: Float32Array | Float64Array | null, collider_count?: number, tuning?: {
85
115
  alpha: number;
86
116
  beta: number;
87
117
  gamma: number;
88
118
  k_min_ratio: number;
89
119
  k_max_ratio: number;
120
+ self_ratio?: number;
90
121
  }): void;
91
122
  //# sourceMappingURL=cloth_step.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloth_step.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/cloth/solver/cloth_step.js"],"names":[],"mappings":"AAiBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AAEH;;;;;;;;;;;GAWG;AACH,kDARW,MAAM,kBACN,MAAM,kBACN,MAAM,kBACN,MAAM,YACN,MAAM,cACN,MAAM,WACN;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,QAsDhG"}
1
+ {"version":3,"file":"cloth_step.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/cloth/solver/cloth_step.js"],"names":[],"mappings":"AA+BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiGG;AAEH;;;;;;;;;;;;;;GAcG;AACH,kDAXW,MAAM,kBACN,MAAM,kBACN,MAAM,kBACN,MAAM,YACN,MAAM,cACN,MAAM,mBACN,YAAY,GAAC,YAAY,GAAC,IAAI,mBAE9B,MAAM,WACN;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAC,QA2FrH"}
@@ -1,10 +1,24 @@
1
1
  import { assert } from "../../../../core/assert.js";
2
+ import { cloth_collider_pose_at } from "../collider/cloth_collider_sdf.js";
2
3
  import { cloth_solve_vertex } from "./cloth_solve_vertex.js";
3
- import { CLOTH_SOLVER_TUNING } from "./cloth_solver_constants.js";
4
+ import {
5
+ CLOTH_CONTACT_K_MIN_RATIO,
6
+ CLOTH_SELF_RATIO,
7
+ CLOTH_SOLVER_TUNING
8
+ } from "./cloth_solver_constants.js";
9
+ import {
10
+ cloth_backstop_dual_update,
11
+ cloth_backstop_warm_start
12
+ } from "./constraint/cloth_backstop_row.js";
4
13
  import {
5
14
  cloth_bend_dual_update,
6
15
  cloth_bend_warm_start
7
16
  } from "./constraint/cloth_bend_row.js";
17
+ import {
18
+ cloth_contact_dual_update,
19
+ cloth_contact_warm_start
20
+ } from "./constraint/cloth_contact_row.js";
21
+ import { cloth_self_accumulate_scratch } from "./constraint/cloth_self_row.js";
8
22
  import {
9
23
  cloth_leash_dual_update,
10
24
  cloth_leash_warm_start
@@ -32,10 +46,29 @@ import { find_non_finite_cloth_state } from "./find_non_finite_cloth_state.js";
32
46
  * k ← min(k + β·|C|, min(k_max, stiffness))
33
47
  * integrate v = (x − x_prev)/h
34
48
  *
35
- * The three row kinds — stretch, bend and leash — each contribute a warm start,
36
- * an accumulate and a dual update, and this function knows nothing else about
37
- * them. A new row kind is a new module and three more calls; §6 is the whole of
38
- * what they have in common.
49
+ * Six row kinds — stretch, bend, leash, backstop, contact and self-collision —
50
+ * each contribute a warm start, an accumulate and a dual update, and this
51
+ * function knows nothing else about them. A new row kind is a new module and
52
+ * three more calls; §6 is the whole of what they have in common.
53
+ *
54
+ * Self-collision is the one that does not fit that shape, and §5.4 is why: it is
55
+ * a **pair**, so it is accumulated into a per-particle scratch once per
56
+ * iteration and read by the vertex updates rather than visited from them. It has
57
+ * no warm start and no dual update because it is a plain penalty
58
+ * (`cloth_self_row.js`).
59
+ *
60
+ * ## The colliders move within the step
61
+ *
62
+ * §8.2's swept collision is one call per substep: the packed collider table is
63
+ * re-posed at the substep's fraction of the way from the previous step's pose to
64
+ * this one's, and every contact row then evaluates against where the collider
65
+ * *is at that moment* rather than where it ended up. The contact **set** is
66
+ * fixed for the step (`cloth_contact_find.js` and its speculative margin); only
67
+ * the poses move.
68
+ *
69
+ * The fraction is the end of each substep rather than its start — with one
70
+ * substep that is the collider's final pose, which is the one a particle must
71
+ * not be inside when the step is over.
39
72
  *
40
73
  * ## What each piece is for
41
74
  *
@@ -67,9 +100,17 @@ import { find_non_finite_cloth_state } from "./find_non_finite_cloth_state.js";
67
100
  *
68
101
  * ## What is deliberately not here
69
102
  *
70
- * *Chebyshev acceleration* (§5.2) is off by default and gated on a particle
71
- * not being in contact this step; contacts arrive at M2 and there is nothing
72
- * in M0 for it to be measured against, so it lands with them.
103
+ * *Chebyshev acceleration* (§5.2), which M2 built, measured and removed. It is
104
+ * gated on a particle not being in contact, so M2 was the first milestone that
105
+ * could evaluate it at all — and it lost on every axis: on a sheet landing at
106
+ * 3 m/s it left the penetration inside the run-to-run spread and made the
107
+ * settled kinetic energy 1.5× worse; on a hanging cape it traded a third off the
108
+ * penetration for ten times the residual energy; and past `ρ = 0.9` it
109
+ * diverges, with `ρ = 0.99` reaching a non-positive-definite Hessian inside a
110
+ * second. A mechanism that costs 2–5%, does not help, and detonates at the top
111
+ * of its own parameter range is worse than its absence. `MEASUREMENTS.md` §21
112
+ * carries the grid, so a future attempt starts from the numbers rather than
113
+ * from the idea.
73
114
  *
74
115
  * *The velocity clamp* named in §7 P1 is derived from `ClothDynamics#thickness`
75
116
  * and belongs with the component that carries it — `ClothSystem` applies it
@@ -97,7 +138,10 @@ import { find_non_finite_cloth_state } from "./find_non_finite_cloth_state.js";
97
138
  * @param {number} acceleration_z
98
139
  * @param {number} substeps
99
140
  * @param {number} iterations descent iterations per substep
100
- * @param {{alpha: number, beta: number, gamma: number, k_min_ratio: number, k_max_ratio: number}} [tuning]
141
+ * @param {Float32Array|Float64Array|null} [collider_table] packed colliders in the
142
+ * cloth's own frame, with their contact rows already found
143
+ * @param {number} [collider_count]
144
+ * @param {{alpha: number, beta: number, gamma: number, k_min_ratio: number, k_max_ratio: number, self_ratio?: number}} [tuning]
101
145
  */
102
146
  export function cloth_step(
103
147
  state,
@@ -105,6 +149,8 @@ export function cloth_step(
105
149
  acceleration_x, acceleration_y, acceleration_z,
106
150
  substeps,
107
151
  iterations,
152
+ collider_table = null,
153
+ collider_count = 0,
108
154
  tuning = CLOTH_SOLVER_TUNING
109
155
  ) {
110
156
  assert.isPositiveInteger(substeps, 'substeps');
@@ -120,28 +166,63 @@ export function cloth_step(
120
166
  const inverse_h = 1 / h;
121
167
  const inverse_h_squared = inverse_h * inverse_h;
122
168
 
169
+ // Both ends of a self-collision pair are free, so the row's effective
170
+ // inertia along the constraint is half a particle's — the same reasoning the
171
+ // stretch table uses for an interior row. `CLOTH_SELF_RATIO` is a multiple
172
+ // of that.
173
+ //
174
+ // Behind the pair count, because `max_inverse_mass` is a scan over every
175
+ // particle and a cloth with the flag clear should not pay for a feature it
176
+ // does not have.
177
+ const self_stiffness = state.self_count > 0 && state.particle_count > 0
178
+ ? (tuning.self_ratio ?? CLOTH_SELF_RATIO) * inverse_h_squared * 0.5 / max_inverse_mass(state)
179
+ : 0;
180
+
123
181
  const colour_count = state.colour_count;
124
182
  const colour_offset = state.colour_offset;
125
183
 
184
+ const contacts = collider_table !== null && collider_count > 0 ? collider_table : null;
185
+
126
186
  for (let substep = 0; substep < substeps; substep++) {
127
187
  cloth_predict(state, h, acceleration_x, acceleration_y, acceleration_z);
128
188
 
189
+ if (contacts !== null) {
190
+ cloth_collider_pose_at(contacts, collider_count, (substep + 1) / substeps);
191
+ }
192
+
129
193
  cloth_stretch_warm_start(state, alpha, gamma, k_min_ratio, k_max_ratio, inverse_h_squared);
130
194
  cloth_bend_warm_start(state, alpha, gamma, k_min_ratio, k_max_ratio, inverse_h_squared);
131
195
  cloth_leash_warm_start(state, alpha, gamma, k_min_ratio, k_max_ratio, inverse_h_squared);
196
+ cloth_backstop_warm_start(state, alpha, gamma, k_min_ratio, k_max_ratio, inverse_h_squared);
197
+
198
+ if (contacts !== null) {
199
+ cloth_contact_warm_start(
200
+ state, alpha, gamma,
201
+ CLOTH_CONTACT_K_MIN_RATIO, k_max_ratio, inverse_h_squared
202
+ );
203
+ }
132
204
 
133
205
  for (let iteration = 0; iteration < iterations; iteration++) {
206
+ if (state.self_count > 0) {
207
+ cloth_self_accumulate_scratch(state, self_stiffness);
208
+ }
209
+
134
210
  for (let colour = 0; colour < colour_count; colour++) {
135
211
  const end = colour_offset[colour + 1];
136
212
 
137
213
  for (let particle = colour_offset[colour]; particle < end; particle++) {
138
- cloth_solve_vertex(state, particle, inverse_h_squared);
214
+ cloth_solve_vertex(state, particle, inverse_h_squared, contacts);
139
215
  }
140
216
  }
141
217
 
142
218
  cloth_stretch_dual_update(state, beta, k_max_ratio, inverse_h_squared);
143
219
  cloth_bend_dual_update(state, beta, k_max_ratio, inverse_h_squared);
144
220
  cloth_leash_dual_update(state, beta, k_max_ratio, inverse_h_squared);
221
+ cloth_backstop_dual_update(state, beta, k_max_ratio, inverse_h_squared);
222
+
223
+ if (contacts !== null) {
224
+ cloth_contact_dual_update(state, contacts, beta, k_max_ratio, inverse_h_squared);
225
+ }
145
226
  }
146
227
 
147
228
  cloth_integrate(state, inverse_h, acceleration_x, acceleration_y, acceleration_z);
@@ -153,6 +234,37 @@ export function cloth_step(
153
234
  );
154
235
  }
155
236
 
237
+ /**
238
+ * The largest inverse mass in the cloth — the lightest particle, which is the
239
+ * one a shared penalty has to be safe for.
240
+ *
241
+ * A self-collision pair's stiffness is one number for the whole cloth rather
242
+ * than one per pair, because every pair has the same two free ends and cloth's
243
+ * particles are uniform (`cloth_build_rows.js` says why a mass is arbitrary
244
+ * here). This is what makes that true even for a state a caller built by hand
245
+ * with mixed masses: the band is measured against the *lightest* particle, so no
246
+ * pair is above it.
247
+ *
248
+ * @param {ClothState} state
249
+ * @returns {number}
250
+ * @private
251
+ */
252
+ function max_inverse_mass(state) {
253
+ const mass_inverse = state.mass_inverse;
254
+
255
+ let worst = 0;
256
+
257
+ for (let i = 0; i < state.particle_count; i++) {
258
+ if (mass_inverse[i] > worst) {
259
+ worst = mass_inverse[i];
260
+ }
261
+ }
262
+
263
+ // Every particle pinned: nothing can move, so the stiffness is unused and
264
+ // any finite value is as good as another.
265
+ return worst > 0 ? worst : 1;
266
+ }
267
+
156
268
  /**
157
269
  * Predict: record where the substep started, build the inertial target every
158
270
  * vertex update pulls towards, and seed the descent.
@@ -0,0 +1,95 @@
1
+ /**
2
+ * # Backstop — the half-space row
3
+ *
4
+ * C(x) = r_b − (x_i − b_i)·n_i
5
+ * ∂C/∂x_i = −n_i
6
+ *
7
+ * §6's row, verbatim: a particle is held at least `r_b` along its own normal
8
+ * from a point `b` just inside the body. `[0, +∞)` admits only forces that
9
+ * *reduce* `C`, which for this `C` means increasing `(x − b)·n` — pushing the
10
+ * particle out along its normal, which is what "never inside the body" means.
11
+ *
12
+ * ## What it is for, and why it is not a collider
13
+ *
14
+ * A cape's collider set approximates a torso with a handful of capsules, and
15
+ * between them is a gap the cloth can settle into. A backstop closes that gap
16
+ * without adding geometry: the plane is anchored to the particle's own
17
+ * *animated* position and its own surface normal, both of which the garment
18
+ * already knows, so it is a bound the mesh carries with it rather than a shape
19
+ * somebody has to author. Chaos Cloth ships the same thing under the same name
20
+ * and for the same reason (§15), and §7 P3 leans on it as one of the three
21
+ * layers that stop a garment tunnelling through a character.
22
+ *
23
+ * It is deliberately **not** a substitute for collision. A backstop is a plane
24
+ * per particle: it knows which way is out and nothing about what is actually
25
+ * there, so it cannot resolve a limb crossing the garment. The two rows answer
26
+ * different halves and a cape wants both.
27
+ *
28
+ * ## Where the plane comes from
29
+ *
30
+ * The caller writes it, per step, into {@link ClothState#backstop_plane} — the
31
+ * same contract {@link ClothState#leash_target} carries, and for the same
32
+ * reason: `b` and `n` are anchored to the animated pose, which moves, and are
33
+ * not properties of the rest shape.
34
+ *
35
+ * **M2 builds the row and does not author it.** The natural source is the
36
+ * garment's own skinned surface — `b` a short way inside along the vertex
37
+ * normal, `r_b` that distance again — and a skinned garment is M3. M2's entity
38
+ * path has a chain rather than a surface, so it has no normal to offer: a rope
39
+ * has no inside. So the row ships with storage, a spec and a playground demo
40
+ * driving it directly, and `cloth_proxy_build` is what will fill it.
41
+ *
42
+ * ## No trust region, and no geometric stiffness
43
+ *
44
+ * `C` is **linear** in `x`, so its second derivative is exactly zero — there is
45
+ * no geometric term to keep or drop — and its gradient is a constant unit
46
+ * vector, so there is no distance beyond which the linearisation stops
47
+ * describing it. A backstop is the one row in the system whose model is not a
48
+ * model.
49
+ *
50
+ * @author Alex Goldring
51
+ * @copyright Company Named Limited (c) 2026
52
+ */
53
+ /**
54
+ * Evaluate one backstop row's constraint value and its gradient.
55
+ *
56
+ * The definition the accumulation below inlines, and the one the
57
+ * finite-difference spec checks — there is no second copy of the arithmetic.
58
+ *
59
+ * @param {Float64Array|number[]} gradient out, `∂C/∂x`
60
+ * @param {number} gradient_offset
61
+ * @param {ClothState} state
62
+ * @param {number} row
63
+ * @returns {number} `C`
64
+ */
65
+ export function cloth_backstop_evaluate(gradient: Float64Array | number[], gradient_offset: number, state: ClothState, row: number): number;
66
+ /**
67
+ * Add every backstop row touching `particle` into the vertex accumulator.
68
+ *
69
+ * @param {Float64Array} accumulator
70
+ * @param {ClothState} state
71
+ * @param {number} particle
72
+ */
73
+ export function cloth_backstop_accumulate(accumulator: Float64Array, state: ClothState, particle: number): void;
74
+ /**
75
+ * Warm start every backstop row for a substep. Every one is hard, so — as with
76
+ * the leash table — there is no soft branch.
77
+ *
78
+ * @param {ClothState} state
79
+ * @param {number} alpha
80
+ * @param {number} gamma
81
+ * @param {number} k_min_ratio
82
+ * @param {number} k_max_ratio
83
+ * @param {number} inverse_h_squared
84
+ */
85
+ export function cloth_backstop_warm_start(state: ClothState, alpha: number, gamma: number, k_min_ratio: number, k_max_ratio: number, inverse_h_squared: number): void;
86
+ /**
87
+ * Dual update for every backstop row.
88
+ *
89
+ * @param {ClothState} state
90
+ * @param {number} beta
91
+ * @param {number} k_max_ratio
92
+ * @param {number} inverse_h_squared
93
+ */
94
+ export function cloth_backstop_dual_update(state: ClothState, beta: number, k_max_ratio: number, inverse_h_squared: number): void;
95
+ //# sourceMappingURL=cloth_backstop_row.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloth_backstop_row.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/physics/cloth/solver/constraint/cloth_backstop_row.js"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH;;;;;;;;;;;GAWG;AACH,kDANW,YAAY,GAAC,MAAM,EAAE,mBACrB,MAAM,0BAEN,MAAM,GACJ,MAAM,CAuBlB;AAED;;;;;;GAMG;AACH,uDAJW,YAAY,+BAEZ,MAAM,QA8EhB;AAED;;;;;;;;;;GAUG;AACH,oEANW,MAAM,SACN,MAAM,eACN,MAAM,eACN,MAAM,qBACN,MAAM,QAuChB;AAED;;;;;;;GAOG;AACH,oEAJW,MAAM,eACN,MAAM,qBACN,MAAM,QA2ChB"}