@vgai/editor 0.2.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 (519) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +35 -0
  3. package/dist/a/b/c/test-nested.json +1 -0
  4. package/dist/assets/CCDIKSolver-Dz-ysxZF.js +1 -0
  5. package/dist/assets/SelectionBox-BXsv_6aH.js +1 -0
  6. package/dist/assets/browser-project-scripts-DOru4L1x.js +2 -0
  7. package/dist/assets/browserAll-CEkWYlFk.js +1 -0
  8. package/dist/assets/collision-world-DniMB0qq.glb +0 -0
  9. package/dist/assets/create-runtime-DYIWr3va.js +1 -0
  10. package/dist/assets/data-asset-Bb9sIM7i.js +5 -0
  11. package/dist/assets/esbuild-BHljloGq.wasm +0 -0
  12. package/dist/assets/fake-three-BMTQ9rl_.js +1 -0
  13. package/dist/assets/game-8SDZPPgH.js +1 -0
  14. package/dist/assets/game-BI111o5f.js +10 -0
  15. package/dist/assets/game-CevorxyV.js +1 -0
  16. package/dist/assets/game-Di1H0gFj.js +1 -0
  17. package/dist/assets/game-Di7XlywD.js +1 -0
  18. package/dist/assets/game-DjnCaw8R.js +1 -0
  19. package/dist/assets/game-Q9A0aXSV.js +1 -0
  20. package/dist/assets/game-pt_1lQLv.js +1 -0
  21. package/dist/assets/game-source-CvidN7AB.js +1 -0
  22. package/dist/assets/game-xsOho_wg.js +83 -0
  23. package/dist/assets/index-Bp5cecD6.js +12 -0
  24. package/dist/assets/index-CPVV6pjw.js +1 -0
  25. package/dist/assets/index-CpOnYyeD.js +213 -0
  26. package/dist/assets/index-Cs8xwv3e.js +1 -0
  27. package/dist/assets/index-DL7RcQ0n.js +27 -0
  28. package/dist/assets/index-Dr8H_6Rl.js +1 -0
  29. package/dist/assets/index-_bCaipUk.js +13650 -0
  30. package/dist/assets/ingest-siblings-Dz8eEqrV.js +1 -0
  31. package/dist/assets/mount-manifest-Bhk0ohsl.js +1 -0
  32. package/dist/assets/recast-navigation.wasm-compat-CvP4UGK6.js +1 -0
  33. package/dist/assets/scene-query-BivAHhJu.js +1 -0
  34. package/dist/assets/stats.module--VATS4Kh.js +1 -0
  35. package/dist/assets/webworkerAll-BLrH0F6F.js +1 -0
  36. package/dist/assets/xstate-animation-binding-1NGLmw6y.js +1 -0
  37. package/dist/assets/xstate.esm-BgUD33nl.js +3 -0
  38. package/dist/index.html +37 -0
  39. package/dist/ingest/racing-game/UPSTREAM.md +68 -0
  40. package/dist/ingest/racing-game/a/Inter-Black.woff +0 -0
  41. package/dist/ingest/racing-game/a/Inter-Black.woff2 +0 -0
  42. package/dist/ingest/racing-game/a/Inter-BlackItalic.woff +0 -0
  43. package/dist/ingest/racing-game/a/Inter-BlackItalic.woff2 +0 -0
  44. package/dist/ingest/racing-game/a/Inter-Bold.woff +0 -0
  45. package/dist/ingest/racing-game/a/Inter-Bold.woff2 +0 -0
  46. package/dist/ingest/racing-game/a/Inter-BoldItalic.woff +0 -0
  47. package/dist/ingest/racing-game/a/Inter-BoldItalic.woff2 +0 -0
  48. package/dist/ingest/racing-game/a/Inter-ExtraBold.woff +0 -0
  49. package/dist/ingest/racing-game/a/Inter-ExtraBold.woff2 +0 -0
  50. package/dist/ingest/racing-game/a/Inter-ExtraBoldItalic.woff +0 -0
  51. package/dist/ingest/racing-game/a/Inter-ExtraBoldItalic.woff2 +0 -0
  52. package/dist/ingest/racing-game/a/Inter-ExtraLight.woff +0 -0
  53. package/dist/ingest/racing-game/a/Inter-ExtraLight.woff2 +0 -0
  54. package/dist/ingest/racing-game/a/Inter-ExtraLightItalic.woff +0 -0
  55. package/dist/ingest/racing-game/a/Inter-ExtraLightItalic.woff2 +0 -0
  56. package/dist/ingest/racing-game/a/Inter-Italic.woff +0 -0
  57. package/dist/ingest/racing-game/a/Inter-Italic.woff2 +0 -0
  58. package/dist/ingest/racing-game/a/Inter-Light.woff +0 -0
  59. package/dist/ingest/racing-game/a/Inter-Light.woff2 +0 -0
  60. package/dist/ingest/racing-game/a/Inter-LightItalic.woff +0 -0
  61. package/dist/ingest/racing-game/a/Inter-LightItalic.woff2 +0 -0
  62. package/dist/ingest/racing-game/a/Inter-Medium.woff +0 -0
  63. package/dist/ingest/racing-game/a/Inter-Medium.woff2 +0 -0
  64. package/dist/ingest/racing-game/a/Inter-MediumItalic.woff +0 -0
  65. package/dist/ingest/racing-game/a/Inter-MediumItalic.woff2 +0 -0
  66. package/dist/ingest/racing-game/a/Inter-Regular.woff +0 -0
  67. package/dist/ingest/racing-game/a/Inter-Regular.woff2 +0 -0
  68. package/dist/ingest/racing-game/a/Inter-SemiBold.woff +0 -0
  69. package/dist/ingest/racing-game/a/Inter-SemiBold.woff2 +0 -0
  70. package/dist/ingest/racing-game/a/Inter-SemiBoldItalic.woff +0 -0
  71. package/dist/ingest/racing-game/a/Inter-SemiBoldItalic.woff2 +0 -0
  72. package/dist/ingest/racing-game/a/Inter-Thin.woff +0 -0
  73. package/dist/ingest/racing-game/a/Inter-Thin.woff2 +0 -0
  74. package/dist/ingest/racing-game/a/Inter-ThinItalic.woff +0 -0
  75. package/dist/ingest/racing-game/a/Inter-ThinItalic.woff2 +0 -0
  76. package/dist/ingest/racing-game/a/Inter-italic.var.woff2 +0 -0
  77. package/dist/ingest/racing-game/a/Inter-roman.var.woff2 +0 -0
  78. package/dist/ingest/racing-game/a/Inter.var.woff2 +0 -0
  79. package/dist/ingest/racing-game/a/index.css +1 -0
  80. package/dist/ingest/racing-game/images/bronze.png +0 -0
  81. package/dist/ingest/racing-game/images/gold.png +0 -0
  82. package/dist/ingest/racing-game/images/silver.png +0 -0
  83. package/dist/ingest/racing-game/index.html +15 -0
  84. package/dist/ingest/racing-game/models/chassis-draco.glb +0 -0
  85. package/dist/ingest/racing-game/models/track-draco.glb +0 -0
  86. package/dist/ingest/racing-game/models/wheel-draco.glb +0 -0
  87. package/dist/ingest/racing-game/rg-entry.js +1 -0
  88. package/dist/ingest/racing-game/sounds/accelerate.mp3 +0 -0
  89. package/dist/ingest/racing-game/sounds/boost.mp3 +0 -0
  90. package/dist/ingest/racing-game/sounds/crash.mp3 +0 -0
  91. package/dist/ingest/racing-game/sounds/engine.mp3 +0 -0
  92. package/dist/ingest/racing-game/sounds/honk.mp3 +0 -0
  93. package/dist/ingest/racing-game/sounds/tire-brake.mp3 +0 -0
  94. package/dist/ingest/racing-game/sounds/train.mp3 +0 -0
  95. package/dist/ingest/racing-game/sounds/water.mp3 +0 -0
  96. package/dist/ingest/racing-game/textures/cursor.svg +3 -0
  97. package/dist/ingest/racing-game/textures/dikhololo_night_1k.hdr +0 -0
  98. package/dist/ingest/racing-game/textures/heightmap_1024.png +0 -0
  99. package/dist/ingest/racing-game/textures/mask.svg +11 -0
  100. package/dist/ingest/racing-game/textures/no_sound.png +0 -0
  101. package/dist/ingest/racing-game/three-r139.module.js +50617 -0
  102. package/dist/ingest/racing-game/vendor.js +179 -0
  103. package/dist/ingest/racing-game/vgai.game.json +20 -0
  104. package/dist/ingest/simcity/UPSTREAM.md +65 -0
  105. package/dist/ingest/simcity/favicon.ico +0 -0
  106. package/dist/ingest/simcity/fonts/Bebas-Regular.otf +0 -0
  107. package/dist/ingest/simcity/fonts/Digitalt.otf +0 -0
  108. package/dist/ingest/simcity/fonts/Stanberry.ttf +0 -0
  109. package/dist/ingest/simcity/icons/bulldozer-color.png +0 -0
  110. package/dist/ingest/simcity/icons/bulldozer.png +0 -0
  111. package/dist/ingest/simcity/icons/calendar.png +0 -0
  112. package/dist/ingest/simcity/icons/factory-color.png +0 -0
  113. package/dist/ingest/simcity/icons/factory.png +0 -0
  114. package/dist/ingest/simcity/icons/house-color.png +0 -0
  115. package/dist/ingest/simcity/icons/house.png +0 -0
  116. package/dist/ingest/simcity/icons/job.png +0 -0
  117. package/dist/ingest/simcity/icons/office.png +0 -0
  118. package/dist/ingest/simcity/icons/pause-color.png +0 -0
  119. package/dist/ingest/simcity/icons/pause.png +0 -0
  120. package/dist/ingest/simcity/icons/person.png +0 -0
  121. package/dist/ingest/simcity/icons/play-color.png +0 -0
  122. package/dist/ingest/simcity/icons/play.png +0 -0
  123. package/dist/ingest/simcity/icons/power-color.png +0 -0
  124. package/dist/ingest/simcity/icons/power-line-color.png +0 -0
  125. package/dist/ingest/simcity/icons/power.png +0 -0
  126. package/dist/ingest/simcity/icons/road-color.png +0 -0
  127. package/dist/ingest/simcity/icons/road.png +0 -0
  128. package/dist/ingest/simcity/icons/select-color.png +0 -0
  129. package/dist/ingest/simcity/icons/select.png +0 -0
  130. package/dist/ingest/simcity/icons/store-color.png +0 -0
  131. package/dist/ingest/simcity/index.html +94 -0
  132. package/dist/ingest/simcity/index.js +58 -0
  133. package/dist/ingest/simcity/main.css +233 -0
  134. package/dist/ingest/simcity/models/armored-truck.glb +0 -0
  135. package/dist/ingest/simcity/models/building-block-4floor-corner.glb +0 -0
  136. package/dist/ingest/simcity/models/building-block-4floor-short.glb +0 -0
  137. package/dist/ingest/simcity/models/building-block-5floor.glb +0 -0
  138. package/dist/ingest/simcity/models/building-burger-joint.glb +0 -0
  139. package/dist/ingest/simcity/models/building-cafe.glb +0 -0
  140. package/dist/ingest/simcity/models/building-casino.glb +0 -0
  141. package/dist/ingest/simcity/models/building-cinema.glb +0 -0
  142. package/dist/ingest/simcity/models/building-house-block-big.glb +0 -0
  143. package/dist/ingest/simcity/models/building-house-family-large.glb +0 -0
  144. package/dist/ingest/simcity/models/building-house-family-small.glb +0 -0
  145. package/dist/ingest/simcity/models/building-office-balcony.glb +0 -0
  146. package/dist/ingest/simcity/models/building-office-big.glb +0 -0
  147. package/dist/ingest/simcity/models/building-office-pyramid.glb +0 -0
  148. package/dist/ingest/simcity/models/building-office-tall.glb +0 -0
  149. package/dist/ingest/simcity/models/building-office.glb +0 -0
  150. package/dist/ingest/simcity/models/building-restaurant.glb +0 -0
  151. package/dist/ingest/simcity/models/building-skyscraper.glb +0 -0
  152. package/dist/ingest/simcity/models/car-ambulance-pickup.glb +0 -0
  153. package/dist/ingest/simcity/models/car-baywatch.glb +0 -0
  154. package/dist/ingest/simcity/models/car-hippie-van.glb +0 -0
  155. package/dist/ingest/simcity/models/car-passenger-race.glb +0 -0
  156. package/dist/ingest/simcity/models/car-passenger.glb +0 -0
  157. package/dist/ingest/simcity/models/car-police.glb +0 -0
  158. package/dist/ingest/simcity/models/car-taxi.glb +0 -0
  159. package/dist/ingest/simcity/models/car-tow-truck.glb +0 -0
  160. package/dist/ingest/simcity/models/car-truck-dump.glb +0 -0
  161. package/dist/ingest/simcity/models/car-veteran.glb +0 -0
  162. package/dist/ingest/simcity/models/construction-small.glb +0 -0
  163. package/dist/ingest/simcity/models/data-center.glb +0 -0
  164. package/dist/ingest/simcity/models/industry-factory-old.glb +0 -0
  165. package/dist/ingest/simcity/models/industry-factory.glb +0 -0
  166. package/dist/ingest/simcity/models/industry-refinery.glb +0 -0
  167. package/dist/ingest/simcity/models/industry-warehouse.glb +0 -0
  168. package/dist/ingest/simcity/models/power_line_pole_modified.glb +0 -0
  169. package/dist/ingest/simcity/models/tile-plain_grass.glb +0 -0
  170. package/dist/ingest/simcity/models/tile-road-curve.glb +0 -0
  171. package/dist/ingest/simcity/models/tile-road-end.glb +0 -0
  172. package/dist/ingest/simcity/models/tile-road-intersection-t.glb +0 -0
  173. package/dist/ingest/simcity/models/tile-road-intersection.glb +0 -0
  174. package/dist/ingest/simcity/models/tile-road-straight.glb +0 -0
  175. package/dist/ingest/simcity/models/truck.glb +0 -0
  176. package/dist/ingest/simcity/statusIcons/no-power.png +0 -0
  177. package/dist/ingest/simcity/statusIcons/no-road-access.png +0 -0
  178. package/dist/ingest/simcity/textures/base.png +0 -0
  179. package/dist/ingest/simcity/textures/grid.png +0 -0
  180. package/dist/ingest/simcity/textures/specular.png +0 -0
  181. package/dist/ingest/simcity/three-r155.module.js +51466 -0
  182. package/dist/ingest/simcity/vgai.game.json +20 -0
  183. package/dist/ingest/tanks/UPSTREAM.md +69 -0
  184. package/dist/ingest/tanks/index.html +214 -0
  185. package/dist/ingest/tanks/models/pixelTank.fbx +0 -0
  186. package/dist/ingest/tanks/tanks-entry.js +92 -0
  187. package/dist/ingest/tanks/three-r170.module.js +54571 -0
  188. package/dist/ingest/tanks/vgai.game.json +20 -0
  189. package/dist/inputmaps/default.inputmap.json +41 -0
  190. package/dist/jsm/libs/draco/gltf/draco_decoder.js +33 -0
  191. package/dist/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
  192. package/dist/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
  193. package/dist/scenes/untitled.vscn.json +29 -0
  194. package/dist/textures/waternormals.jpg +0 -0
  195. package/dist-server/packaged.mjs +18155 -0
  196. package/package.json +36 -0
  197. package/server/asset-library-routes.ts +833 -0
  198. package/server/canonical-path.ts +50 -0
  199. package/server/cloud-asset-catalog.ts +101 -0
  200. package/server/dev.ts +350 -0
  201. package/server/editor-server.ts +1537 -0
  202. package/server/editor-sse.ts +73 -0
  203. package/server/game-globals-shadow.ts +58 -0
  204. package/server/local-asset-catalog.ts +163 -0
  205. package/server/packaged.ts +571 -0
  206. package/server/prod.ts +75 -0
  207. package/server/project-optimize-deps-entries.ts +149 -0
  208. package/server/project-view.ts +65 -0
  209. package/server/server-utils.ts +430 -0
  210. package/server/session-registry.ts +93 -0
  211. package/src/EditorContext.tsx +87 -0
  212. package/src/achieved-tier.ts +126 -0
  213. package/src/action-registry.ts +268 -0
  214. package/src/adapter-resolver.ts +1858 -0
  215. package/src/asset-events.ts +100 -0
  216. package/src/authoring/active-adapter.ts +98 -0
  217. package/src/authoring/active-systems.ts +50 -0
  218. package/src/authoring/boundary-authoring-adapter.ts +178 -0
  219. package/src/authoring/composite-authoring-adapter.ts +891 -0
  220. package/src/authoring/design-time-layers.ts +776 -0
  221. package/src/authoring/edit-mode-authoring.ts +643 -0
  222. package/src/authoring/embed-only-adapter.ts +57 -0
  223. package/src/authoring/ephemeral-persistence.ts +26 -0
  224. package/src/authoring/eyedropper-session.ts +69 -0
  225. package/src/authoring/first-party-hierarchy-menu.ts +74 -0
  226. package/src/authoring/first-party-inspector/AddSectionMenu.tsx +118 -0
  227. package/src/authoring/first-party-inspector/EnvironmentSection.tsx +550 -0
  228. package/src/authoring/first-party-inspector/HeaderSection.tsx +329 -0
  229. package/src/authoring/first-party-inspector/is-first-party-selection.ts +44 -0
  230. package/src/authoring/first-party-inspector/register.tsx +108 -0
  231. package/src/authoring/ingest-authoring-adapter.ts +475 -0
  232. package/src/authoring/ingest-capture.ts +94 -0
  233. package/src/authoring/ingest-game-adapter.ts +291 -0
  234. package/src/authoring/ingest-iframe-adapter.ts +38 -0
  235. package/src/authoring/ingest-iframe-reachable-adapter.ts +401 -0
  236. package/src/authoring/ingest-overlay.ts +193 -0
  237. package/src/authoring/layered-pick.ts +87 -0
  238. package/src/authoring/mount-failure-report.ts +112 -0
  239. package/src/authoring/overlay-report.ts +52 -0
  240. package/src/authoring/project-scenes.ts +111 -0
  241. package/src/authoring/react-dom-authoring-adapter.ts +795 -0
  242. package/src/authoring/react-inspector-section.tsx +1041 -0
  243. package/src/authoring/react-world-authoring-adapter.ts +2400 -0
  244. package/src/authoring/root-activation.ts +61 -0
  245. package/src/authoring/scene-ui-layer.ts +485 -0
  246. package/src/authoring/story-overlay.ts +48 -0
  247. package/src/authoring/ui-authoring-adapter.ts +325 -0
  248. package/src/authoring/ui-hierarchy-menu.ts +40 -0
  249. package/src/authoring/ui-inspector-sections.tsx +156 -0
  250. package/src/authoring/undo-timeline.ts +145 -0
  251. package/src/authoring/vgai-scene-authoring-adapter.ts +746 -0
  252. package/src/authoring/viewport-pick-context.ts +31 -0
  253. package/src/authoring/viewport-raycast.ts +80 -0
  254. package/src/authoring/world-overlay.ts +76 -0
  255. package/src/authoring/world-pan-state.ts +115 -0
  256. package/src/authoring/world-session-state.ts +102 -0
  257. package/src/authoring/world2d-authoring-adapter.ts +313 -0
  258. package/src/browser-external-watch.ts +98 -0
  259. package/src/browser-play-entry.ts +112 -0
  260. package/src/browser-project-scripts.ts +112 -0
  261. package/src/browser-transpile.ts +246 -0
  262. package/src/browser-watch-reload.ts +100 -0
  263. package/src/command-listener.ts +326 -0
  264. package/src/components/AlignToolbar.tsx +189 -0
  265. package/src/components/AppRoot.tsx +73 -0
  266. package/src/components/AssetBrowser.tsx +502 -0
  267. package/src/components/BottomPanel.tsx +326 -0
  268. package/src/components/BuildPanel.tsx +455 -0
  269. package/src/components/CameraInfo.tsx +56 -0
  270. package/src/components/CommandPalette.tsx +265 -0
  271. package/src/components/ConsolePanel.tsx +272 -0
  272. package/src/components/DataPanel.tsx +1268 -0
  273. package/src/components/DefaultEditorLayout.tsx +509 -0
  274. package/src/components/FolderTree.tsx +179 -0
  275. package/src/components/GameHierarchy.tsx +1349 -0
  276. package/src/components/Inspector.tsx +671 -0
  277. package/src/components/InspectorToolSection.tsx +74 -0
  278. package/src/components/MountFailureBanner.tsx +110 -0
  279. package/src/components/NetworkSection.tsx +109 -0
  280. package/src/components/OnlineAssetBrowser.tsx +448 -0
  281. package/src/components/OverlayReportBanner.tsx +122 -0
  282. package/src/components/PlayBar.tsx +356 -0
  283. package/src/components/PrefabSaveDialog.tsx +201 -0
  284. package/src/components/ProjectHeader.tsx +174 -0
  285. package/src/components/ProjectScreen.tsx +1004 -0
  286. package/src/components/RightPanel.tsx +166 -0
  287. package/src/components/RootActivateDialog.tsx +105 -0
  288. package/src/components/SaveDialog.tsx +197 -0
  289. package/src/components/SaveStatus.tsx +69 -0
  290. package/src/components/StatsOverlay.tsx +35 -0
  291. package/src/components/ToolHost.tsx +80 -0
  292. package/src/components/Toolbar.tsx +307 -0
  293. package/src/components/ViewportOverlay.tsx +234 -0
  294. package/src/components/ViewportPanel.tsx +369 -0
  295. package/src/components/WorldContextMenu.tsx +257 -0
  296. package/src/components/WorldSelectionOverlay.tsx +2003 -0
  297. package/src/components/WorldTextEditor.tsx +86 -0
  298. package/src/components/asset-viewers/AssetViewer.tsx +38 -0
  299. package/src/components/asset-viewers/AudioViewer.tsx +149 -0
  300. package/src/components/asset-viewers/GenericJsonViewer.tsx +77 -0
  301. package/src/components/asset-viewers/ImageViewer.tsx +65 -0
  302. package/src/components/asset-viewers/InputMapViewer.tsx +206 -0
  303. package/src/components/asset-viewers/ModelViewer.tsx +180 -0
  304. package/src/components/asset-viewers/OnlineAssetDetail.tsx +236 -0
  305. package/src/components/data-csv.ts +301 -0
  306. package/src/components/entity-name-commit.ts +18 -0
  307. package/src/components/inspector-property-grouping.ts +65 -0
  308. package/src/components/inspector-widgets/AlignmentGrid.tsx +188 -0
  309. package/src/components/inspector-widgets/BorderEditor.tsx +305 -0
  310. package/src/components/inspector-widgets/ColorPicker.tsx +548 -0
  311. package/src/components/inspector-widgets/FilterEditor.tsx +108 -0
  312. package/src/components/inspector-widgets/FontPicker.tsx +196 -0
  313. package/src/components/inspector-widgets/GradientEditor.tsx +277 -0
  314. package/src/components/inspector-widgets/ScrubbableInput.tsx +152 -0
  315. package/src/components/inspector-widgets/ShadowEditor.tsx +332 -0
  316. package/src/components/inspector-widgets/color-utils.ts +202 -0
  317. package/src/components/inspector-widgets/gallery-main.tsx +167 -0
  318. package/src/components/inspector-widgets/index.ts +22 -0
  319. package/src/components/inspector-widgets/shared.tsx +135 -0
  320. package/src/components/inspectors/AnimationSection.tsx +143 -0
  321. package/src/components/inspectors/AudioSection.tsx +215 -0
  322. package/src/components/inspectors/BoneAttachmentSection.tsx +82 -0
  323. package/src/components/inspectors/CameraSection.tsx +105 -0
  324. package/src/components/inspectors/ComponentsSection.tsx +358 -0
  325. package/src/components/inspectors/JointsSection.tsx +255 -0
  326. package/src/components/inspectors/LightSection.tsx +116 -0
  327. package/src/components/inspectors/MaterialSection.tsx +1189 -0
  328. package/src/components/inspectors/MeshSection.tsx +99 -0
  329. package/src/components/inspectors/NavigationSection.tsx +58 -0
  330. package/src/components/inspectors/ParticleSection.tsx +681 -0
  331. package/src/components/inspectors/PhysicsSection.tsx +326 -0
  332. package/src/components/inspectors/PostProcessingSection.tsx +1290 -0
  333. package/src/components/inspectors/RenderingSection.tsx +109 -0
  334. package/src/components/inspectors/ShadowSection.tsx +84 -0
  335. package/src/components/inspectors/SplineSection.tsx +191 -0
  336. package/src/components/inspectors/TransformSection.tsx +191 -0
  337. package/src/components/inspectors/json-schema-fields.ts +281 -0
  338. package/src/components/inspectors/multi-edit-utils.ts +49 -0
  339. package/src/components/inspectors/zod-fields.ts +184 -0
  340. package/src/components/primitives/ColorInput.tsx +109 -0
  341. package/src/components/primitives/JsonInput.tsx +98 -0
  342. package/src/components/primitives/NumberInput.tsx +164 -0
  343. package/src/components/primitives/Panel.tsx +68 -0
  344. package/src/components/primitives/SectionHeader.tsx +45 -0
  345. package/src/components/primitives/Tooltip.tsx +82 -0
  346. package/src/components/primitives/Vec3Input.tsx +62 -0
  347. package/src/components/world-overlay-gestures.ts +1438 -0
  348. package/src/doctor/data-checks.ts +285 -0
  349. package/src/doctor/report.ts +386 -0
  350. package/src/editor-api.ts +700 -0
  351. package/src/editor-console.ts +194 -0
  352. package/src/editor-hotkeys.ts +441 -0
  353. package/src/editor-layers.ts +2 -0
  354. package/src/editor-mode.ts +28 -0
  355. package/src/editor-store.ts +2225 -0
  356. package/src/editor-viewport.ts +1924 -0
  357. package/src/entity-factory.ts +221 -0
  358. package/src/game-globals-prelude.ts +27 -0
  359. package/src/gated-globals.ts +131 -0
  360. package/src/generated/setup-by-example.generated.ts +53 -0
  361. package/src/gizmo-registry.ts +608 -0
  362. package/src/handle-system.ts +221 -0
  363. package/src/hierarchy-menu-registry.ts +70 -0
  364. package/src/hierarchy-rows.ts +73 -0
  365. package/src/hosted-example.ts +160 -0
  366. package/src/hotkeys.ts +142 -0
  367. package/src/ingest/discovery-react.ts +132 -0
  368. package/src/ingest/discovery.ts +205 -0
  369. package/src/ingest/discovery2d.ts +204 -0
  370. package/src/ingest/esm-entry-resolution.ts +102 -0
  371. package/src/ingest/games/bloom-composer/game.js +99 -0
  372. package/src/ingest/games/bloom-composer/vgai.game.json +21 -0
  373. package/src/ingest/games/embed-bundle/embed.html +10 -0
  374. package/src/ingest/games/embed-bundle/vgai.game.json +21 -0
  375. package/src/ingest/games/games-fps/assets/collision-world.glb +0 -0
  376. package/src/ingest/games/games-fps/game.js +479 -0
  377. package/src/ingest/games/games-fps/vgai.game.json +24 -0
  378. package/src/ingest/games/iframe-reachable/game-source.js +58 -0
  379. package/src/ingest/games/iframe-reachable/vgai.game.json +21 -0
  380. package/src/ingest/games/interactive-cubes/game.js +139 -0
  381. package/src/ingest/games/interactive-cubes/vgai.game.json +21 -0
  382. package/src/ingest/games/mismatched-three/fake-three.js +38 -0
  383. package/src/ingest/games/mismatched-three/game.js +10 -0
  384. package/src/ingest/games/mismatched-three/vgai.game.json +22 -0
  385. package/src/ingest/games/own-three-bundled/game.js +43 -0
  386. package/src/ingest/games/own-three-bundled/vgai.game.json +21 -0
  387. package/src/ingest/games/pointerlock-maze/game.js +288 -0
  388. package/src/ingest/games/pointerlock-maze/vgai.game.json +22 -0
  389. package/src/ingest/games-2d/_fixtures/first-party-scene.scn2d.json +124 -0
  390. package/src/ingest/games-2d/bubbo-bubbo/vgai.game.json +25 -0
  391. package/src/ingest/games-2d/bunnymark/game.ts +88 -0
  392. package/src/ingest/games-2d/bunnymark/vgai.game.json +21 -0
  393. package/src/ingest/games-2d/flappy/game.ts +148 -0
  394. package/src/ingest/games-2d/flappy/vgai.game.json +21 -0
  395. package/src/ingest/games-2d/flappy-pixi/vgai.game.json +25 -0
  396. package/src/ingest/games-2d/puzzling-potions/vgai.game.json +25 -0
  397. package/src/ingest/games-2d/tilemap-camera/game.ts +115 -0
  398. package/src/ingest/games-2d/tilemap-camera/vgai.game.json +21 -0
  399. package/src/ingest/games-react/react-rpg/entry.tsx +185 -0
  400. package/src/ingest/games-react/react-rpg/shims.d.ts +26 -0
  401. package/src/ingest/games-react/react-rpg/src/ui/game-state.tsx +129 -0
  402. package/src/ingest/games-react/react-rpg/vgai.game.json +21 -0
  403. package/src/ingest/host-namespace-registry.ts +196 -0
  404. package/src/ingest/types.ts +127 -0
  405. package/src/ingest-hierarchy-rows.ts +207 -0
  406. package/src/ingest-mode.ts +2092 -0
  407. package/src/ingest-siblings.ts +466 -0
  408. package/src/initial-scene.ts +194 -0
  409. package/src/inspector-section-registry.ts +117 -0
  410. package/src/interactive-edit-scope.ts +34 -0
  411. package/src/main.tsx +23 -0
  412. package/src/manifest-project.ts +43 -0
  413. package/src/model-thumbnail.ts +156 -0
  414. package/src/module-mode.ts +513 -0
  415. package/src/navmesh-handler.ts +94 -0
  416. package/src/packaged-runtime.ts +49 -0
  417. package/src/play-mode.ts +1148 -0
  418. package/src/playground/main.ts +206 -0
  419. package/src/project-manager.ts +173 -0
  420. package/src/scene-diff.ts +112 -0
  421. package/src/scene-serializer.ts +144 -0
  422. package/src/scene-sync.ts +587 -0
  423. package/src/scene-ui-mode/active-scene-ui.ts +58 -0
  424. package/src/scene-ui-mode/scene-ui-palette-panel.tsx +152 -0
  425. package/src/scene-ui-mode/scene-ui-timeline-tab.tsx +29 -0
  426. package/src/scene-ui-mode/ui-prefab-dialog-host.tsx +74 -0
  427. package/src/scene-ui-mode/ui-prefab-hierarchy-menu.ts +49 -0
  428. package/src/scene-ui-mode/ui-prefab-io.ts +76 -0
  429. package/src/script-loader.ts +143 -0
  430. package/src/storage/fsa-storage.ts +55 -0
  431. package/src/storage/handle-storage.ts +150 -0
  432. package/src/storage/handle-store.ts +116 -0
  433. package/src/storage/http-storage.ts +137 -0
  434. package/src/storage/index.ts +147 -0
  435. package/src/storage/mem-storage.ts +135 -0
  436. package/src/storage/opfs-storage.ts +25 -0
  437. package/src/storage/paths.ts +26 -0
  438. package/src/storage/seed.ts +106 -0
  439. package/src/storage/types.ts +110 -0
  440. package/src/tool-loader.ts +291 -0
  441. package/src/ui-editor/inspector.tsx +563 -0
  442. package/src/ui-editor/node-ops.ts +266 -0
  443. package/src/ui-editor/overlay.tsx +380 -0
  444. package/src/ui-editor/palette.tsx +114 -0
  445. package/src/ui-editor/react-store.ts +78 -0
  446. package/src/ui-editor/timeline-panel.tsx +443 -0
  447. package/src/ui-editor/ui-edit-store.ts +552 -0
  448. package/src/ui-editor/ui-prefab.ts +95 -0
  449. package/src/ui-source/inspect.ts +544 -0
  450. package/src/ui-source/oid-transform.ts +133 -0
  451. package/src/ui-source/source-write-backend.ts +203 -0
  452. package/src/ui-source/tailwind-map.ts +174 -0
  453. package/src/ui-source/writer.ts +1175 -0
  454. package/src/widgets/index.ts +73 -0
  455. package/template/.claude/skills/editor/SKILL.md +252 -0
  456. package/template/CLAUDE.md +512 -0
  457. package/template/LICENSE +30 -0
  458. package/template/emit-schemas.ts +42 -0
  459. package/template/index.html +17 -0
  460. package/template/package.json +53 -0
  461. package/template/project.json +9 -0
  462. package/template/public/audio/ambient-arena.wav +0 -0
  463. package/template/public/audio/ambient-music.wav +0 -0
  464. package/template/public/audio/ambient-nature.wav +0 -0
  465. package/template/public/audio/ambient-wind.wav +0 -0
  466. package/template/public/inputmaps/default.inputmap.json +41 -0
  467. package/template/public/jsm/libs/draco/gltf/draco_decoder.js +33 -0
  468. package/template/public/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
  469. package/template/public/jsm/libs/draco/gltf/draco_encoder.js +33 -0
  470. package/template/public/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
  471. package/template/public/scenes/default.vscn.json +43 -0
  472. package/template/public/scenes/untitled.vscn.json +29 -0
  473. package/template/public/textures/waternormals.jpg +0 -0
  474. package/template/server/colyseus-setup.ts +85 -0
  475. package/template/server/main.ts +34 -0
  476. package/template/server/rooms/arena-room.ts +420 -0
  477. package/template/server/rooms/game-room.ts +162 -0
  478. package/template/server/rooms.ts +15 -0
  479. package/template/server/tsconfig.json +19 -0
  480. package/template/src/data/README.md +40 -0
  481. package/template/src/data/assets.ts +31 -0
  482. package/template/src/data/tuning.data.json +5 -0
  483. package/template/src/data/tuning.schema.json +21 -0
  484. package/template/src/data/tuning.schema.ts +34 -0
  485. package/template/src/data/tuning.ts +24 -0
  486. package/template/src/main.ts +157 -0
  487. package/template/src/scripts/components/data-spinner.ts +45 -0
  488. package/template/src/scripts/components/scene-camera.ts +40 -0
  489. package/template/src/scripts/main.ts +94 -0
  490. package/template/src/scripts/registry.ts +46 -0
  491. package/template/src/tools/README.md +14 -0
  492. package/template/src/tools/example.tool.tsx +91 -0
  493. package/template/src/tools/spin.tool.tsx +101 -0
  494. package/template/src/ui/Button.tsx +27 -0
  495. package/template/src/ui/DefaultHud.tsx +40 -0
  496. package/template/src/ui/KeyHint.tsx +25 -0
  497. package/template/src/ui/Panel.tsx +52 -0
  498. package/template/src/ui/RebindPanel.tsx +129 -0
  499. package/template/src/ui/Slider.tsx +28 -0
  500. package/template/src/ui/StatusDot.tsx +22 -0
  501. package/template/src/ui/Toggle.tsx +19 -0
  502. package/template/src/ui/game-state.tsx +37 -0
  503. package/template/src/ui/index.ts +7 -0
  504. package/template/src/ui/mount-default-hud.tsx +19 -0
  505. package/template/src/ui/mount-ui.ts +15 -0
  506. package/template/tsconfig.json +36 -0
  507. package/template/validate-asset-content.ts +78 -0
  508. package/template/validate-manifest.ts +55 -0
  509. package/template/validate-scenes.ts +211 -0
  510. package/template/vgai.game.json +21 -0
  511. package/template/vite.config.ts +128 -0
  512. package/vite-plugin-example-assets.ts +60 -0
  513. package/vite-plugin-game-globals.ts +46 -0
  514. package/vite-plugin-game-static.ts +71 -0
  515. package/vite-plugin-project-game-static.ts +84 -0
  516. package/vite-plugin-react-game-static.ts +98 -0
  517. package/vite-plugin-react-world-provider.ts +81 -0
  518. package/vite-plugin-react-world-runtime.ts +76 -0
  519. package/vite-plugin-ui-oid.ts +561 -0
@@ -0,0 +1,475 @@
1
+ /**
2
+ * IngestAuthoringAdapter — the {@link AuthoringAdapter} for an UNMODIFIED external
3
+ * three.js game, implemented DIRECTLY over the game's live `Object3D` tree.
4
+ *
5
+ * This is the Phase-B anti-shim deliverable. The old `reflectSceneForIngest`
6
+ * fabricated a first-party `SceneEntity` for every foreign object so the
7
+ * `.vscn`-centric editor would accept it — the inversion run backwards. This
8
+ * adapter implements the SAME providers the editor already consumes
9
+ * (hierarchy/selection/transforms/inspector) with **no `SceneEntity` anywhere**:
10
+ * - hierarchy walks `scene.children`,
11
+ * - transforms read/write the `Object3D` pose,
12
+ * - inspector reflects real three.js fields (name, visibility, material color).
13
+ *
14
+ * Identity: each object gets a deterministic **structural-path id** (stable for a
15
+ * given scene shape — the basis for Phase-C overlay persistence) which is also
16
+ * written to `userData.entityId` so the editor's existing object map / gizmo /
17
+ * selection (which key on `entityId`) bind to foreign objects with no further
18
+ * shim.
19
+ *
20
+ * Persistence (Phase C): edits are accumulated into an {@link IngestOverlay} keyed
21
+ * by that stable id and saved to `.vgai/overlays/<game>.json`; on the next ingest
22
+ * the overlay is reapplied after the game rebuilds its scene. We never touch the
23
+ * game's source — same `PersistenceProvider` interface as `.vscn`, different
24
+ * source of truth.
25
+ *
26
+ * Selection is editor-global UI state, so it delegates to the store's selection
27
+ * set (shared with the first-party adapter); everything else is over `Object3D`.
28
+ */
29
+
30
+ import type {
31
+ AuthoringAdapter,
32
+ AuthoringCapabilities,
33
+ EditorNode,
34
+ HierarchyProvider,
35
+ InspectorProvider,
36
+ PersistenceProvider,
37
+ PickProvider,
38
+ PropertyDescriptor,
39
+ SelectionProvider,
40
+ Transform,
41
+ TransformOwner,
42
+ TransformProvider,
43
+ } from '@engine/adapter';
44
+ import {
45
+ applyOverlayToObjects,
46
+ assignStructuralIds,
47
+ colorMaterialOf,
48
+ } from '@engine/adapter/ingest/overlay-apply';
49
+ import type * as THREE from 'three';
50
+ import type { EditorStore } from '../editor-store';
51
+ import { captureSubtreeToVscn } from './ingest-capture';
52
+ import type { IngestOverlay } from './ingest-overlay';
53
+ import { notifyUndoableOp, registerUndoAdapter } from './undo-timeline';
54
+ import { raycastPick } from './viewport-raycast';
55
+
56
+ /** Loop-gate hooks so a stable edit can pause the game that owns the rAF. */
57
+ export interface IngestLoopControl {
58
+ pause(): void;
59
+ resume(): void;
60
+ }
61
+
62
+ export interface IngestAuthoringOptions {
63
+ loop?: IngestLoopControl | undefined;
64
+ /** Overlay to reapply once the scene is walked (edits from a previous session). */
65
+ overlay?: IngestOverlay | undefined;
66
+ /** Save callback for `persistence.save()`. Absence ⇒ runtime-only (persist:false). */
67
+ persist?: ((overlay: IngestOverlay) => Promise<void>) | undefined;
68
+ /**
69
+ * Human-readable overlay destination (design §2), e.g. `.vgai/overlays/<id>.json`
70
+ * (see `overlayPath()`). Absence (no `persist` callback) reads as runtime-only.
71
+ */
72
+ destination?: string | undefined;
73
+ /** The captured render camera — surfaced as an inspectable hierarchy node. */
74
+ camera?: THREE.Camera | undefined;
75
+ }
76
+
77
+ function kindOf(o: THREE.Object3D): EditorNode['kind'] {
78
+ const any = o as THREE.Object3D & {
79
+ isMesh?: boolean;
80
+ isLight?: boolean;
81
+ isCamera?: boolean;
82
+ };
83
+ if (any.isLight) return 'light';
84
+ if (any.isCamera) return 'camera';
85
+ if (any.isMesh) return 'mesh';
86
+ if (o.children.length > 0) return 'group';
87
+ return 'object';
88
+ }
89
+
90
+ /** The object as a Light (intensity + color), if it is one. */
91
+ function lightOf(o: THREE.Object3D): THREE.Light | null {
92
+ return (o as THREE.Object3D & { isLight?: boolean }).isLight ? (o as THREE.Light) : null;
93
+ }
94
+
95
+ /** The object as a PerspectiveCamera (fov/near/far), if it is one. */
96
+ function perspectiveCameraOf(o: THREE.Object3D): THREE.PerspectiveCamera | null {
97
+ const c = o as THREE.PerspectiveCamera & { isPerspectiveCamera?: boolean };
98
+ return c.isPerspectiveCamera ? c : null;
99
+ }
100
+
101
+ /** The object's mesh geometry, if it is a mesh. */
102
+ function geometryOf(o: THREE.Object3D): THREE.BufferGeometry | null {
103
+ const m = o as THREE.Mesh & { isMesh?: boolean };
104
+ return m.isMesh && m.geometry ? m.geometry : null;
105
+ }
106
+
107
+ export class IngestAuthoringAdapter implements AuthoringAdapter {
108
+ private readonly byId = new Map<string, THREE.Object3D>();
109
+ private readonly loop: IngestLoopControl | undefined;
110
+ private readonly persistFn: ((overlay: IngestOverlay) => Promise<void>) | undefined;
111
+ /** Human-readable overlay destination (design §2) — see {@link IngestAuthoringOptions.destination}. */
112
+ private readonly destination: string;
113
+ /** Captured render camera (not a scene child) surfaced as an extra root node. */
114
+ private readonly camera: THREE.Camera | undefined;
115
+ /** Accumulated edits (this session + reapplied overlay), keyed by stable id. */
116
+ private overlay: IngestOverlay = {};
117
+ private dirty = false;
118
+
119
+ readonly capabilities: AuthoringCapabilities;
120
+
121
+ constructor(
122
+ private readonly store: EditorStore,
123
+ private readonly scene: THREE.Scene,
124
+ opts: IngestAuthoringOptions = {},
125
+ ) {
126
+ this.loop = opts.loop;
127
+ this.persistFn = opts.persist;
128
+ this.destination =
129
+ opts.destination ?? (opts.persist ? '(ingest overlay)' : '(runtime-only — not persisted)');
130
+ this.camera = opts.camera;
131
+ // T3.2 slice 2: this adapter IS the undo/redo target the editor-global
132
+ // timeline dispatches to for entries it records below (`pushUndo`).
133
+ // `ingest-mode.ts`'s `exitIngestMode()` unregisters it on teardown, so a
134
+ // Ctrl+Z reaching an entry from a since-uninstalled ingest session drops it
135
+ // (with a warn) instead of undoing against a stale live scene.
136
+ registerUndoAdapter(this);
137
+ this.capabilities = {
138
+ transform: true,
139
+ material: true,
140
+ inspectorFields: true,
141
+ // Structural authoring (create/delete/reparent) is NOT supported for an
142
+ // ingested game: the foreign game owns + rebuilds its own scene graph, so a
143
+ // create/delete/reparent isn't deterministically reapplicable through the
144
+ // overlay. The overlay is bounded to per-object transform/material/visibility
145
+ // (visibility=false is the only "soft-delete" we can persist). This is the
146
+ // named Phase-F ceiling for external games — no `structure` provider here.
147
+ create: false,
148
+ delete: false,
149
+ reparent: false,
150
+ persist: !!this.persistFn, // runtime-only unless a persist callback is wired
151
+ };
152
+ this.refresh();
153
+ if (opts.overlay) this.applyOverlay(opts.overlay);
154
+ }
155
+
156
+ /**
157
+ * (Re)assign structural-path ids to every object and rebuild the id→object map.
158
+ * Idempotent and deterministic from scene structure (position + type + name),
159
+ * so the id re-binds to the same object after the game rebuilds its scene —
160
+ * the prerequisite for overlay persistence. Call again after streamed assets.
161
+ *
162
+ * Delegates the actual walk to `@engine/adapter/ingest/overlay-apply.ts`'s
163
+ * `assignStructuralIds` (T3.8, docs/DECISIONS-PENDING.md §D9) — the SAME
164
+ * structural-id scheme the ship-path runtime applier uses on an independent
165
+ * fresh walk, so ids minted here and ids minted there agree byte-for-byte.
166
+ */
167
+ refresh(): { count: number; meshes: number; lights: number } {
168
+ const { byId, count, meshes, lights } = assignStructuralIds(this.scene, this.camera);
169
+ this.byId.clear();
170
+ for (const [id, o] of byId) this.byId.set(id, o);
171
+ return { count, meshes, lights };
172
+ }
173
+
174
+ /**
175
+ * Result of the most recent {@link applyOverlay} call (constructor-time
176
+ * reapply, or a later `persistence.applyExternal`) — the seam `ingest-mode.ts`
177
+ * reads to build the D9 `OverlayApplyReport` (`./overlay-report.ts`) without
178
+ * re-deriving orphan/applied counts itself.
179
+ */
180
+ private _lastApplyResult: { applied: number; orphanedIds: string[] } = {
181
+ applied: 0,
182
+ orphanedIds: [],
183
+ };
184
+ get lastApplyResult(): { applied: number; orphanedIds: string[] } {
185
+ return this._lastApplyResult;
186
+ }
187
+
188
+ /**
189
+ * Reapply a saved overlay onto the (already-walked) live scene, and seed the
190
+ * in-memory overlay so a later save preserves it — INCLUDING orphaned ids
191
+ * (D9 fix: previously only entries that resolved to a live object were
192
+ * seeded, so an orphan `applyOverlay` couldn't resolve this session was
193
+ * silently DROPPED on the next `save()` — exactly the silent loss D9
194
+ * forbids; `ingest-authoring-adapter.test.ts`'s "orphaned entries survive a
195
+ * save round-trip" pins the fix). Ids absent from the current scene are
196
+ * "orphaned" — e.g. a dynamic child that didn't respawn, or the vendored
197
+ * game was bumped and restructured — never an error; reported (never
198
+ * silently) via `./overlay-report.ts`.
199
+ *
200
+ * The mutation loop itself (per-id resolve + apply position/rotation/scale/
201
+ * color/visible, collect orphans) delegates to `@engine/adapter/ingest/
202
+ * overlay-apply.ts`'s `applyOverlayToObjects` (T3.8) — the SAME core the
203
+ * ship-path runtime applier uses; this method adds only the editor-specific
204
+ * bookkeeping described above (seed EVERY entry, applied or orphaned, into
205
+ * `this.overlay`).
206
+ */
207
+ applyOverlay(overlay: IngestOverlay): {
208
+ applied: number;
209
+ orphaned: number;
210
+ orphanedIds: string[];
211
+ } {
212
+ const { applied, orphanedIds } = applyOverlayToObjects(this.byId, overlay);
213
+ for (const [id, ov] of Object.entries(overlay)) {
214
+ this.overlay[id] = { ...this.overlay[id], ...ov };
215
+ }
216
+ this._lastApplyResult = { applied, orphanedIds };
217
+ return { applied, orphaned: orphanedIds.length, orphanedIds };
218
+ }
219
+
220
+ private record(id: string, patch: Partial<IngestOverlay[string]>): void {
221
+ this.overlay[id] = { ...this.overlay[id], ...patch };
222
+ this.dirty = true;
223
+ }
224
+
225
+ private idOf(o: THREE.Object3D): string | null {
226
+ return (o.userData?.['entityId'] as string | undefined) ?? null;
227
+ }
228
+
229
+ private toNode(o: THREE.Object3D, parentId: string | null): EditorNode {
230
+ const id = this.idOf(o)!;
231
+ return {
232
+ id,
233
+ label: o.name || o.type || 'Object3D',
234
+ kind: kindOf(o),
235
+ parentId,
236
+ childIds: o.children.map((c) => this.idOf(c)!).filter(Boolean),
237
+ flags: { runtimeOnly: true, transformOwner: 'external' },
238
+ };
239
+ }
240
+
241
+ subscribe(listener: () => void): () => void {
242
+ return this.store.subscribe(listener);
243
+ }
244
+
245
+ readonly hierarchy: HierarchyProvider = {
246
+ roots: () => {
247
+ const roots = this.scene.children.map((o) => this.toNode(o, null));
248
+ if (this.camera) roots.push(this.toNode(this.camera, null));
249
+ return roots;
250
+ },
251
+ node: (id) => {
252
+ const o = this.byId.get(id);
253
+ if (!o) return null;
254
+ const parentId = o.parent && o.parent !== this.scene ? this.idOf(o.parent) : null;
255
+ return this.toNode(o, parentId);
256
+ },
257
+ object3D: (id) => this.byId.get(id) ?? null,
258
+ idForObject3D: (o) => this.idOf(o),
259
+ };
260
+
261
+ readonly selection: SelectionProvider = {
262
+ get: () => [...this.store.selectedEntityIds],
263
+ set: (ids) => this.store.selectMultiple(ids),
264
+ };
265
+
266
+ /**
267
+ * D12 (B4) — the threejs viewport raycast, delegating to the SHARED
268
+ * `raycastPick` helper (`viewport-raycast.ts`). An ingested foreign threejs
269
+ * game's objects sit in `store.objectMap` (`enterPlayScene`) tagged with
270
+ * `userData.entityId` — the SAME shape the raycast reads for a first-party
271
+ * scene — so this restores the pre-B4 canvas click-select that
272
+ * `_onPointerUp`'s unconditional raycast used to provide (both bare ingest
273
+ * mode, where THIS adapter is the active override, AND multi-world PLAY,
274
+ * where THIS adapter is the composite's threejs child the layered pick walk
275
+ * reaches last). The returned `entityId` routes through the store-backed
276
+ * `selection` above exactly as it did pre-B4.
277
+ */
278
+ readonly pickable: PickProvider = {
279
+ pick: (clientX, clientY) => raycastPick(this.store, clientX, clientY),
280
+ };
281
+
282
+ readonly transforms: TransformProvider = {
283
+ get: (id): Transform => {
284
+ const o = this.byId.get(id);
285
+ if (!o) return { position: [0, 0, 0], rotation: [0, 0, 0, 1], scale: [1, 1, 1] };
286
+ return {
287
+ position: o.position.toArray() as [number, number, number],
288
+ rotation: o.quaternion.toArray() as [number, number, number, number],
289
+ scale: o.scale.toArray() as [number, number, number],
290
+ };
291
+ },
292
+ // The game owns this object's transform; the editor freezes the game loop so
293
+ // its gizmo edit isn't overwritten next frame, then resumes on release.
294
+ owner: (): TransformOwner => 'external',
295
+ beginEdit: (id) => {
296
+ this.loop?.pause();
297
+ this.editStartTransform = this.transforms.get(id); // snapshot for undo
298
+ },
299
+ apply: (id, t) => this.writeTransform(id, t),
300
+ endEdit: (id) => {
301
+ this.loop?.resume();
302
+ const prev = this.editStartTransform;
303
+ this.editStartTransform = undefined;
304
+ if (!prev) return;
305
+ const next = this.transforms.get(id);
306
+ this.pushUndo({
307
+ undo: () => this.writeTransform(id, prev),
308
+ redo: () => this.writeTransform(id, next),
309
+ });
310
+ },
311
+ };
312
+
313
+ /** Write a transform to the live object + record it in the overlay (no undo push). */
314
+ private writeTransform(id: string, t: Transform): void {
315
+ const o = this.byId.get(id);
316
+ if (!o) return;
317
+ o.position.set(t.position[0], t.position[1], t.position[2]);
318
+ o.quaternion.set(t.rotation[0], t.rotation[1], t.rotation[2], t.rotation[3]);
319
+ o.scale.set(t.scale[0], t.scale[1], t.scale[2]);
320
+ this.record(id, { position: t.position, rotation: t.rotation, scale: t.scale });
321
+ }
322
+
323
+ readonly inspector: InspectorProvider = {
324
+ // The editor's generic InspectorProvider-driven panel renders THIS list — the
325
+ // reflectable three.js fields per object kind (no SceneEntity, no hardcoded UI).
326
+ properties: (id): PropertyDescriptor[] => {
327
+ const o = this.byId.get(id);
328
+ if (!o) return [];
329
+ const props: PropertyDescriptor[] = [
330
+ { path: 'name', label: 'Name', type: 'string' },
331
+ { path: 'visible', label: 'Visible', type: 'boolean' },
332
+ ];
333
+ if (colorMaterialOf(o)) props.push({ path: 'material.color', label: 'Color', type: 'color' });
334
+ const light = lightOf(o);
335
+ if (light) {
336
+ props.push({ path: 'light.intensity', label: 'Intensity', type: 'number' });
337
+ props.push({ path: 'light.color', label: 'Light Color', type: 'color' });
338
+ }
339
+ if (perspectiveCameraOf(o)) {
340
+ props.push({ path: 'camera.fov', label: 'FOV', type: 'number' });
341
+ props.push({ path: 'camera.near', label: 'Near', type: 'number' });
342
+ props.push({ path: 'camera.far', label: 'Far', type: 'number' });
343
+ }
344
+ if (geometryOf(o)) {
345
+ props.push({ path: 'mesh.vertices', label: 'Vertices', type: 'number', readonly: true });
346
+ // Shadow flags are reflectable from the mesh (Physics/Animation are N/A for a
347
+ // foreign object — no engine body/anim-graph — so they are simply absent).
348
+ props.push({ path: 'shadow.cast', label: 'Cast Shadow', type: 'boolean' });
349
+ props.push({ path: 'shadow.receive', label: 'Receive Shadow', type: 'boolean' });
350
+ }
351
+ return props;
352
+ },
353
+ get: (id, path) => {
354
+ const o = this.byId.get(id);
355
+ if (!o) return undefined;
356
+ if (path === 'name') return o.name;
357
+ if (path === 'visible') return o.visible;
358
+ if (path === 'material.color') {
359
+ const m = colorMaterialOf(o);
360
+ return m ? `#${m.color.getHexString()}` : undefined;
361
+ }
362
+ if (path === 'light.intensity') return lightOf(o)?.intensity;
363
+ if (path === 'light.color') {
364
+ const l = lightOf(o);
365
+ return l ? `#${l.color.getHexString()}` : undefined;
366
+ }
367
+ if (path === 'camera.fov') return perspectiveCameraOf(o)?.fov;
368
+ if (path === 'camera.near') return perspectiveCameraOf(o)?.near;
369
+ if (path === 'camera.far') return perspectiveCameraOf(o)?.far;
370
+ if (path === 'mesh.vertices') {
371
+ const g = geometryOf(o);
372
+ return g ? (g.getAttribute('position')?.count ?? 0) : undefined;
373
+ }
374
+ if (path === 'shadow.cast') return o.castShadow;
375
+ if (path === 'shadow.receive') return o.receiveShadow;
376
+ return undefined;
377
+ },
378
+ set: (id, path, value) => {
379
+ const prev = this.inspector.get(id, path); // snapshot for undo
380
+ this.writeProp(id, path, value);
381
+ this.pushUndo({
382
+ undo: () => this.writeProp(id, path, prev),
383
+ redo: () => this.writeProp(id, path, value),
384
+ });
385
+ },
386
+ };
387
+
388
+ /** Mutate one reflectable field on the live object + record persistable ones (no undo push). */
389
+ private writeProp(id: string, path: string, value: unknown): void {
390
+ const o = this.byId.get(id);
391
+ if (!o) return;
392
+ if (path === 'name') o.name = String(value);
393
+ else if (path === 'visible') {
394
+ o.visible = Boolean(value);
395
+ this.record(id, { visible: o.visible });
396
+ } else if (path === 'material.color') {
397
+ const m = colorMaterialOf(o);
398
+ if (m) {
399
+ m.color.set(String(value));
400
+ this.record(id, { color: `#${m.color.getHexString()}` });
401
+ }
402
+ } else if (path === 'light.intensity') {
403
+ const l = lightOf(o);
404
+ if (l) l.intensity = Number(value);
405
+ } else if (path === 'light.color') {
406
+ lightOf(o)?.color.set(String(value));
407
+ } else if (path === 'camera.fov' || path === 'camera.near' || path === 'camera.far') {
408
+ const c = perspectiveCameraOf(o);
409
+ if (c) {
410
+ (c as unknown as Record<string, number>)[path.split('.')[1]!] = Number(value);
411
+ c.updateProjectionMatrix();
412
+ }
413
+ } else if (path === 'shadow.cast') o.castShadow = Boolean(value);
414
+ else if (path === 'shadow.receive') o.receiveShadow = Boolean(value);
415
+ // (light/camera edits are live-only; transform/material/visibility persist via the overlay.)
416
+ this.store.notifyIngestEdit();
417
+ }
418
+
419
+ // --- Overlay-backed undo/redo (feat-edit-undo for ingested games) ---
420
+ private readonly undoStack: Array<{ undo: () => void; redo: () => void }> = [];
421
+ private readonly redoStack: Array<{ undo: () => void; redo: () => void }> = [];
422
+ private editStartTransform: Transform | undefined;
423
+
424
+ private pushUndo(entry: { undo: () => void; redo: () => void }): void {
425
+ this.undoStack.push(entry);
426
+ this.redoStack.length = 0;
427
+ notifyUndoableOp(this); // T3.2 slice 2 — tag this op on the global timeline
428
+ }
429
+
430
+ undo(): boolean {
431
+ // `false` tells the undo timeline "couldn't act — put the entry back"
432
+ // (see UndoAdapter in ./undo-timeline.ts).
433
+ const e = this.undoStack.pop();
434
+ if (!e) return false;
435
+ e.undo();
436
+ this.redoStack.push(e);
437
+ this.store.notifyIngestEdit();
438
+ return true;
439
+ }
440
+
441
+ redo(): boolean {
442
+ const e = this.redoStack.pop();
443
+ if (!e) return false;
444
+ e.redo();
445
+ this.undoStack.push(e);
446
+ this.store.notifyIngestEdit();
447
+ return true;
448
+ }
449
+
450
+ // A class GETTER, not a field initializer: a field initializer runs before the
451
+ // constructor body assigns `this.destination`, so a plain object-literal field
452
+ // would capture it as `undefined` (see the same fix on VgaiSceneAuthoringAdapter).
453
+ get persistence(): PersistenceProvider {
454
+ return {
455
+ isDirty: () => this.dirty,
456
+ save: async () => {
457
+ if (!this.persistFn) return;
458
+ await this.persistFn(this.overlay);
459
+ this.dirty = false;
460
+ },
461
+ serialize: () => this.overlay,
462
+ // Migration: capture selected ingested subtrees into first-party SceneEntities
463
+ // (the sanctioned ingest→.vscn bridge; geometry ceiling documented there).
464
+ captureToVscn: (ids) => captureSubtreeToVscn((id) => this.byId.get(id) ?? null, ids),
465
+ destination: this.destination,
466
+ // The reload contract (design §5): re-run the existing `applyOverlay` remount
467
+ // logic against the LIVE scene — it already IS the remount-free re-apply path
468
+ // (the constructor calls it the same way after the initial `refresh()`), just
469
+ // exposed here so a changed overlay file needn't remount the game.
470
+ applyExternal: (content) => {
471
+ this.applyOverlay(content as IngestOverlay);
472
+ },
473
+ };
474
+ }
475
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Capture-to-`.vscn` — the sanctioned migration bridge from an ingested game's live
3
+ * `Object3D` subtree to first-party `SceneEntity` descriptors (the founding doc's
4
+ * "Capture to VGAI" persistence mode: `ADAPTER-VISION-ORIGINAL.md` §'Persistence
5
+ * Strategies'). This is the ONE place outside the first-party adapter where the
6
+ * ingest path deliberately produces `SceneEntity` — turning "inspect a foreign
7
+ * game" into "author first-party content".
8
+ *
9
+ * HONEST CEILING (geometry): a vgai `SceneEntity.mesh` only expresses the built-in
10
+ * primitive types (box/sphere/plane/cylinder/capsule) or a GLTF `src`. A foreign
11
+ * game's arbitrary `BufferGeometry` is NOT expressible — recognizable primitives map
12
+ * faithfully; anything else captures as a placeholder `box` (transform/material/
13
+ * hierarchy are captured faithfully). This is the documented bound on capture.
14
+ */
15
+
16
+ import type { SceneEntity, SceneMesh } from '@engine/scene/scene-types';
17
+ import type * as THREE from 'three';
18
+
19
+ function meshOf(o: THREE.Object3D): SceneMesh | undefined {
20
+ const g = (o as THREE.Mesh).geometry;
21
+ if (!g) return undefined;
22
+ switch (g.type) {
23
+ case 'BoxGeometry':
24
+ return { type: 'box' };
25
+ case 'SphereGeometry':
26
+ return { type: 'sphere' };
27
+ case 'PlaneGeometry':
28
+ return { type: 'plane' };
29
+ case 'CylinderGeometry':
30
+ return { type: 'cylinder' };
31
+ case 'CapsuleGeometry':
32
+ return { type: 'capsule' };
33
+ default:
34
+ return { type: 'box' }; // foreign geometry not expressible → placeholder (ceiling)
35
+ }
36
+ }
37
+
38
+ function colorOf(o: THREE.Object3D): string | undefined {
39
+ const mat = (o as THREE.Mesh).material;
40
+ if (!mat || Array.isArray(mat)) return undefined;
41
+ const c = (mat as THREE.MeshStandardMaterial).color;
42
+ return c ? `#${c.getHexString()}` : undefined;
43
+ }
44
+
45
+ function lightTypeOf(o: THREE.Object3D): NonNullable<SceneEntity['light']>['type'] {
46
+ const t = o.type;
47
+ if (t.includes('Directional')) return 'directional';
48
+ if (t.includes('Hemisphere') || t.includes('Ambient')) return 'hemisphere';
49
+ if (t.includes('Spot')) return 'spot';
50
+ return 'point';
51
+ }
52
+
53
+ function captureOne(o: THREE.Object3D): SceneEntity {
54
+ const any = o as THREE.Object3D & { isMesh?: boolean; isLight?: boolean };
55
+ const e: SceneEntity = {
56
+ id: crypto.randomUUID(),
57
+ name: o.name || o.type || 'Object3D',
58
+ transform: {
59
+ position: o.position.toArray() as [number, number, number],
60
+ rotation: o.quaternion.toArray() as [number, number, number, number],
61
+ scale: o.scale.toArray() as [number, number, number],
62
+ },
63
+ visible: o.visible,
64
+ };
65
+ if (any.isMesh) {
66
+ e.mesh = meshOf(o);
67
+ const color = colorOf(o);
68
+ if (color) e.material = { type: 'standard', color };
69
+ }
70
+ if (any.isLight) {
71
+ e.light = { type: lightTypeOf(o), intensity: (o as THREE.Light).intensity };
72
+ }
73
+ const kids = o.children
74
+ .filter((c) => {
75
+ const a = c as THREE.Object3D & { isMesh?: boolean; isLight?: boolean };
76
+ return a.isMesh || a.isLight || c.children.length > 0;
77
+ })
78
+ .map(captureOne);
79
+ if (kids.length) e.children = kids;
80
+ return e;
81
+ }
82
+
83
+ /** Capture the given ingested object ids (+ their subtrees) as first-party SceneEntities. */
84
+ export function captureSubtreeToVscn(
85
+ getObject: (id: string) => THREE.Object3D | null,
86
+ ids: string[],
87
+ ): SceneEntity[] {
88
+ const out: SceneEntity[] = [];
89
+ for (const id of ids) {
90
+ const o = getObject(id);
91
+ if (o) out.push(captureOne(o));
92
+ }
93
+ return out;
94
+ }