@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
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Character primitive — spring-bone chains (hair, ponytails, cloth strips)
3
+ * on top of `@pixiv/three-vrm-springbone`'s raw API.
4
+ *
5
+ * This is a THIN COMPOSITION HELPER, not a wrapper (CLAUDE.md "use libraries
6
+ * directly"): every export here is a small function that assembles the
7
+ * library's own objects (`THREE.Bone`, `VRMSpringBoneJoint`,
8
+ * `VRMSpringBoneCollider`, `VRMSpringBoneManager`) — nothing from the
9
+ * library is re-exported, and callers still import `@pixiv/three-vrm-
10
+ * springbone` types directly for anything beyond what's here. Kept
11
+ * deliberately bare: composing a specific character (a caped viking, a
12
+ * ponytailed ranger) is ordinary calling code on top of these pieces, not a
13
+ * costume/preset system in the engine.
14
+ *
15
+ * Sharp edge this module exists partly to contain (see the mission brief /
16
+ * `cloth-hair-bench/spring-rig.ts`'s header for the full discovery):
17
+ * `VRMSpringBoneCollider` overrides `updateWorldMatrix` (the ON-DEMAND,
18
+ * manually-invoked method), NOT `updateMatrixWorld` (the one the renderer
19
+ * calls automatically) — `SpringColliderRig.reposition()` below calls
20
+ * `collider.updateWorldMatrix(true, false)` itself every tick so callers
21
+ * can't forget it.
22
+ */
23
+
24
+ import {
25
+ VRMSpringBoneCollider,
26
+ type VRMSpringBoneColliderGroup,
27
+ VRMSpringBoneColliderShapeCapsule,
28
+ VRMSpringBoneColliderShapeSphere,
29
+ VRMSpringBoneJoint,
30
+ VRMSpringBoneManager,
31
+ } from '@pixiv/three-vrm-springbone';
32
+ import * as THREE from 'three';
33
+
34
+ const DEFAULT_GRAVITY_DIR = new THREE.Vector3(0, -1, 0);
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Bone chains
38
+ // ---------------------------------------------------------------------------
39
+
40
+ export interface BoneChain {
41
+ /** Swinging bones, root (nearest the anchor) first. */
42
+ bones: THREE.Bone[];
43
+ /** A non-simulated leaf `Object3D` past the last bone — the spring joint
44
+ * "tail" target for the last bone in the chain. */
45
+ tip: THREE.Object3D;
46
+ }
47
+
48
+ /** World-space offset from `parent`'s CURRENT world position -> the local
49
+ * position a new direct child of `parent` must have to sit there. Requires
50
+ * `parent.matrixWorld` to be current. Sidesteps ever reasoning about a
51
+ * bone's local rest-axis convention (arbitrary per-bone on an imported
52
+ * rig) — the caller only ever thinks in world-space directions. */
53
+ export function worldOffsetToLocalPos(
54
+ parent: THREE.Object3D,
55
+ worldDir: THREE.Vector3,
56
+ length: number,
57
+ ): THREE.Vector3 {
58
+ const parentWorld = new THREE.Vector3();
59
+ parent.getWorldPosition(parentWorld);
60
+ const targetWorld = parentWorld.clone().addScaledVector(worldDir, length);
61
+ return parent.worldToLocal(targetWorld.clone());
62
+ }
63
+
64
+ /** Build a straight chain of `count` new `THREE.Bone`s under `parent`, each
65
+ * offset `segmentLength` further along the WORLD-space unit vector
66
+ * `worldDir` — the natural rest shape for hair/cloth hanging under gravity.
67
+ * Ends with a `tip` `Object3D` (one more segment past the last bone) so
68
+ * every bone has an explicit spring-joint child target. Can be anchored to
69
+ * either an existing rig bone (hair off a head) or any plain `Object3D`. */
70
+ export function buildBoneChain(
71
+ parent: THREE.Object3D,
72
+ namePrefix: string,
73
+ count: number,
74
+ segmentLength: number,
75
+ worldDir: THREE.Vector3,
76
+ ): BoneChain {
77
+ const bones: THREE.Bone[] = [];
78
+ let cur: THREE.Object3D = parent;
79
+ cur.updateWorldMatrix(true, false);
80
+ for (let i = 0; i < count; i++) {
81
+ const localPos = worldOffsetToLocalPos(cur, worldDir, segmentLength);
82
+ const bone = new THREE.Bone();
83
+ bone.name = `${namePrefix}${i}`;
84
+ bone.position.copy(localPos);
85
+ cur.add(bone);
86
+ bone.updateWorldMatrix(true, false);
87
+ bones.push(bone);
88
+ cur = bone;
89
+ }
90
+ const tip = new THREE.Object3D();
91
+ tip.name = `${namePrefix}Tip`;
92
+ tip.position.copy(worldOffsetToLocalPos(cur, worldDir, segmentLength));
93
+ cur.add(tip);
94
+ return { bones, tip };
95
+ }
96
+
97
+ // ---------------------------------------------------------------------------
98
+ // Spring joints
99
+ // ---------------------------------------------------------------------------
100
+
101
+ export interface ChainJointSettings {
102
+ stiffness?: number;
103
+ gravityPower?: number;
104
+ gravityDir?: THREE.Vector3;
105
+ dragForce?: number;
106
+ hitRadius?: number;
107
+ }
108
+
109
+ export interface ChainJointHandle {
110
+ joints: VRMSpringBoneJoint[];
111
+ /** Deregisters every joint this chain added from `manager`. Does NOT
112
+ * detach the bones from the scene graph (the caller owns that). */
113
+ dispose(): void;
114
+ }
115
+
116
+ /** Register one `VRMSpringBoneJoint` per bone in `chain` on `manager` (each
117
+ * targeting the next bone, the last targeting `chain.tip`). Does not call
118
+ * `manager.setInitState()` — batch that once after all chains for a
119
+ * manager are registered and the scene's world matrices are current (sharp
120
+ * edge: `VRMSpringBoneJoint` sets `bone.matrixAutoUpdate = false`, so
121
+ * `setInitState()` must run AFTER `scene.updateMatrixWorld(true)`). */
122
+ export function registerChainJoints(
123
+ manager: VRMSpringBoneManager,
124
+ chain: BoneChain,
125
+ colliderGroups: VRMSpringBoneColliderGroup[],
126
+ settings: ChainJointSettings = {},
127
+ ): ChainJointHandle {
128
+ const joints: VRMSpringBoneJoint[] = [];
129
+ for (let i = 0; i < chain.bones.length; i++) {
130
+ const bone = chain.bones[i]!;
131
+ const child = chain.bones[i + 1] ?? chain.tip;
132
+ const joint = new VRMSpringBoneJoint(
133
+ bone,
134
+ child,
135
+ {
136
+ stiffness: settings.stiffness ?? 1.2,
137
+ gravityPower: settings.gravityPower ?? 0.9,
138
+ gravityDir: settings.gravityDir ?? DEFAULT_GRAVITY_DIR,
139
+ dragForce: settings.dragForce ?? 0.35,
140
+ hitRadius: settings.hitRadius ?? 0.02,
141
+ },
142
+ colliderGroups,
143
+ );
144
+ manager.addJoint(joint);
145
+ joints.push(joint);
146
+ }
147
+ return {
148
+ joints,
149
+ dispose(): void {
150
+ for (const joint of joints) manager.deleteJoint(joint);
151
+ },
152
+ };
153
+ }
154
+
155
+ // ---------------------------------------------------------------------------
156
+ // Colliders
157
+ // ---------------------------------------------------------------------------
158
+
159
+ export interface SphereColliderDef {
160
+ shape: 'sphere';
161
+ name: string;
162
+ /** The collider tracks this object's world position every `reposition()`. */
163
+ followBone: THREE.Object3D;
164
+ radius: number;
165
+ /** Extra WORLD-space offset added after sampling `followBone`'s position
166
+ * (e.g. nudging a head collider up from the neck joint). */
167
+ worldOffset?: THREE.Vector3;
168
+ }
169
+
170
+ export interface CapsuleColliderDef {
171
+ shape: 'capsule';
172
+ name: string;
173
+ followBone: THREE.Object3D;
174
+ radius: number;
175
+ /** Capsule tail, in the collider's own local space (world-axis-aligned —
176
+ * colliders are parented directly to the scene, see `buildSpringColliderRig`). */
177
+ tail: THREE.Vector3;
178
+ worldOffset?: THREE.Vector3;
179
+ }
180
+
181
+ export type ColliderDef = SphereColliderDef | CapsuleColliderDef;
182
+
183
+ export interface SpringColliderRig {
184
+ group: VRMSpringBoneColliderGroup;
185
+ colliders: Map<string, VRMSpringBoneCollider>;
186
+ /** Reposition every collider from its `followBone`'s CURRENT world
187
+ * transform and refresh its `colliderMatrix` (see module header). Call
188
+ * once per tick, BEFORE `manager.update(dt)`. */
189
+ reposition(): void;
190
+ dispose(): void;
191
+ }
192
+
193
+ /** Build sphere/capsule colliders parented directly to `scene` (world
194
+ * space) — sidesteps reasoning about any bone's local rest-axis convention
195
+ * for the collider shape's offset/tail vectors. */
196
+ export function buildSpringColliderRig(scene: THREE.Scene, defs: ColliderDef[]): SpringColliderRig {
197
+ const colliders = new Map<string, VRMSpringBoneCollider>();
198
+ const followers: Array<{ def: ColliderDef; collider: VRMSpringBoneCollider }> = [];
199
+
200
+ for (const def of defs) {
201
+ const collider =
202
+ def.shape === 'sphere'
203
+ ? new VRMSpringBoneCollider(new VRMSpringBoneColliderShapeSphere({ radius: def.radius }))
204
+ : new VRMSpringBoneCollider(
205
+ new VRMSpringBoneColliderShapeCapsule({
206
+ radius: def.radius,
207
+ offset: new THREE.Vector3(0, 0, 0),
208
+ tail: def.tail,
209
+ }),
210
+ );
211
+ scene.add(collider);
212
+ colliders.set(def.name, collider);
213
+ followers.push({ def, collider });
214
+ }
215
+
216
+ const group: VRMSpringBoneColliderGroup = {
217
+ name: 'body',
218
+ colliders: [...colliders.values()],
219
+ };
220
+
221
+ const worldTmp = new THREE.Vector3();
222
+ return {
223
+ group,
224
+ colliders,
225
+ reposition(): void {
226
+ for (const { def, collider } of followers) {
227
+ def.followBone.getWorldPosition(worldTmp);
228
+ collider.position.copy(worldTmp);
229
+ if (def.worldOffset) collider.position.add(def.worldOffset);
230
+ collider.updateWorldMatrix(true, false);
231
+ }
232
+ },
233
+ dispose(): void {
234
+ for (const collider of colliders.values()) scene.remove(collider);
235
+ colliders.clear();
236
+ },
237
+ };
238
+ }
239
+
240
+ // ---------------------------------------------------------------------------
241
+ // Composed chain rig (the common case: one chain, its own manager)
242
+ // ---------------------------------------------------------------------------
243
+
244
+ export interface SpringChainRigOptions {
245
+ parent: THREE.Object3D;
246
+ namePrefix: string;
247
+ count: number;
248
+ segmentLength: number;
249
+ worldDir: THREE.Vector3;
250
+ /** Colliders this chain collides against. Omit for a chain with no
251
+ * collision (e.g. a side strand tucked away from the body). */
252
+ colliderRig?: SpringColliderRig;
253
+ settings?: ChainJointSettings;
254
+ }
255
+
256
+ export interface SpringChainRig {
257
+ bones: THREE.Bone[];
258
+ tip: THREE.Object3D;
259
+ /** The manager owning this chain's joints — a "manager-compatible handle"
260
+ * a caller can also register additional joints on directly. */
261
+ manager: VRMSpringBoneManager;
262
+ /** Repositions `colliderRig` (if given) and steps the spring simulation.
263
+ * Handles the collider `updateWorldMatrix` footgun internally — callers
264
+ * never need to call `collider.updateWorldMatrix` themselves. */
265
+ tick(dt: number): void;
266
+ dispose(): void;
267
+ }
268
+
269
+ /** Compose `buildBoneChain` + `registerChainJoints` behind one call for the
270
+ * common single-chain case: builds the chain, gives it its OWN
271
+ * `VRMSpringBoneManager` (managers are cheap — one per independent chain
272
+ * keeps `dispose()`/`tick()` fully self-contained), registers its joints,
273
+ * and calls `setInitState()` once construction is complete.
274
+ *
275
+ * For MULTIPLE chains sharing one collider rig (e.g. a ponytail + two side
276
+ * strands all colliding against the same head), build each chain with its
277
+ * own `buildSpringChainRig` call (same `colliderRig` passed to each) and
278
+ * tick all of them — `colliderRig.reposition()` is idempotent per frame, so
279
+ * calling it once per chain tick is correct, just slightly redundant work
280
+ * (cheap: it only touches a handful of colliders). */
281
+ export function buildSpringChainRig(options: SpringChainRigOptions): SpringChainRig {
282
+ const chain = buildBoneChain(
283
+ options.parent,
284
+ options.namePrefix,
285
+ options.count,
286
+ options.segmentLength,
287
+ options.worldDir,
288
+ );
289
+ const manager = new VRMSpringBoneManager();
290
+ const colliderGroups = options.colliderRig ? [options.colliderRig.group] : [];
291
+ const jointHandle = registerChainJoints(manager, chain, colliderGroups, options.settings);
292
+ manager.setInitState();
293
+
294
+ return {
295
+ bones: chain.bones,
296
+ tip: chain.tip,
297
+ manager,
298
+ tick(dt: number): void {
299
+ options.colliderRig?.reposition();
300
+ manager.update(dt);
301
+ },
302
+ dispose(): void {
303
+ jointHandle.dispose();
304
+ chain.bones[0]?.removeFromParent();
305
+ },
306
+ };
307
+ }
@@ -1,4 +1,4 @@
1
- import type { GameLoopConfig } from './types';
1
+ import type { GameLoopConfig, GameLoopLiveness } from './types';
2
2
 
3
3
  /**
4
4
  * Fixed-timestep game loop with accumulator pattern.
@@ -104,12 +104,22 @@ export function createGameLoop(config: GameLoopConfig) {
104
104
  // listener to fire in the first place.
105
105
  if (externalDrive) return;
106
106
 
107
- rafId = requestAnimationFrame(frame);
108
-
107
+ // A play session can be started while the editor tab is ALREADY
108
+ // hidden. In that case no future visibilitychange-to-hidden event will
109
+ // arrive, and arming rAF would leave `running=true` forever while the
110
+ // browser executes zero callbacks. Classify and park the loop before
111
+ // arming it so status is truthful and visibility restore can resume it.
109
112
  if (!visibilityListenerAttached && typeof document !== 'undefined') {
110
113
  document.addEventListener('visibilitychange', handleVisibilityChange);
111
114
  visibilityListenerAttached = true;
112
115
  }
116
+ if (typeof document !== 'undefined' && document.hidden) {
117
+ running = false;
118
+ hiddenPaused = true;
119
+ return;
120
+ }
121
+
122
+ rafId = requestAnimationFrame(frame);
113
123
  },
114
124
 
115
125
  stop() {
@@ -127,6 +137,22 @@ export function createGameLoop(config: GameLoopConfig) {
127
137
  return running;
128
138
  },
129
139
 
140
+ /**
141
+ * Truthful liveness (issue #175 — see `GameLoopLiveness`'s doc comment
142
+ * in `core/types.ts` for the full contract). Unlike `isRunning` above,
143
+ * this DISTINGUISHES a hidden-tab pause from an actually-stopped loop:
144
+ * `hiddenPaused` is only ever set by `handleVisibilityChange` (T2.1's
145
+ * idle throttle) and cleared by `start()`/`stop()`/visibility-restore,
146
+ * so it can never be true at the same time `running` is true. An
147
+ * `externalDrive` loop never installs the visibility listener at all
148
+ * (see `start()` above), so `hiddenPaused` stays permanently `false` for
149
+ * it — it only ever reports `'running'` or `'stopped'`.
150
+ */
151
+ get liveness(): GameLoopLiveness {
152
+ if (hiddenPaused) return 'hidden-paused';
153
+ return running ? 'running' : 'stopped';
154
+ },
155
+
130
156
  set timeScale(value: number) {
131
157
  const clamped = Math.min(8, Math.max(0, value));
132
158
  if (clamped !== value) {
@@ -140,5 +166,17 @@ export function createGameLoop(config: GameLoopConfig) {
140
166
  get timeScale() {
141
167
  return timeScale;
142
168
  },
169
+
170
+ /**
171
+ * The fixed substep timestep (seconds) this loop's accumulator consumes
172
+ * per `config.update()` call (`config.fixedTimestep ?? 1/60`) — read by
173
+ * `Game.runTicks` (D15, `runtime/game.ts`) so a synchronous fast-forward
174
+ * burst drives `game.runFrame` with the SAME `fixedDt` this loop's own
175
+ * rAF-driven accumulator would have used, without exposing (or
176
+ * `runTicks` needing) any other internal loop state.
177
+ */
178
+ get fixedDt() {
179
+ return fixedDt;
180
+ },
143
181
  };
144
182
  }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * D15 — the seeded-random core (`docs/D15-DETERMINISM-DESIGN.md` §2.a,
3
+ * T-D15.1). `ctx.random` is a game-scoped PRNG with NAMED STREAMS: calling
4
+ * the object itself (`ctx.random()`) draws from the `'gameplay'` stream;
5
+ * `ctx.random.stream('vfx')` (or any other name) derives an INDEPENDENT
6
+ * generator, so a cosmetic/VFX draw can never perturb the gameplay draw
7
+ * order — the classic replay-drift trap option B ("one game-scoped PRNG
8
+ * only") would fall into (see the design doc's §2.a option table).
9
+ *
10
+ * Each stream is seeded from `fnv1a(name) ^ rootSeed` — deterministic given
11
+ * the root seed, independent of draw order across streams (drawing from
12
+ * `'vfx'` never advances `'gameplay'`'s generator, since they are two
13
+ * separate mulberry32 instances). `reseed(seed)` re-derives every stream
14
+ * that has EVER been asked for via `.stream(name)` (including the implicit
15
+ * `'gameplay'` stream `ctx.random()` itself draws from) — future draws only:
16
+ * numbers already returned before a `reseed()` call are not (and cannot be)
17
+ * un-returned; this matches `play.seed.set`'s documented semantics (T-D15.6).
18
+ *
19
+ * Deliberately reuses {@link createMulberry32} FROM `runtime/render-seed.ts`
20
+ * (the render/capture door's own generator) rather than duplicating the
21
+ * algorithm — `render-seed.ts` stays a zero-import module itself (this file
22
+ * imports FROM it, never the reverse), so this is a dependency-clean
23
+ * direction: the render/capture door and the gameplay determinism door share
24
+ * one PRNG implementation without either depending on the other's door
25
+ * logic (§2.a: "Kept as-is for the render/capture door only" — the two doors
26
+ * stay orthogonal; only the generator function itself is shared).
27
+ */
28
+
29
+ import { createMulberry32 } from '../runtime/render-seed';
30
+
31
+ /** The name `ctx.random()` (called with no `.stream(...)`) draws from. */
32
+ export const GAMEPLAY_STREAM = 'gameplay';
33
+
34
+ /** The fixed fallback seed a `SeededRandom` boots with when no explicit seed
35
+ * is supplied (mirrors `render-seed.ts`'s own `DEFAULT_RENDER_SEED` in
36
+ * spirit — an arbitrary but fixed golden-ratio-derived constant, never
37
+ * wall-clock/`Math.random`-derived, so an UNDECLARED project's `ctx.random`
38
+ * is still perfectly reproducible on its own terms even though nothing in
39
+ * the manifest asked for that — it just isn't a documented CONTRACT until
40
+ * `determinism.seededRandom` is declared, per T4.1's "no dead field" rule). */
41
+ export const DEFAULT_SEEDED_RANDOM_SEED = 0x9e3779b9;
42
+
43
+ /**
44
+ * The `ctx.random` surface (`GameContext.random`, `runtime/types.ts`).
45
+ * Callable (draws from the `'gameplay'` stream), plus:
46
+ * - `stream(name)` — an independent named generator, `[0, 1)` floats, same
47
+ * call signature as `Math.random`/the bare `SeededRandom` call itself.
48
+ * - `reseed(seed)` — re-derive every stream ever requested so far (future
49
+ * draws only) from a NEW root seed.
50
+ * - `seed` — the CURRENT root seed (reflects the last `reseed()` call, if
51
+ * any) — a live getter, not a value snapshotted at construction.
52
+ */
53
+ export interface SeededRandom {
54
+ (): number;
55
+ stream(name: string): () => number;
56
+ reseed(seed: number): void;
57
+ readonly seed: number;
58
+ }
59
+
60
+ /** fnv1a-32, the standard non-cryptographic string hash — used only to mix a
61
+ * stream NAME into the root seed, never as a determinism primitive on its
62
+ * own (mulberry32 is still what actually produces the `[0,1)` sequence). */
63
+ function fnv1a32(str: string): number {
64
+ let hash = 0x811c9dc5;
65
+ for (let i = 0; i < str.length; i++) {
66
+ hash ^= str.charCodeAt(i);
67
+ hash = Math.imul(hash, 0x01000193);
68
+ }
69
+ return hash >>> 0;
70
+ }
71
+
72
+ interface StreamBox {
73
+ /** Reassigned wholesale on `reseed()` — the wrapper function `.stream(name)`
74
+ * returns to callers stays the SAME identity forever; only what it reads
75
+ * from changes, which is what makes reseed observable through a handle a
76
+ * caller obtained long before the reseed call. */
77
+ gen: () => number;
78
+ }
79
+
80
+ /**
81
+ * Construct a fresh, game-scoped seeded-random surface. `initialSeed` is
82
+ * coerced to an unsigned 32-bit integer the same way `render-seed.ts`'s
83
+ * `installDeterministicRandom` does (`>>> 0`), so any finite JS number is
84
+ * accepted.
85
+ */
86
+ export function createSeededRandom(initialSeed: number): SeededRandom {
87
+ let rootSeed = initialSeed >>> 0;
88
+ const streams = new Map<string, StreamBox>();
89
+
90
+ function deriveStreamSeed(name: string): number {
91
+ return (fnv1a32(name) ^ rootSeed) >>> 0;
92
+ }
93
+
94
+ function getBox(name: string): StreamBox {
95
+ let box = streams.get(name);
96
+ if (!box) {
97
+ box = { gen: createMulberry32(deriveStreamSeed(name)) };
98
+ streams.set(name, box);
99
+ }
100
+ return box;
101
+ }
102
+
103
+ function streamFn(name: string): () => number {
104
+ const box = getBox(name);
105
+ return () => box.gen();
106
+ }
107
+
108
+ const random = (() => getBox(GAMEPLAY_STREAM).gen()) as SeededRandom;
109
+
110
+ Object.defineProperties(random, {
111
+ stream: { value: streamFn, enumerable: true },
112
+ reseed: {
113
+ value: (seed: number) => {
114
+ rootSeed = seed >>> 0;
115
+ // Re-derive every stream ANYONE has ever asked for (including the
116
+ // implicit 'gameplay' stream, once `random()` or
117
+ // `.stream('gameplay')` has been called at least once) — a handle a
118
+ // caller stashed before this call keeps its identity but now reads
119
+ // from the freshly-seeded generator on its NEXT call (future draws
120
+ // only, per the design doc's `play.seed.set` semantics).
121
+ for (const [name, box] of streams) {
122
+ box.gen = createMulberry32(deriveStreamSeed(name));
123
+ }
124
+ },
125
+ enumerable: true,
126
+ },
127
+ seed: {
128
+ get: () => rootSeed,
129
+ enumerable: true,
130
+ },
131
+ });
132
+
133
+ return random;
134
+ }
135
+
136
+ // ---------------------------------------------------------------------------
137
+ // Game-scoped registry — mirrors `runtime/debug-registry.ts`'s
138
+ // `registerDebugRegistry`/`getDebugRegistry` WeakMap pattern exactly, but
139
+ // keyed on a bare `object` (not `Game`) so this module never needs to import
140
+ // `runtime/game.ts` even as a type — `core/` stays independent of `runtime/`
141
+ // except for the one explicit, documented `render-seed.ts` reuse above.
142
+ // `createGame` (`runtime/game.ts`) is the one real registrant, passing itself
143
+ // (the `GameInternal` shell) as the key, exactly like it does for
144
+ // `registerDebugRegistry(gameInternal, debugRegistry)`.
145
+ // ---------------------------------------------------------------------------
146
+
147
+ const registryByOwner = new WeakMap<object, SeededRandom>();
148
+
149
+ /** Called once by `createGame`, right after both the seeded-random surface
150
+ * and the Game shell object exist — mirrors `registerDebugRegistry`. */
151
+ export function registerSeededRandom(owner: object, random: SeededRandom): void {
152
+ registryByOwner.set(owner, random);
153
+ }
154
+
155
+ /** The game-scoped `SeededRandom` backing every world's `ctx.random` — `null`
156
+ * for an owner built without one (there is always one for every real
157
+ * `createGame` call; `null` only for a hand-built `Game`-shaped stand-in a
158
+ * test constructs without going through `createGame`). */
159
+ export function getSeededRandom(owner: object): SeededRandom | null {
160
+ return registryByOwner.get(owner) ?? null;
161
+ }
@@ -1,4 +1,11 @@
1
- import { PHASE_ORDER, type SystemDef, type SystemFn, type SystemPhaseName } from './types';
1
+ import {
2
+ PHASE_ORDER,
3
+ type SystemDef,
4
+ type SystemFn,
5
+ type SystemOptions,
6
+ type SystemPhaseName,
7
+ type SystemRunObserver,
8
+ } from './types';
2
9
 
3
10
  /**
4
11
  * Ordered system execution by named phase.
@@ -25,10 +32,12 @@ import { PHASE_ORDER, type SystemDef, type SystemFn, type SystemPhaseName } from
25
32
  *
26
33
  * Within each bucket, systems run in registration order.
27
34
  */
28
- export function createSystemRunner() {
35
+ export function createSystemRunner(observer?: SystemRunObserver, scope = 'world') {
29
36
  const systems = new Map<SystemPhaseName, SystemFn[]>();
30
37
  const registered: SystemDef[] = [];
31
38
  const componentTicks = new Map<SystemPhaseName, SystemFn | null>();
39
+ const labels = new Map<SystemFn, string>();
40
+ let anonymousId = 0;
32
41
 
33
42
  // Initialize all phases with empty arrays / no component tick.
34
43
  for (const phase of PHASE_ORDER) {
@@ -56,11 +65,16 @@ export function createSystemRunner() {
56
65
 
57
66
  /** Run one system function, isolating a throw so it never wedges the frame. */
58
67
  function runOne(fn: SystemFn, phase: SystemPhaseName, dt: number): void {
68
+ const label = labels.get(fn) ?? fn.name ?? `anonymous-${++anonymousId}`;
69
+ labels.set(fn, label);
70
+ observer?.beginSystem(scope, phase, label);
59
71
  try {
60
72
  fn(dt);
61
73
  } catch (err) {
62
74
  const label = fn.name ? `"${fn.name}"` : '(anonymous)';
63
75
  console.error(`[system-runner] system ${label} in phase "${phase}" threw:`, err);
76
+ } finally {
77
+ observer?.endSystem(scope, phase, label);
64
78
  }
65
79
  }
66
80
 
@@ -69,12 +83,13 @@ export function createSystemRunner() {
69
83
  * Register a bare system function in a specific phase.
70
84
  * Systems within the same phase run in the order they were added.
71
85
  */
72
- add(phase: SystemPhaseName, fn: SystemFn) {
86
+ add(phase: SystemPhaseName, fn: SystemFn, options?: SystemOptions) {
73
87
  const list = systems.get(phase);
74
88
  if (!list) {
75
89
  throw new Error(`Unknown phase: ${phase}. Valid phases: ${PHASE_ORDER.join(', ')}`);
76
90
  }
77
91
  list.push(fn);
92
+ labels.set(fn, options?.name ?? fn.name ?? `anonymous-${++anonymousId}`);
78
93
  },
79
94
 
80
95
  /**
@@ -102,6 +117,7 @@ export function createSystemRunner() {
102
117
  throw new Error(`Unknown phase: ${system.phase}. Valid phases: ${PHASE_ORDER.join(', ')}`);
103
118
  }
104
119
  list.push(system.update);
120
+ labels.set(system.update, system.name ?? system.update.name ?? `anonymous-${++anonymousId}`);
105
121
  registered.push(system);
106
122
  },
107
123
 
@@ -141,6 +157,7 @@ export function createSystemRunner() {
141
157
  );
142
158
  }
143
159
  componentTicks.set(phase, fn);
160
+ labels.set(fn, `components.${phase}`);
144
161
  },
145
162
 
146
163
  /**
package/src/core/types.ts CHANGED
@@ -30,8 +30,20 @@ export const PHASE_ORDER: SystemPhaseName[] = [
30
30
  /** A system is just a function that takes delta time */
31
31
  export type SystemFn = (dt: number) => void;
32
32
 
33
+ export interface SystemOptions {
34
+ /** Stable diagnostic label; falls back to the function name. */
35
+ readonly name?: string;
36
+ }
37
+
38
+ export interface SystemRunObserver {
39
+ beginSystem(scope: string, phase: SystemPhaseName, name: string): void;
40
+ endSystem(scope: string, phase: SystemPhaseName, name: string): void;
41
+ }
42
+
33
43
  /** A lifecycle system with init/update/dispose hooks. */
34
44
  export interface SystemDef {
45
+ /** Stable diagnostic label; falls back to the update function name. */
46
+ readonly name?: string;
35
47
  /** Which phase the update function runs in */
36
48
  phase: SystemPhaseName;
37
49
  /** One-time setup after all entities are spawned, before the first frame */
@@ -69,3 +81,28 @@ export interface GameLoopConfig {
69
81
  */
70
82
  externalDrive?: boolean;
71
83
  }
84
+
85
+ /**
86
+ * Truthful liveness classification for `createGameLoop`'s returned loop
87
+ * (issue #175 — "state must never claim health it cannot observe"). See
88
+ * `GameLoop.liveness`'s doc comment in `game-loop.ts` for the full contract;
89
+ * exported here (rather than only inline on the loop's return type) so
90
+ * callers elsewhere in the engine (`runtime/game.ts`, `runtime/
91
+ * debug-registry.ts`) can name the union without importing `game-loop.ts`
92
+ * as a value.
93
+ *
94
+ * - `'running'` — the rAF chain is armed and `config.update` is being
95
+ * driven by real frames (or, for an `externalDrive` loop, `start()` has
96
+ * been called — see that mode's own liveness note below).
97
+ * - `'hidden-paused'` — the T2.1 idle throttle stopped the loop because
98
+ * `document.hidden` is true; this is deliberate and reversible (resumes
99
+ * automatically on visibility), but ZERO ticks are advancing right now.
100
+ * - `'stopped'` — `stop()` was called, or `start()` was never called.
101
+ *
102
+ * `isRunning` collapses `'hidden-paused'` and `'stopped'` into the same
103
+ * `false` — exactly the ambiguity that let a hidden-paused game report as
104
+ * healthy ("playing") through every consumer that only read `isRunning`/
105
+ * store-level play state. Read `liveness` wherever "is this game actually
106
+ * advancing" matters.
107
+ */
108
+ export type GameLoopLiveness = 'running' | 'hidden-paused' | 'stopped';
@@ -25,7 +25,7 @@
25
25
  * Game code reads `tuning.get()` inside its update loop (never cache fields
26
26
  * across frames) or `subscribe()`s; an edit to the `.data.json` — from
27
27
  * VS Code, an agent, or the future editor Data tab (W2) — flows through Vite
28
- * HMR into the running game with no remount. React worlds/tools get a
28
+ * HMR into the running game with no remount. React roots/tools get a
29
29
  * `useData` hook with W3's tool-hooks package (deferred with it: the engine
30
30
  * core stays React-free).
31
31
  */