@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,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
|
+
}
|