@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.
- package/LICENSE +661 -0
- package/README.md +35 -0
- package/dist/a/b/c/test-nested.json +1 -0
- package/dist/assets/CCDIKSolver-Dz-ysxZF.js +1 -0
- package/dist/assets/SelectionBox-BXsv_6aH.js +1 -0
- package/dist/assets/browser-project-scripts-DOru4L1x.js +2 -0
- package/dist/assets/browserAll-CEkWYlFk.js +1 -0
- package/dist/assets/collision-world-DniMB0qq.glb +0 -0
- package/dist/assets/create-runtime-DYIWr3va.js +1 -0
- package/dist/assets/data-asset-Bb9sIM7i.js +5 -0
- package/dist/assets/esbuild-BHljloGq.wasm +0 -0
- package/dist/assets/fake-three-BMTQ9rl_.js +1 -0
- package/dist/assets/game-8SDZPPgH.js +1 -0
- package/dist/assets/game-BI111o5f.js +10 -0
- package/dist/assets/game-CevorxyV.js +1 -0
- package/dist/assets/game-Di1H0gFj.js +1 -0
- package/dist/assets/game-Di7XlywD.js +1 -0
- package/dist/assets/game-DjnCaw8R.js +1 -0
- package/dist/assets/game-Q9A0aXSV.js +1 -0
- package/dist/assets/game-pt_1lQLv.js +1 -0
- package/dist/assets/game-source-CvidN7AB.js +1 -0
- package/dist/assets/game-xsOho_wg.js +83 -0
- package/dist/assets/index-Bp5cecD6.js +12 -0
- package/dist/assets/index-CPVV6pjw.js +1 -0
- package/dist/assets/index-CpOnYyeD.js +213 -0
- package/dist/assets/index-Cs8xwv3e.js +1 -0
- package/dist/assets/index-DL7RcQ0n.js +27 -0
- package/dist/assets/index-Dr8H_6Rl.js +1 -0
- package/dist/assets/index-_bCaipUk.js +13650 -0
- package/dist/assets/ingest-siblings-Dz8eEqrV.js +1 -0
- package/dist/assets/mount-manifest-Bhk0ohsl.js +1 -0
- package/dist/assets/recast-navigation.wasm-compat-CvP4UGK6.js +1 -0
- package/dist/assets/scene-query-BivAHhJu.js +1 -0
- package/dist/assets/stats.module--VATS4Kh.js +1 -0
- package/dist/assets/webworkerAll-BLrH0F6F.js +1 -0
- package/dist/assets/xstate-animation-binding-1NGLmw6y.js +1 -0
- package/dist/assets/xstate.esm-BgUD33nl.js +3 -0
- package/dist/index.html +37 -0
- package/dist/ingest/racing-game/UPSTREAM.md +68 -0
- package/dist/ingest/racing-game/a/Inter-Black.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Black.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-BlackItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-BlackItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Bold.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Bold.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-BoldItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-BoldItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBold.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBold.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBoldItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBoldItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLight.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLight.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLightItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLightItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Italic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Italic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Light.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Light.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-LightItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-LightItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Medium.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Medium.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-MediumItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-MediumItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Regular.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Regular.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBold.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBold.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBoldItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBoldItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Thin.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Thin.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ThinItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ThinItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-italic.var.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-roman.var.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter.var.woff2 +0 -0
- package/dist/ingest/racing-game/a/index.css +1 -0
- package/dist/ingest/racing-game/images/bronze.png +0 -0
- package/dist/ingest/racing-game/images/gold.png +0 -0
- package/dist/ingest/racing-game/images/silver.png +0 -0
- package/dist/ingest/racing-game/index.html +15 -0
- package/dist/ingest/racing-game/models/chassis-draco.glb +0 -0
- package/dist/ingest/racing-game/models/track-draco.glb +0 -0
- package/dist/ingest/racing-game/models/wheel-draco.glb +0 -0
- package/dist/ingest/racing-game/rg-entry.js +1 -0
- package/dist/ingest/racing-game/sounds/accelerate.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/boost.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/crash.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/engine.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/honk.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/tire-brake.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/train.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/water.mp3 +0 -0
- package/dist/ingest/racing-game/textures/cursor.svg +3 -0
- package/dist/ingest/racing-game/textures/dikhololo_night_1k.hdr +0 -0
- package/dist/ingest/racing-game/textures/heightmap_1024.png +0 -0
- package/dist/ingest/racing-game/textures/mask.svg +11 -0
- package/dist/ingest/racing-game/textures/no_sound.png +0 -0
- package/dist/ingest/racing-game/three-r139.module.js +50617 -0
- package/dist/ingest/racing-game/vendor.js +179 -0
- package/dist/ingest/racing-game/vgai.game.json +20 -0
- package/dist/ingest/simcity/UPSTREAM.md +65 -0
- package/dist/ingest/simcity/favicon.ico +0 -0
- package/dist/ingest/simcity/fonts/Bebas-Regular.otf +0 -0
- package/dist/ingest/simcity/fonts/Digitalt.otf +0 -0
- package/dist/ingest/simcity/fonts/Stanberry.ttf +0 -0
- package/dist/ingest/simcity/icons/bulldozer-color.png +0 -0
- package/dist/ingest/simcity/icons/bulldozer.png +0 -0
- package/dist/ingest/simcity/icons/calendar.png +0 -0
- package/dist/ingest/simcity/icons/factory-color.png +0 -0
- package/dist/ingest/simcity/icons/factory.png +0 -0
- package/dist/ingest/simcity/icons/house-color.png +0 -0
- package/dist/ingest/simcity/icons/house.png +0 -0
- package/dist/ingest/simcity/icons/job.png +0 -0
- package/dist/ingest/simcity/icons/office.png +0 -0
- package/dist/ingest/simcity/icons/pause-color.png +0 -0
- package/dist/ingest/simcity/icons/pause.png +0 -0
- package/dist/ingest/simcity/icons/person.png +0 -0
- package/dist/ingest/simcity/icons/play-color.png +0 -0
- package/dist/ingest/simcity/icons/play.png +0 -0
- package/dist/ingest/simcity/icons/power-color.png +0 -0
- package/dist/ingest/simcity/icons/power-line-color.png +0 -0
- package/dist/ingest/simcity/icons/power.png +0 -0
- package/dist/ingest/simcity/icons/road-color.png +0 -0
- package/dist/ingest/simcity/icons/road.png +0 -0
- package/dist/ingest/simcity/icons/select-color.png +0 -0
- package/dist/ingest/simcity/icons/select.png +0 -0
- package/dist/ingest/simcity/icons/store-color.png +0 -0
- package/dist/ingest/simcity/index.html +94 -0
- package/dist/ingest/simcity/index.js +58 -0
- package/dist/ingest/simcity/main.css +233 -0
- package/dist/ingest/simcity/models/armored-truck.glb +0 -0
- package/dist/ingest/simcity/models/building-block-4floor-corner.glb +0 -0
- package/dist/ingest/simcity/models/building-block-4floor-short.glb +0 -0
- package/dist/ingest/simcity/models/building-block-5floor.glb +0 -0
- package/dist/ingest/simcity/models/building-burger-joint.glb +0 -0
- package/dist/ingest/simcity/models/building-cafe.glb +0 -0
- package/dist/ingest/simcity/models/building-casino.glb +0 -0
- package/dist/ingest/simcity/models/building-cinema.glb +0 -0
- package/dist/ingest/simcity/models/building-house-block-big.glb +0 -0
- package/dist/ingest/simcity/models/building-house-family-large.glb +0 -0
- package/dist/ingest/simcity/models/building-house-family-small.glb +0 -0
- package/dist/ingest/simcity/models/building-office-balcony.glb +0 -0
- package/dist/ingest/simcity/models/building-office-big.glb +0 -0
- package/dist/ingest/simcity/models/building-office-pyramid.glb +0 -0
- package/dist/ingest/simcity/models/building-office-tall.glb +0 -0
- package/dist/ingest/simcity/models/building-office.glb +0 -0
- package/dist/ingest/simcity/models/building-restaurant.glb +0 -0
- package/dist/ingest/simcity/models/building-skyscraper.glb +0 -0
- package/dist/ingest/simcity/models/car-ambulance-pickup.glb +0 -0
- package/dist/ingest/simcity/models/car-baywatch.glb +0 -0
- package/dist/ingest/simcity/models/car-hippie-van.glb +0 -0
- package/dist/ingest/simcity/models/car-passenger-race.glb +0 -0
- package/dist/ingest/simcity/models/car-passenger.glb +0 -0
- package/dist/ingest/simcity/models/car-police.glb +0 -0
- package/dist/ingest/simcity/models/car-taxi.glb +0 -0
- package/dist/ingest/simcity/models/car-tow-truck.glb +0 -0
- package/dist/ingest/simcity/models/car-truck-dump.glb +0 -0
- package/dist/ingest/simcity/models/car-veteran.glb +0 -0
- package/dist/ingest/simcity/models/construction-small.glb +0 -0
- package/dist/ingest/simcity/models/data-center.glb +0 -0
- package/dist/ingest/simcity/models/industry-factory-old.glb +0 -0
- package/dist/ingest/simcity/models/industry-factory.glb +0 -0
- package/dist/ingest/simcity/models/industry-refinery.glb +0 -0
- package/dist/ingest/simcity/models/industry-warehouse.glb +0 -0
- package/dist/ingest/simcity/models/power_line_pole_modified.glb +0 -0
- package/dist/ingest/simcity/models/tile-plain_grass.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-curve.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-end.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-intersection-t.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-intersection.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-straight.glb +0 -0
- package/dist/ingest/simcity/models/truck.glb +0 -0
- package/dist/ingest/simcity/statusIcons/no-power.png +0 -0
- package/dist/ingest/simcity/statusIcons/no-road-access.png +0 -0
- package/dist/ingest/simcity/textures/base.png +0 -0
- package/dist/ingest/simcity/textures/grid.png +0 -0
- package/dist/ingest/simcity/textures/specular.png +0 -0
- package/dist/ingest/simcity/three-r155.module.js +51466 -0
- package/dist/ingest/simcity/vgai.game.json +20 -0
- package/dist/ingest/tanks/UPSTREAM.md +69 -0
- package/dist/ingest/tanks/index.html +214 -0
- package/dist/ingest/tanks/models/pixelTank.fbx +0 -0
- package/dist/ingest/tanks/tanks-entry.js +92 -0
- package/dist/ingest/tanks/three-r170.module.js +54571 -0
- package/dist/ingest/tanks/vgai.game.json +20 -0
- package/dist/inputmaps/default.inputmap.json +41 -0
- package/dist/jsm/libs/draco/gltf/draco_decoder.js +33 -0
- package/dist/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
- package/dist/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
- package/dist/scenes/untitled.vscn.json +29 -0
- package/dist/textures/waternormals.jpg +0 -0
- package/dist-server/packaged.mjs +18155 -0
- package/package.json +36 -0
- package/server/asset-library-routes.ts +833 -0
- package/server/canonical-path.ts +50 -0
- package/server/cloud-asset-catalog.ts +101 -0
- package/server/dev.ts +350 -0
- package/server/editor-server.ts +1537 -0
- package/server/editor-sse.ts +73 -0
- package/server/game-globals-shadow.ts +58 -0
- package/server/local-asset-catalog.ts +163 -0
- package/server/packaged.ts +571 -0
- package/server/prod.ts +75 -0
- package/server/project-optimize-deps-entries.ts +149 -0
- package/server/project-view.ts +65 -0
- package/server/server-utils.ts +430 -0
- package/server/session-registry.ts +93 -0
- package/src/EditorContext.tsx +87 -0
- package/src/achieved-tier.ts +126 -0
- package/src/action-registry.ts +268 -0
- package/src/adapter-resolver.ts +1858 -0
- package/src/asset-events.ts +100 -0
- package/src/authoring/active-adapter.ts +98 -0
- package/src/authoring/active-systems.ts +50 -0
- package/src/authoring/boundary-authoring-adapter.ts +178 -0
- package/src/authoring/composite-authoring-adapter.ts +891 -0
- package/src/authoring/design-time-layers.ts +776 -0
- package/src/authoring/edit-mode-authoring.ts +643 -0
- package/src/authoring/embed-only-adapter.ts +57 -0
- package/src/authoring/ephemeral-persistence.ts +26 -0
- package/src/authoring/eyedropper-session.ts +69 -0
- package/src/authoring/first-party-hierarchy-menu.ts +74 -0
- package/src/authoring/first-party-inspector/AddSectionMenu.tsx +118 -0
- package/src/authoring/first-party-inspector/EnvironmentSection.tsx +550 -0
- package/src/authoring/first-party-inspector/HeaderSection.tsx +329 -0
- package/src/authoring/first-party-inspector/is-first-party-selection.ts +44 -0
- package/src/authoring/first-party-inspector/register.tsx +108 -0
- package/src/authoring/ingest-authoring-adapter.ts +475 -0
- package/src/authoring/ingest-capture.ts +94 -0
- package/src/authoring/ingest-game-adapter.ts +291 -0
- package/src/authoring/ingest-iframe-adapter.ts +38 -0
- package/src/authoring/ingest-iframe-reachable-adapter.ts +401 -0
- package/src/authoring/ingest-overlay.ts +193 -0
- package/src/authoring/layered-pick.ts +87 -0
- package/src/authoring/mount-failure-report.ts +112 -0
- package/src/authoring/overlay-report.ts +52 -0
- package/src/authoring/project-scenes.ts +111 -0
- package/src/authoring/react-dom-authoring-adapter.ts +795 -0
- package/src/authoring/react-inspector-section.tsx +1041 -0
- package/src/authoring/react-world-authoring-adapter.ts +2400 -0
- package/src/authoring/root-activation.ts +61 -0
- package/src/authoring/scene-ui-layer.ts +485 -0
- package/src/authoring/story-overlay.ts +48 -0
- package/src/authoring/ui-authoring-adapter.ts +325 -0
- package/src/authoring/ui-hierarchy-menu.ts +40 -0
- package/src/authoring/ui-inspector-sections.tsx +156 -0
- package/src/authoring/undo-timeline.ts +145 -0
- package/src/authoring/vgai-scene-authoring-adapter.ts +746 -0
- package/src/authoring/viewport-pick-context.ts +31 -0
- package/src/authoring/viewport-raycast.ts +80 -0
- package/src/authoring/world-overlay.ts +76 -0
- package/src/authoring/world-pan-state.ts +115 -0
- package/src/authoring/world-session-state.ts +102 -0
- package/src/authoring/world2d-authoring-adapter.ts +313 -0
- package/src/browser-external-watch.ts +98 -0
- package/src/browser-play-entry.ts +112 -0
- package/src/browser-project-scripts.ts +112 -0
- package/src/browser-transpile.ts +246 -0
- package/src/browser-watch-reload.ts +100 -0
- package/src/command-listener.ts +326 -0
- package/src/components/AlignToolbar.tsx +189 -0
- package/src/components/AppRoot.tsx +73 -0
- package/src/components/AssetBrowser.tsx +502 -0
- package/src/components/BottomPanel.tsx +326 -0
- package/src/components/BuildPanel.tsx +455 -0
- package/src/components/CameraInfo.tsx +56 -0
- package/src/components/CommandPalette.tsx +265 -0
- package/src/components/ConsolePanel.tsx +272 -0
- package/src/components/DataPanel.tsx +1268 -0
- package/src/components/DefaultEditorLayout.tsx +509 -0
- package/src/components/FolderTree.tsx +179 -0
- package/src/components/GameHierarchy.tsx +1349 -0
- package/src/components/Inspector.tsx +671 -0
- package/src/components/InspectorToolSection.tsx +74 -0
- package/src/components/MountFailureBanner.tsx +110 -0
- package/src/components/NetworkSection.tsx +109 -0
- package/src/components/OnlineAssetBrowser.tsx +448 -0
- package/src/components/OverlayReportBanner.tsx +122 -0
- package/src/components/PlayBar.tsx +356 -0
- package/src/components/PrefabSaveDialog.tsx +201 -0
- package/src/components/ProjectHeader.tsx +174 -0
- package/src/components/ProjectScreen.tsx +1004 -0
- package/src/components/RightPanel.tsx +166 -0
- package/src/components/RootActivateDialog.tsx +105 -0
- package/src/components/SaveDialog.tsx +197 -0
- package/src/components/SaveStatus.tsx +69 -0
- package/src/components/StatsOverlay.tsx +35 -0
- package/src/components/ToolHost.tsx +80 -0
- package/src/components/Toolbar.tsx +307 -0
- package/src/components/ViewportOverlay.tsx +234 -0
- package/src/components/ViewportPanel.tsx +369 -0
- package/src/components/WorldContextMenu.tsx +257 -0
- package/src/components/WorldSelectionOverlay.tsx +2003 -0
- package/src/components/WorldTextEditor.tsx +86 -0
- package/src/components/asset-viewers/AssetViewer.tsx +38 -0
- package/src/components/asset-viewers/AudioViewer.tsx +149 -0
- package/src/components/asset-viewers/GenericJsonViewer.tsx +77 -0
- package/src/components/asset-viewers/ImageViewer.tsx +65 -0
- package/src/components/asset-viewers/InputMapViewer.tsx +206 -0
- package/src/components/asset-viewers/ModelViewer.tsx +180 -0
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +236 -0
- package/src/components/data-csv.ts +301 -0
- package/src/components/entity-name-commit.ts +18 -0
- package/src/components/inspector-property-grouping.ts +65 -0
- package/src/components/inspector-widgets/AlignmentGrid.tsx +188 -0
- package/src/components/inspector-widgets/BorderEditor.tsx +305 -0
- package/src/components/inspector-widgets/ColorPicker.tsx +548 -0
- package/src/components/inspector-widgets/FilterEditor.tsx +108 -0
- package/src/components/inspector-widgets/FontPicker.tsx +196 -0
- package/src/components/inspector-widgets/GradientEditor.tsx +277 -0
- package/src/components/inspector-widgets/ScrubbableInput.tsx +152 -0
- package/src/components/inspector-widgets/ShadowEditor.tsx +332 -0
- package/src/components/inspector-widgets/color-utils.ts +202 -0
- package/src/components/inspector-widgets/gallery-main.tsx +167 -0
- package/src/components/inspector-widgets/index.ts +22 -0
- package/src/components/inspector-widgets/shared.tsx +135 -0
- package/src/components/inspectors/AnimationSection.tsx +143 -0
- package/src/components/inspectors/AudioSection.tsx +215 -0
- package/src/components/inspectors/BoneAttachmentSection.tsx +82 -0
- package/src/components/inspectors/CameraSection.tsx +105 -0
- package/src/components/inspectors/ComponentsSection.tsx +358 -0
- package/src/components/inspectors/JointsSection.tsx +255 -0
- package/src/components/inspectors/LightSection.tsx +116 -0
- package/src/components/inspectors/MaterialSection.tsx +1189 -0
- package/src/components/inspectors/MeshSection.tsx +99 -0
- package/src/components/inspectors/NavigationSection.tsx +58 -0
- package/src/components/inspectors/ParticleSection.tsx +681 -0
- package/src/components/inspectors/PhysicsSection.tsx +326 -0
- package/src/components/inspectors/PostProcessingSection.tsx +1290 -0
- package/src/components/inspectors/RenderingSection.tsx +109 -0
- package/src/components/inspectors/ShadowSection.tsx +84 -0
- package/src/components/inspectors/SplineSection.tsx +191 -0
- package/src/components/inspectors/TransformSection.tsx +191 -0
- package/src/components/inspectors/json-schema-fields.ts +281 -0
- package/src/components/inspectors/multi-edit-utils.ts +49 -0
- package/src/components/inspectors/zod-fields.ts +184 -0
- package/src/components/primitives/ColorInput.tsx +109 -0
- package/src/components/primitives/JsonInput.tsx +98 -0
- package/src/components/primitives/NumberInput.tsx +164 -0
- package/src/components/primitives/Panel.tsx +68 -0
- package/src/components/primitives/SectionHeader.tsx +45 -0
- package/src/components/primitives/Tooltip.tsx +82 -0
- package/src/components/primitives/Vec3Input.tsx +62 -0
- package/src/components/world-overlay-gestures.ts +1438 -0
- package/src/doctor/data-checks.ts +285 -0
- package/src/doctor/report.ts +386 -0
- package/src/editor-api.ts +700 -0
- package/src/editor-console.ts +194 -0
- package/src/editor-hotkeys.ts +441 -0
- package/src/editor-layers.ts +2 -0
- package/src/editor-mode.ts +28 -0
- package/src/editor-store.ts +2225 -0
- package/src/editor-viewport.ts +1924 -0
- package/src/entity-factory.ts +221 -0
- package/src/game-globals-prelude.ts +27 -0
- package/src/gated-globals.ts +131 -0
- package/src/generated/setup-by-example.generated.ts +53 -0
- package/src/gizmo-registry.ts +608 -0
- package/src/handle-system.ts +221 -0
- package/src/hierarchy-menu-registry.ts +70 -0
- package/src/hierarchy-rows.ts +73 -0
- package/src/hosted-example.ts +160 -0
- package/src/hotkeys.ts +142 -0
- package/src/ingest/discovery-react.ts +132 -0
- package/src/ingest/discovery.ts +205 -0
- package/src/ingest/discovery2d.ts +204 -0
- package/src/ingest/esm-entry-resolution.ts +102 -0
- package/src/ingest/games/bloom-composer/game.js +99 -0
- package/src/ingest/games/bloom-composer/vgai.game.json +21 -0
- package/src/ingest/games/embed-bundle/embed.html +10 -0
- package/src/ingest/games/embed-bundle/vgai.game.json +21 -0
- package/src/ingest/games/games-fps/assets/collision-world.glb +0 -0
- package/src/ingest/games/games-fps/game.js +479 -0
- package/src/ingest/games/games-fps/vgai.game.json +24 -0
- package/src/ingest/games/iframe-reachable/game-source.js +58 -0
- package/src/ingest/games/iframe-reachable/vgai.game.json +21 -0
- package/src/ingest/games/interactive-cubes/game.js +139 -0
- package/src/ingest/games/interactive-cubes/vgai.game.json +21 -0
- package/src/ingest/games/mismatched-three/fake-three.js +38 -0
- package/src/ingest/games/mismatched-three/game.js +10 -0
- package/src/ingest/games/mismatched-three/vgai.game.json +22 -0
- package/src/ingest/games/own-three-bundled/game.js +43 -0
- package/src/ingest/games/own-three-bundled/vgai.game.json +21 -0
- package/src/ingest/games/pointerlock-maze/game.js +288 -0
- package/src/ingest/games/pointerlock-maze/vgai.game.json +22 -0
- package/src/ingest/games-2d/_fixtures/first-party-scene.scn2d.json +124 -0
- package/src/ingest/games-2d/bubbo-bubbo/vgai.game.json +25 -0
- package/src/ingest/games-2d/bunnymark/game.ts +88 -0
- package/src/ingest/games-2d/bunnymark/vgai.game.json +21 -0
- package/src/ingest/games-2d/flappy/game.ts +148 -0
- package/src/ingest/games-2d/flappy/vgai.game.json +21 -0
- package/src/ingest/games-2d/flappy-pixi/vgai.game.json +25 -0
- package/src/ingest/games-2d/puzzling-potions/vgai.game.json +25 -0
- package/src/ingest/games-2d/tilemap-camera/game.ts +115 -0
- package/src/ingest/games-2d/tilemap-camera/vgai.game.json +21 -0
- package/src/ingest/games-react/react-rpg/entry.tsx +185 -0
- package/src/ingest/games-react/react-rpg/shims.d.ts +26 -0
- package/src/ingest/games-react/react-rpg/src/ui/game-state.tsx +129 -0
- package/src/ingest/games-react/react-rpg/vgai.game.json +21 -0
- package/src/ingest/host-namespace-registry.ts +196 -0
- package/src/ingest/types.ts +127 -0
- package/src/ingest-hierarchy-rows.ts +207 -0
- package/src/ingest-mode.ts +2092 -0
- package/src/ingest-siblings.ts +466 -0
- package/src/initial-scene.ts +194 -0
- package/src/inspector-section-registry.ts +117 -0
- package/src/interactive-edit-scope.ts +34 -0
- package/src/main.tsx +23 -0
- package/src/manifest-project.ts +43 -0
- package/src/model-thumbnail.ts +156 -0
- package/src/module-mode.ts +513 -0
- package/src/navmesh-handler.ts +94 -0
- package/src/packaged-runtime.ts +49 -0
- package/src/play-mode.ts +1148 -0
- package/src/playground/main.ts +206 -0
- package/src/project-manager.ts +173 -0
- package/src/scene-diff.ts +112 -0
- package/src/scene-serializer.ts +144 -0
- package/src/scene-sync.ts +587 -0
- package/src/scene-ui-mode/active-scene-ui.ts +58 -0
- package/src/scene-ui-mode/scene-ui-palette-panel.tsx +152 -0
- package/src/scene-ui-mode/scene-ui-timeline-tab.tsx +29 -0
- package/src/scene-ui-mode/ui-prefab-dialog-host.tsx +74 -0
- package/src/scene-ui-mode/ui-prefab-hierarchy-menu.ts +49 -0
- package/src/scene-ui-mode/ui-prefab-io.ts +76 -0
- package/src/script-loader.ts +143 -0
- package/src/storage/fsa-storage.ts +55 -0
- package/src/storage/handle-storage.ts +150 -0
- package/src/storage/handle-store.ts +116 -0
- package/src/storage/http-storage.ts +137 -0
- package/src/storage/index.ts +147 -0
- package/src/storage/mem-storage.ts +135 -0
- package/src/storage/opfs-storage.ts +25 -0
- package/src/storage/paths.ts +26 -0
- package/src/storage/seed.ts +106 -0
- package/src/storage/types.ts +110 -0
- package/src/tool-loader.ts +291 -0
- package/src/ui-editor/inspector.tsx +563 -0
- package/src/ui-editor/node-ops.ts +266 -0
- package/src/ui-editor/overlay.tsx +380 -0
- package/src/ui-editor/palette.tsx +114 -0
- package/src/ui-editor/react-store.ts +78 -0
- package/src/ui-editor/timeline-panel.tsx +443 -0
- package/src/ui-editor/ui-edit-store.ts +552 -0
- package/src/ui-editor/ui-prefab.ts +95 -0
- package/src/ui-source/inspect.ts +544 -0
- package/src/ui-source/oid-transform.ts +133 -0
- package/src/ui-source/source-write-backend.ts +203 -0
- package/src/ui-source/tailwind-map.ts +174 -0
- package/src/ui-source/writer.ts +1175 -0
- package/src/widgets/index.ts +73 -0
- package/template/.claude/skills/editor/SKILL.md +252 -0
- package/template/CLAUDE.md +512 -0
- package/template/LICENSE +30 -0
- package/template/emit-schemas.ts +42 -0
- package/template/index.html +17 -0
- package/template/package.json +53 -0
- package/template/project.json +9 -0
- package/template/public/audio/ambient-arena.wav +0 -0
- package/template/public/audio/ambient-music.wav +0 -0
- package/template/public/audio/ambient-nature.wav +0 -0
- package/template/public/audio/ambient-wind.wav +0 -0
- package/template/public/inputmaps/default.inputmap.json +41 -0
- package/template/public/jsm/libs/draco/gltf/draco_decoder.js +33 -0
- package/template/public/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
- package/template/public/jsm/libs/draco/gltf/draco_encoder.js +33 -0
- package/template/public/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
- package/template/public/scenes/default.vscn.json +43 -0
- package/template/public/scenes/untitled.vscn.json +29 -0
- package/template/public/textures/waternormals.jpg +0 -0
- package/template/server/colyseus-setup.ts +85 -0
- package/template/server/main.ts +34 -0
- package/template/server/rooms/arena-room.ts +420 -0
- package/template/server/rooms/game-room.ts +162 -0
- package/template/server/rooms.ts +15 -0
- package/template/server/tsconfig.json +19 -0
- package/template/src/data/README.md +40 -0
- package/template/src/data/assets.ts +31 -0
- package/template/src/data/tuning.data.json +5 -0
- package/template/src/data/tuning.schema.json +21 -0
- package/template/src/data/tuning.schema.ts +34 -0
- package/template/src/data/tuning.ts +24 -0
- package/template/src/main.ts +157 -0
- package/template/src/scripts/components/data-spinner.ts +45 -0
- package/template/src/scripts/components/scene-camera.ts +40 -0
- package/template/src/scripts/main.ts +94 -0
- package/template/src/scripts/registry.ts +46 -0
- package/template/src/tools/README.md +14 -0
- package/template/src/tools/example.tool.tsx +91 -0
- package/template/src/tools/spin.tool.tsx +101 -0
- package/template/src/ui/Button.tsx +27 -0
- package/template/src/ui/DefaultHud.tsx +40 -0
- package/template/src/ui/KeyHint.tsx +25 -0
- package/template/src/ui/Panel.tsx +52 -0
- package/template/src/ui/RebindPanel.tsx +129 -0
- package/template/src/ui/Slider.tsx +28 -0
- package/template/src/ui/StatusDot.tsx +22 -0
- package/template/src/ui/Toggle.tsx +19 -0
- package/template/src/ui/game-state.tsx +37 -0
- package/template/src/ui/index.ts +7 -0
- package/template/src/ui/mount-default-hud.tsx +19 -0
- package/template/src/ui/mount-ui.ts +15 -0
- package/template/tsconfig.json +36 -0
- package/template/validate-asset-content.ts +78 -0
- package/template/validate-manifest.ts +55 -0
- package/template/validate-scenes.ts +211 -0
- package/template/vgai.game.json +21 -0
- package/template/vite.config.ts +128 -0
- package/vite-plugin-example-assets.ts +60 -0
- package/vite-plugin-game-globals.ts +46 -0
- package/vite-plugin-game-static.ts +71 -0
- package/vite-plugin-project-game-static.ts +84 -0
- package/vite-plugin-react-game-static.ts +98 -0
- package/vite-plugin-react-world-provider.ts +81 -0
- package/vite-plugin-react-world-runtime.ts +76 -0
- 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
|
+
}
|