@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,587 @@
1
+ import { loadingManager, resolveUrl } from '@engine/loader';
2
+ import { DEFAULTS } from '@engine/scene/defaults';
3
+ import { parseMaterialFile, parsePrefabFile } from '@engine/scene/parse';
4
+ import {
5
+ mergePrefabInstance,
6
+ type PrefabFile,
7
+ type SceneEntity,
8
+ type SceneEnvironment,
9
+ type SceneFile,
10
+ type SceneMaterial,
11
+ } from '@engine/scene/scene-types';
12
+ import { deleteUserData, getUserData, setUserData } from '@engine/scene/user-data';
13
+ import * as THREE from 'three';
14
+ import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
15
+ import type { BatchedRenderer, ParticleSystem } from 'three.quarks';
16
+ import { readProjectTextFile } from './editor-api';
17
+ import type { ShadingMode } from './editor-store';
18
+ import { createEntityObject, disposeEntityAnimation } from './entity-factory';
19
+
20
+ /**
21
+ * Resolve any scene-authored animated attachments whose target skeleton is
22
+ * available. Safe to call every editor frame: GLTF content arrives async, so
23
+ * an attachment that cannot resolve during buildPreview is picked up as soon
24
+ * as the named bone exists.
25
+ */
26
+ export function resolvePreviewBoneAttachments(objectMap: Map<string, THREE.Object3D>): void {
27
+ for (const object of objectMap.values()) {
28
+ const entity = getUserData(object, 'entity') as SceneEntity | undefined;
29
+ const attachment = entity?.boneAttachment;
30
+ if (!attachment) continue;
31
+ const target = objectMap.get(attachment.target);
32
+ const socket = target?.getObjectByName(attachment.bone);
33
+ if (!socket || !object.parent) continue;
34
+
35
+ // Preserve the editor's authored entity hierarchy. serializeScene derives
36
+ // the .vscn tree from that hierarchy, so physically reparenting here would
37
+ // make a top-level weapon disappear from the Play snapshot. Instead,
38
+ // evaluate socketWorld * authoredOffset into the object's existing parent
39
+ // space. The serializer keeps the authored offset for attachments.
40
+ socket.updateWorldMatrix(true, false);
41
+ object.parent.updateWorldMatrix(true, false);
42
+ const offset = entity.transform;
43
+ const offsetMatrix = new THREE.Matrix4().compose(
44
+ new THREE.Vector3().fromArray(offset?.position ?? DEFAULTS.transform.position),
45
+ new THREE.Quaternion().fromArray(offset?.rotation ?? DEFAULTS.transform.rotation),
46
+ new THREE.Vector3().fromArray(offset?.scale ?? DEFAULTS.transform.scale),
47
+ );
48
+ const localMatrix = object.parent.matrixWorld
49
+ .clone()
50
+ .invert()
51
+ .multiply(socket.matrixWorld)
52
+ .multiply(offsetMatrix);
53
+ localMatrix.decompose(object.position, object.quaternion, object.scale);
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Dispose the geometry + materials owned by an Object3D subtree (ED4/ED5).
59
+ *
60
+ * Skips nodes tagged `__sharedGeometry` (GLTF clones share geometry/materials
61
+ * with the cached source — freeing them corrupts the cache) and particle
62
+ * emitters (managed by three.quarks / unregisterParticles). Also stops any
63
+ * AnimationMixer so it doesn't leak.
64
+ */
65
+ function disposeObject3D(root: THREE.Object3D): void {
66
+ root.traverse((node) => {
67
+ if (getUserData(node, '_animMixer')) disposeEntityAnimation(node);
68
+ if (getUserData(node, '__sharedGeometry')) return;
69
+ if (getUserData(node, 'editorHelperType') === 'particles') return;
70
+ const mesh = node as THREE.Mesh;
71
+ if (mesh.geometry) mesh.geometry.dispose();
72
+ const mat = mesh.material as THREE.Material | THREE.Material[] | undefined;
73
+ if (mat) {
74
+ for (const m of Array.isArray(mat) ? mat : [mat]) m.dispose();
75
+ }
76
+ });
77
+ }
78
+
79
+ /** Cache for prefab files fetched for editor preview. */
80
+ const prefabPreviewCache = new Map<string, PrefabFile>();
81
+
82
+ /** Cache for loaded .mat.json materials. */
83
+ const materialRefCache = new Map<string, SceneMaterial>();
84
+
85
+ /** Cache for processed environment map textures. */
86
+ const envTextureCache = new Map<string, THREE.Texture>();
87
+ let _pmremGenerator: THREE.PMREMGenerator | null = null;
88
+
89
+ /** Resolve a prefab entity by fetching and merging the prefab definition. */
90
+ async function resolvePrefabEntity(entity: SceneEntity): Promise<SceneEntity> {
91
+ if (!entity.prefab) return entity;
92
+
93
+ let prefab = prefabPreviewCache.get(entity.prefab);
94
+ if (!prefab) {
95
+ try {
96
+ // Storage-first (so a just-saved OPFS prefab resolves in the browser build),
97
+ // HTTP fallback for shipped static prefabs.
98
+ const text = await readProjectTextFile(entity.prefab);
99
+ if (text === null) return entity;
100
+ prefab = parsePrefabFile(JSON.parse(text), entity.prefab);
101
+ prefabPreviewCache.set(entity.prefab, prefab);
102
+ } catch {
103
+ return entity;
104
+ }
105
+ }
106
+
107
+ return tryMergePrefabInstance(prefab.root, entity);
108
+ }
109
+
110
+ /**
111
+ * Editor-safe wrapper over `mergePrefabInstance`, which (since T1.10)
112
+ * re-validates the merged entity and THROWS on an invalid combination (e.g.
113
+ * prefab inline `material` + instance `materialRef`). The runtime loader is
114
+ * right to throw — but the editor's sync path must not crash the whole panel
115
+ * over one bad entity: log loudly and render the instance un-merged (its raw
116
+ * descriptor, no prefab expansion) so the user can see and fix it.
117
+ */
118
+ function tryMergePrefabInstance(prefabRoot: SceneEntity, entity: SceneEntity): SceneEntity {
119
+ try {
120
+ return mergePrefabInstance(prefabRoot, entity);
121
+ } catch (err) {
122
+ console.error(
123
+ `[scene-sync] invalid prefab instance "${entity.name}" (prefab: ${entity.prefab}) — rendering un-merged:`,
124
+ err,
125
+ );
126
+ return entity;
127
+ }
128
+ }
129
+
130
+ /** Invalidate the prefab preview cache (e.g. when a prefab is saved). */
131
+ export function clearPrefabPreviewCache(): void {
132
+ prefabPreviewCache.clear();
133
+ }
134
+
135
+ /** Invalidate the material ref cache (e.g. when a .mat.json is saved). */
136
+ export function clearMaterialRefCache(): void {
137
+ materialRefCache.clear();
138
+ }
139
+
140
+ /**
141
+ * If entity has materialRef, return a copy with material resolved from cache.
142
+ * If not cached yet, kicks off async load and returns entity as-is (will rebuild later).
143
+ */
144
+ function resolveEntityMaterial(
145
+ entity: SceneEntity,
146
+ objectMap: Map<string, THREE.Object3D>,
147
+ scene: THREE.Scene,
148
+ ): SceneEntity {
149
+ if (!entity.materialRef) return entity;
150
+
151
+ const cached = materialRefCache.get(entity.materialRef);
152
+ if (cached) {
153
+ return { ...entity, material: cached };
154
+ }
155
+
156
+ // Async load and rebuild when ready. Validated via Zod (T4.6) — a malformed
157
+ // .mat.json is logged loudly (SceneParseError names the file + issues)
158
+ // rather than corrupting the live preview with a partially-typed value; the
159
+ // entity just keeps its unresolved materialRef (same fallback as an
160
+ // unreachable/404 file).
161
+ const ref = entity.materialRef;
162
+ const id = entity.id;
163
+ fetch(resolveUrl(ref))
164
+ .then((res) => (res.ok ? res.json() : null))
165
+ .then((json) => (json === null ? null : parseMaterialFile(json, ref)))
166
+ .then((file) => {
167
+ if (!file) return;
168
+ materialRefCache.set(ref, file.material);
169
+ // Rebuild the entity's preview object with the loaded material
170
+ if (id) {
171
+ const resolved = { ...entity, material: file.material };
172
+ updateEntityPreview(resolved, objectMap, scene);
173
+ }
174
+ })
175
+ .catch((err) => {
176
+ console.error(`[scene-sync] invalid material ref "${ref}" — not resolved:`, err);
177
+ });
178
+
179
+ return entity;
180
+ }
181
+
182
+ // --- Particle system lifecycle ---
183
+
184
+ /** Register any particle system stashed in obj.userData with the BatchedRenderer. */
185
+ export function registerParticles(
186
+ obj: THREE.Object3D,
187
+ batchedRenderer: BatchedRenderer | null,
188
+ ): void {
189
+ if (!batchedRenderer) return;
190
+ const ps = getUserData(obj, '_particleSystem') as ParticleSystem | undefined;
191
+ if (ps) batchedRenderer.addSystem(ps);
192
+ // Also check children (e.g. prefab resolved objects)
193
+ for (const child of obj.children) {
194
+ registerParticles(child, batchedRenderer);
195
+ }
196
+ }
197
+
198
+ /** Unregister any particle system stashed in obj.userData from the BatchedRenderer. */
199
+ export function unregisterParticles(
200
+ obj: THREE.Object3D,
201
+ batchedRenderer: BatchedRenderer | null,
202
+ ): void {
203
+ if (!batchedRenderer) return;
204
+ const ps = getUserData(obj, '_particleSystem') as ParticleSystem | undefined;
205
+ if (ps) {
206
+ try {
207
+ batchedRenderer.deleteSystem(ps);
208
+ } catch {
209
+ // System may already be removed
210
+ }
211
+ }
212
+ for (const child of obj.children) {
213
+ unregisterParticles(child, batchedRenderer);
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Build the full Three.js preview from a SceneFile document.
219
+ * Returns a map of entity id → Object3D for lookup.
220
+ *
221
+ * Prefab handling follows the real-engine pattern (Unity/Unreal/Godot):
222
+ * - If the prefab file is already cached, resolve synchronously — the visual
223
+ * object IS the entity in the scene graph, no placeholder wrapper.
224
+ * - If not cached (first load), create a Group placeholder and resolve async.
225
+ * The placeholder carries the original instance descriptor (with `prefab`
226
+ * field) so serialization always preserves the prefab reference.
227
+ */
228
+ export function buildPreview(
229
+ doc: SceneFile,
230
+ scene: THREE.Scene,
231
+ shadingMode: ShadingMode = 'solid',
232
+ renderer?: THREE.WebGLRenderer,
233
+ batchedRenderer?: BatchedRenderer | null,
234
+ ): Map<string, THREE.Object3D> {
235
+ // Remove all editor-managed objects (tagged with entityId or envObject)
236
+ clearPreview(scene, batchedRenderer);
237
+
238
+ const objectMap = new Map<string, THREE.Object3D>();
239
+ const br = batchedRenderer ?? null;
240
+
241
+ function addEntities(entities: SceneEntity[], parent: THREE.Object3D): void {
242
+ for (const entity of entities) {
243
+ if (entity.prefab) {
244
+ addPrefabEntity(entity, parent, objectMap, br);
245
+ continue;
246
+ }
247
+
248
+ const resolved = resolveEntityMaterial(entity, objectMap, scene);
249
+ const obj = createEntityObject(resolved);
250
+ // Restore original (unresolved) entity so userData['entity'] doesn't have
251
+ // a stale resolved material from materialRef cache.
252
+ setUserData(obj, 'entity', JSON.parse(JSON.stringify(entity)) as SceneEntity);
253
+ obj.visible = entity.visible !== false;
254
+ parent.add(obj);
255
+ registerParticles(obj, br);
256
+ if (entity.id) objectMap.set(entity.id, obj);
257
+ if (entity.children) {
258
+ addEntities(entity.children, obj);
259
+ }
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Add a prefab instance to the scene graph.
265
+ *
266
+ * Two paths:
267
+ * 1. Cached (synchronous) — prefab already fetched. Create the visual object
268
+ * directly from merged data. This is the common path on rebuilds (undo,
269
+ * redo, external updates) and eliminates race conditions.
270
+ * 2. Not cached (async) — first encounter. Create a Group placeholder, fetch
271
+ * the prefab file, add visual as child when ready.
272
+ *
273
+ * In both cases, `userData['entity']` stores the ORIGINAL instance descriptor
274
+ * (with `prefab` field intact) — never the merged result.
275
+ */
276
+ function addPrefabEntity(
277
+ entity: SceneEntity,
278
+ parent: THREE.Object3D,
279
+ map: Map<string, THREE.Object3D>,
280
+ batchedR: BatchedRenderer | null,
281
+ ): void {
282
+ const cachedPrefab = prefabPreviewCache.get(entity.prefab!);
283
+
284
+ if (cachedPrefab) {
285
+ // --- Synchronous path: prefab cached, create visual directly ---
286
+ const merged = tryMergePrefabInstance(cachedPrefab.root, entity);
287
+ const obj = createEntityObject(merged);
288
+ setUserData(obj, 'entityId', entity.id as string);
289
+ // Store the ORIGINAL instance descriptor (with prefab ref) as source of truth.
290
+ setUserData(obj, 'entity', JSON.parse(JSON.stringify(entity)) as SceneEntity);
291
+ const t = entity.transform;
292
+ if (t?.position) obj.position.set(t.position[0], t.position[1], t.position[2]);
293
+ if (t?.rotation)
294
+ obj.quaternion.set(t.rotation[0], t.rotation[1], t.rotation[2], t.rotation[3]);
295
+ if (t?.scale) obj.scale.set(t.scale[0], t.scale[1], t.scale[2]);
296
+ obj.visible = entity.visible !== false;
297
+ parent.add(obj);
298
+ registerParticles(obj, batchedR);
299
+ if (entity.id) map.set(entity.id, obj);
300
+ if (merged.children) {
301
+ addEntities(merged.children, obj);
302
+ }
303
+ return;
304
+ }
305
+
306
+ // --- Async path: first load, prefab not yet fetched ---
307
+ const placeholder = new THREE.Group();
308
+ setUserData(placeholder, 'entityId', entity.id as string);
309
+ setUserData(placeholder, 'entity', JSON.parse(JSON.stringify(entity)) as SceneEntity);
310
+ const t = entity.transform;
311
+ if (t?.position) placeholder.position.set(t.position[0], t.position[1], t.position[2]);
312
+ if (t?.rotation)
313
+ placeholder.quaternion.set(t.rotation[0], t.rotation[1], t.rotation[2], t.rotation[3]);
314
+ if (t?.scale) placeholder.scale.set(t.scale[0], t.scale[1], t.scale[2]);
315
+ placeholder.visible = entity.visible !== false;
316
+ parent.add(placeholder);
317
+ if (entity.id) map.set(entity.id, placeholder);
318
+
319
+ // Fetch and populate
320
+ resolvePrefabEntity(entity).then((resolved) => {
321
+ const obj = createEntityObject(resolved);
322
+ // Visual child of the placeholder — strip entity tags so _buildEntityTree
323
+ // doesn't double-count it.
324
+ deleteUserData(obj, 'entityId');
325
+ deleteUserData(obj, 'entity');
326
+ placeholder.add(obj);
327
+ registerParticles(obj, batchedR);
328
+ if (resolved.children) {
329
+ addEntities(resolved.children, obj);
330
+ }
331
+ });
332
+ }
333
+
334
+ addEntities(doc.entities, scene);
335
+ resolvePreviewBoneAttachments(objectMap);
336
+
337
+ // Apply environment
338
+ applyEnvironment(doc.environment, scene, renderer);
339
+
340
+ // Apply shading mode override
341
+ if (shadingMode !== 'solid') {
342
+ applyShadingOverride(scene, shadingMode);
343
+ }
344
+
345
+ return objectMap;
346
+ }
347
+
348
+ /** Remove all objects tagged with entityId or envObject from the scene. */
349
+ export function clearPreview(scene: THREE.Scene, batchedRenderer?: BatchedRenderer | null): void {
350
+ const toRemove: THREE.Object3D[] = [];
351
+ scene.traverse((obj) => {
352
+ if (getUserData(obj, 'entityId') !== undefined || getUserData(obj, 'envObject')) {
353
+ toRemove.push(obj);
354
+ }
355
+ });
356
+ // Only remove top-level tagged objects (children are removed with parent)
357
+ for (const obj of toRemove) {
358
+ if (
359
+ obj.parent &&
360
+ getUserData(obj.parent, 'entityId') === undefined &&
361
+ !getUserData(obj.parent, 'envObject')
362
+ ) {
363
+ unregisterParticles(obj, batchedRenderer ?? null);
364
+ disposeObject3D(obj);
365
+ obj.parent.remove(obj);
366
+ }
367
+ }
368
+ // Clear fog and environment
369
+ scene.fog = null;
370
+ scene.environment = null;
371
+ }
372
+
373
+ /**
374
+ * Update a single entity's Object3D in the map.
375
+ * Rebuilds the object if structural properties changed, otherwise patches in place.
376
+ */
377
+ export function updateEntityPreview(
378
+ entity: SceneEntity,
379
+ objectMap: Map<string, THREE.Object3D>,
380
+ scene: THREE.Scene,
381
+ batchedRenderer?: BatchedRenderer | null,
382
+ ): void {
383
+ if (!entity.id) return;
384
+
385
+ const existing = objectMap.get(entity.id);
386
+ if (!existing) return;
387
+
388
+ // Unregister old particle systems (only on the entity itself, not child entities)
389
+ unregisterParticles(existing, batchedRenderer ?? null);
390
+
391
+ // Collect entity children to reparent (not helpers) and detach them so the
392
+ // disposal below doesn't free resources we're about to re-use.
393
+ const entityChildren: THREE.Object3D[] = [];
394
+ for (const child of [...existing.children]) {
395
+ if (getUserData(child, 'entityId') !== undefined && !getUserData(child, 'editorHelper')) {
396
+ entityChildren.push(child);
397
+ existing.remove(child);
398
+ }
399
+ }
400
+
401
+ // Rebuild the object in place
402
+ const parent = existing.parent ?? scene;
403
+ const index = parent.children.indexOf(existing);
404
+ // Dispose the old object's owned geometry/materials + animation (ED4/ED5).
405
+ disposeObject3D(existing);
406
+ parent.remove(existing);
407
+
408
+ // For prefab instances, build visual from merged data but keep original descriptor.
409
+ let visualEntity: SceneEntity;
410
+ if (entity.prefab) {
411
+ // The entity still has the prefab ref — resolve it for visual creation.
412
+ // Use cached prefab data (already loaded by buildPreview).
413
+ const cached = prefabPreviewCache.get(entity.prefab);
414
+ visualEntity = cached ? tryMergePrefabInstance(cached.root, entity) : entity;
415
+ } else {
416
+ visualEntity = resolveEntityMaterial(entity, objectMap, scene);
417
+ }
418
+ const newObj = createEntityObject(visualEntity);
419
+ // Restore the original (unresolved) entity as the source of truth.
420
+ // This preserves prefab ref, materialRef, and any instance-level overrides.
421
+ setUserData(newObj, 'entity', JSON.parse(JSON.stringify(entity)) as SceneEntity);
422
+ newObj.visible = entity.visible !== false;
423
+
424
+ // Reparent entity children from old object to new object
425
+ for (const child of entityChildren) {
426
+ newObj.add(child);
427
+ }
428
+
429
+ if (index >= 0) {
430
+ parent.children.splice(index, 0, newObj);
431
+ newObj.parent = parent;
432
+ } else {
433
+ parent.add(newObj);
434
+ }
435
+ objectMap.set(entity.id, newObj);
436
+
437
+ // Register new particle systems
438
+ registerParticles(newObj, batchedRenderer ?? null);
439
+ }
440
+
441
+ /** Apply scene environment settings (background, ambient light, fog, skybox/IBL). */
442
+ export function applyEnvironment(
443
+ env: SceneEnvironment | undefined,
444
+ scene: THREE.Scene,
445
+ renderer?: THREE.WebGLRenderer,
446
+ ): void {
447
+ // Always reset to defaults — callers (buildPreview, play-mode restore) clear the
448
+ // scene first, so we must re-apply even when env is undefined.
449
+ const DEFAULT_BG = '#aaaaaa';
450
+ const DEFAULT_AMBIENT_COLOR = '#ffffff';
451
+ const DEFAULT_AMBIENT_INTENSITY = 0.5;
452
+
453
+ scene.background = new THREE.Color(env?.background ?? DEFAULT_BG);
454
+
455
+ // Ambient light
456
+ const ambientColor = env?.ambient?.color ?? DEFAULT_AMBIENT_COLOR;
457
+ const ambientIntensity = env?.ambient?.intensity ?? DEFAULT_AMBIENT_INTENSITY;
458
+ const ambient = new THREE.AmbientLight(ambientColor, ambientIntensity);
459
+ setUserData(ambient, 'envObject', true);
460
+ scene.add(ambient);
461
+
462
+ if (env?.fog) {
463
+ if (env.fog.type === 'linear') {
464
+ // Fallbacks from DEFAULTS (T1.13) — this previously used `?? 10` for
465
+ // near while the runtime loader used the DEFAULTS value of 1, so the
466
+ // editor preview and the running game disagreed on default fog.
467
+ scene.fog = new THREE.Fog(
468
+ env.fog.color,
469
+ env.fog.near ?? DEFAULTS.fog.linear.near,
470
+ env.fog.far ?? DEFAULTS.fog.linear.far,
471
+ );
472
+ } else {
473
+ scene.fog = new THREE.FogExp2(
474
+ env.fog.color,
475
+ env.fog.density ?? DEFAULTS.fog.exponential.density,
476
+ );
477
+ }
478
+ }
479
+
480
+ // Skybox / IBL environment map
481
+ if (env?.skybox && renderer) {
482
+ const cached = envTextureCache.get(env.skybox);
483
+ if (cached) {
484
+ scene.background = cached;
485
+ scene.environment = cached;
486
+ scene.environmentIntensity = env.envMapIntensity ?? 1.0;
487
+ } else {
488
+ loadEnvTexture(env.skybox, renderer).then((texture) => {
489
+ if (texture) {
490
+ envTextureCache.set(env.skybox!, texture);
491
+ scene.background = texture;
492
+ scene.environment = texture;
493
+ scene.environmentIntensity = env.envMapIntensity ?? 1.0;
494
+ }
495
+ });
496
+ }
497
+ }
498
+ }
499
+
500
+ /** Load an equirectangular texture and process with PMREMGenerator for IBL. */
501
+ async function loadEnvTexture(
502
+ path: string,
503
+ renderer: THREE.WebGLRenderer,
504
+ ): Promise<THREE.Texture | null> {
505
+ try {
506
+ if (!_pmremGenerator) {
507
+ _pmremGenerator = new THREE.PMREMGenerator(renderer);
508
+ _pmremGenerator.compileEquirectangularShader();
509
+ }
510
+
511
+ const ext = path.split('.').pop()?.toLowerCase();
512
+ let rawTexture: THREE.Texture;
513
+
514
+ if (ext === 'hdr') {
515
+ rawTexture = await new RGBELoader(loadingManager).loadAsync(path);
516
+ } else {
517
+ rawTexture = await new THREE.TextureLoader(loadingManager).loadAsync(path);
518
+ }
519
+
520
+ rawTexture.mapping = THREE.EquirectangularReflectionMapping;
521
+ const envMap = _pmremGenerator.fromEquirectangular(rawTexture).texture;
522
+ rawTexture.dispose();
523
+ return envMap;
524
+ } catch {
525
+ return null;
526
+ }
527
+ }
528
+
529
+ function asMaterialArray(m: THREE.Material | THREE.Material[]): THREE.Material[] {
530
+ return Array.isArray(m) ? m : [m];
531
+ }
532
+
533
+ /**
534
+ * Apply (and revert) the viewport shading override on a single mesh.
535
+ *
536
+ * Reversible across all three modes so the toolbar toggle can re-shade live
537
+ * meshes without a full scene rebuild (ED1):
538
+ * - solid: restore the original material(s), clear wireframe.
539
+ * - wireframe: restore original material(s), set wireframe on.
540
+ * - unlit: swap in MeshBasicMaterial(s) built from the original color.
541
+ *
542
+ * The original material is stashed in `userData['__shadeOrig']` the first time
543
+ * the mesh is touched; unlit overrides are stashed in `__shadeUnlit` so they
544
+ * can be disposed when leaving unlit.
545
+ */
546
+ function applyShadingToMesh(mesh: THREE.Mesh, mode: ShadingMode): void {
547
+ if (getUserData(mesh, '__shadeOrig') === undefined) {
548
+ setUserData(mesh, '__shadeOrig', mesh.material);
549
+ }
550
+ const orig = getUserData(mesh, '__shadeOrig') as THREE.Material | THREE.Material[];
551
+ const unlit = getUserData(mesh, '__shadeUnlit') as THREE.Material[] | undefined;
552
+
553
+ if (mode === 'unlit') {
554
+ if (unlit) {
555
+ mesh.material = Array.isArray(orig) ? unlit : unlit[0]!;
556
+ return;
557
+ }
558
+ const created = asMaterialArray(orig).map((m) => {
559
+ const color = (m as THREE.MeshStandardMaterial).color?.clone() ?? new THREE.Color(0x888888);
560
+ return new THREE.MeshBasicMaterial({ color });
561
+ });
562
+ setUserData(mesh, '__shadeUnlit', created);
563
+ mesh.material = Array.isArray(orig) ? created : created[0]!;
564
+ return;
565
+ }
566
+
567
+ // solid or wireframe: restore originals and drop any unlit override.
568
+ mesh.material = orig;
569
+ if (unlit) {
570
+ for (const m of unlit) m.dispose();
571
+ deleteUserData(mesh, '__shadeUnlit');
572
+ }
573
+ const wire = mode === 'wireframe';
574
+ for (const m of asMaterialArray(orig)) {
575
+ if ('wireframe' in m) (m as THREE.MeshStandardMaterial).wireframe = wire;
576
+ }
577
+ }
578
+
579
+ /** Apply viewport shading override to all entity meshes. Reversible (ED1). */
580
+ export function applyShadingOverride(scene: THREE.Scene, mode: ShadingMode): void {
581
+ scene.traverse((obj) => {
582
+ if (getUserData(obj, 'entityId') === undefined) return;
583
+ const mesh = obj as THREE.Mesh;
584
+ if (!mesh.isMesh || !mesh.material) return;
585
+ applyShadingToMesh(mesh, mode);
586
+ });
587
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Scene-UI-mode chrome (E1.a/b/c, spec-27 §7 E1 — the remaining half of "Absorb
3
+ * scene-UI as a mode, not a separate app": palette, keyframe timeline, prefabs).
4
+ *
5
+ * Files under THIS folder are the §10 item-7 grep-ban exemption's home
6
+ * ("outside the migrated scene-UI-mode files"): they may import `UIEditStore`
7
+ * and its neighbours (`ui-editor/*`) freely, because they exist SPECIFICALLY to
8
+ * author a `.vscn` `ui` tree. The generic editor shell (`components/
9
+ * DefaultEditorLayout.tsx`, `components/GameHierarchy.tsx`, `components/
10
+ * BottomPanel.tsx`, …) may compose a `scene-ui-mode/*` component/hook by
11
+ * IMPORTING it (exactly like it already composes `GameHierarchy`/`RightPanel`),
12
+ * but must never import `UIEditStore` (or `SceneEntity`/`THREE.`) itself — that
13
+ * boundary is what keeps `rg "UIEditStore" src/components` a meaningful,
14
+ * zero-hit grep gate outside this folder.
15
+ *
16
+ * This module resolves WHICH `UIAuthoringAdapter` (if any) is the open
17
+ * project's live scene-UI child, so every other file in this folder has one
18
+ * place to ask "is scene-UI authoring live right now?" instead of re-deriving
19
+ * the composite-child lookup.
20
+ */
21
+
22
+ import { useSyncExternalStore } from 'react';
23
+ import { getAuthoringOverride } from '../authoring/active-adapter';
24
+ import { CompositeAuthoringAdapter } from '../authoring/composite-authoring-adapter';
25
+ import { UIAuthoringAdapter } from '../authoring/ui-authoring-adapter';
26
+ import { useEditorStore } from '../EditorContext';
27
+
28
+ /**
29
+ * The active `UIAuthoringAdapter`, whether it is installed directly as the
30
+ * override (the standalone harness's pre-C3 shape, `ui-editor/harness.tsx`) or
31
+ * nested as the edit-mode `CompositeAuthoringAdapter`'s `kind: 'scene-ui'`
32
+ * child (C3, the unified editor's real shape — at most one such child exists
33
+ * per open scene, `edit-mode-authoring.ts`'s `buildUIChild`). `null` when
34
+ * neither applies: no override installed, a bare non-UI override (ingest/
35
+ * play), or a composite with no scene-UI child yet (the open scene has no
36
+ * `ui` roots — §3.1's install gate).
37
+ */
38
+ export function resolveActiveSceneUIAdapter(): UIAuthoringAdapter | null {
39
+ const active = getAuthoringOverride();
40
+ if (active instanceof UIAuthoringAdapter) return active;
41
+ if (active instanceof CompositeAuthoringAdapter) {
42
+ const child = active.childAdapters().find((c) => c.kind === 'scene-ui');
43
+ return child?.adapter instanceof UIAuthoringAdapter ? child.adapter : null;
44
+ }
45
+ return null;
46
+ }
47
+
48
+ /**
49
+ * React-hook form of {@link resolveActiveSceneUIAdapter} — re-resolves on
50
+ * every `EditorStore` notify (edit-mode composite install/teardown, scene
51
+ * switch, undo/redo of the whole document all fire one), which is exactly the
52
+ * signal that can flip whether a scene-UI child exists at all.
53
+ */
54
+ export function useActiveSceneUIAdapter(): UIAuthoringAdapter | null {
55
+ const store = useEditorStore();
56
+ useSyncExternalStore(store.subscribe, store.getSnapshot);
57
+ return resolveActiveSceneUIAdapter();
58
+ }