@vgai/engine 0.5.0 → 0.5.2

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 (538) hide show
  1. package/package.json +1 -1
  2. package/schemas/engine-capabilities.json +4 -4
  3. package/schemas/vgai-game.schema.json +2 -2
  4. package/src/adapter/authoring.ts +42 -0
  5. package/src/adapter/index.ts +3 -0
  6. package/src/adapter/ingest/overlay-apply.ts +50 -6
  7. package/src/adapter/ingest/scene-capture.ts +51 -2
  8. package/src/adapter/vgai-scene-game-adapter.ts +24 -0
  9. package/src/manifest/schema.ts +7 -3
  10. package/src/runtime/create-runtime.ts +97 -0
  11. package/src/runtime/game.ts +11 -1
  12. package/src/scene/asset-ref-check.ts +1 -1
  13. package/src/scene/scene-loader.ts +2 -2
  14. package/src/scene/user-data.ts +18 -0
  15. package/src/world3d-react/behavior.tsx +19 -11
  16. package/src/world3d-react/r3f-adapter.tsx +8 -0
  17. package/dist/adapter/adapter-surface.d.ts +0 -30
  18. package/dist/adapter/adapter-surface.d.ts.map +0 -1
  19. package/dist/adapter/adapter-surface.js +0 -18
  20. package/dist/adapter/authoring.d.ts +0 -461
  21. package/dist/adapter/authoring.d.ts.map +0 -1
  22. package/dist/adapter/authoring.js +0 -22
  23. package/dist/adapter/colyseus-networking-adapter.d.ts +0 -43
  24. package/dist/adapter/colyseus-networking-adapter.d.ts.map +0 -1
  25. package/dist/adapter/colyseus-networking-adapter.js +0 -38
  26. package/dist/adapter/first-party-systems.d.ts +0 -38
  27. package/dist/adapter/first-party-systems.d.ts.map +0 -1
  28. package/dist/adapter/first-party-systems.js +0 -77
  29. package/dist/adapter/game-adapter.d.ts +0 -133
  30. package/dist/adapter/game-adapter.d.ts.map +0 -1
  31. package/dist/adapter/game-adapter.js +0 -10
  32. package/dist/adapter/host-context.d.ts +0 -71
  33. package/dist/adapter/host-context.d.ts.map +0 -1
  34. package/dist/adapter/host-context.js +0 -16
  35. package/dist/adapter/index.d.ts +0 -22
  36. package/dist/adapter/index.d.ts.map +0 -1
  37. package/dist/adapter/index.js +0 -15
  38. package/dist/adapter/ingest/game-contract.d.ts +0 -53
  39. package/dist/adapter/ingest/game-contract.d.ts.map +0 -1
  40. package/dist/adapter/ingest/game-contract.js +0 -30
  41. package/dist/adapter/ingest/overlay-applier.d.ts +0 -118
  42. package/dist/adapter/ingest/overlay-applier.d.ts.map +0 -1
  43. package/dist/adapter/ingest/overlay-applier.js +0 -132
  44. package/dist/adapter/ingest/overlay-apply.d.ts +0 -60
  45. package/dist/adapter/ingest/overlay-apply.d.ts.map +0 -1
  46. package/dist/adapter/ingest/overlay-apply.js +0 -101
  47. package/dist/adapter/ingest/overlay-file.d.ts +0 -79
  48. package/dist/adapter/ingest/overlay-file.d.ts.map +0 -1
  49. package/dist/adapter/ingest/overlay-file.js +0 -66
  50. package/dist/adapter/ingest/overlay-report.d.ts +0 -113
  51. package/dist/adapter/ingest/overlay-report.d.ts.map +0 -1
  52. package/dist/adapter/ingest/overlay-report.js +0 -103
  53. package/dist/adapter/ingest/scene-capture.d.ts +0 -97
  54. package/dist/adapter/ingest/scene-capture.d.ts.map +0 -1
  55. package/dist/adapter/ingest/scene-capture.js +0 -248
  56. package/dist/adapter/ingest/upstream-pin.d.ts +0 -47
  57. package/dist/adapter/ingest/upstream-pin.d.ts.map +0 -1
  58. package/dist/adapter/ingest/upstream-pin.js +0 -50
  59. package/dist/adapter/loop-gate-report.d.ts +0 -34
  60. package/dist/adapter/loop-gate-report.d.ts.map +0 -1
  61. package/dist/adapter/loop-gate-report.js +0 -37
  62. package/dist/adapter/rapier-physics-adapter.d.ts +0 -12
  63. package/dist/adapter/rapier-physics-adapter.d.ts.map +0 -1
  64. package/dist/adapter/rapier-physics-adapter.js +0 -46
  65. package/dist/adapter/system-adapter.d.ts +0 -206
  66. package/dist/adapter/system-adapter.d.ts.map +0 -1
  67. package/dist/adapter/system-adapter.js +0 -13
  68. package/dist/adapter/transform.d.ts +0 -18
  69. package/dist/adapter/transform.d.ts.map +0 -1
  70. package/dist/adapter/transform.js +0 -1
  71. package/dist/adapter/vgai-scene-game-adapter.d.ts +0 -108
  72. package/dist/adapter/vgai-scene-game-adapter.d.ts.map +0 -1
  73. package/dist/adapter/vgai-scene-game-adapter.js +0 -762
  74. package/dist/ai/navigation.d.ts +0 -64
  75. package/dist/ai/navigation.d.ts.map +0 -1
  76. package/dist/ai/navigation.js +0 -133
  77. package/dist/animation/anim-graph-types.d.ts +0 -25
  78. package/dist/animation/anim-graph-types.d.ts.map +0 -1
  79. package/dist/animation/anim-graph-types.js +0 -1
  80. package/dist/animation/animation-clock.d.ts +0 -213
  81. package/dist/animation/animation-clock.d.ts.map +0 -1
  82. package/dist/animation/animation-clock.js +0 -301
  83. package/dist/animation/blend-node.d.ts +0 -22
  84. package/dist/animation/blend-node.d.ts.map +0 -1
  85. package/dist/animation/blend-node.js +0 -97
  86. package/dist/animation/camera-ownership.d.ts +0 -250
  87. package/dist/animation/camera-ownership.d.ts.map +0 -1
  88. package/dist/animation/camera-ownership.js +0 -169
  89. package/dist/animation/cinematic-cues.d.ts +0 -200
  90. package/dist/animation/cinematic-cues.d.ts.map +0 -1
  91. package/dist/animation/cinematic-cues.js +0 -213
  92. package/dist/animation/clip-map.d.ts +0 -12
  93. package/dist/animation/clip-map.d.ts.map +0 -1
  94. package/dist/animation/clip-map.js +0 -37
  95. package/dist/animation/gsap-registration.d.ts +0 -119
  96. package/dist/animation/gsap-registration.d.ts.map +0 -1
  97. package/dist/animation/gsap-registration.js +0 -92
  98. package/dist/animation/theatre-clock-binding.d.ts +0 -89
  99. package/dist/animation/theatre-clock-binding.d.ts.map +0 -1
  100. package/dist/animation/theatre-clock-binding.js +0 -29
  101. package/dist/animation/theatre-director.d.ts +0 -242
  102. package/dist/animation/theatre-director.d.ts.map +0 -1
  103. package/dist/animation/theatre-director.js +0 -235
  104. package/dist/animation/theatre-object-binding.d.ts +0 -305
  105. package/dist/animation/theatre-object-binding.d.ts.map +0 -1
  106. package/dist/animation/theatre-object-binding.js +0 -351
  107. package/dist/animation/xstate-animation-binding.d.ts +0 -56
  108. package/dist/animation/xstate-animation-binding.d.ts.map +0 -1
  109. package/dist/animation/xstate-animation-binding.js +0 -331
  110. package/dist/animation/xstate-animation-meta.d.ts +0 -266
  111. package/dist/animation/xstate-animation-meta.d.ts.map +0 -1
  112. package/dist/animation/xstate-animation-meta.js +0 -252
  113. package/dist/assets.d.ts +0 -26
  114. package/dist/assets.d.ts.map +0 -1
  115. package/dist/assets.js +0 -55
  116. package/dist/audio/index.d.ts +0 -4
  117. package/dist/audio/index.d.ts.map +0 -1
  118. package/dist/audio/index.js +0 -24
  119. package/dist/audio/tone-clock-binding.d.ts +0 -75
  120. package/dist/audio/tone-clock-binding.d.ts.map +0 -1
  121. package/dist/audio/tone-clock-binding.js +0 -29
  122. package/dist/audio/tone-context.d.ts +0 -114
  123. package/dist/audio/tone-context.d.ts.map +0 -1
  124. package/dist/audio/tone-context.js +0 -39
  125. package/dist/audio/tone-offline-render.d.ts +0 -90
  126. package/dist/audio/tone-offline-render.d.ts.map +0 -1
  127. package/dist/audio/tone-offline-render.js +0 -56
  128. package/dist/audio/wav-encode.d.ts +0 -38
  129. package/dist/audio/wav-encode.d.ts.map +0 -1
  130. package/dist/audio/wav-encode.js +0 -97
  131. package/dist/character/cloth-sim.d.ts +0 -179
  132. package/dist/character/cloth-sim.d.ts.map +0 -1
  133. package/dist/character/cloth-sim.js +0 -334
  134. package/dist/character/spring-chain.d.ts +0 -135
  135. package/dist/character/spring-chain.d.ts.map +0 -1
  136. package/dist/character/spring-chain.js +0 -165
  137. package/dist/core/game-loop.d.ts +0 -42
  138. package/dist/core/game-loop.d.ts.map +0 -1
  139. package/dist/core/game-loop.js +0 -167
  140. package/dist/core/seeded-random.d.ts +0 -69
  141. package/dist/core/seeded-random.d.ts.map +0 -1
  142. package/dist/core/seeded-random.js +0 -121
  143. package/dist/core/system-runner.d.ts +0 -120
  144. package/dist/core/system-runner.d.ts.map +0 -1
  145. package/dist/core/system-runner.js +0 -297
  146. package/dist/core/types.d.ts +0 -92
  147. package/dist/core/types.d.ts.map +0 -1
  148. package/dist/core/types.js +0 -25
  149. package/dist/data/data-asset.d.ts +0 -101
  150. package/dist/data/data-asset.d.ts.map +0 -1
  151. package/dist/data/data-asset.js +0 -122
  152. package/dist/data/data-check-core.d.ts +0 -110
  153. package/dist/data/data-check-core.d.ts.map +0 -1
  154. package/dist/data/data-check-core.js +0 -178
  155. package/dist/data/data-ref.d.ts +0 -79
  156. package/dist/data/data-ref.d.ts.map +0 -1
  157. package/dist/data/data-ref.js +0 -130
  158. package/dist/data/vite-plugin-data.d.ts +0 -79
  159. package/dist/data/vite-plugin-data.d.ts.map +0 -1
  160. package/dist/data/vite-plugin-data.js +0 -217
  161. package/dist/dev/console-bridge.d.ts +0 -22
  162. package/dist/dev/console-bridge.d.ts.map +0 -1
  163. package/dist/dev/console-bridge.js +0 -71
  164. package/dist/dev/debug-draw.d.ts +0 -24
  165. package/dist/dev/debug-draw.d.ts.map +0 -1
  166. package/dist/dev/debug-draw.js +0 -73
  167. package/dist/dev/logger.d.ts +0 -30
  168. package/dist/dev/logger.d.ts.map +0 -1
  169. package/dist/dev/logger.js +0 -90
  170. package/dist/dev/performance-profiler.d.ts +0 -60
  171. package/dist/dev/performance-profiler.d.ts.map +0 -1
  172. package/dist/dev/performance-profiler.js +0 -181
  173. package/dist/dev/webgl-gpu-timer.d.ts +0 -8
  174. package/dist/dev/webgl-gpu-timer.d.ts.map +0 -1
  175. package/dist/dev/webgl-gpu-timer.js +0 -52
  176. package/dist/ecs/component-manager.d.ts +0 -205
  177. package/dist/ecs/component-manager.d.ts.map +0 -1
  178. package/dist/ecs/component-manager.js +0 -706
  179. package/dist/ecs/game-component.d.ts +0 -146
  180. package/dist/ecs/game-component.d.ts.map +0 -1
  181. package/dist/ecs/game-component.js +0 -141
  182. package/dist/ecs/hmr-swap-report.d.ts +0 -53
  183. package/dist/ecs/hmr-swap-report.d.ts.map +0 -1
  184. package/dist/ecs/hmr-swap-report.js +0 -55
  185. package/dist/humanoid/bake.operation.d.ts +0 -119
  186. package/dist/humanoid/bake.operation.d.ts.map +0 -1
  187. package/dist/humanoid/bake.operation.js +0 -228
  188. package/dist/humanoid/body.d.ts +0 -101
  189. package/dist/humanoid/body.d.ts.map +0 -1
  190. package/dist/humanoid/body.js +0 -530
  191. package/dist/humanoid/clips.d.ts +0 -62
  192. package/dist/humanoid/clips.d.ts.map +0 -1
  193. package/dist/humanoid/clips.js +0 -121
  194. package/dist/humanoid/compose.d.ts +0 -75
  195. package/dist/humanoid/compose.d.ts.map +0 -1
  196. package/dist/humanoid/compose.js +0 -176
  197. package/dist/humanoid/generate.d.ts +0 -92
  198. package/dist/humanoid/generate.d.ts.map +0 -1
  199. package/dist/humanoid/generate.js +0 -124
  200. package/dist/humanoid/index.d.ts +0 -24
  201. package/dist/humanoid/index.d.ts.map +0 -1
  202. package/dist/humanoid/index.js +0 -18
  203. package/dist/humanoid/schema.d.ts +0 -35
  204. package/dist/humanoid/schema.d.ts.map +0 -1
  205. package/dist/humanoid/schema.js +0 -87
  206. package/dist/humanoid/skeleton.d.ts +0 -103
  207. package/dist/humanoid/skeleton.d.ts.map +0 -1
  208. package/dist/humanoid/skeleton.js +0 -289
  209. package/dist/index.d.ts +0 -37
  210. package/dist/index.d.ts.map +0 -1
  211. package/dist/index.js +0 -30
  212. package/dist/input/input-manager.d.ts +0 -945
  213. package/dist/input/input-manager.d.ts.map +0 -1
  214. package/dist/input/input-manager.js +0 -2207
  215. package/dist/input/input-types.d.ts +0 -174
  216. package/dist/input/input-types.d.ts.map +0 -1
  217. package/dist/input/input-types.js +0 -1
  218. package/dist/input/prompt-labels.d.ts +0 -21
  219. package/dist/input/prompt-labels.d.ts.map +0 -1
  220. package/dist/input/prompt-labels.js +0 -121
  221. package/dist/input/rebind-controller.d.ts +0 -59
  222. package/dist/input/rebind-controller.d.ts.map +0 -1
  223. package/dist/input/rebind-controller.js +0 -85
  224. package/dist/input/schema.d.ts +0 -16
  225. package/dist/input/schema.d.ts.map +0 -1
  226. package/dist/input/schema.js +0 -228
  227. package/dist/loader.d.ts +0 -43
  228. package/dist/loader.d.ts.map +0 -1
  229. package/dist/loader.js +0 -59
  230. package/dist/manifest/index.d.ts +0 -5
  231. package/dist/manifest/index.d.ts.map +0 -1
  232. package/dist/manifest/index.js +0 -8
  233. package/dist/manifest/load-file.d.ts +0 -4
  234. package/dist/manifest/load-file.d.ts.map +0 -1
  235. package/dist/manifest/load-file.js +0 -14
  236. package/dist/manifest/load.d.ts +0 -144
  237. package/dist/manifest/load.d.ts.map +0 -1
  238. package/dist/manifest/load.js +0 -273
  239. package/dist/manifest/schema.d.ts +0 -230
  240. package/dist/manifest/schema.d.ts.map +0 -1
  241. package/dist/manifest/schema.js +0 -394
  242. package/dist/physics/collision-system.d.ts +0 -41
  243. package/dist/physics/collision-system.d.ts.map +0 -1
  244. package/dist/physics/collision-system.js +0 -64
  245. package/dist/physics/physics-registry.d.ts +0 -45
  246. package/dist/physics/physics-registry.d.ts.map +0 -1
  247. package/dist/physics/physics-registry.js +0 -65
  248. package/dist/physics/transform-writer.d.ts +0 -16
  249. package/dist/physics/transform-writer.d.ts.map +0 -1
  250. package/dist/physics/transform-writer.js +0 -38
  251. package/dist/physics/trigger-dispatch.d.ts +0 -24
  252. package/dist/physics/trigger-dispatch.d.ts.map +0 -1
  253. package/dist/physics/trigger-dispatch.js +0 -80
  254. package/dist/react/game-state.d.ts +0 -156
  255. package/dist/react/game-state.d.ts.map +0 -1
  256. package/dist/react/game-state.js +0 -222
  257. package/dist/react/root-adapter.d.ts +0 -11
  258. package/dist/react/root-adapter.d.ts.map +0 -1
  259. package/dist/react/root-adapter.js +0 -41
  260. package/dist/react/unmanaged-root-detector.d.ts +0 -12
  261. package/dist/react/unmanaged-root-detector.d.ts.map +0 -1
  262. package/dist/react/unmanaged-root-detector.js +0 -45
  263. package/dist/react/use-data.d.ts +0 -60
  264. package/dist/react/use-data.d.ts.map +0 -1
  265. package/dist/react/use-data.js +0 -101
  266. package/dist/react/use-selection.d.ts +0 -75
  267. package/dist/react/use-selection.d.ts.map +0 -1
  268. package/dist/react/use-selection.js +0 -90
  269. package/dist/render/auto-batcher.d.ts +0 -34
  270. package/dist/render/auto-batcher.d.ts.map +0 -1
  271. package/dist/render/auto-batcher.js +0 -140
  272. package/dist/render/render-batch-system.d.ts +0 -33
  273. package/dist/render/render-batch-system.d.ts.map +0 -1
  274. package/dist/render/render-batch-system.js +0 -230
  275. package/dist/render/render-features.d.ts +0 -47
  276. package/dist/render/render-features.d.ts.map +0 -1
  277. package/dist/render/render-features.js +0 -105
  278. package/dist/render/render-settings.d.ts +0 -36
  279. package/dist/render/render-settings.d.ts.map +0 -1
  280. package/dist/render/render-settings.js +0 -51
  281. package/dist/runtime/create-runtime.d.ts +0 -249
  282. package/dist/runtime/create-runtime.d.ts.map +0 -1
  283. package/dist/runtime/create-runtime.js +0 -641
  284. package/dist/runtime/debug-bridge.d.ts +0 -257
  285. package/dist/runtime/debug-bridge.d.ts.map +0 -1
  286. package/dist/runtime/debug-bridge.js +0 -224
  287. package/dist/runtime/debug-registry.d.ts +0 -288
  288. package/dist/runtime/debug-registry.d.ts.map +0 -1
  289. package/dist/runtime/debug-registry.js +0 -513
  290. package/dist/runtime/frame-selector-cache.d.ts +0 -41
  291. package/dist/runtime/frame-selector-cache.d.ts.map +0 -1
  292. package/dist/runtime/frame-selector-cache.js +0 -65
  293. package/dist/runtime/game.d.ts +0 -496
  294. package/dist/runtime/game.d.ts.map +0 -1
  295. package/dist/runtime/game.js +0 -741
  296. package/dist/runtime/gameplay-rng-trap.d.ts +0 -48
  297. package/dist/runtime/gameplay-rng-trap.d.ts.map +0 -1
  298. package/dist/runtime/gameplay-rng-trap.js +0 -96
  299. package/dist/runtime/input-router.d.ts +0 -78
  300. package/dist/runtime/input-router.d.ts.map +0 -1
  301. package/dist/runtime/input-router.js +0 -171
  302. package/dist/runtime/mount-game.d.ts +0 -129
  303. package/dist/runtime/mount-game.d.ts.map +0 -1
  304. package/dist/runtime/mount-game.js +0 -152
  305. package/dist/runtime/mount-manifest.d.ts +0 -173
  306. package/dist/runtime/mount-manifest.d.ts.map +0 -1
  307. package/dist/runtime/mount-manifest.js +0 -297
  308. package/dist/runtime/render-audio-control.d.ts +0 -110
  309. package/dist/runtime/render-audio-control.d.ts.map +0 -1
  310. package/dist/runtime/render-audio-control.js +0 -111
  311. package/dist/runtime/render-control.d.ts +0 -291
  312. package/dist/runtime/render-control.d.ts.map +0 -1
  313. package/dist/runtime/render-control.js +0 -264
  314. package/dist/runtime/render-seed.d.ts +0 -57
  315. package/dist/runtime/render-seed.d.ts.map +0 -1
  316. package/dist/runtime/render-seed.js +0 -74
  317. package/dist/runtime/state-bridge.d.ts +0 -61
  318. package/dist/runtime/state-bridge.d.ts.map +0 -1
  319. package/dist/runtime/state-bridge.js +0 -63
  320. package/dist/runtime/types.d.ts +0 -253
  321. package/dist/runtime/types.d.ts.map +0 -1
  322. package/dist/runtime/types.js +0 -1
  323. package/dist/scene/asset-loaders.d.ts +0 -79
  324. package/dist/scene/asset-loaders.d.ts.map +0 -1
  325. package/dist/scene/asset-loaders.js +0 -141
  326. package/dist/scene/asset-paths.d.ts +0 -11
  327. package/dist/scene/asset-paths.d.ts.map +0 -1
  328. package/dist/scene/asset-paths.js +0 -108
  329. package/dist/scene/asset-ref-check.d.ts +0 -88
  330. package/dist/scene/asset-ref-check.d.ts.map +0 -1
  331. package/dist/scene/asset-ref-check.js +0 -214
  332. package/dist/scene/asset-registry.d.ts +0 -38
  333. package/dist/scene/asset-registry.d.ts.map +0 -1
  334. package/dist/scene/asset-registry.js +0 -66
  335. package/dist/scene/collider-dimensions.d.ts +0 -63
  336. package/dist/scene/collider-dimensions.d.ts.map +0 -1
  337. package/dist/scene/collider-dimensions.js +0 -98
  338. package/dist/scene/component-registry.d.ts +0 -22
  339. package/dist/scene/component-registry.d.ts.map +0 -1
  340. package/dist/scene/component-registry.js +0 -37
  341. package/dist/scene/defaults.d.ts +0 -252
  342. package/dist/scene/defaults.d.ts.map +0 -1
  343. package/dist/scene/defaults.js +0 -151
  344. package/dist/scene/geometries/index.d.ts +0 -8
  345. package/dist/scene/geometries/index.d.ts.map +0 -1
  346. package/dist/scene/geometries/index.js +0 -7
  347. package/dist/scene/geometries/terrain.d.ts +0 -2
  348. package/dist/scene/geometries/terrain.d.ts.map +0 -1
  349. package/dist/scene/geometries/terrain.js +0 -37
  350. package/dist/scene/geometry-registry.d.ts +0 -24
  351. package/dist/scene/geometry-registry.d.ts.map +0 -1
  352. package/dist/scene/geometry-registry.js +0 -20
  353. package/dist/scene/instance-registry.d.ts +0 -36
  354. package/dist/scene/instance-registry.d.ts.map +0 -1
  355. package/dist/scene/instance-registry.js +0 -55
  356. package/dist/scene/instancers/grid.d.ts +0 -2
  357. package/dist/scene/instancers/grid.d.ts.map +0 -1
  358. package/dist/scene/instancers/grid.js +0 -36
  359. package/dist/scene/instancers/index.d.ts +0 -8
  360. package/dist/scene/instancers/index.d.ts.map +0 -1
  361. package/dist/scene/instancers/index.js +0 -7
  362. package/dist/scene/light-camera-factory.d.ts +0 -23
  363. package/dist/scene/light-camera-factory.d.ts.map +0 -1
  364. package/dist/scene/light-camera-factory.js +0 -68
  365. package/dist/scene/material-factory.d.ts +0 -17
  366. package/dist/scene/material-factory.d.ts.map +0 -1
  367. package/dist/scene/material-factory.js +0 -197
  368. package/dist/scene/material-registry.d.ts +0 -40
  369. package/dist/scene/material-registry.d.ts.map +0 -1
  370. package/dist/scene/material-registry.js +0 -44
  371. package/dist/scene/materials/index.d.ts +0 -8
  372. package/dist/scene/materials/index.d.ts.map +0 -1
  373. package/dist/scene/materials/index.js +0 -7
  374. package/dist/scene/materials/water.d.ts +0 -2
  375. package/dist/scene/materials/water.d.ts.map +0 -1
  376. package/dist/scene/materials/water.js +0 -54
  377. package/dist/scene/parse.d.ts +0 -23
  378. package/dist/scene/parse.d.ts.map +0 -1
  379. package/dist/scene/parse.js +0 -179
  380. package/dist/scene/particles-factory.d.ts +0 -33
  381. package/dist/scene/particles-factory.d.ts.map +0 -1
  382. package/dist/scene/particles-factory.js +0 -282
  383. package/dist/scene/scene-apply.d.ts +0 -109
  384. package/dist/scene/scene-apply.d.ts.map +0 -1
  385. package/dist/scene/scene-apply.js +0 -370
  386. package/dist/scene/scene-diff-schema.d.ts +0 -285
  387. package/dist/scene/scene-diff-schema.d.ts.map +0 -1
  388. package/dist/scene/scene-diff-schema.js +0 -92
  389. package/dist/scene/scene-diff-types.d.ts +0 -40
  390. package/dist/scene/scene-diff-types.d.ts.map +0 -1
  391. package/dist/scene/scene-diff-types.js +0 -14
  392. package/dist/scene/scene-loader.d.ts +0 -167
  393. package/dist/scene/scene-loader.d.ts.map +0 -1
  394. package/dist/scene/scene-loader.js +0 -1211
  395. package/dist/scene/scene-query.d.ts +0 -11
  396. package/dist/scene/scene-query.d.ts.map +0 -1
  397. package/dist/scene/scene-query.js +0 -63
  398. package/dist/scene/scene-types.d.ts +0 -10
  399. package/dist/scene/scene-types.d.ts.map +0 -1
  400. package/dist/scene/scene-types.js +0 -8
  401. package/dist/scene/scene-version.d.ts +0 -26
  402. package/dist/scene/scene-version.d.ts.map +0 -1
  403. package/dist/scene/scene-version.js +0 -34
  404. package/dist/scene/schema/animation.d.ts +0 -8
  405. package/dist/scene/schema/animation.d.ts.map +0 -1
  406. package/dist/scene/schema/animation.js +0 -42
  407. package/dist/scene/schema/audio.d.ts +0 -13
  408. package/dist/scene/schema/audio.d.ts.map +0 -1
  409. package/dist/scene/schema/audio.js +0 -22
  410. package/dist/scene/schema/camera.d.ts +0 -28
  411. package/dist/scene/schema/camera.d.ts.map +0 -1
  412. package/dist/scene/schema/camera.js +0 -17
  413. package/dist/scene/schema/collider.d.ts +0 -40
  414. package/dist/scene/schema/collider.d.ts.map +0 -1
  415. package/dist/scene/schema/collider.js +0 -55
  416. package/dist/scene/schema/entity-ref.d.ts +0 -31
  417. package/dist/scene/schema/entity-ref.d.ts.map +0 -1
  418. package/dist/scene/schema/entity-ref.js +0 -61
  419. package/dist/scene/schema/entity.d.ts +0 -1036
  420. package/dist/scene/schema/entity.d.ts.map +0 -1
  421. package/dist/scene/schema/entity.js +0 -153
  422. package/dist/scene/schema/environment.d.ts +0 -425
  423. package/dist/scene/schema/environment.d.ts.map +0 -1
  424. package/dist/scene/schema/environment.js +0 -357
  425. package/dist/scene/schema/index.d.ts +0 -37
  426. package/dist/scene/schema/index.d.ts.map +0 -1
  427. package/dist/scene/schema/index.js +0 -19
  428. package/dist/scene/schema/instances.d.ts +0 -14
  429. package/dist/scene/schema/instances.d.ts.map +0 -1
  430. package/dist/scene/schema/instances.js +0 -28
  431. package/dist/scene/schema/joint.d.ts +0 -23
  432. package/dist/scene/schema/joint.d.ts.map +0 -1
  433. package/dist/scene/schema/joint.js +0 -23
  434. package/dist/scene/schema/light.d.ts +0 -38
  435. package/dist/scene/schema/light.d.ts.map +0 -1
  436. package/dist/scene/schema/light.js +0 -26
  437. package/dist/scene/schema/material.d.ts +0 -199
  438. package/dist/scene/schema/material.d.ts.map +0 -1
  439. package/dist/scene/schema/material.js +0 -103
  440. package/dist/scene/schema/mesh.d.ts +0 -48
  441. package/dist/scene/schema/mesh.d.ts.map +0 -1
  442. package/dist/scene/schema/mesh.js +0 -82
  443. package/dist/scene/schema/particles.d.ts +0 -2178
  444. package/dist/scene/schema/particles.d.ts.map +0 -1
  445. package/dist/scene/schema/particles.js +0 -321
  446. package/dist/scene/schema/physics.d.ts +0 -56
  447. package/dist/scene/schema/physics.d.ts.map +0 -1
  448. package/dist/scene/schema/physics.js +0 -37
  449. package/dist/scene/schema/scene-file.d.ts +0 -303
  450. package/dist/scene/schema/scene-file.d.ts.map +0 -1
  451. package/dist/scene/schema/scene-file.js +0 -256
  452. package/dist/scene/schema/shadow.d.ts +0 -28
  453. package/dist/scene/schema/shadow.d.ts.map +0 -1
  454. package/dist/scene/schema/shadow.js +0 -20
  455. package/dist/scene/schema/spline.d.ts +0 -25
  456. package/dist/scene/schema/spline.d.ts.map +0 -1
  457. package/dist/scene/schema/spline.js +0 -15
  458. package/dist/scene/schema/tuples.d.ts +0 -12
  459. package/dist/scene/schema/tuples.d.ts.map +0 -1
  460. package/dist/scene/schema/tuples.js +0 -14
  461. package/dist/scene/user-data.d.ts +0 -185
  462. package/dist/scene/user-data.d.ts.map +0 -1
  463. package/dist/scene/user-data.js +0 -144
  464. package/dist/setup/setup-audio.d.ts +0 -20
  465. package/dist/setup/setup-audio.d.ts.map +0 -1
  466. package/dist/setup/setup-audio.js +0 -41
  467. package/dist/setup/setup-particles.d.ts +0 -17
  468. package/dist/setup/setup-particles.d.ts.map +0 -1
  469. package/dist/setup/setup-particles.js +0 -16
  470. package/dist/setup/setup-physics.d.ts +0 -25
  471. package/dist/setup/setup-physics.d.ts.map +0 -1
  472. package/dist/setup/setup-physics.js +0 -51
  473. package/dist/setup/setup-renderer.d.ts +0 -69
  474. package/dist/setup/setup-renderer.d.ts.map +0 -1
  475. package/dist/setup/setup-renderer.js +0 -363
  476. package/dist/tools/define-tool.d.ts +0 -130
  477. package/dist/tools/define-tool.d.ts.map +0 -1
  478. package/dist/tools/define-tool.js +0 -96
  479. package/dist/world2d/authoring-2d.d.ts +0 -80
  480. package/dist/world2d/authoring-2d.d.ts.map +0 -1
  481. package/dist/world2d/authoring-2d.js +0 -195
  482. package/dist/world2d/capture-to-scene2d.d.ts +0 -5
  483. package/dist/world2d/capture-to-scene2d.d.ts.map +0 -1
  484. package/dist/world2d/capture-to-scene2d.js +0 -49
  485. package/dist/world2d/collision-2d.d.ts +0 -52
  486. package/dist/world2d/collision-2d.d.ts.map +0 -1
  487. package/dist/world2d/collision-2d.js +0 -70
  488. package/dist/world2d/components-2d.d.ts +0 -47
  489. package/dist/world2d/components-2d.d.ts.map +0 -1
  490. package/dist/world2d/components-2d.js +0 -77
  491. package/dist/world2d/index.d.ts +0 -34
  492. package/dist/world2d/index.d.ts.map +0 -1
  493. package/dist/world2d/index.js +0 -32
  494. package/dist/world2d/ingest-iframe-2d.d.ts +0 -82
  495. package/dist/world2d/ingest-iframe-2d.d.ts.map +0 -1
  496. package/dist/world2d/ingest-iframe-2d.js +0 -166
  497. package/dist/world2d/ingest2d.d.ts +0 -97
  498. package/dist/world2d/ingest2d.d.ts.map +0 -1
  499. package/dist/world2d/ingest2d.js +0 -40
  500. package/dist/world2d/physics2d-registry.d.ts +0 -24
  501. package/dist/world2d/physics2d-registry.d.ts.map +0 -1
  502. package/dist/world2d/physics2d-registry.js +0 -38
  503. package/dist/world2d/pixi-game-adapter.d.ts +0 -107
  504. package/dist/world2d/pixi-game-adapter.d.ts.map +0 -1
  505. package/dist/world2d/pixi-game-adapter.js +0 -210
  506. package/dist/world2d/pixi-surface.d.ts +0 -39
  507. package/dist/world2d/pixi-surface.d.ts.map +0 -1
  508. package/dist/world2d/pixi-surface.js +0 -47
  509. package/dist/world2d/scene-capture-2d.d.ts +0 -43
  510. package/dist/world2d/scene-capture-2d.d.ts.map +0 -1
  511. package/dist/world2d/scene-capture-2d.js +0 -92
  512. package/dist/world2d/scene2d-loader.d.ts +0 -27
  513. package/dist/world2d/scene2d-loader.d.ts.map +0 -1
  514. package/dist/world2d/scene2d-loader.js +0 -275
  515. package/dist/world2d/schema/entity2d.d.ts +0 -102
  516. package/dist/world2d/schema/entity2d.d.ts.map +0 -1
  517. package/dist/world2d/schema/entity2d.js +0 -119
  518. package/dist/world2d/schema/physics2d.d.ts +0 -46
  519. package/dist/world2d/schema/physics2d.d.ts.map +0 -1
  520. package/dist/world2d/schema/physics2d.js +0 -45
  521. package/dist/world2d/schema/sprite.d.ts +0 -42
  522. package/dist/world2d/schema/sprite.d.ts.map +0 -1
  523. package/dist/world2d/schema/sprite.js +0 -63
  524. package/dist/world2d/schema/tilemap.d.ts +0 -15
  525. package/dist/world2d/schema/tilemap.d.ts.map +0 -1
  526. package/dist/world2d/schema/tilemap.js +0 -19
  527. package/dist/world2d/schema/tuples2d.d.ts +0 -20
  528. package/dist/world2d/schema/tuples2d.d.ts.map +0 -1
  529. package/dist/world2d/schema/tuples2d.js +0 -19
  530. package/dist/world2d/system-adapters-2d.d.ts +0 -16
  531. package/dist/world2d/system-adapters-2d.d.ts.map +0 -1
  532. package/dist/world2d/system-adapters-2d.js +0 -37
  533. package/dist/world2d/transform-writer-2d.d.ts +0 -16
  534. package/dist/world2d/transform-writer-2d.d.ts.map +0 -1
  535. package/dist/world2d/transform-writer-2d.js +0 -22
  536. package/dist/world2d/types.d.ts +0 -59
  537. package/dist/world2d/types.d.ts.map +0 -1
  538. package/dist/world2d/types.js +0 -1
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@vgai/engine",
3
3
  "author": "Volter AI, Inc.",
4
4
  "license": "Apache-2.0",
5
- "version": "0.5.0",
5
+ "version": "0.5.2",
6
6
  "description": "Readable TypeScript game engine and universal host for Three.js, PixiJS, and React games.",
7
7
  "keywords": ["game-engine", "three.js", "pixijs", "react", "ai-agents"],
8
8
  "type": "module",
@@ -16,7 +16,7 @@
16
16
  ],
17
17
  "notes": "Grepped packages/engine/src for ccd/CCD/setCcdEnabled: zero matches, 2D and 3D. No call ever sets a ccd flag. Workaround: keep dynamic-body per-step travel small relative to the thinnest collider, or add intermediate colliders.",
18
18
  "evidenceHashes": {
19
- "packages/engine/src/scene/scene-loader.ts": "53c8f6d756363c0638c576b7e5a3d425fe0e77ce929dac9d462b8999b7000db8",
19
+ "packages/engine/src/scene/scene-loader.ts": "07fb9086e95959fd84e71ccc57581bb00d1f6eca1137853492b6a569755e8db5",
20
20
  "packages/engine/src/world2d/scene2d-loader.ts": "f0f74eefd9cce16bbe7826225a52f2b0407ab141641dcdd370bcff1d091bce69"
21
21
  }
22
22
  },
@@ -32,7 +32,7 @@
32
32
  "notes": "Grepped for maxBodies/MAX_ENTITIES/entity limit/body limit/entityCount: no matches. No invented numbers — nothing caps entity/body count and no perf test here establishes a safe ceiling. Treat large counts (hundreds+ dynamic bodies) as unvalidated, not 'supported at scale'.",
33
33
  "evidenceHashes": {
34
34
  "packages/engine/src/setup/setup-physics.ts": "0540c502f420018b24dac8b77c486f30f905f0a05b02ead7ed3899e5aa612ee5",
35
- "packages/engine/src/scene/scene-loader.ts": "53c8f6d756363c0638c576b7e5a3d425fe0e77ce929dac9d462b8999b7000db8"
35
+ "packages/engine/src/scene/scene-loader.ts": "07fb9086e95959fd84e71ccc57581bb00d1f6eca1137853492b6a569755e8db5"
36
36
  }
37
37
  },
38
38
  {
@@ -91,7 +91,7 @@
91
91
  ],
92
92
  "notes": "Track P shipped 2026-07-03; proven: bubbo-bubbo/puzzling-potions/flappy-pixi. Tiers/limits: docs/CAPABILITY-TIERS.md, docs/PIXI-INGEST-LANDING-DESIGN.md.",
93
93
  "evidenceHashes": {
94
- "docs/BACKBONE-TASKS.md": "206ce2818efcf2b12876160d7366d41a6afec6b545b025a98a3fcf2915cdf824"
94
+ "docs/BACKBONE-TASKS.md": "4d58398e22ed0261ec246668cac115ec9cfdde9881920070e95ee642f1323eb1"
95
95
  }
96
96
  },
97
97
  {
@@ -104,7 +104,7 @@
104
104
  ],
105
105
  "notes": "Scope is native-DOM React ONLY (D-N1): R3F/@pixi/react games are canvas games and ride the three.js/pixi seams instead. Generality limit: ONE proven game (react-rpg — menu-to-dungeon e2e under React 19; both game-2 candidates rejected on evidence, R-N7). Game deps must be host-installed root deps (D-N6). Open R-N6: vendored react games' raw window listeners bypass the play-mode input gate. Design: docs/REACT-INGEST-LANDING-DESIGN.md.",
106
106
  "evidenceHashes": {
107
- "docs/BACKBONE-TASKS.md": "206ce2818efcf2b12876160d7366d41a6afec6b545b025a98a3fcf2915cdf824"
107
+ "docs/BACKBONE-TASKS.md": "4d58398e22ed0261ec246668cac115ec9cfdde9881920070e95ee642f1323eb1"
108
108
  }
109
109
  },
110
110
  {
@@ -446,11 +446,11 @@
446
446
  "description": "Short kebab-case feature tags used as gallery/catalog filter facets"
447
447
  },
448
448
  "thumbnail": {
449
- "description": "Optional thumbnail image reference (engine-repo-relative, e.g. docs/example-screenshots/<id>/<shot>.png; generated per FT-11, never hand-made)",
449
+ "description": "Optional thumbnail image reference, PROJECT-relative (canonical path: learn/thumbnail.png the §3 schema path). Generated per FT-11 by the deterministic render tooling (scripts/generate-learn-thumbnails.ts), never hand-made",
450
450
  "type": "string"
451
451
  },
452
452
  "preview": {
453
- "description": "Optional short preview video/loop reference (generated per FT-11)",
453
+ "description": "Optional short preview video/loop reference, PROJECT-relative (canonical path: learn/preview.mp4; generated per FT-11)",
454
454
  "type": "string"
455
455
  },
456
456
  "lesson": {
@@ -89,6 +89,7 @@ export type EditorNodeRole =
89
89
  | 'document'
90
90
  | 'story'
91
91
  | 'component'
92
+ | 'instance'
92
93
  | 'element'
93
94
  | 'entity'
94
95
  | 'boundary';
@@ -153,6 +154,34 @@ export interface HierarchyProvider {
153
154
  export interface SelectionProvider {
154
155
  get(): string[];
155
156
  set(ids: string[]): void;
157
+ /** Resolve a raw native/render hit to the semantic authoring subject. The
158
+ * shell must not infer ownership from ancestry or substrate-specific kinds.
159
+ * Older adapters may omit this; raw ids then remain the selection ids. */
160
+ resolve?(
161
+ rawId: string,
162
+ options?: { intent?: 'normal' | 'deep'; scopeId?: string | null },
163
+ ): SelectionResolution | null;
164
+ }
165
+
166
+ /** Adapter-owned result of semantic selection resolution. Separate ids keep
167
+ * selection, inspection, and manipulation from becoming accidental aliases. */
168
+ export interface SelectionResolution {
169
+ /** Hierarchy selection and default Inspector subject. */
170
+ id: string;
171
+ /** Optional alternate Inspector subject. Defaults to {@link id}. */
172
+ inspectorId?: string;
173
+ /** Native transform subject. `null` means the selection is non-spatial. */
174
+ transformId?: string | null;
175
+ /** Nearest closed component/prefab/assembly boundary, when any. */
176
+ boundaryId?: string | null;
177
+ }
178
+
179
+ export type TransformChannel = 'position' | 'rotation' | 'scale';
180
+
181
+ export interface TransformEditability {
182
+ writable: boolean;
183
+ /** Human-facing explanation for a disabled gizmo/field. */
184
+ reason?: string;
156
185
  }
157
186
 
158
187
  export interface TransformProvider {
@@ -160,6 +189,10 @@ export interface TransformProvider {
160
189
  dimensions?(id: string): '2d' | '3d' | null;
161
190
  get(id: string): Transform;
162
191
  owner(id: string): TransformOwner;
192
+ /** Pre-gesture persistence check. Omitted means the adapter's transform
193
+ * capability applies uniformly. A false result must suppress manipulation
194
+ * before the live object is moved, never discover refusal at drag end. */
195
+ editability?(id: string, channel: TransformChannel): TransformEditability;
163
196
  /** Pause whatever controller fights the gizmo (physics/script) for editing. */
164
197
  beginEdit(id: string): void;
165
198
  apply(id: string, t: Transform): void;
@@ -237,6 +270,15 @@ export interface StructureProvider {
237
270
  * `wrap`). Absent ⇒ the shell's context menu shows no Unwrap item for this
238
271
  * adapter. */
239
272
  unwrap?(id: string): void;
273
+ /** Group sibling authoring objects beneath one new spatial parent. The
274
+ * adapter owns transform preservation, persistence, and undo semantics.
275
+ * Returns the new group id, or null when the selection cannot be grouped. */
276
+ group?(ids: readonly string[]): string | null;
277
+ /** Dissolve a plain spatial group and return the ids of its promoted
278
+ * children. Absent means this adapter has no lossless ungroup operation. */
279
+ ungroup?(id: string): string[];
280
+ /** Preflight for an Ungroup affordance. Omit when ungroup is never offered. */
281
+ canUngroup?(id: string): boolean;
240
282
  /**
241
283
  * D4.R2 (spec27 §6 D4 reopen) — remove every id in `ids` as ONE undoable
242
284
  * op (a single Ctrl+Z restores the whole batch), instead of the caller
@@ -39,6 +39,7 @@ export type {
39
39
  RootGroup,
40
40
  RootGroupsProvider,
41
41
  SelectionProvider,
42
+ SelectionResolution,
42
43
  SpriteAtlas2D,
43
44
  SpriteAtlasFrame2D,
44
45
  SpriteFrames2DDescriptor,
@@ -50,6 +51,8 @@ export type {
50
51
  TilemapCell,
51
52
  TilemapEditGrid,
52
53
  TilemapEditProvider,
54
+ TransformChannel,
55
+ TransformEditability,
53
56
  TransformProvider,
54
57
  } from './authoring';
55
58
  export {
@@ -26,6 +26,24 @@ import type { IngestOverlay } from './overlay-file';
26
26
  /** Fixed id for the captured render camera (not a scene child, so no structural path). */
27
27
  export const CAMERA_ID = 'ingest:camera';
28
28
 
29
+ /**
30
+ * The editor parks its OWN objects — grid, its two lights, the particle
31
+ * BatchedRenderer, every TransformControls/gizmo helper — on layer 31
32
+ * (`packages/editor/src/editor-layers.ts`'s `EDITOR_LAYER`) so the game camera
33
+ * never sees them. In ingest mode those objects are added to the GAME'S OWN
34
+ * scene, which is the same tree this walk indexes.
35
+ */
36
+ const EDITOR_ONLY_LAYER_MASK = 1 << 31;
37
+
38
+ /**
39
+ * Editor furniture, not game content. Excluded from the walk entirely — see
40
+ * {@link assignStructuralIds} for why that exclusion is what makes the id
41
+ * scheme's central promise true.
42
+ */
43
+ function isEditorOnly(o: THREE.Object3D): boolean {
44
+ return (o.layers.mask & EDITOR_ONLY_LAYER_MASK) !== 0;
45
+ }
46
+
29
47
  /** The single (non-array) standard-ish material of a mesh, if it has a color. */
30
48
  export function colorMaterialOf(o: THREE.Object3D): THREE.MeshStandardMaterial | null {
31
49
  const mat = (o as THREE.Mesh).material;
@@ -49,6 +67,28 @@ export interface StructuralIdWalk {
49
67
  * (position + type + name) — the prerequisite for overlay reapplication
50
68
  * across an independent fresh walk (editor reload, or a shipped build that
51
69
  * never ran the editor at all).
70
+ *
71
+ * EDITOR FURNITURE IS EXCLUDED, and that exclusion is load-bearing rather than
72
+ * cosmetic. In ingest mode the editor adds its grid, lights, particle
73
+ * BatchedRenderer and TransformControls gizmo to the GAME'S OWN scene, and
74
+ * they arrive asynchronously — some before the walk, some after. Indexing them
75
+ * made a game object's path depend on how much editor furniture happened to be
76
+ * attached at walk time, which broke the two promises above:
77
+ *
78
+ * • Across a RELOAD, the same mesh changed id. Observed in the games-fps
79
+ * overlay-persistence e2e: an object saved as `ingest:106/0/0/0:…` came
80
+ * back as `ingest:102/0:Mesh:Cube004` — four root slots earlier, exactly
81
+ * the grid + two editor lights + BatchedRenderer — so the persisted
82
+ * override landed on nothing and was reported as an orphan.
83
+ * • A SHIPPED build (no editor, so no furniture at all) minted a different
84
+ * id set than the editor that authored the overlay — i.e. the ship path
85
+ * this module exists to serve could not reliably consume its own output.
86
+ *
87
+ * Skipping the furniture entirely — not merely declining to give it an id —
88
+ * is what fixes both: the game's own children occupy 0..N-1 whatever else is
89
+ * parented alongside them. It also keeps gizmo handles and the grid out of the
90
+ * hierarchy/inspector projections built on this walk, where they were
91
+ * selectable and colorable as if they were game content.
52
92
  */
53
93
  export function assignStructuralIds(
54
94
  scene: THREE.Object3D,
@@ -68,13 +108,17 @@ export function assignStructuralIds(
68
108
  count++;
69
109
  if (o.isLight) lights++;
70
110
  else if (o.isMesh) meshes++;
71
- o.children.forEach((c, i) => {
72
- visit(c, `${path}/${i}`);
73
- });
111
+ visitChildren(o, path);
112
+ };
113
+ const visitChildren = (o: THREE.Object3D, path: string): void => {
114
+ let i = 0;
115
+ for (const c of o.children) {
116
+ if (isEditorOnly(c)) continue;
117
+ visit(c, path ? `${path}/${i}` : `${i}`);
118
+ i++;
119
+ }
74
120
  };
75
- scene.children.forEach((c, i) => {
76
- visit(c, `${i}`);
77
- });
121
+ visitChildren(scene, '');
78
122
  if (camera) {
79
123
  setUserData(camera, 'entityId', CAMERA_ID);
80
124
  byId.set(CAMERA_ID, camera);
@@ -59,6 +59,32 @@ export interface CapturedRuntime {
59
59
  renderer: THREE.WebGLRenderer;
60
60
  }
61
61
 
62
+ /** Options for {@link installSceneCapture}. */
63
+ export interface SceneCaptureOptions {
64
+ /**
65
+ * True when `renderer` is one the HOST constructed for its own drawing.
66
+ *
67
+ * The trap lives on the shared `WebGLRenderer.prototype` — that sharing is
68
+ * the whole mechanism — so the host's renders arrive here too, and the trap
69
+ * captures the first scene it sees. Any host renderer built after the trap
70
+ * installs (the editor bakes model thumbnails and asset previews on demand,
71
+ * each with its own offscreen renderer and its own little light rig) is a
72
+ * candidate to be captured AS THE GAME.
73
+ *
74
+ * That is what made `cov-mismatched-three` — the spec for "a game bundling
75
+ * its own three is DETECTED and degrades to embed-only, never silently" —
76
+ * pass or fail on timing alone: whether a thumbnail happened to bake inside
77
+ * the game's 2.5s capture window. When it did, the editor adopted its own
78
+ * preview scene as the game and `captureFailed` never fired.
79
+ *
80
+ * Identity, not shape, is the discriminator: a host renderer and a game
81
+ * renderer are the same class, both built after install, both drawing real
82
+ * scenes. The host is the only party that knows which is which, so it says
83
+ * so (`packages/editor/src/host-renderers.ts`).
84
+ */
85
+ isHostRenderer?: (renderer: unknown) => boolean;
86
+ }
87
+
62
88
  /** Handle returned by {@link installSceneCapture}. */
63
89
  export interface SceneCaptureHandle {
64
90
  /** The captured runtime, or null until the game renders its first frame. */
@@ -120,6 +146,7 @@ interface EffectComposerCtorLike {
120
146
  export function installSceneCapture(
121
147
  threeNamespace: unknown,
122
148
  effectComposerCtor?: unknown,
149
+ opts?: SceneCaptureOptions,
123
150
  ): SceneCaptureHandle {
124
151
  const THREE_NS = threeNamespace as ThreeLike;
125
152
  const proto = THREE_NS.WebGLRenderer.prototype;
@@ -173,8 +200,30 @@ export function installSceneCapture(
173
200
  const self = this;
174
201
  return function render(this: unknown, ...args: unknown[]) {
175
202
  const [scene, camera] = args;
176
- drawCount++;
177
- if (!captured && scene && (scene as { isScene?: boolean }).isScene) {
203
+ // Attribute the draw to the GAME, not to whoever shares this prototype.
204
+ // The trap patches `WebGLRenderer.prototype.render` on the editor's own
205
+ // three, so the EDITOR viewport's renders land here too. Counting them
206
+ // made `getDrawCount()` climb while the ingested game was paused, which
207
+ // is what the pause assertions in `14-ingest-feature-matrix` were
208
+ // measuring — the loop gate was doing its job (both games use
209
+ // `setAnimationLoop`, so they ARE gateable) and the instrument was
210
+ // reporting someone else's frames. Before capture every render still
211
+ // counts: that is how the first game frame is detected at all.
212
+ if (!captured || (self as unknown) === captured.renderer) drawCount++;
213
+ // Never capture a scene the HOST owns. The trap sits on the shared
214
+ // `WebGLRenderer.prototype`, so the editor's own viewport renders
215
+ // arrive here too — and when a game bundles its own MISMATCHED three
216
+ // (the case this whole degrade-to-embed-only path exists for), the
217
+ // editor's scene is then the only thing that ever reaches the trap.
218
+ // It was duly "captured" as the game: the editor got a hierarchy of
219
+ // its own GridHelper, BatchedRenderer and viewport lights presented as
220
+ // the ingested game's content, and `captureFailed` — the signal the
221
+ // whole graceful-degradation contract is built on — never fired.
222
+ // Whether that happened at all came down to whether the editor
223
+ // rendered a frame inside the game's capture window, so the same
224
+ // session could pass or fail on timing alone.
225
+ const isHostRender = opts?.isHostRenderer?.(self) === true;
226
+ if (!captured && !isHostRender && scene && (scene as { isScene?: boolean }).isScene) {
178
227
  captured = {
179
228
  scene: scene as THREE.Scene,
180
229
  camera: camera as THREE.Camera,
@@ -668,6 +668,30 @@ export class VgaiSceneGameAdapter implements GameAdapter {
668
668
  };
669
669
  }
670
670
 
671
+ // Warm the GPU program cache before the first visible frame. A mount gets
672
+ // a fresh WebGL context, so without this every scene material compiles
673
+ // lazily INSIDE the first draw calls — measured as ~0.5s of main-thread
674
+ // driver stalls on every editor play press. Gated on
675
+ // KHR_parallel_shader_compile: with the extension, compileAsync overlaps
676
+ // the compiles with the play transition; WITHOUT it (software
677
+ // rasterizers — SwiftShader in CI) compileAsync degenerates into one big
678
+ // synchronous compile that delays mount by whole seconds, strictly worse
679
+ // than the lazy first-draw path it was replacing. Best-effort: lazy
680
+ // compilation stays correct if it throws, and test hosts pass renderer
681
+ // stubs without these methods.
682
+ const glContext = typeof renderer.getContext === 'function' ? renderer.getContext() : undefined;
683
+ const parallelCompile =
684
+ !!glContext &&
685
+ typeof glContext.getExtension === 'function' &&
686
+ !!glContext.getExtension('KHR_parallel_shader_compile');
687
+ if (!headless && parallelCompile && typeof renderer.compileAsync === 'function') {
688
+ try {
689
+ await renderer.compileAsync(scene, camera);
690
+ } catch {
691
+ // Fall back to lazy compilation.
692
+ }
693
+ }
694
+
671
695
  // Idempotency guard for disposeGame — see below. Declared here (not inside
672
696
  // disposeGame) so hotReload can also refuse to run on an already-disposed
673
697
  // adapter.
@@ -346,13 +346,17 @@ export const LearnMetadataSchema = z
346
346
  .string()
347
347
  .optional()
348
348
  .describe(
349
- 'Optional thumbnail image reference (engine-repo-relative, e.g. ' +
350
- 'docs/example-screenshots/<id>/<shot>.png; generated per FT-11, never hand-made)',
349
+ 'Optional thumbnail image reference, PROJECT-relative (canonical path: ' +
350
+ 'learn/thumbnail.png the §3 schema path). Generated per FT-11 by the deterministic ' +
351
+ 'render tooling (scripts/generate-learn-thumbnails.ts), never hand-made',
351
352
  ),
352
353
  preview: z
353
354
  .string()
354
355
  .optional()
355
- .describe('Optional short preview video/loop reference (generated per FT-11)'),
356
+ .describe(
357
+ 'Optional short preview video/loop reference, PROJECT-relative (canonical path: ' +
358
+ 'learn/preview.mp4; generated per FT-11)',
359
+ ),
356
360
  lesson: z
357
361
  .string()
358
362
  .optional()
@@ -25,6 +25,7 @@ import {
25
25
  type WorldInstance,
26
26
  } from './game';
27
27
  import { createInputRouter, type RouterAdapterRoot, stackOrder } from './input-router';
28
+ import { installRenderControlHarness, isRenderModeRequested } from './render-control';
28
29
 
29
30
  // Re-export the camera-precedence helper from its new home so existing importers
30
31
  // (`import { adoptSceneCamera } from '@engine/runtime/create-runtime'`) keep working.
@@ -393,6 +394,23 @@ export interface WorldsRuntimeConfig {
393
394
  * (a test, a bespoke host) may also set it directly. Omitting it falls
394
395
  * back to `createGame`'s own fixed default. */
395
396
  seed?: number | undefined;
397
+ /**
398
+ * G3/FT-11 (docs/FTUE-LEARN-SYSTEM-DESIGN.md §8 G3) — test-only overrides
399
+ * for the render-control seam this host now wires up for EVERY session
400
+ * (see `createWorldsGameRuntime`): `location` is where `?vgai-render=1`
401
+ * is read from (defaults to the real `window.location`; a Node test has
402
+ * no `window` and must inject one to exercise the seam), `target` is
403
+ * where the harness publishes (defaults to the real `window` — override
404
+ * in a unit test to avoid touching the global object; mirrors
405
+ * `RenderControlHarnessOptions.target`/`.location` and the
406
+ * `debugBridge.url` override precedent in `mount-manifest.ts`).
407
+ */
408
+ renderControl?:
409
+ | {
410
+ readonly location?: { readonly search: string } | undefined;
411
+ readonly target?: Record<string, unknown> | undefined;
412
+ }
413
+ | undefined;
396
414
  }
397
415
 
398
416
  /**
@@ -818,6 +836,57 @@ async function mountAllWorldSpecs(
818
836
  return { mountedEntries, routerEntries };
819
837
  }
820
838
 
839
+ /**
840
+ * G3/FT-11 — publish the render-control harness (`render-control.ts`) for a
841
+ * live session, returning its retraction (wired into the session's own
842
+ * `stop()`, same leave-nothing-live rule as the debug bridge's `uninstall()`
843
+ * in mount-manifest.ts — render-control has no uninstall surface of its own,
844
+ * a render page being a single-load host by design, so the retraction is
845
+ * the publication's inverse). Split out of `createWorldsGameRuntime` purely
846
+ * to keep that function's cyclomatic complexity down (the same reason
847
+ * `mountAllWorldSpecs`/`installDefaultWorldDevGlobals` are split out).
848
+ *
849
+ * The clock is a documented no-op: a manifest-mounted game has no global
850
+ * cinematic AnimationClock to seek (a cinematic fixture that HAS one
851
+ * installs its own harness with the real clock, e.g.
852
+ * packages/engine/e2e/render-cinematic/main.ts) — the meaningful advance
853
+ * path for a plain game is `simulateSubsteps` (I5 `--simulate`), which
854
+ * drives `game.runFrame`.
855
+ */
856
+ function installSessionRenderHarness(
857
+ game: GameInternal,
858
+ location: { readonly search: string },
859
+ targetOverride: Record<string, unknown> | undefined,
860
+ ): (() => void) | undefined {
861
+ const target =
862
+ targetOverride ??
863
+ (typeof window !== 'undefined' ? (window as unknown as Record<string, unknown>) : undefined);
864
+ if (target === undefined) return undefined;
865
+ installRenderControlHarness({
866
+ game,
867
+ clock: {
868
+ seek() {
869
+ /* no global cinematic clock on a manifest-mounted game — see above */
870
+ },
871
+ seekFrame() {
872
+ /* intentional no-op, as seek() */
873
+ },
874
+ },
875
+ readiness: {
876
+ // Mount is complete by the time this harness exists (the install site
877
+ // in `createWorldsGameRuntime` is after `mountAllWorldSpecs`
878
+ // resolved) — the hook resolving immediately IS the readiness
879
+ // statement, not an assumption.
880
+ scene() {},
881
+ },
882
+ location,
883
+ target,
884
+ });
885
+ return () => {
886
+ delete target['__vgaiRender'];
887
+ };
888
+ }
889
+
821
890
  async function createWorldsGameRuntime(config: WorldsRuntimeConfig): Promise<GameSession> {
822
891
  const { container, roots: specs, width, height, headless = false, seed } = config;
823
892
  if (specs.length === 0) {
@@ -896,10 +965,27 @@ async function createWorldsGameRuntime(config: WorldsRuntimeConfig): Promise<Gam
896
965
  return () => extraUpdaters.delete(fn);
897
966
  },
898
967
  };
968
+ // G3/FT-11 (docs/FTUE-LEARN-SYSTEM-DESIGN.md §8 G3): every manifest/host-
969
+ // mounted game is a deterministic-capture candidate — the render-control
970
+ // seam (`?vgai-render=1`, render-control.ts) is wired HERE, at the one
971
+ // host every boot path shares (mountGameFromManifest, mountManifestWorlds,
972
+ // and direct createGameRuntime callers all reach this function), instead
973
+ // of asking every project's entry page to install it the way the e2e
974
+ // fixtures do. Production-protected twice over: `isRenderModeRequested`
975
+ // gates on the query param, and `installRenderControlHarness` re-checks it
976
+ // internally (its AC 4), so a normal gameplay page sees zero change.
977
+ const renderModeLocation =
978
+ config.renderControl?.location ?? (typeof window !== 'undefined' ? window.location : undefined);
979
+ const renderMode = renderModeLocation !== undefined && isRenderModeRequested(renderModeLocation);
899
980
  let started = false;
900
981
  const loop = createGameLoop({
901
982
  fixedTimestep: 1 / 60,
902
983
  maxSubSteps: 8,
984
+ // Render mode runs the loop in EXTERNAL-DRIVE mode (game-loop.ts, I2):
985
+ // no RAF is ever armed, so nothing advances gameplay between the capture
986
+ // host's explicit `simulateSubsteps`/`renderOnce` calls — wall-clock time
987
+ // passing while a screenshot is taken must not move the world.
988
+ externalDrive: renderMode,
903
989
  update: (dt) => {
904
990
  if (started) game.runFrame(dt);
905
991
  for (const fn of extraUpdaters) fn(dt);
@@ -942,6 +1028,16 @@ async function createWorldsGameRuntime(config: WorldsRuntimeConfig): Promise<Gam
942
1028
  // purely to keep this function's own cyclomatic complexity down.
943
1029
  const retractDevGlobals = installDefaultWorldDevGlobals(game);
944
1030
 
1031
+ // G3/FT-11 — publish `window.__vgaiRender` for the deterministic capture
1032
+ // host. Installed at the TAIL of the mount (same position as
1033
+ // mount-manifest.ts's debug bridge): every world's `mount()`/`setup()` has
1034
+ // resolved by now, so the harness's `scene` readiness hook reporting ready
1035
+ // is truthful. See `installSessionRenderHarness` below.
1036
+ const retractRenderHarness =
1037
+ renderMode && renderModeLocation !== undefined
1038
+ ? installSessionRenderHarness(game, renderModeLocation, config.renderControl?.target)
1039
+ : undefined;
1040
+
945
1041
  function fullCleanup(): void {
946
1042
  loop.stop();
947
1043
  router.dispose();
@@ -964,6 +1060,7 @@ async function createWorldsGameRuntime(config: WorldsRuntimeConfig): Promise<Gam
964
1060
  }
965
1061
  game.dispose();
966
1062
  retractDevGlobals();
1063
+ retractRenderHarness?.();
967
1064
  }
968
1065
 
969
1066
  return {
@@ -1262,7 +1262,17 @@ export function createGame(opts: {
1262
1262
  // "no state bridge" warning at registration (probe: every real react
1263
1263
  // world mount), eroding the signal for a genuinely un-observable
1264
1264
  // ingested world.
1265
- if (!isFirstPartyMounted(world.mounted) && world.kind !== 'react' && !world.mounted.observe) {
1265
+ // A mount exposing the engine's real ComponentManager (notably the R3F
1266
+ // first-party adapter) is observable through Game.queryByComponent +
1267
+ // Game.state even though it does not carry the classic scene adapter's
1268
+ // `firstParty` brand. Requiring a foreign-world `observe` bridge there
1269
+ // produces a false warning while the canonical bridge is already live.
1270
+ if (
1271
+ !isFirstPartyMounted(world.mounted) &&
1272
+ !world.mounted.components &&
1273
+ world.kind !== 'react' &&
1274
+ !world.mounted.observe
1275
+ ) {
1266
1276
  console.warn(
1267
1277
  `[game] world "${world.id}" (kind: ${world.kind}, adapter: "${world.adapter.id}"): ` +
1268
1278
  'no state bridge — this mounted game has no `observe` (WorldStateObserver); ' +
@@ -189,7 +189,7 @@ function formatIssues(issues: { path: PropertyKey[]; message: string }[]): strin
189
189
  * structured errors (empty = every new reference resolved and validated) —
190
190
  * never throws; callers decide how to present/escalate a non-empty result
191
191
  * (the CLI folds these into one `ApplyDiffCliError` message, the SDK raises
192
- * a declared `ASSET_REF_INVALID` `OperationError`).
192
+ * a declared `ASSET_REF_INVALID` `ToolError`).
193
193
  */
194
194
  export function dereferenceNewAssetRefs(
195
195
  inputScene: SceneFile,
@@ -1,6 +1,6 @@
1
1
  import type RAPIER from '@dimforge/rapier3d-compat';
2
2
  import * as THREE from 'three';
3
- import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
3
+ import { HDRLoader } from 'three/addons/loaders/HDRLoader.js';
4
4
  import type { BatchedRenderer } from 'three.quarks';
5
5
  import { initNavigation, NavMeshManager } from '../ai/navigation';
6
6
  import { buildClipMap } from '../animation/clip-map';
@@ -148,7 +148,7 @@ async function loadEnvTexture(
148
148
  const ext = path.split('.').pop()?.toLowerCase();
149
149
  const rawTexture =
150
150
  ext === 'hdr'
151
- ? await new RGBELoader(loadingManager).loadAsync(resolveUrl(path))
151
+ ? await new HDRLoader(loadingManager).loadAsync(resolveUrl(path))
152
152
  : await new THREE.TextureLoader(loadingManager).loadAsync(resolveUrl(path));
153
153
  rawTexture.mapping = THREE.EquirectangularReflectionMapping;
154
154
  const envMap = pmremGenerator.fromEquirectangular(rawTexture).texture;
@@ -73,6 +73,16 @@
73
73
  * surfaced in the play-mode hierarchy with a *synthetic*
74
74
  * descriptor (it has no `.vscn` record). Marks it as
75
75
  * ephemeral: selectable/movable for testing, never saved.
76
+ * - `authoringRoot` — explicit opt-in for a runtime descendant to become
77
+ * its own authoring object instead of a part of the
78
+ * nearest ancestor owner.
79
+ * - `authoringInstance` — source OID of the custom-component callsite that
80
+ * rendered this existing Object3D. Used to group
81
+ * implementation parts without adding wrappers.
82
+ * - `authoringLabel` — authored literal name (or component tag fallback)
83
+ * for that source-backed component instance.
84
+ * - `behaviorType` — registry key on the compatibility R3F Behavior
85
+ * attachment group; authoring projects it as a component.
76
86
  * - `editorHelper` — `true` for editor-only helper objects (gizmos, wireframes).
77
87
  * - `editorHelperType` — which kind of helper (lights/particles/pivot/navmesh/...).
78
88
  * - `editorIcon` — `true` for editor billboard icon sprites.
@@ -145,6 +155,10 @@ export interface UserDataSchema {
145
155
  __ingestNextId: number;
146
156
  engineInternal: boolean;
147
157
  liveObject: boolean;
158
+ authoringRoot: boolean;
159
+ authoringInstance: string;
160
+ authoringLabel: string;
161
+ behaviorType: string;
148
162
  editorHelper: boolean;
149
163
  editorHelperType: EditorHelperType;
150
164
  editorIcon: boolean;
@@ -190,6 +204,10 @@ export const UserDataKeys = {
190
204
  __ingestNextId: '__ingestNextId',
191
205
  engineInternal: 'engineInternal',
192
206
  liveObject: 'liveObject',
207
+ authoringRoot: 'authoringRoot',
208
+ authoringInstance: 'authoringInstance',
209
+ authoringLabel: 'authoringLabel',
210
+ behaviorType: 'behaviorType',
193
211
  editorHelper: 'editorHelper',
194
212
  editorHelperType: 'editorHelperType',
195
213
  editorIcon: 'editorIcon',
@@ -22,12 +22,11 @@
22
22
  * byte-identical to an un-wrapped mesh. `cloneElement` would have to
23
23
  * merge a ref without clobbering an authored one — fragile across
24
24
  * forwardRef/ref-as-prop shapes — and adds nothing the group doesn't.
25
- * - The wrapper is also the Behavior's OWN presence in the scene graph: it
26
- * carries the `<Behavior>` call site's `userData-oid` stamp (fiber
27
- * pierces the forwarded dashed prop onto the group's userData), which is
28
- * what makes the Behavior node itself selectable/source-addressable in
29
- * the editor hierarchy, with its schema-declared config editable like any
30
- * other JSX prop.
25
+ * - The wrapper carries the `<Behavior>` call site's `userData-oid` stamp so
26
+ * its config stays source-addressable. It is an INTERNAL attachment node,
27
+ * not an independent spatial authoring object: the R3F authoring adapter
28
+ * projects this config onto the wrapped object's Inspector and promotes
29
+ * the child through the hierarchy.
31
30
  * - Transform semantics: the group stays at identity, so the child's
32
31
  * authored `position`/`rotation`/`scale` are unchanged by wrapping.
33
32
  *
@@ -43,7 +42,7 @@
43
42
  // their fiber import — project entry files importing this module keep their
44
43
  // own `@react-three/fiber` import for the scene itself.
45
44
  import type { ThreeElements } from '@react-three/fiber';
46
- import { createElement, useContext, useEffect, useRef } from 'react';
45
+ import { createElement, useContext, useEffect, useLayoutEffect, useRef } from 'react';
47
46
  import type { Group } from 'three';
48
47
  import type { GameComponent } from '../ecs/game-component';
49
48
  import { type BehaviorRegistry, EngineBridge, resolveBehaviorClass } from './engine-bridge';
@@ -70,7 +69,11 @@ export function Behavior<Reg extends BehaviorRegistry = BehaviorRegistry>(
70
69
  // The live instance the config-reapply effect targets (never re-attach).
71
70
  const liveRef = useRef<GameComponent | null>(null);
72
71
 
73
- useEffect(() => {
72
+ // Attachment is part of the committed world, not deferred follow-up work.
73
+ // A layout effect runs before `createR3FAdapter.mount()` resolves its first
74
+ // committed tree, so deterministic hosts can begin stepping immediately
75
+ // without losing every warm-up tick to an unflushed passive effect.
76
+ useLayoutEffect(() => {
74
77
  if (!bridge?.registry) {
75
78
  // Loud degrade, never a crash: a mount without the adapter's bridge
76
79
  // (e.g. a foreign harness rendering this tree directly) — or a bridge
@@ -123,13 +126,18 @@ export function Behavior<Reg extends BehaviorRegistry = BehaviorRegistry>(
123
126
  // OID stamper rewrites JSX syntax only, so a JSX `<group>` here would get
124
127
  // behavior.tsx's OWN stamp AND the forwarded call-site oid (a duplicate
125
128
  // `userData-oid` attribute). The wrapper group must carry exactly ONE
126
- // identity — the `<Behavior>` CALL SITE's oid — so the editor's hierarchy
127
- // selects/edits the Behavior element the author actually wrote.
129
+ // identity — the `<Behavior>` CALL SITE's oid — so the editor can edit the
130
+ // config while projecting the wrapper as a component of its child.
128
131
  return createElement(
129
132
  'group',
130
133
  {
131
134
  ref: groupRef,
132
- name: `Behavior:${type}`,
135
+ // `Behavior: ` WITH the space: the editor's R3F adapter labels this node
136
+ // `Behavior: ${behaviorType}` when it projects it as an entity, but falls
137
+ // back to this `name` when the same node is projected as a component
138
+ // boundary. Without the space the hierarchy showed one of two different
139
+ // labels for the identical concept depending on which branch ran.
140
+ name: `Behavior: ${type}`,
133
141
  'userData-oid': oid,
134
142
  'userData-behaviorType': type,
135
143
  },