@vgai/engine 0.3.0 → 0.4.0-canary.20260715.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 (559) hide show
  1. package/package.json +8 -2
  2. package/schemas/engine-api.json +124 -0
  3. package/schemas/engine-api.md +53 -0
  4. package/schemas/engine-capabilities.json +124 -0
  5. package/schemas/inputmap.schema.json +314 -0
  6. package/schemas/mat.schema.json +286 -0
  7. package/schemas/prefab.schema.json +10148 -0
  8. package/schemas/scn2d.schema.json +475 -0
  9. package/schemas/vgai-game.schema.json +383 -0
  10. package/schemas/vscn.schema.json +11007 -0
  11. package/src/adapter/{world-kind.ts → adapter-surface.ts} +6 -6
  12. package/src/adapter/authoring.ts +67 -0
  13. package/src/adapter/game-adapter.ts +8 -8
  14. package/src/adapter/host-context.ts +2 -4
  15. package/src/adapter/index.ts +3 -1
  16. package/src/adapter/system-adapter.ts +73 -0
  17. package/src/adapter/vgai-scene-game-adapter.ts +239 -180
  18. package/src/animation/xstate-animation-binding.ts +300 -262
  19. package/src/animation/xstate-animation-meta.ts +50 -13
  20. package/src/character/cloth-sim.ts +533 -0
  21. package/src/character/spring-chain.ts +307 -0
  22. package/src/core/game-loop.ts +41 -3
  23. package/src/core/seeded-random.ts +161 -0
  24. package/src/core/system-runner.ts +20 -3
  25. package/src/core/types.ts +37 -0
  26. package/src/data/data-asset.ts +1 -1
  27. package/src/dev/performance-profiler.ts +213 -0
  28. package/src/dev/webgl-gpu-timer.ts +53 -0
  29. package/src/ecs/component-manager.ts +45 -12
  30. package/src/ecs/game-component.ts +95 -11
  31. package/src/humanoid/bake.operation.ts +326 -0
  32. package/src/humanoid/body.ts +663 -0
  33. package/src/humanoid/clips.ts +149 -0
  34. package/src/humanoid/compose.ts +209 -0
  35. package/src/humanoid/generate.ts +189 -0
  36. package/src/humanoid/index.ts +36 -0
  37. package/src/humanoid/schema.ts +108 -0
  38. package/src/humanoid/skeleton.ts +345 -0
  39. package/src/index.ts +5 -5
  40. package/src/input/input-manager.ts +701 -22
  41. package/src/input/input-types.ts +5 -2
  42. package/src/manifest/index.ts +5 -5
  43. package/src/manifest/load.ts +125 -72
  44. package/src/manifest/schema.ts +362 -255
  45. package/src/react/game-state.tsx +135 -32
  46. package/src/react/root-adapter.tsx +49 -0
  47. package/src/react/unmanaged-root-detector.ts +66 -0
  48. package/src/react/use-data.ts +12 -2
  49. package/src/runtime/create-runtime.ts +100 -282
  50. package/src/runtime/debug-bridge.ts +483 -0
  51. package/src/runtime/debug-registry.ts +856 -0
  52. package/src/runtime/game.ts +342 -93
  53. package/src/runtime/gameplay-rng-trap.ts +134 -0
  54. package/src/runtime/input-router.ts +7 -7
  55. package/src/runtime/mount-game.ts +40 -38
  56. package/src/runtime/mount-manifest.ts +169 -37
  57. package/src/runtime/render-control.ts +13 -13
  58. package/src/runtime/render-seed.ts +1 -1
  59. package/src/runtime/state-bridge.ts +24 -10
  60. package/src/runtime/types.ts +110 -28
  61. package/src/scene/asset-registry.ts +22 -0
  62. package/src/scene/component-registry.ts +14 -3
  63. package/src/scene/defaults.ts +1 -0
  64. package/src/scene/light-camera-factory.ts +11 -3
  65. package/src/scene/scene-apply.ts +55 -4
  66. package/src/scene/scene-loader.ts +10 -27
  67. package/src/scene/scene-types.ts +0 -1
  68. package/src/scene/schema/index.ts +0 -34
  69. package/src/scene/schema/light.ts +16 -1
  70. package/src/scene/schema/material.ts +96 -91
  71. package/src/scene/schema/scene-file.ts +1 -7
  72. package/src/scene/user-data.ts +14 -1
  73. package/src/setup/setup-renderer.ts +1 -1
  74. package/src/tools/define-tool.ts +50 -11
  75. package/src/world2d/authoring-2d.ts +17 -1
  76. package/src/world2d/collision-2d.ts +1 -1
  77. package/src/world2d/pixi-game-adapter.ts +19 -17
  78. package/src/world2d/scene2d-loader.ts +1 -0
  79. package/src/world2d/types.ts +8 -2
  80. package/dist/adapter/authoring.d.ts +0 -404
  81. package/dist/adapter/authoring.d.ts.map +0 -1
  82. package/dist/adapter/authoring.js +0 -22
  83. package/dist/adapter/colyseus-networking-adapter.d.ts +0 -43
  84. package/dist/adapter/colyseus-networking-adapter.d.ts.map +0 -1
  85. package/dist/adapter/colyseus-networking-adapter.js +0 -38
  86. package/dist/adapter/first-party-systems.d.ts +0 -38
  87. package/dist/adapter/first-party-systems.d.ts.map +0 -1
  88. package/dist/adapter/first-party-systems.js +0 -77
  89. package/dist/adapter/game-adapter.d.ts +0 -133
  90. package/dist/adapter/game-adapter.d.ts.map +0 -1
  91. package/dist/adapter/game-adapter.js +0 -10
  92. package/dist/adapter/host-context.d.ts +0 -73
  93. package/dist/adapter/host-context.d.ts.map +0 -1
  94. package/dist/adapter/host-context.js +0 -16
  95. package/dist/adapter/index.d.ts +0 -22
  96. package/dist/adapter/index.d.ts.map +0 -1
  97. package/dist/adapter/index.js +0 -15
  98. package/dist/adapter/ingest/game-contract.d.ts +0 -53
  99. package/dist/adapter/ingest/game-contract.d.ts.map +0 -1
  100. package/dist/adapter/ingest/game-contract.js +0 -30
  101. package/dist/adapter/ingest/overlay-applier.d.ts +0 -118
  102. package/dist/adapter/ingest/overlay-applier.d.ts.map +0 -1
  103. package/dist/adapter/ingest/overlay-applier.js +0 -132
  104. package/dist/adapter/ingest/overlay-apply.d.ts +0 -60
  105. package/dist/adapter/ingest/overlay-apply.d.ts.map +0 -1
  106. package/dist/adapter/ingest/overlay-apply.js +0 -101
  107. package/dist/adapter/ingest/overlay-file.d.ts +0 -79
  108. package/dist/adapter/ingest/overlay-file.d.ts.map +0 -1
  109. package/dist/adapter/ingest/overlay-file.js +0 -66
  110. package/dist/adapter/ingest/overlay-report.d.ts +0 -113
  111. package/dist/adapter/ingest/overlay-report.d.ts.map +0 -1
  112. package/dist/adapter/ingest/overlay-report.js +0 -103
  113. package/dist/adapter/ingest/scene-capture.d.ts +0 -97
  114. package/dist/adapter/ingest/scene-capture.d.ts.map +0 -1
  115. package/dist/adapter/ingest/scene-capture.js +0 -248
  116. package/dist/adapter/ingest/upstream-pin.d.ts +0 -47
  117. package/dist/adapter/ingest/upstream-pin.d.ts.map +0 -1
  118. package/dist/adapter/ingest/upstream-pin.js +0 -50
  119. package/dist/adapter/loop-gate-report.d.ts +0 -34
  120. package/dist/adapter/loop-gate-report.d.ts.map +0 -1
  121. package/dist/adapter/loop-gate-report.js +0 -37
  122. package/dist/adapter/rapier-physics-adapter.d.ts +0 -12
  123. package/dist/adapter/rapier-physics-adapter.d.ts.map +0 -1
  124. package/dist/adapter/rapier-physics-adapter.js +0 -46
  125. package/dist/adapter/system-adapter.d.ts +0 -133
  126. package/dist/adapter/system-adapter.d.ts.map +0 -1
  127. package/dist/adapter/system-adapter.js +0 -13
  128. package/dist/adapter/transform.d.ts +0 -18
  129. package/dist/adapter/transform.d.ts.map +0 -1
  130. package/dist/adapter/transform.js +0 -1
  131. package/dist/adapter/vgai-scene-game-adapter.d.ts +0 -116
  132. package/dist/adapter/vgai-scene-game-adapter.d.ts.map +0 -1
  133. package/dist/adapter/vgai-scene-game-adapter.js +0 -714
  134. package/dist/adapter/world-kind.d.ts +0 -30
  135. package/dist/adapter/world-kind.d.ts.map +0 -1
  136. package/dist/adapter/world-kind.js +0 -18
  137. package/dist/ai/navigation.d.ts +0 -64
  138. package/dist/ai/navigation.d.ts.map +0 -1
  139. package/dist/ai/navigation.js +0 -133
  140. package/dist/animation/anim-graph-types.d.ts +0 -25
  141. package/dist/animation/anim-graph-types.d.ts.map +0 -1
  142. package/dist/animation/anim-graph-types.js +0 -1
  143. package/dist/animation/animation-clock.d.ts +0 -213
  144. package/dist/animation/animation-clock.d.ts.map +0 -1
  145. package/dist/animation/animation-clock.js +0 -301
  146. package/dist/animation/blend-node.d.ts +0 -22
  147. package/dist/animation/blend-node.d.ts.map +0 -1
  148. package/dist/animation/blend-node.js +0 -97
  149. package/dist/animation/camera-ownership.d.ts +0 -250
  150. package/dist/animation/camera-ownership.d.ts.map +0 -1
  151. package/dist/animation/camera-ownership.js +0 -169
  152. package/dist/animation/cinematic-cues.d.ts +0 -200
  153. package/dist/animation/cinematic-cues.d.ts.map +0 -1
  154. package/dist/animation/cinematic-cues.js +0 -213
  155. package/dist/animation/clip-map.d.ts +0 -12
  156. package/dist/animation/clip-map.d.ts.map +0 -1
  157. package/dist/animation/clip-map.js +0 -37
  158. package/dist/animation/gsap-registration.d.ts +0 -119
  159. package/dist/animation/gsap-registration.d.ts.map +0 -1
  160. package/dist/animation/gsap-registration.js +0 -92
  161. package/dist/animation/theatre-clock-binding.d.ts +0 -89
  162. package/dist/animation/theatre-clock-binding.d.ts.map +0 -1
  163. package/dist/animation/theatre-clock-binding.js +0 -29
  164. package/dist/animation/theatre-director.d.ts +0 -242
  165. package/dist/animation/theatre-director.d.ts.map +0 -1
  166. package/dist/animation/theatre-director.js +0 -235
  167. package/dist/animation/theatre-object-binding.d.ts +0 -305
  168. package/dist/animation/theatre-object-binding.d.ts.map +0 -1
  169. package/dist/animation/theatre-object-binding.js +0 -351
  170. package/dist/animation/xstate-animation-binding.d.ts +0 -111
  171. package/dist/animation/xstate-animation-binding.d.ts.map +0 -1
  172. package/dist/animation/xstate-animation-binding.js +0 -307
  173. package/dist/animation/xstate-animation-meta.d.ts +0 -228
  174. package/dist/animation/xstate-animation-meta.d.ts.map +0 -1
  175. package/dist/animation/xstate-animation-meta.js +0 -221
  176. package/dist/assets.d.ts +0 -26
  177. package/dist/assets.d.ts.map +0 -1
  178. package/dist/assets.js +0 -55
  179. package/dist/audio/index.d.ts +0 -4
  180. package/dist/audio/index.d.ts.map +0 -1
  181. package/dist/audio/index.js +0 -24
  182. package/dist/audio/tone-clock-binding.d.ts +0 -75
  183. package/dist/audio/tone-clock-binding.d.ts.map +0 -1
  184. package/dist/audio/tone-clock-binding.js +0 -29
  185. package/dist/audio/tone-context.d.ts +0 -114
  186. package/dist/audio/tone-context.d.ts.map +0 -1
  187. package/dist/audio/tone-context.js +0 -39
  188. package/dist/audio/tone-offline-render.d.ts +0 -90
  189. package/dist/audio/tone-offline-render.d.ts.map +0 -1
  190. package/dist/audio/tone-offline-render.js +0 -56
  191. package/dist/audio/wav-encode.d.ts +0 -38
  192. package/dist/audio/wav-encode.d.ts.map +0 -1
  193. package/dist/audio/wav-encode.js +0 -97
  194. package/dist/core/game-loop.d.ts +0 -21
  195. package/dist/core/game-loop.d.ts.map +0 -1
  196. package/dist/core/game-loop.js +0 -130
  197. package/dist/core/system-runner.d.ts +0 -120
  198. package/dist/core/system-runner.d.ts.map +0 -1
  199. package/dist/core/system-runner.js +0 -286
  200. package/dist/core/types.d.ts +0 -58
  201. package/dist/core/types.d.ts.map +0 -1
  202. package/dist/core/types.js +0 -25
  203. package/dist/data/data-asset.d.ts +0 -101
  204. package/dist/data/data-asset.d.ts.map +0 -1
  205. package/dist/data/data-asset.js +0 -122
  206. package/dist/data/data-check-core.d.ts +0 -110
  207. package/dist/data/data-check-core.d.ts.map +0 -1
  208. package/dist/data/data-check-core.js +0 -178
  209. package/dist/data/data-ref.d.ts +0 -79
  210. package/dist/data/data-ref.d.ts.map +0 -1
  211. package/dist/data/data-ref.js +0 -130
  212. package/dist/data/vite-plugin-data.d.ts +0 -79
  213. package/dist/data/vite-plugin-data.d.ts.map +0 -1
  214. package/dist/data/vite-plugin-data.js +0 -217
  215. package/dist/dev/console-bridge.d.ts +0 -22
  216. package/dist/dev/console-bridge.d.ts.map +0 -1
  217. package/dist/dev/console-bridge.js +0 -71
  218. package/dist/dev/debug-draw.d.ts +0 -24
  219. package/dist/dev/debug-draw.d.ts.map +0 -1
  220. package/dist/dev/debug-draw.js +0 -73
  221. package/dist/dev/logger.d.ts +0 -30
  222. package/dist/dev/logger.d.ts.map +0 -1
  223. package/dist/dev/logger.js +0 -90
  224. package/dist/ecs/component-manager.d.ts +0 -189
  225. package/dist/ecs/component-manager.d.ts.map +0 -1
  226. package/dist/ecs/component-manager.js +0 -676
  227. package/dist/ecs/game-component.d.ts +0 -97
  228. package/dist/ecs/game-component.d.ts.map +0 -1
  229. package/dist/ecs/game-component.js +0 -80
  230. package/dist/ecs/hmr-swap-report.d.ts +0 -53
  231. package/dist/ecs/hmr-swap-report.d.ts.map +0 -1
  232. package/dist/ecs/hmr-swap-report.js +0 -55
  233. package/dist/index.d.ts +0 -37
  234. package/dist/index.d.ts.map +0 -1
  235. package/dist/index.js +0 -30
  236. package/dist/input/input-manager.d.ts +0 -596
  237. package/dist/input/input-manager.d.ts.map +0 -1
  238. package/dist/input/input-manager.js +0 -1564
  239. package/dist/input/input-types.d.ts +0 -171
  240. package/dist/input/input-types.d.ts.map +0 -1
  241. package/dist/input/input-types.js +0 -1
  242. package/dist/input/prompt-labels.d.ts +0 -21
  243. package/dist/input/prompt-labels.d.ts.map +0 -1
  244. package/dist/input/prompt-labels.js +0 -121
  245. package/dist/input/rebind-controller.d.ts +0 -59
  246. package/dist/input/rebind-controller.d.ts.map +0 -1
  247. package/dist/input/rebind-controller.js +0 -85
  248. package/dist/input/schema.d.ts +0 -16
  249. package/dist/input/schema.d.ts.map +0 -1
  250. package/dist/input/schema.js +0 -228
  251. package/dist/loader.d.ts +0 -43
  252. package/dist/loader.d.ts.map +0 -1
  253. package/dist/loader.js +0 -59
  254. package/dist/manifest/index.d.ts +0 -5
  255. package/dist/manifest/index.d.ts.map +0 -1
  256. package/dist/manifest/index.js +0 -8
  257. package/dist/manifest/load-file.d.ts +0 -4
  258. package/dist/manifest/load-file.d.ts.map +0 -1
  259. package/dist/manifest/load-file.js +0 -14
  260. package/dist/manifest/load.d.ts +0 -117
  261. package/dist/manifest/load.d.ts.map +0 -1
  262. package/dist/manifest/load.js +0 -255
  263. package/dist/manifest/schema.d.ts +0 -173
  264. package/dist/manifest/schema.d.ts.map +0 -1
  265. package/dist/manifest/schema.js +0 -302
  266. package/dist/physics/collision-system.d.ts +0 -41
  267. package/dist/physics/collision-system.d.ts.map +0 -1
  268. package/dist/physics/collision-system.js +0 -64
  269. package/dist/physics/physics-registry.d.ts +0 -45
  270. package/dist/physics/physics-registry.d.ts.map +0 -1
  271. package/dist/physics/physics-registry.js +0 -65
  272. package/dist/physics/transform-writer.d.ts +0 -16
  273. package/dist/physics/transform-writer.d.ts.map +0 -1
  274. package/dist/physics/transform-writer.js +0 -38
  275. package/dist/physics/trigger-dispatch.d.ts +0 -24
  276. package/dist/physics/trigger-dispatch.d.ts.map +0 -1
  277. package/dist/physics/trigger-dispatch.js +0 -80
  278. package/dist/react/game-state.d.ts +0 -105
  279. package/dist/react/game-state.d.ts.map +0 -1
  280. package/dist/react/game-state.js +0 -144
  281. package/dist/react/use-data.d.ts +0 -60
  282. package/dist/react/use-data.d.ts.map +0 -1
  283. package/dist/react/use-data.js +0 -96
  284. package/dist/react/use-selection.d.ts +0 -75
  285. package/dist/react/use-selection.d.ts.map +0 -1
  286. package/dist/react/use-selection.js +0 -90
  287. package/dist/render/auto-batcher.d.ts +0 -34
  288. package/dist/render/auto-batcher.d.ts.map +0 -1
  289. package/dist/render/auto-batcher.js +0 -140
  290. package/dist/render/render-batch-system.d.ts +0 -33
  291. package/dist/render/render-batch-system.d.ts.map +0 -1
  292. package/dist/render/render-batch-system.js +0 -230
  293. package/dist/render/render-features.d.ts +0 -47
  294. package/dist/render/render-features.d.ts.map +0 -1
  295. package/dist/render/render-features.js +0 -105
  296. package/dist/render/render-settings.d.ts +0 -36
  297. package/dist/render/render-settings.d.ts.map +0 -1
  298. package/dist/render/render-settings.js +0 -51
  299. package/dist/runtime/create-runtime.d.ts +0 -280
  300. package/dist/runtime/create-runtime.d.ts.map +0 -1
  301. package/dist/runtime/create-runtime.js +0 -795
  302. package/dist/runtime/frame-selector-cache.d.ts +0 -41
  303. package/dist/runtime/frame-selector-cache.d.ts.map +0 -1
  304. package/dist/runtime/frame-selector-cache.js +0 -65
  305. package/dist/runtime/game.d.ts +0 -433
  306. package/dist/runtime/game.d.ts.map +0 -1
  307. package/dist/runtime/game.js +0 -569
  308. package/dist/runtime/input-router.d.ts +0 -78
  309. package/dist/runtime/input-router.d.ts.map +0 -1
  310. package/dist/runtime/input-router.js +0 -171
  311. package/dist/runtime/mount-game.d.ts +0 -129
  312. package/dist/runtime/mount-game.d.ts.map +0 -1
  313. package/dist/runtime/mount-game.js +0 -152
  314. package/dist/runtime/mount-manifest.d.ts +0 -119
  315. package/dist/runtime/mount-manifest.d.ts.map +0 -1
  316. package/dist/runtime/mount-manifest.js +0 -204
  317. package/dist/runtime/render-audio-control.d.ts +0 -110
  318. package/dist/runtime/render-audio-control.d.ts.map +0 -1
  319. package/dist/runtime/render-audio-control.js +0 -111
  320. package/dist/runtime/render-control.d.ts +0 -291
  321. package/dist/runtime/render-control.d.ts.map +0 -1
  322. package/dist/runtime/render-control.js +0 -264
  323. package/dist/runtime/render-seed.d.ts +0 -57
  324. package/dist/runtime/render-seed.d.ts.map +0 -1
  325. package/dist/runtime/render-seed.js +0 -74
  326. package/dist/runtime/scene-ui-bridge.d.ts +0 -69
  327. package/dist/runtime/scene-ui-bridge.d.ts.map +0 -1
  328. package/dist/runtime/scene-ui-bridge.js +0 -23
  329. package/dist/runtime/scene-ui-data.d.ts +0 -13
  330. package/dist/runtime/scene-ui-data.d.ts.map +0 -1
  331. package/dist/runtime/scene-ui-data.js +0 -92
  332. package/dist/runtime/state-bridge.d.ts +0 -47
  333. package/dist/runtime/state-bridge.d.ts.map +0 -1
  334. package/dist/runtime/state-bridge.js +0 -53
  335. package/dist/runtime/types.d.ts +0 -179
  336. package/dist/runtime/types.d.ts.map +0 -1
  337. package/dist/runtime/types.js +0 -1
  338. package/dist/scene/asset-loaders.d.ts +0 -79
  339. package/dist/scene/asset-loaders.d.ts.map +0 -1
  340. package/dist/scene/asset-loaders.js +0 -141
  341. package/dist/scene/asset-paths.d.ts +0 -11
  342. package/dist/scene/asset-paths.d.ts.map +0 -1
  343. package/dist/scene/asset-paths.js +0 -108
  344. package/dist/scene/asset-ref-check.d.ts +0 -88
  345. package/dist/scene/asset-ref-check.d.ts.map +0 -1
  346. package/dist/scene/asset-ref-check.js +0 -214
  347. package/dist/scene/asset-registry.d.ts +0 -30
  348. package/dist/scene/asset-registry.d.ts.map +0 -1
  349. package/dist/scene/asset-registry.js +0 -50
  350. package/dist/scene/collider-dimensions.d.ts +0 -63
  351. package/dist/scene/collider-dimensions.d.ts.map +0 -1
  352. package/dist/scene/collider-dimensions.js +0 -98
  353. package/dist/scene/component-registry.d.ts +0 -17
  354. package/dist/scene/component-registry.d.ts.map +0 -1
  355. package/dist/scene/component-registry.js +0 -28
  356. package/dist/scene/defaults.d.ts +0 -252
  357. package/dist/scene/defaults.d.ts.map +0 -1
  358. package/dist/scene/defaults.js +0 -151
  359. package/dist/scene/geometries/index.d.ts +0 -8
  360. package/dist/scene/geometries/index.d.ts.map +0 -1
  361. package/dist/scene/geometries/index.js +0 -7
  362. package/dist/scene/geometries/terrain.d.ts +0 -2
  363. package/dist/scene/geometries/terrain.d.ts.map +0 -1
  364. package/dist/scene/geometries/terrain.js +0 -37
  365. package/dist/scene/geometry-registry.d.ts +0 -24
  366. package/dist/scene/geometry-registry.d.ts.map +0 -1
  367. package/dist/scene/geometry-registry.js +0 -20
  368. package/dist/scene/instance-registry.d.ts +0 -36
  369. package/dist/scene/instance-registry.d.ts.map +0 -1
  370. package/dist/scene/instance-registry.js +0 -55
  371. package/dist/scene/instancers/grid.d.ts +0 -2
  372. package/dist/scene/instancers/grid.d.ts.map +0 -1
  373. package/dist/scene/instancers/grid.js +0 -36
  374. package/dist/scene/instancers/index.d.ts +0 -8
  375. package/dist/scene/instancers/index.d.ts.map +0 -1
  376. package/dist/scene/instancers/index.js +0 -7
  377. package/dist/scene/light-camera-factory.d.ts +0 -23
  378. package/dist/scene/light-camera-factory.d.ts.map +0 -1
  379. package/dist/scene/light-camera-factory.js +0 -68
  380. package/dist/scene/material-factory.d.ts +0 -17
  381. package/dist/scene/material-factory.d.ts.map +0 -1
  382. package/dist/scene/material-factory.js +0 -197
  383. package/dist/scene/material-registry.d.ts +0 -40
  384. package/dist/scene/material-registry.d.ts.map +0 -1
  385. package/dist/scene/material-registry.js +0 -44
  386. package/dist/scene/materials/index.d.ts +0 -8
  387. package/dist/scene/materials/index.d.ts.map +0 -1
  388. package/dist/scene/materials/index.js +0 -7
  389. package/dist/scene/materials/water.d.ts +0 -2
  390. package/dist/scene/materials/water.d.ts.map +0 -1
  391. package/dist/scene/materials/water.js +0 -54
  392. package/dist/scene/parse.d.ts +0 -23
  393. package/dist/scene/parse.d.ts.map +0 -1
  394. package/dist/scene/parse.js +0 -179
  395. package/dist/scene/particles-factory.d.ts +0 -33
  396. package/dist/scene/particles-factory.d.ts.map +0 -1
  397. package/dist/scene/particles-factory.js +0 -282
  398. package/dist/scene/scene-apply.d.ts +0 -109
  399. package/dist/scene/scene-apply.d.ts.map +0 -1
  400. package/dist/scene/scene-apply.js +0 -326
  401. package/dist/scene/scene-diff-schema.d.ts +0 -285
  402. package/dist/scene/scene-diff-schema.d.ts.map +0 -1
  403. package/dist/scene/scene-diff-schema.js +0 -92
  404. package/dist/scene/scene-diff-types.d.ts +0 -40
  405. package/dist/scene/scene-diff-types.d.ts.map +0 -1
  406. package/dist/scene/scene-diff-types.js +0 -14
  407. package/dist/scene/scene-loader.d.ts +0 -177
  408. package/dist/scene/scene-loader.d.ts.map +0 -1
  409. package/dist/scene/scene-loader.js +0 -1213
  410. package/dist/scene/scene-query.d.ts +0 -11
  411. package/dist/scene/scene-query.d.ts.map +0 -1
  412. package/dist/scene/scene-query.js +0 -63
  413. package/dist/scene/scene-types.d.ts +0 -10
  414. package/dist/scene/scene-types.d.ts.map +0 -1
  415. package/dist/scene/scene-types.js +0 -8
  416. package/dist/scene/scene-version.d.ts +0 -26
  417. package/dist/scene/scene-version.d.ts.map +0 -1
  418. package/dist/scene/scene-version.js +0 -34
  419. package/dist/scene/schema/animation.d.ts +0 -8
  420. package/dist/scene/schema/animation.d.ts.map +0 -1
  421. package/dist/scene/schema/animation.js +0 -42
  422. package/dist/scene/schema/audio.d.ts +0 -13
  423. package/dist/scene/schema/audio.d.ts.map +0 -1
  424. package/dist/scene/schema/audio.js +0 -22
  425. package/dist/scene/schema/camera.d.ts +0 -28
  426. package/dist/scene/schema/camera.d.ts.map +0 -1
  427. package/dist/scene/schema/camera.js +0 -17
  428. package/dist/scene/schema/collider.d.ts +0 -40
  429. package/dist/scene/schema/collider.d.ts.map +0 -1
  430. package/dist/scene/schema/collider.js +0 -55
  431. package/dist/scene/schema/entity-ref.d.ts +0 -31
  432. package/dist/scene/schema/entity-ref.d.ts.map +0 -1
  433. package/dist/scene/schema/entity-ref.js +0 -61
  434. package/dist/scene/schema/entity.d.ts +0 -1036
  435. package/dist/scene/schema/entity.d.ts.map +0 -1
  436. package/dist/scene/schema/entity.js +0 -153
  437. package/dist/scene/schema/environment.d.ts +0 -425
  438. package/dist/scene/schema/environment.d.ts.map +0 -1
  439. package/dist/scene/schema/environment.js +0 -357
  440. package/dist/scene/schema/index.d.ts +0 -39
  441. package/dist/scene/schema/index.d.ts.map +0 -1
  442. package/dist/scene/schema/index.js +0 -20
  443. package/dist/scene/schema/instances.d.ts +0 -14
  444. package/dist/scene/schema/instances.d.ts.map +0 -1
  445. package/dist/scene/schema/instances.js +0 -28
  446. package/dist/scene/schema/joint.d.ts +0 -23
  447. package/dist/scene/schema/joint.d.ts.map +0 -1
  448. package/dist/scene/schema/joint.js +0 -23
  449. package/dist/scene/schema/light.d.ts +0 -38
  450. package/dist/scene/schema/light.d.ts.map +0 -1
  451. package/dist/scene/schema/light.js +0 -26
  452. package/dist/scene/schema/material.d.ts +0 -199
  453. package/dist/scene/schema/material.d.ts.map +0 -1
  454. package/dist/scene/schema/material.js +0 -99
  455. package/dist/scene/schema/mesh.d.ts +0 -48
  456. package/dist/scene/schema/mesh.d.ts.map +0 -1
  457. package/dist/scene/schema/mesh.js +0 -82
  458. package/dist/scene/schema/particles.d.ts +0 -2178
  459. package/dist/scene/schema/particles.d.ts.map +0 -1
  460. package/dist/scene/schema/particles.js +0 -321
  461. package/dist/scene/schema/physics.d.ts +0 -56
  462. package/dist/scene/schema/physics.d.ts.map +0 -1
  463. package/dist/scene/schema/physics.js +0 -37
  464. package/dist/scene/schema/scene-file.d.ts +0 -304
  465. package/dist/scene/schema/scene-file.d.ts.map +0 -1
  466. package/dist/scene/schema/scene-file.js +0 -262
  467. package/dist/scene/schema/shadow.d.ts +0 -28
  468. package/dist/scene/schema/shadow.d.ts.map +0 -1
  469. package/dist/scene/schema/shadow.js +0 -20
  470. package/dist/scene/schema/spline.d.ts +0 -25
  471. package/dist/scene/schema/spline.d.ts.map +0 -1
  472. package/dist/scene/schema/spline.js +0 -15
  473. package/dist/scene/schema/tuples.d.ts +0 -12
  474. package/dist/scene/schema/tuples.d.ts.map +0 -1
  475. package/dist/scene/schema/tuples.js +0 -14
  476. package/dist/scene/schema/ui.d.ts +0 -880
  477. package/dist/scene/schema/ui.d.ts.map +0 -1
  478. package/dist/scene/schema/ui.js +0 -506
  479. package/dist/scene/user-data.d.ts +0 -171
  480. package/dist/scene/user-data.d.ts.map +0 -1
  481. package/dist/scene/user-data.js +0 -137
  482. package/dist/setup/setup-audio.d.ts +0 -20
  483. package/dist/setup/setup-audio.d.ts.map +0 -1
  484. package/dist/setup/setup-audio.js +0 -41
  485. package/dist/setup/setup-particles.d.ts +0 -17
  486. package/dist/setup/setup-particles.d.ts.map +0 -1
  487. package/dist/setup/setup-particles.js +0 -16
  488. package/dist/setup/setup-physics.d.ts +0 -25
  489. package/dist/setup/setup-physics.d.ts.map +0 -1
  490. package/dist/setup/setup-physics.js +0 -51
  491. package/dist/setup/setup-renderer.d.ts +0 -69
  492. package/dist/setup/setup-renderer.d.ts.map +0 -1
  493. package/dist/setup/setup-renderer.js +0 -363
  494. package/dist/tools/define-tool.d.ts +0 -105
  495. package/dist/tools/define-tool.d.ts.map +0 -1
  496. package/dist/tools/define-tool.js +0 -82
  497. package/dist/world2d/authoring-2d.d.ts +0 -76
  498. package/dist/world2d/authoring-2d.d.ts.map +0 -1
  499. package/dist/world2d/authoring-2d.js +0 -176
  500. package/dist/world2d/capture-to-scene2d.d.ts +0 -5
  501. package/dist/world2d/capture-to-scene2d.d.ts.map +0 -1
  502. package/dist/world2d/capture-to-scene2d.js +0 -49
  503. package/dist/world2d/collision-2d.d.ts +0 -52
  504. package/dist/world2d/collision-2d.d.ts.map +0 -1
  505. package/dist/world2d/collision-2d.js +0 -70
  506. package/dist/world2d/components-2d.d.ts +0 -47
  507. package/dist/world2d/components-2d.d.ts.map +0 -1
  508. package/dist/world2d/components-2d.js +0 -77
  509. package/dist/world2d/index.d.ts +0 -34
  510. package/dist/world2d/index.d.ts.map +0 -1
  511. package/dist/world2d/index.js +0 -32
  512. package/dist/world2d/ingest-iframe-2d.d.ts +0 -82
  513. package/dist/world2d/ingest-iframe-2d.d.ts.map +0 -1
  514. package/dist/world2d/ingest-iframe-2d.js +0 -166
  515. package/dist/world2d/ingest2d.d.ts +0 -97
  516. package/dist/world2d/ingest2d.d.ts.map +0 -1
  517. package/dist/world2d/ingest2d.js +0 -40
  518. package/dist/world2d/physics2d-registry.d.ts +0 -24
  519. package/dist/world2d/physics2d-registry.d.ts.map +0 -1
  520. package/dist/world2d/physics2d-registry.js +0 -38
  521. package/dist/world2d/pixi-game-adapter.d.ts +0 -107
  522. package/dist/world2d/pixi-game-adapter.d.ts.map +0 -1
  523. package/dist/world2d/pixi-game-adapter.js +0 -211
  524. package/dist/world2d/pixi-surface.d.ts +0 -39
  525. package/dist/world2d/pixi-surface.d.ts.map +0 -1
  526. package/dist/world2d/pixi-surface.js +0 -47
  527. package/dist/world2d/scene-capture-2d.d.ts +0 -43
  528. package/dist/world2d/scene-capture-2d.d.ts.map +0 -1
  529. package/dist/world2d/scene-capture-2d.js +0 -92
  530. package/dist/world2d/scene2d-loader.d.ts +0 -27
  531. package/dist/world2d/scene2d-loader.d.ts.map +0 -1
  532. package/dist/world2d/scene2d-loader.js +0 -274
  533. package/dist/world2d/schema/entity2d.d.ts +0 -102
  534. package/dist/world2d/schema/entity2d.d.ts.map +0 -1
  535. package/dist/world2d/schema/entity2d.js +0 -119
  536. package/dist/world2d/schema/physics2d.d.ts +0 -46
  537. package/dist/world2d/schema/physics2d.d.ts.map +0 -1
  538. package/dist/world2d/schema/physics2d.js +0 -45
  539. package/dist/world2d/schema/sprite.d.ts +0 -42
  540. package/dist/world2d/schema/sprite.d.ts.map +0 -1
  541. package/dist/world2d/schema/sprite.js +0 -63
  542. package/dist/world2d/schema/tilemap.d.ts +0 -15
  543. package/dist/world2d/schema/tilemap.d.ts.map +0 -1
  544. package/dist/world2d/schema/tilemap.js +0 -19
  545. package/dist/world2d/schema/tuples2d.d.ts +0 -20
  546. package/dist/world2d/schema/tuples2d.d.ts.map +0 -1
  547. package/dist/world2d/schema/tuples2d.js +0 -19
  548. package/dist/world2d/system-adapters-2d.d.ts +0 -16
  549. package/dist/world2d/system-adapters-2d.d.ts.map +0 -1
  550. package/dist/world2d/system-adapters-2d.js +0 -37
  551. package/dist/world2d/transform-writer-2d.d.ts +0 -16
  552. package/dist/world2d/transform-writer-2d.d.ts.map +0 -1
  553. package/dist/world2d/transform-writer-2d.js +0 -22
  554. package/dist/world2d/types.d.ts +0 -53
  555. package/dist/world2d/types.d.ts.map +0 -1
  556. package/dist/world2d/types.js +0 -1
  557. package/src/runtime/scene-ui-bridge.ts +0 -86
  558. package/src/runtime/scene-ui-data.ts +0 -119
  559. package/src/scene/schema/ui.ts +0 -602
@@ -16,20 +16,13 @@ import { z } from 'zod';
16
16
  * `meta.animation` is a loud Zod "unrecognized key" error, not a silently
17
17
  * ignored field (E1 AC: "Metadata contains no transition language").
18
18
  *
19
- * ## Deliberately out of scope: layers, additive blending, bone masks
19
+ * ## Native layered composition
20
20
  *
21
- * The spec asks for layer/weight/additive metadata "if retained" and bone
22
- * masks "if supported by the Three implementation" (§11 E1). Neither is
23
- * supported by the current Three-facing animation code: no bone-masking or
24
- * blend-compositing across layers exists (the former `AnimGraph` layers, now
25
- * removed by E5, never had it either, and rejected `blendMode`/`weight`/
26
- * `boneMask` at parse for exactly this reason — those fields had no runtime
27
- * reader anywhere in the engine). Per this repo's policy ("Do not author
28
- * schema fields that have no runtime reader" — CLAUDE.md), this module does
29
- * not declare fields for capabilities nothing reads. A single XState machine
30
- * drives one mixer's weight budget; multi-machine/multi-layer composition
31
- * (if ever added) is a distinct, future binding concern with its own reader,
32
- * not a dead field here.
21
+ * Parallel XState regions may drive independent named layers. Each layer can
22
+ * filter a clip to selected bone subtrees, scale its weight, and use normal
23
+ * override or Three's native additive blend mode. XState still owns all
24
+ * state/transition semantics; these fields only describe how the active
25
+ * state's native `AnimationAction`s are composed by the binding.
33
26
  *
34
27
  * ## Blend tree shape
35
28
  *
@@ -136,6 +129,48 @@ const AnimationCrossfadeMetaSchema = z
136
129
 
137
130
  export type AnimationCrossfadeMeta = z.infer<typeof AnimationCrossfadeMetaSchema>;
138
131
 
132
+ const AnimationBoneMaskSchema = z
133
+ .object({
134
+ include: z
135
+ .array(z.string().min(1))
136
+ .min(1)
137
+ .optional()
138
+ .describe('Bone/object names whose complete descendant subtrees are retained'),
139
+ exclude: z
140
+ .array(z.string().min(1))
141
+ .min(1)
142
+ .optional()
143
+ .describe('Bone/object names whose complete descendant subtrees are removed'),
144
+ })
145
+ .strict()
146
+ .refine((mask) => Boolean(mask.include?.length || mask.exclude?.length), {
147
+ message: 'boneMask needs at least one include or exclude bone name',
148
+ })
149
+ .describe('Filters animation tracks by named Object3D/Bone subtrees');
150
+
151
+ export type AnimationBoneMask = z.infer<typeof AnimationBoneMaskSchema>;
152
+
153
+ const AnimationLayerFields = {
154
+ layer: z
155
+ .string()
156
+ .min(1)
157
+ .optional()
158
+ .describe('Composition layer name; omitted means the single base layer'),
159
+ weight: z
160
+ .number()
161
+ .min(0)
162
+ .max(1)
163
+ .optional()
164
+ .describe('Layer weight multiplier from 0 to 1; defaults to 1'),
165
+ blendMode: z
166
+ .enum(['override', 'additive'])
167
+ .optional()
168
+ .describe('Native Three normal/override or additive action blending; defaults to override'),
169
+ boneMask: AnimationBoneMaskSchema.optional().describe(
170
+ 'Optional include/exclude filter applied to every clip track in this state',
171
+ ),
172
+ };
173
+
139
174
  // ---------------------------------------------------------------------------
140
175
  // Per-state animation metadata: single clip OR blend tree, plus crossfade
141
176
  // ---------------------------------------------------------------------------
@@ -155,6 +190,7 @@ const ClipAnimationMetaSchema = z
155
190
  .default(1)
156
191
  .describe('Playback speed multiplier (AnimationAction.timeScale)'),
157
192
  crossfade: AnimationCrossfadeMetaSchema.optional(),
193
+ ...AnimationLayerFields,
158
194
  })
159
195
  .strict();
160
196
 
@@ -166,6 +202,7 @@ const BlendTreeAnimationMetaSchema = z
166
202
  'Multiple clips blended by a live context/selector-driven parameter',
167
203
  ),
168
204
  crossfade: AnimationCrossfadeMetaSchema.optional(),
205
+ ...AnimationLayerFields,
169
206
  })
170
207
  .strict();
171
208
 
@@ -0,0 +1,533 @@
1
+ /**
2
+ * Character primitive — Jolt soft-body cloth grids (capes, skirts, banners)
3
+ * on top of the raw `jolt-physics` WASM bindings.
4
+ *
5
+ * THIN COMPOSITION HELPER, not a wrapper (CLAUDE.md "use libraries
6
+ * directly"): every export assembles the library's own objects
7
+ * (`Jolt.SoftBodySharedSettings`, `Jolt.Body`, `Jolt.JoltInterface`) —
8
+ * nothing from the library is re-exported. This file never imports the WASM
9
+ * module itself — the caller does the async `JoltInit()` and passes the
10
+ * resolved module in, mirroring `setup-physics.ts`'s `setupPhysics(rapier:
11
+ * typeof RAPIER, ...)` pattern for Rapier. `import type Jolt from
12
+ * 'jolt-physics/wasm-compat'` below is TYPE-ONLY (erased at compile time —
13
+ * no runtime WASM import here).
14
+ *
15
+ * Sharp edges this module exists partly to contain (see the mission brief /
16
+ * `cloth-hair-bench/jolt-cloth.ts`'s header for the full discovery):
17
+ * - `Body.GetMotionProperties()` needs the `Jolt.castObject` downcast to
18
+ * `Jolt.SoftBodyMotionProperties` to reach `GetVertex`.
19
+ * - `SoftBodySharedSettings.CreateConstraints` takes a POINTER-typed
20
+ * 1-element `ArraySoftBodySharedSettingsVertexAttributes` (push_back,
21
+ * not resize+at — `.at()` on these embind vectors returns a copy).
22
+ * - Kinematic pins must set BOTH `SoftBodyVertex.mPosition` and
23
+ * `.mVelocity` (finite-differenced) every tick, or the cloth's own
24
+ * constraint solver fights a teleporting anchor.
25
+ * - NO per-frame embind allocations (reviewer finding on Phase 1's
26
+ * spike, which leaked a `new Jolt.Vec3(...)`/`new Jolt.RVec3(...)` per
27
+ * pinned vertex per frame): `tick()` below preallocates ONE `Jolt.Vec3`
28
+ * for position and ONE for velocity and reuses them via `.Set(...)` —
29
+ * embind property setters (`vertex.mPosition = tmpVec3`) copy the value
30
+ * in, so reassigning the SAME wrapper object across many vertices in one
31
+ * frame is safe. `KinematicCollider.moveTo` does the same with one
32
+ * `Jolt.RVec3` + one `Jolt.Quat` (identity, orientation is not simulated
33
+ * here) allocated once at construction.
34
+ * - CONSTRUCTION-TIME embind temporaries need `Jolt.destroy(...)` too, once
35
+ * Jolt has copied their data out (reviewer finding on Phase 2 — these
36
+ * were leaking WASM heap per build/dispose cycle even with `tick()`
37
+ * itself allocation-free). Two different lifetimes are at play:
38
+ * - Per-vertex/per-face grid temporaries (`SoftBodySharedSettingsVertex`
39
+ * + its `Float3`, `SoftBodySharedSettingsFace`, the single
40
+ * `vertexAttr`) are copied BY VALUE into `sharedSettings`'s own
41
+ * storage the instant `push_back`/the property setter runs — freed
42
+ * right there, matching the reuse-then-`Jolt.destroy()`-once pattern
43
+ * in Jolt's own `soft-body-creator.js` sample. `bodyCreation`
44
+ * (`SoftBodyCreationSettings`) is the same story: `CreateSoftBody`
45
+ * copies what it needs, so it's freed right after — same contract as
46
+ * `JoltSettings` → `JoltInterface` below.
47
+ * - `sharedSettings` itself (`SoftBodySharedSettings`) is different: it
48
+ * is REFCOUNTED (`AddRef`/`Release`/`GetRefCount` in the vendored
49
+ * typings, like `Shape`) and the body reads it every step
50
+ * (`SoftBodyMotionProperties.GetSettings()`) for face/constraint
51
+ * topology. Refcounted RefTargets are freed BY JOLT ITSELF when the
52
+ * last reference drops (Release of the final ref IS the delete) —
53
+ * so it gets NO explicit `Jolt.destroy(...)` at all: destroying
54
+ * `bodyCreation` releases the ctor's ref, and `dispose()`'s
55
+ * `DestroyBody` releases the body's (last) ref, which deletes it.
56
+ * An explicit destroy on top would be a use-after-free (empirically
57
+ * confirmed with a `GetRefCount()` probe against real Jolt WASM).
58
+ * Same rule for the collider's `Shape`: `destroy(creation)` releases
59
+ * the creation-settings ref, `DestroyBody` releases the body's —
60
+ * never destroy the shape directly.
61
+ * - The preallocated tick-loop temporaries described above (`tmpPos`/
62
+ * `tmpVel` in `buildClothGrid`, `tmpPos`/`identityQuat` in
63
+ * `buildKinematicCollider`) are plain (non-refcounted) `Vec3`/`RVec3`/
64
+ * `Quat` wrappers reused for the object's whole lifetime — freed in that
65
+ * object's own `dispose()`.
66
+ */
67
+
68
+ import type JoltNS from 'jolt-physics/wasm-compat';
69
+ import * as THREE from 'three';
70
+
71
+ /** The type of an ALREADY-INITIALIZED `jolt-physics/wasm-compat` module
72
+ * (i.e. what `await JoltInit()` resolves to) — the parameter type every
73
+ * function below takes instead of importing/initializing Jolt itself. */
74
+ export type JoltModule = typeof JoltNS;
75
+
76
+ const LAYER_NON_MOVING = 0;
77
+ const LAYER_MOVING = 1;
78
+ const NUM_OBJECT_LAYERS = 2;
79
+ const NUM_BROAD_PHASE_LAYERS = 2;
80
+
81
+ // ---------------------------------------------------------------------------
82
+ // Jolt world (the minimal two-layer broadphase boilerplate every Jolt scene
83
+ // needs, regardless of what bodies it holds)
84
+ // ---------------------------------------------------------------------------
85
+
86
+ export interface JoltWorld {
87
+ jolt: JoltModule;
88
+ joltInterface: JoltNS.JoltInterface;
89
+ physicsSystem: JoltNS.PhysicsSystem;
90
+ bodyInterface: JoltNS.BodyInterface;
91
+ /** The single "moving" object layer every body built through this world's
92
+ * helpers is placed on (cloth + kinematic colliders all collide with
93
+ * each other; nothing here needs more than one moving layer). */
94
+ movingLayer: number;
95
+ /** Advance the simulation by `dt` seconds (`options.collisionSteps` Jolt
96
+ * collision sub-steps). Position any kinematic bodies (colliders, cloth
97
+ * pins) for this frame BEFORE calling `step` — this is what
98
+ * `ClothGrid.tick` does internally. */
99
+ step(dt: number): void;
100
+ dispose(): void;
101
+ }
102
+
103
+ export interface JoltWorldOptions {
104
+ gravity?: { x: number; y: number; z: number };
105
+ /** Jolt collision sub-steps per `step()` call. Soft-body contact has no
106
+ * CCD — a cloth vertex moving faster than its collision radius per
107
+ * collision step TUNNELS through kinematic colliders (measured live on a
108
+ * whipping cape). 2 halves the per-step travel for ~2x the (sub-ms) sim
109
+ * cost. @default 1 */
110
+ collisionSteps?: number;
111
+ }
112
+
113
+ export function createJoltWorld(jolt: JoltModule, options: JoltWorldOptions = {}): JoltWorld {
114
+ const gravity = options.gravity ?? { x: 0, y: -9.81, z: 0 };
115
+ const collisionSteps = options.collisionSteps ?? 1;
116
+ const objectFilter = new jolt.ObjectLayerPairFilterTable(NUM_OBJECT_LAYERS);
117
+ objectFilter.EnableCollision(LAYER_MOVING, LAYER_MOVING);
118
+ const bpInterface = new jolt.BroadPhaseLayerInterfaceTable(
119
+ NUM_OBJECT_LAYERS,
120
+ NUM_BROAD_PHASE_LAYERS,
121
+ );
122
+ bpInterface.MapObjectToBroadPhaseLayer(
123
+ LAYER_NON_MOVING,
124
+ new jolt.BroadPhaseLayer(LAYER_NON_MOVING),
125
+ );
126
+ bpInterface.MapObjectToBroadPhaseLayer(LAYER_MOVING, new jolt.BroadPhaseLayer(LAYER_MOVING));
127
+
128
+ const settings = new jolt.JoltSettings();
129
+ settings.mObjectLayerPairFilter = objectFilter;
130
+ settings.mBroadPhaseLayerInterface = bpInterface;
131
+ settings.mObjectVsBroadPhaseLayerFilter = new jolt.ObjectVsBroadPhaseLayerFilterTable(
132
+ bpInterface,
133
+ NUM_BROAD_PHASE_LAYERS,
134
+ objectFilter,
135
+ NUM_OBJECT_LAYERS,
136
+ );
137
+ const joltInterface = new jolt.JoltInterface(settings);
138
+ jolt.destroy(settings);
139
+ const physicsSystem = joltInterface.GetPhysicsSystem();
140
+ physicsSystem.SetGravity(new jolt.Vec3(gravity.x, gravity.y, gravity.z));
141
+ const bodyInterface = physicsSystem.GetBodyInterface();
142
+
143
+ return {
144
+ jolt,
145
+ joltInterface,
146
+ physicsSystem,
147
+ bodyInterface,
148
+ movingLayer: LAYER_MOVING,
149
+ step(dt: number): void {
150
+ joltInterface.Step(dt, collisionSteps);
151
+ },
152
+ dispose(): void {
153
+ jolt.destroy(joltInterface);
154
+ },
155
+ };
156
+ }
157
+
158
+ // ---------------------------------------------------------------------------
159
+ // Kinematic colliders (torso/head/limb shapes the cloth drapes over)
160
+ // ---------------------------------------------------------------------------
161
+
162
+ export type KinematicColliderShape =
163
+ | { kind: 'sphere'; radius: number }
164
+ | { kind: 'capsule'; radius: number; halfHeight: number };
165
+
166
+ export interface KinematicCollider {
167
+ body: JoltNS.Body;
168
+ /** Move the collider to `worldPos` (identity rotation — these fixtures
169
+ * only need translation) for tick `dt`. Call BEFORE the cloth grid's
170
+ * `tick()` for the same frame (that is what steps the Jolt world). */
171
+ moveTo(worldPos: THREE.Vector3, dt: number): void;
172
+ dispose(): void;
173
+ }
174
+
175
+ export function buildKinematicCollider(
176
+ world: JoltWorld,
177
+ shape: KinematicColliderShape,
178
+ ): KinematicCollider {
179
+ const { jolt, bodyInterface } = world;
180
+ const joltShape =
181
+ shape.kind === 'sphere'
182
+ ? new jolt.SphereShape(shape.radius)
183
+ : new jolt.CapsuleShape(shape.halfHeight, shape.radius);
184
+ const creation = new jolt.BodyCreationSettings(
185
+ joltShape,
186
+ new jolt.RVec3(0, 0, 0),
187
+ new jolt.Quat(0, 0, 0, 1),
188
+ jolt.EMotionType_Kinematic,
189
+ world.movingLayer,
190
+ );
191
+ // A per-tick-driven kinematic collider must NEVER sleep: Jolt puts a
192
+ // near-zero-velocity kinematic body to sleep after ~0.5 s, and
193
+ // `Body.MoveKinematic` (unlike the BodyInterface variant) does NOT wake
194
+ // it — after any brief stillness (e.g. a garment-settle warm-up) the
195
+ // collider would silently freeze at its old pose FOREVER while the
196
+ // character walks away (observed live: the whole cape sailed through the
197
+ // torso and every contact-robustness knob was powerless against it).
198
+ creation.mAllowSleeping = false;
199
+ const body = bodyInterface.CreateBody(creation);
200
+ bodyInterface.AddBody(body.GetID(), jolt.EActivation_Activate);
201
+ // `CreateBody` copied what it needs — free the one-shot settings struct
202
+ // now (same contract as `SoftBodyCreationSettings` in `buildClothGrid`).
203
+ // This also releases `creation`'s reference on the REFCOUNTED `joltShape`;
204
+ // the body holds the remaining shape ref, so `joltShape` must NOT get a
205
+ // `jolt.destroy` anywhere — `DestroyBody` in `dispose()` releases that
206
+ // last ref and Jolt deletes the shape itself (see module header).
207
+ jolt.destroy(creation);
208
+
209
+ // Preallocated once, reused every `moveTo` call — see module header.
210
+ const tmpPos = new jolt.RVec3(0, 0, 0);
211
+ const identityQuat = new jolt.Quat(0, 0, 0, 1);
212
+
213
+ return {
214
+ body,
215
+ moveTo(worldPos: THREE.Vector3, dt: number): void {
216
+ tmpPos.Set(worldPos.x, worldPos.y, worldPos.z);
217
+ body.MoveKinematic(tmpPos, identityQuat, dt);
218
+ },
219
+ dispose(): void {
220
+ bodyInterface.RemoveBody(body.GetID());
221
+ bodyInterface.DestroyBody(body.GetID());
222
+ jolt.destroy(tmpPos);
223
+ jolt.destroy(identityQuat);
224
+ },
225
+ };
226
+ }
227
+
228
+ // ---------------------------------------------------------------------------
229
+ // Cloth grid
230
+ // ---------------------------------------------------------------------------
231
+
232
+ export interface ClothPin {
233
+ row: number;
234
+ col: number;
235
+ }
236
+
237
+ export interface ClothGridOptions {
238
+ cols: number;
239
+ rows: number;
240
+ /** World-space rest position for grid vertex (row, col) — row 0 is
241
+ * conventionally the top/pinned edge, but that's a caller convention,
242
+ * not enforced here. */
243
+ restPosition(row: number, col: number): THREE.Vector3;
244
+ /** Which vertices are pinned (kinematically driven every tick, infinite
245
+ * mass). Default: every vertex in row 0. */
246
+ pins?: ClothPin[];
247
+ vertexMassKg?: number;
248
+ compliance?: number;
249
+ shearCompliance?: number;
250
+ bendCompliance?: number;
251
+ numIterations?: number;
252
+ gravityFactor?: number;
253
+ /** Collision radius of every cloth vertex (Jolt `mVertexRadius`), metres.
254
+ * The default 0 lets a pressed vertex sit exactly ON (and, under
255
+ * sustained gait-driven contact, sink INTO) a collider surface; a few cm
256
+ * here keeps the cloth standing off colliders and makes contact
257
+ * resolution far more robust against sinking. @default 0 (Jolt's own) */
258
+ vertexRadius?: number;
259
+ /** Jolt `mLinearDamping` for the soft body (higher = calmer cloth).
260
+ * @default Jolt's own default */
261
+ linearDamping?: number;
262
+ color?: number;
263
+ /** @default true */
264
+ doubleSided?: boolean;
265
+ }
266
+
267
+ export interface ClothGrid {
268
+ cols: number;
269
+ rows: number;
270
+ vertexCount: number;
271
+ /** Pin list this grid was built with, in the order `tick()`'s
272
+ * `pinTargets` must match. */
273
+ pins: ClothPin[];
274
+ mesh: THREE.Mesh;
275
+ geometry: THREE.BufferGeometry;
276
+ /** One tick: overwrite each pinned vertex's position/velocity from
277
+ * `pinTargets[i]` (world-space, matching `pins` order — velocity is
278
+ * finite-differenced against the previous call), STEP the owning
279
+ * `JoltWorld` (this is the only thing in this module that calls
280
+ * `world.step`, so build at most one `ClothGrid` per `JoltWorld` unless
281
+ * you step the world yourself and skip that side effect), then sync
282
+ * `geometry`'s `position` attribute + recompute normals from the
283
+ * simulated vertices. Position any kinematic colliders for this frame
284
+ * BEFORE calling this. */
285
+ tick(dt: number, pinTargets: readonly THREE.Vector3[]): void;
286
+ /** World-space position of every vertex, row-major (`row * cols + col`).
287
+ * Reuses `out` if given (avoids an allocation for callers sampling every
288
+ * frame, e.g. a numeric collision-clearance check). */
289
+ samplePoints(out?: THREE.Vector3[]): THREE.Vector3[];
290
+ dispose(): void;
291
+ }
292
+
293
+ type GridIndex = (row: number, col: number) => number;
294
+
295
+ /** Build the `SoftBodySharedSettings` (vertex grid + face list + edge/shear/
296
+ * bend constraints) for a `cols`x`rows` cloth. Split out of `buildClothGrid`
297
+ * purely to keep that function's complexity down — not reused elsewhere. */
298
+ function buildSoftBodySharedSettings(
299
+ jolt: JoltModule,
300
+ options: ClothGridOptions,
301
+ pins: ClothPin[],
302
+ idx: GridIndex,
303
+ ): JoltNS.SoftBodySharedSettings {
304
+ const { cols, rows } = options;
305
+ const vertexMassKg = options.vertexMassKg ?? 0.02;
306
+ const pinSet = new Set(pins.map((p) => idx(p.row, p.col)));
307
+
308
+ const sharedSettings = new jolt.SoftBodySharedSettings();
309
+ const vertices = sharedSettings.mVertices;
310
+ for (let row = 0; row < rows; row++) {
311
+ for (let col = 0; col < cols; col++) {
312
+ const p = options.restPosition(row, col);
313
+ const v = new jolt.SoftBodySharedSettingsVertex();
314
+ const pos = new jolt.Float3(p.x, p.y, p.z);
315
+ v.mPosition = pos;
316
+ v.mInvMass = pinSet.has(idx(row, col)) ? 0 : 1 / vertexMassKg;
317
+ vertices.push_back(v);
318
+ // `push_back` (like the property-setter assignment above it) copies
319
+ // BY VALUE into `sharedSettings`'s own storage — Jolt's own
320
+ // `soft-body-creator.js` sample reuses/destroys a single `v` the same
321
+ // way across its whole grid loop — so both temporaries are safe to
322
+ // free the instant the copy has happened.
323
+ jolt.destroy(pos);
324
+ jolt.destroy(v);
325
+ }
326
+ }
327
+ sharedSettings.mVertices = vertices;
328
+
329
+ const faces = sharedSettings.mFaces;
330
+ for (let row = 0; row < rows - 1; row++) {
331
+ for (let col = 0; col < cols - 1; col++) {
332
+ const faceA = new jolt.SoftBodySharedSettingsFace(
333
+ idx(row, col),
334
+ idx(row + 1, col),
335
+ idx(row, col + 1),
336
+ 0,
337
+ );
338
+ faces.push_back(faceA);
339
+ jolt.destroy(faceA);
340
+ const faceB = new jolt.SoftBodySharedSettingsFace(
341
+ idx(row, col + 1),
342
+ idx(row + 1, col),
343
+ idx(row + 1, col + 1),
344
+ 0,
345
+ );
346
+ faces.push_back(faceB);
347
+ jolt.destroy(faceB);
348
+ }
349
+ }
350
+ sharedSettings.mFaces = faces;
351
+
352
+ const vertexAttr = new jolt.SoftBodySharedSettingsVertexAttributes();
353
+ vertexAttr.mCompliance = options.compliance ?? 0.0002;
354
+ vertexAttr.mShearCompliance = options.shearCompliance ?? 0.0002;
355
+ vertexAttr.mBendCompliance = options.bendCompliance ?? 0.002;
356
+ const vertexAttrArr = new jolt.ArraySoftBodySharedSettingsVertexAttributes();
357
+ vertexAttrArr.push_back(vertexAttr);
358
+ // Same value-copy-then-free story as above: `CreateConstraints` below
359
+ // reads from `vertexAttrArr`'s OWN backing copy (via `.data()`), not this
360
+ // original wrapper, so it's safe to free right after the push_back.
361
+ jolt.destroy(vertexAttr);
362
+ sharedSettings.CreateConstraints(
363
+ vertexAttrArr.data(),
364
+ vertexAttrArr.size(),
365
+ jolt.SoftBodySharedSettings_EBendType_Distance,
366
+ );
367
+ sharedSettings.Optimize();
368
+ jolt.destroy(vertexAttrArr);
369
+ return sharedSettings;
370
+ }
371
+
372
+ /** Build the renderable (non-skinned) mesh: a plain `BufferGeometry` whose
373
+ * `position` attribute `ClothGrid.tick` overwrites every frame. */
374
+ function buildClothMesh(
375
+ scene: THREE.Scene,
376
+ options: ClothGridOptions,
377
+ idx: GridIndex,
378
+ ): {
379
+ mesh: THREE.Mesh;
380
+ geometry: THREE.BufferGeometry;
381
+ positionAttr: THREE.BufferAttribute;
382
+ material: THREE.MeshStandardMaterial;
383
+ } {
384
+ const { cols, rows } = options;
385
+ const geometry = new THREE.BufferGeometry();
386
+ const positionAttr = new THREE.BufferAttribute(new Float32Array(rows * cols * 3), 3);
387
+ positionAttr.setUsage(THREE.DynamicDrawUsage);
388
+ geometry.setAttribute('position', positionAttr);
389
+ const indices: number[] = [];
390
+ for (let row = 0; row < rows - 1; row++) {
391
+ for (let col = 0; col < cols - 1; col++) {
392
+ indices.push(idx(row, col), idx(row + 1, col), idx(row, col + 1));
393
+ indices.push(idx(row, col + 1), idx(row + 1, col), idx(row + 1, col + 1));
394
+ }
395
+ }
396
+ geometry.setIndex(indices);
397
+ const material = new THREE.MeshStandardMaterial({
398
+ color: options.color ?? 0x7a2f3a,
399
+ roughness: 0.85,
400
+ side: options.doubleSided === false ? THREE.FrontSide : THREE.DoubleSide,
401
+ });
402
+ const mesh = new THREE.Mesh(geometry, material);
403
+ mesh.castShadow = true;
404
+ mesh.frustumCulled = false;
405
+ scene.add(mesh);
406
+ return { mesh, geometry, positionAttr, material };
407
+ }
408
+
409
+ export function buildClothGrid(
410
+ world: JoltWorld,
411
+ scene: THREE.Scene,
412
+ options: ClothGridOptions,
413
+ ): ClothGrid {
414
+ const { jolt, bodyInterface } = world;
415
+ const { cols, rows } = options;
416
+ const pins = options.pins ?? Array.from({ length: cols }, (_, col) => ({ row: 0, col }));
417
+ const idx: GridIndex = (row, col) => row * cols + col;
418
+
419
+ const sharedSettings = buildSoftBodySharedSettings(jolt, options, pins, idx);
420
+
421
+ const bodyCreation = new jolt.SoftBodyCreationSettings(
422
+ sharedSettings,
423
+ new jolt.RVec3(0, 0, 0),
424
+ new jolt.Quat(0, 0, 0, 1),
425
+ world.movingLayer,
426
+ );
427
+ bodyCreation.mNumIterations = options.numIterations ?? 4;
428
+ if (options.vertexRadius !== undefined) bodyCreation.mVertexRadius = options.vertexRadius;
429
+ if (options.linearDamping !== undefined) bodyCreation.mLinearDamping = options.linearDamping;
430
+ bodyCreation.mPressure = 0;
431
+ bodyCreation.mGravityFactor = options.gravityFactor ?? 1;
432
+ bodyCreation.mFacesDoubleSided = true;
433
+ bodyCreation.mUpdatePosition = false;
434
+ // Kinematically-pinned cloth must never sleep either: `tick()` drives the
435
+ // pins by writing `SoftBodyVertex.mPosition/.mVelocity` DIRECTLY, which
436
+ // does not count as activity — a cape that settles still for ~0.5 s would
437
+ // sleep and stay frozen while its pins walk away.
438
+ bodyCreation.mAllowSleeping = false;
439
+ const clothBody = bodyInterface.CreateSoftBody(bodyCreation);
440
+ bodyInterface.AddBody(clothBody.GetID(), jolt.EActivation_Activate);
441
+ // `CreateSoftBody` copies what it needs out of `bodyCreation` into the
442
+ // body's own storage (same "settings object is a one-shot constructor
443
+ // argument" contract as `JoltSettings` → `JoltInterface` above) — safe to
444
+ // free right away, same as that pattern. `bodyCreation` itself is a plain
445
+ // (non-refcounted) settings struct, unlike `sharedSettings` below: freeing
446
+ // it here still correctly releases bodyCreation's OWN internal reference
447
+ // to `sharedSettings` (which the body has, by now, separately taken its
448
+ // own reference to via `CreateSoftBody`).
449
+ jolt.destroy(bodyCreation);
450
+ // `Body` only exposes the base `GetMotionProperties()`; soft bodies need
451
+ // the downcast to reach `GetVertex`/`GetVertices`.
452
+ const motionProperties = jolt.castObject(
453
+ clothBody.GetMotionProperties(),
454
+ jolt.SoftBodyMotionProperties,
455
+ );
456
+
457
+ const { mesh, geometry, positionAttr, material } = buildClothMesh(scene, options, idx);
458
+
459
+ // Preallocated tick-loop temporaries — see module header.
460
+ const tmpPos = new jolt.Vec3(0, 0, 0);
461
+ const tmpVel = new jolt.Vec3(0, 0, 0);
462
+ const prevPinWorld: THREE.Vector3[] = pins.map((p) => options.restPosition(p.row, p.col));
463
+
464
+ return {
465
+ cols,
466
+ rows,
467
+ vertexCount: rows * cols,
468
+ pins,
469
+ mesh,
470
+ geometry,
471
+ tick(dt: number, pinTargets: readonly THREE.Vector3[]): void {
472
+ for (let i = 0; i < pins.length; i++) {
473
+ const target = pinTargets[i];
474
+ if (!target) continue;
475
+ const pin = pins[i]!;
476
+ const prev = prevPinWorld[i]!;
477
+ const vertex = motionProperties.GetVertex(idx(pin.row, pin.col));
478
+ tmpPos.Set(target.x, target.y, target.z);
479
+ vertex.mPosition = tmpPos;
480
+ if (dt > 0) {
481
+ tmpVel.Set((target.x - prev.x) / dt, (target.y - prev.y) / dt, (target.z - prev.z) / dt);
482
+ } else {
483
+ tmpVel.Set(0, 0, 0);
484
+ }
485
+ vertex.mVelocity = tmpVel;
486
+ prev.copy(target);
487
+ }
488
+
489
+ world.step(dt);
490
+
491
+ const posArr = positionAttr.array as Float32Array;
492
+ for (let i = 0; i < rows * cols; i++) {
493
+ const v = motionProperties.GetVertex(i);
494
+ const p = v.mPosition;
495
+ posArr[i * 3] = p.GetX();
496
+ posArr[i * 3 + 1] = p.GetY();
497
+ posArr[i * 3 + 2] = p.GetZ();
498
+ }
499
+ positionAttr.needsUpdate = true;
500
+ geometry.computeVertexNormals();
501
+ },
502
+ samplePoints(out: THREE.Vector3[] = []): THREE.Vector3[] {
503
+ const n = rows * cols;
504
+ out.length = n;
505
+ for (let i = 0; i < n; i++) {
506
+ const v = motionProperties.GetVertex(i);
507
+ const p = v.mPosition;
508
+ if (!out[i]) out[i] = new THREE.Vector3();
509
+ out[i]!.set(p.GetX(), p.GetY(), p.GetZ());
510
+ }
511
+ return out;
512
+ },
513
+ dispose(): void {
514
+ bodyInterface.RemoveBody(clothBody.GetID());
515
+ // NO explicit `jolt.destroy(sharedSettings)` — anywhere. It is a
516
+ // refcounted RefTarget that Jolt frees ITSELF when its last reference
517
+ // drops, and in Jolt's RefTarget::Release releasing the last ref IS
518
+ // the delete. Refcount chain, probed live against real Jolt WASM via
519
+ // `GetRefCount()`: 0 after `new` → 1 after the
520
+ // `SoftBodyCreationSettings` ctor took its ref → 2 after
521
+ // `CreateSoftBody` (the body's own ref) → back to 1 after the
522
+ // construction-time `destroy(bodyCreation)` → `DestroyBody` below
523
+ // releases that last (body) reference and thereby deletes it. Any
524
+ // `destroy(sharedSettings)` after this line would be a use-after-free.
525
+ bodyInterface.DestroyBody(clothBody.GetID());
526
+ jolt.destroy(tmpPos);
527
+ jolt.destroy(tmpVel);
528
+ scene.remove(mesh);
529
+ geometry.dispose();
530
+ material.dispose();
531
+ },
532
+ };
533
+ }