@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,608 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gizmo registry — maps entity types to their editor visual representation.
|
|
3
|
+
*
|
|
4
|
+
* Inspired by Godot's EditorNode3DGizmoPlugin pattern: each entity type
|
|
5
|
+
* registers a gizmo definition that describes:
|
|
6
|
+
* - icon: a clickable billboard sprite (for selecting non-mesh entities)
|
|
7
|
+
* - helpers: wireframe visualizations (frustums, ranges, collider outlines)
|
|
8
|
+
*
|
|
9
|
+
* Icons are raycastable (for click-to-select). Helpers are visual-only.
|
|
10
|
+
* Both live on EDITOR_LAYER so the game camera never sees them.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
type ColliderWorldDimensions,
|
|
15
|
+
computeColliderWorldDimensions,
|
|
16
|
+
} from '@engine/scene/collider-dimensions';
|
|
17
|
+
import { DEFAULTS } from '@engine/scene/defaults';
|
|
18
|
+
import type {
|
|
19
|
+
SceneCollider,
|
|
20
|
+
SceneEntity,
|
|
21
|
+
SceneLight,
|
|
22
|
+
SceneSpline,
|
|
23
|
+
} from '@engine/scene/scene-types';
|
|
24
|
+
import { getUserData, setUserData } from '@engine/scene/user-data';
|
|
25
|
+
import * as THREE from 'three';
|
|
26
|
+
import { EDITOR_LAYER } from './editor-layers';
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Registry types
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
/** A gizmo definition for a particular entity feature. */
|
|
33
|
+
interface GizmoEntry {
|
|
34
|
+
/**
|
|
35
|
+
* Test whether this gizmo applies to the given entity.
|
|
36
|
+
* Returning false skips both icon and helpers.
|
|
37
|
+
*/
|
|
38
|
+
test(entity: SceneEntity, obj: THREE.Object3D): boolean;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a clickable icon billboard.
|
|
42
|
+
* The sprite is NOT tagged editorHelper — it participates in raycast selection.
|
|
43
|
+
*/
|
|
44
|
+
createIcon(): THREE.Sprite;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Create wireframe helper objects (frustums, ranges, outlines, etc.).
|
|
48
|
+
* Each returned object is tagged editorHelper and excluded from raycasts.
|
|
49
|
+
*/
|
|
50
|
+
createHelpers(entity: SceneEntity, obj: THREE.Object3D): THREE.Object3D[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Icon billboard factory (shared by all entries)
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
const iconTextureCache = new Map<string, THREE.Texture>();
|
|
58
|
+
|
|
59
|
+
function makeIconSprite(symbol: string, cacheKey: string): THREE.Sprite {
|
|
60
|
+
let texture = iconTextureCache.get(cacheKey);
|
|
61
|
+
if (!texture) {
|
|
62
|
+
const size = 64;
|
|
63
|
+
const c = document.createElement('canvas');
|
|
64
|
+
c.width = size;
|
|
65
|
+
c.height = size;
|
|
66
|
+
const ctx = c.getContext('2d')!;
|
|
67
|
+
ctx.font = '48px system-ui, -apple-system, sans-serif';
|
|
68
|
+
ctx.textAlign = 'center';
|
|
69
|
+
ctx.textBaseline = 'middle';
|
|
70
|
+
ctx.fillText(symbol, size / 2, size / 2);
|
|
71
|
+
texture = new THREE.CanvasTexture(c);
|
|
72
|
+
iconTextureCache.set(cacheKey, texture);
|
|
73
|
+
}
|
|
74
|
+
const mat = new THREE.SpriteMaterial({ map: texture, transparent: true, depthTest: false });
|
|
75
|
+
const sprite = new THREE.Sprite(mat);
|
|
76
|
+
sprite.scale.set(0.5, 0.5, 0.5);
|
|
77
|
+
setUserData(sprite, 'editorIcon', true);
|
|
78
|
+
sprite.layers.set(EDITOR_LAYER);
|
|
79
|
+
return sprite;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// Shared geometry helpers
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
function createCircleGeometry(radius: number, segments = 32): THREE.BufferGeometry {
|
|
87
|
+
const pts: THREE.Vector3[] = [];
|
|
88
|
+
for (let i = 0; i <= segments; i++) {
|
|
89
|
+
const angle = (i / segments) * Math.PI * 2;
|
|
90
|
+
pts.push(new THREE.Vector3(Math.cos(angle) * radius, Math.sin(angle) * radius, 0));
|
|
91
|
+
}
|
|
92
|
+
return new THREE.BufferGeometry().setFromPoints(pts);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function createSemiArcGeometry(
|
|
96
|
+
radius: number,
|
|
97
|
+
centerY: number,
|
|
98
|
+
plane: 'xz' | 'yz',
|
|
99
|
+
top: boolean,
|
|
100
|
+
segments = 16,
|
|
101
|
+
): THREE.BufferGeometry {
|
|
102
|
+
const pts: THREE.Vector3[] = [];
|
|
103
|
+
const startAngle = top ? 0 : Math.PI;
|
|
104
|
+
for (let i = 0; i <= segments; i++) {
|
|
105
|
+
const angle = startAngle + (i / segments) * Math.PI;
|
|
106
|
+
const h = Math.cos(angle) * radius;
|
|
107
|
+
const v = centerY + Math.sin(angle) * radius;
|
|
108
|
+
if (plane === 'xz') {
|
|
109
|
+
pts.push(new THREE.Vector3(h, v, 0));
|
|
110
|
+
} else {
|
|
111
|
+
pts.push(new THREE.Vector3(0, v, h));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return new THREE.BufferGeometry().setFromPoints(pts);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Light gizmo
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
const COLLIDER_COLOR = 0x00ff88;
|
|
122
|
+
const SENSOR_COLOR = 0xffcc00;
|
|
123
|
+
|
|
124
|
+
function createLightHelper(light: THREE.Light, def: SceneLight): THREE.Object3D | null {
|
|
125
|
+
switch (def.type) {
|
|
126
|
+
case 'directional':
|
|
127
|
+
return new THREE.DirectionalLightHelper(light as THREE.DirectionalLight, 1);
|
|
128
|
+
case 'point':
|
|
129
|
+
return new THREE.PointLightHelper(light as THREE.PointLight, 0.5);
|
|
130
|
+
case 'spot':
|
|
131
|
+
return new THREE.SpotLightHelper(light as THREE.SpotLight);
|
|
132
|
+
default:
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function createLightRangeHelper(distance: number, color: string): THREE.Object3D {
|
|
138
|
+
const mat = new THREE.LineBasicMaterial({
|
|
139
|
+
color: color,
|
|
140
|
+
transparent: true,
|
|
141
|
+
opacity: 0.3,
|
|
142
|
+
depthTest: false,
|
|
143
|
+
});
|
|
144
|
+
const group = new THREE.Group();
|
|
145
|
+
group.add(new THREE.Line(createCircleGeometry(distance, 48), mat));
|
|
146
|
+
const xzRing = new THREE.Line(createCircleGeometry(distance, 48), mat);
|
|
147
|
+
xzRing.rotation.x = -Math.PI / 2;
|
|
148
|
+
group.add(xzRing);
|
|
149
|
+
const yzRing = new THREE.Line(createCircleGeometry(distance, 48), mat);
|
|
150
|
+
yzRing.rotation.y = Math.PI / 2;
|
|
151
|
+
group.add(yzRing);
|
|
152
|
+
return group;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const lightGizmo: GizmoEntry = {
|
|
156
|
+
test: (entity) => !!entity.light,
|
|
157
|
+
|
|
158
|
+
createIcon: () => makeIconSprite('☀', 'light'),
|
|
159
|
+
|
|
160
|
+
createHelpers(entity, obj) {
|
|
161
|
+
const helpers: THREE.Object3D[] = [];
|
|
162
|
+
const lightDef = entity.light!;
|
|
163
|
+
|
|
164
|
+
if (obj instanceof THREE.Light) {
|
|
165
|
+
const helper = createLightHelper(obj, lightDef);
|
|
166
|
+
if (helper) helpers.push(helper);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if ((lightDef.type === 'point' || lightDef.type === 'spot') && lightDef.distance) {
|
|
170
|
+
helpers.push(
|
|
171
|
+
createLightRangeHelper(lightDef.distance, lightDef.color ?? DEFAULTS.light.color),
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return helpers;
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
// Camera gizmo
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
|
|
183
|
+
function createFrustumHelper(camera: THREE.Camera): THREE.LineSegments {
|
|
184
|
+
const inv = camera.projectionMatrixInverse;
|
|
185
|
+
const ndc = [
|
|
186
|
+
[-1, -1, -1],
|
|
187
|
+
[1, -1, -1],
|
|
188
|
+
[1, 1, -1],
|
|
189
|
+
[-1, 1, -1],
|
|
190
|
+
[-1, -1, 1],
|
|
191
|
+
[1, -1, 1],
|
|
192
|
+
[1, 1, 1],
|
|
193
|
+
[-1, 1, 1],
|
|
194
|
+
];
|
|
195
|
+
const c = ndc.map(([x, y, z]) => new THREE.Vector3(x, y, z).applyMatrix4(inv));
|
|
196
|
+
const [n0, n1, n2, n3, f0, f1, f2, f3] = c as [
|
|
197
|
+
THREE.Vector3,
|
|
198
|
+
THREE.Vector3,
|
|
199
|
+
THREE.Vector3,
|
|
200
|
+
THREE.Vector3,
|
|
201
|
+
THREE.Vector3,
|
|
202
|
+
THREE.Vector3,
|
|
203
|
+
THREE.Vector3,
|
|
204
|
+
THREE.Vector3,
|
|
205
|
+
];
|
|
206
|
+
const verts: number[] = [];
|
|
207
|
+
function line(a: THREE.Vector3, b: THREE.Vector3) {
|
|
208
|
+
verts.push(a.x, a.y, a.z, b.x, b.y, b.z);
|
|
209
|
+
}
|
|
210
|
+
line(n0, n1);
|
|
211
|
+
line(n1, n2);
|
|
212
|
+
line(n2, n3);
|
|
213
|
+
line(n3, n0);
|
|
214
|
+
line(f0, f1);
|
|
215
|
+
line(f1, f2);
|
|
216
|
+
line(f2, f3);
|
|
217
|
+
line(f3, f0);
|
|
218
|
+
line(n0, f0);
|
|
219
|
+
line(n1, f1);
|
|
220
|
+
line(n2, f2);
|
|
221
|
+
line(n3, f3);
|
|
222
|
+
|
|
223
|
+
const geo = new THREE.BufferGeometry();
|
|
224
|
+
geo.setAttribute('position', new THREE.Float32BufferAttribute(verts, 3));
|
|
225
|
+
return new THREE.LineSegments(
|
|
226
|
+
geo,
|
|
227
|
+
new THREE.LineBasicMaterial({ color: 0xffaa00, depthTest: false }),
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const cameraGizmo: GizmoEntry = {
|
|
232
|
+
test: (entity) => !!entity.camera,
|
|
233
|
+
|
|
234
|
+
createIcon: () => makeIconSprite('📷', 'camera'),
|
|
235
|
+
|
|
236
|
+
createHelpers(entity, obj) {
|
|
237
|
+
// Find existing Three.js camera (editor entities wrap in a Group with a Camera child)
|
|
238
|
+
let camera: THREE.Camera | null = null;
|
|
239
|
+
if (obj instanceof THREE.Camera) {
|
|
240
|
+
camera = obj;
|
|
241
|
+
} else {
|
|
242
|
+
obj.traverse((child) => {
|
|
243
|
+
if (!camera && child instanceof THREE.Camera) camera = child;
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Game scene objects don't have a Three.js Camera — build a temporary one from the entity def
|
|
248
|
+
if (!camera && entity.camera) {
|
|
249
|
+
const def = entity.camera;
|
|
250
|
+
const dCam = DEFAULTS.camera;
|
|
251
|
+
const aspect = (def.width ?? dCam.width) / (def.height ?? dCam.height);
|
|
252
|
+
if (def.type === 'orthographic') {
|
|
253
|
+
const size = def.orthoSize ?? dCam.orthoSize;
|
|
254
|
+
const ortho = new THREE.OrthographicCamera(
|
|
255
|
+
-size * aspect,
|
|
256
|
+
size * aspect,
|
|
257
|
+
size,
|
|
258
|
+
-size,
|
|
259
|
+
def.near ?? dCam.near,
|
|
260
|
+
def.far ?? dCam.far,
|
|
261
|
+
);
|
|
262
|
+
ortho.updateProjectionMatrix();
|
|
263
|
+
camera = ortho;
|
|
264
|
+
} else {
|
|
265
|
+
const persp = new THREE.PerspectiveCamera(
|
|
266
|
+
def.fov ?? dCam.fov,
|
|
267
|
+
aspect,
|
|
268
|
+
def.near ?? dCam.near,
|
|
269
|
+
def.far ?? dCam.far,
|
|
270
|
+
);
|
|
271
|
+
persp.updateProjectionMatrix();
|
|
272
|
+
camera = persp;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return camera ? [createFrustumHelper(camera)] : [];
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// ---------------------------------------------------------------------------
|
|
281
|
+
// Audio gizmo
|
|
282
|
+
// ---------------------------------------------------------------------------
|
|
283
|
+
|
|
284
|
+
function createAudioHelper(
|
|
285
|
+
spatial: boolean,
|
|
286
|
+
refDistance: number,
|
|
287
|
+
maxDistance: number,
|
|
288
|
+
): THREE.Object3D {
|
|
289
|
+
const group = new THREE.Group();
|
|
290
|
+
const mat = new THREE.LineBasicMaterial({
|
|
291
|
+
color: 0x44bbff,
|
|
292
|
+
transparent: true,
|
|
293
|
+
opacity: 0.6,
|
|
294
|
+
depthTest: false,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Small speaker cone indicator
|
|
298
|
+
const speakerVerts: number[] = [];
|
|
299
|
+
speakerVerts.push(-0.1, -0.15, 0, -0.1, 0.15, 0);
|
|
300
|
+
speakerVerts.push(-0.1, 0.15, 0, -0.1, -0.15, 0);
|
|
301
|
+
speakerVerts.push(-0.1, 0.15, 0, 0.2, 0.3, 0);
|
|
302
|
+
speakerVerts.push(-0.1, -0.15, 0, 0.2, -0.3, 0);
|
|
303
|
+
speakerVerts.push(0.2, 0.3, 0, 0.2, -0.3, 0);
|
|
304
|
+
const speakerGeo = new THREE.BufferGeometry();
|
|
305
|
+
speakerGeo.setAttribute('position', new THREE.Float32BufferAttribute(speakerVerts, 3));
|
|
306
|
+
group.add(new THREE.LineSegments(speakerGeo, mat));
|
|
307
|
+
|
|
308
|
+
if (spatial) {
|
|
309
|
+
const refMat = new THREE.LineBasicMaterial({
|
|
310
|
+
color: 0x44bbff,
|
|
311
|
+
transparent: true,
|
|
312
|
+
opacity: 0.4,
|
|
313
|
+
depthTest: false,
|
|
314
|
+
});
|
|
315
|
+
const refRing = new THREE.Line(createCircleGeometry(refDistance, 32), refMat);
|
|
316
|
+
refRing.rotation.x = -Math.PI / 2;
|
|
317
|
+
group.add(refRing);
|
|
318
|
+
|
|
319
|
+
if (maxDistance < 10000) {
|
|
320
|
+
const maxMat = new THREE.LineBasicMaterial({
|
|
321
|
+
color: 0x44bbff,
|
|
322
|
+
transparent: true,
|
|
323
|
+
opacity: 0.15,
|
|
324
|
+
depthTest: false,
|
|
325
|
+
});
|
|
326
|
+
const maxRing = new THREE.Line(createCircleGeometry(maxDistance, 48), maxMat);
|
|
327
|
+
maxRing.rotation.x = -Math.PI / 2;
|
|
328
|
+
group.add(maxRing);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return group;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const audioGizmo: GizmoEntry = {
|
|
336
|
+
test: (entity) => !!entity.audio,
|
|
337
|
+
|
|
338
|
+
createIcon: () => makeIconSprite('🔊', 'audio'),
|
|
339
|
+
|
|
340
|
+
createHelpers(entity) {
|
|
341
|
+
const audioDef = entity.audio!;
|
|
342
|
+
const spatial = audioDef.spatial !== false;
|
|
343
|
+
return [
|
|
344
|
+
createAudioHelper(
|
|
345
|
+
spatial,
|
|
346
|
+
audioDef.refDistance ?? DEFAULTS.audio.refDistance,
|
|
347
|
+
audioDef.maxDistance ?? DEFAULTS.audio.maxDistance,
|
|
348
|
+
),
|
|
349
|
+
];
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// ---------------------------------------------------------------------------
|
|
354
|
+
// Collider gizmo (no icon — colliders are on mesh entities)
|
|
355
|
+
// ---------------------------------------------------------------------------
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Build the collider wireframe from WORLD-SCALED dimensions (T1.2) — the same
|
|
359
|
+
* numbers the runtime physics loader bakes into the actual Rapier collider
|
|
360
|
+
* (see `computeColliderWorldDimensions` in `@engine/scene/collider-dimensions`).
|
|
361
|
+
* `dims` already has the entity's composed world scale (self × ancestors)
|
|
362
|
+
* applied, so this function itself must NOT apply any further scale.
|
|
363
|
+
*/
|
|
364
|
+
function createColliderHelper(
|
|
365
|
+
collider: SceneCollider,
|
|
366
|
+
dims: ColliderWorldDimensions,
|
|
367
|
+
): THREE.Object3D | null {
|
|
368
|
+
const mat = new THREE.LineBasicMaterial({
|
|
369
|
+
color: collider.isSensor ? SENSOR_COLOR : COLLIDER_COLOR,
|
|
370
|
+
transparent: true,
|
|
371
|
+
opacity: 0.7,
|
|
372
|
+
depthTest: false,
|
|
373
|
+
});
|
|
374
|
+
const group = new THREE.Group();
|
|
375
|
+
|
|
376
|
+
switch (collider.type) {
|
|
377
|
+
case 'cuboid': {
|
|
378
|
+
const he = dims.halfExtents!;
|
|
379
|
+
const geo = new THREE.BoxGeometry(he[0] * 2, he[1] * 2, he[2] * 2);
|
|
380
|
+
group.add(new THREE.LineSegments(new THREE.EdgesGeometry(geo), mat));
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
case 'ball': {
|
|
384
|
+
const r = dims.radius!;
|
|
385
|
+
group.add(new THREE.Line(createCircleGeometry(r), mat));
|
|
386
|
+
const xzRing = new THREE.Line(createCircleGeometry(r), mat);
|
|
387
|
+
xzRing.rotation.x = -Math.PI / 2;
|
|
388
|
+
group.add(xzRing);
|
|
389
|
+
const yzRing = new THREE.Line(createCircleGeometry(r), mat);
|
|
390
|
+
yzRing.rotation.y = Math.PI / 2;
|
|
391
|
+
group.add(yzRing);
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
case 'capsule': {
|
|
395
|
+
const r = dims.radius!;
|
|
396
|
+
const hh = dims.halfHeight!;
|
|
397
|
+
const topRing = new THREE.Line(createCircleGeometry(r), mat);
|
|
398
|
+
topRing.rotation.x = -Math.PI / 2;
|
|
399
|
+
topRing.position.y = hh;
|
|
400
|
+
group.add(topRing);
|
|
401
|
+
const botRing = new THREE.Line(createCircleGeometry(r), mat);
|
|
402
|
+
botRing.rotation.x = -Math.PI / 2;
|
|
403
|
+
botRing.position.y = -hh;
|
|
404
|
+
group.add(botRing);
|
|
405
|
+
for (const [x, z] of [
|
|
406
|
+
[r, 0],
|
|
407
|
+
[-r, 0],
|
|
408
|
+
[0, r],
|
|
409
|
+
[0, -r],
|
|
410
|
+
] as [number, number][]) {
|
|
411
|
+
const lineGeo = new THREE.BufferGeometry().setFromPoints([
|
|
412
|
+
new THREE.Vector3(x, -hh, z),
|
|
413
|
+
new THREE.Vector3(x, hh, z),
|
|
414
|
+
]);
|
|
415
|
+
group.add(new THREE.Line(lineGeo, mat));
|
|
416
|
+
}
|
|
417
|
+
group.add(new THREE.Line(createSemiArcGeometry(r, hh, 'xz', true), mat));
|
|
418
|
+
group.add(new THREE.Line(createSemiArcGeometry(r, hh, 'yz', true), mat));
|
|
419
|
+
group.add(new THREE.Line(createSemiArcGeometry(r, -hh, 'xz', false), mat));
|
|
420
|
+
group.add(new THREE.Line(createSemiArcGeometry(r, -hh, 'yz', false), mat));
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
default:
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return group.children.length > 0 ? group : null;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const colliderGizmo: GizmoEntry = {
|
|
431
|
+
test: (entity) => !!entity.physics?.collider,
|
|
432
|
+
createIcon: () => makeIconSprite('⬡', 'collider'),
|
|
433
|
+
createHelpers(entity, obj) {
|
|
434
|
+
const collider = entity.physics?.collider;
|
|
435
|
+
if (!collider) return [];
|
|
436
|
+
|
|
437
|
+
// T1.2: size the wireframe from the entity's composed WORLD scale (self ×
|
|
438
|
+
// ancestors) via the SAME helper the runtime physics loader uses, so the
|
|
439
|
+
// gizmo always matches the actual Rapier collider. `obj.updateWorldMatrix`
|
|
440
|
+
// forces this entity's (and its ancestors') matrixWorld to be current
|
|
441
|
+
// right now, rather than trusting whatever the last render pass left.
|
|
442
|
+
obj.updateWorldMatrix(true, false);
|
|
443
|
+
const worldScaleVec = obj.getWorldScale(new THREE.Vector3());
|
|
444
|
+
const worldScale: [number, number, number] = [
|
|
445
|
+
worldScaleVec.x,
|
|
446
|
+
worldScaleVec.y,
|
|
447
|
+
worldScaleVec.z,
|
|
448
|
+
];
|
|
449
|
+
|
|
450
|
+
let dims: ColliderWorldDimensions;
|
|
451
|
+
try {
|
|
452
|
+
dims = computeColliderWorldDimensions(collider, worldScale, entity.name);
|
|
453
|
+
} catch {
|
|
454
|
+
// Invalid configuration (e.g. non-uniform scale on a round collider) —
|
|
455
|
+
// this throws a loud load error at Play time. Don't crash the editor
|
|
456
|
+
// over a config the user hasn't fixed yet; just don't render a
|
|
457
|
+
// misleading shape.
|
|
458
|
+
return [];
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const helper = createColliderHelper(collider, dims);
|
|
462
|
+
if (!helper) return [];
|
|
463
|
+
|
|
464
|
+
// `helper` is added as a CHILD of `obj` below (applyEntityGizmos), so
|
|
465
|
+
// Three's normal hierarchy would otherwise apply `obj`'s (self + ancestor)
|
|
466
|
+
// world scale a SECOND time on top of the world-scaled dimensions just
|
|
467
|
+
// baked into `dims` above. Cancel that inherited scale here so the
|
|
468
|
+
// dimensions above are the only place scale is applied (avoid
|
|
469
|
+
// double-scaling).
|
|
470
|
+
if (worldScaleVec.x !== 0 && worldScaleVec.y !== 0 && worldScaleVec.z !== 0) {
|
|
471
|
+
helper.scale.set(1 / worldScaleVec.x, 1 / worldScaleVec.y, 1 / worldScaleVec.z);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// R1c: render the wireframe at the collider's WORLD-SCALED offset (the
|
|
475
|
+
// runtime physics loader bakes `offset * worldScale` into the Rapier
|
|
476
|
+
// collider's translation — see `computeColliderWorldOffset`). `helper` is
|
|
477
|
+
// a CHILD of `obj` (added below by applyEntityGizmos), so Three's normal
|
|
478
|
+
// parent-child matrix composition ALREADY multiplies a child's local
|
|
479
|
+
// `position` by the parent's world scale when computing its world
|
|
480
|
+
// position — unlike geometry vertices (handled above), a child's own
|
|
481
|
+
// `position` is untouched by `helper.scale` (that only affects further
|
|
482
|
+
// descendants' vertices). So setting `helper.position` to the RAW
|
|
483
|
+
// authored (unscaled) offset already renders at the correct world-scaled
|
|
484
|
+
// offset; scaling it again here would double-apply worldScale.
|
|
485
|
+
if (collider.offset) {
|
|
486
|
+
helper.position.set(collider.offset[0], collider.offset[1], collider.offset[2]);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return [helper];
|
|
490
|
+
},
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
// ---------------------------------------------------------------------------
|
|
494
|
+
// Spline gizmo
|
|
495
|
+
// ---------------------------------------------------------------------------
|
|
496
|
+
|
|
497
|
+
function createSplineHelper(spline: SceneSpline): THREE.Object3D {
|
|
498
|
+
const group = new THREE.Group();
|
|
499
|
+
if (spline.points.length < 2) return group;
|
|
500
|
+
|
|
501
|
+
const points = spline.points.map((p) => new THREE.Vector3(p[0], p[1], p[2]));
|
|
502
|
+
const curve = new THREE.CatmullRomCurve3(
|
|
503
|
+
points,
|
|
504
|
+
spline.closed ?? false,
|
|
505
|
+
spline.curveType,
|
|
506
|
+
spline.tension ?? 0.5,
|
|
507
|
+
);
|
|
508
|
+
const resolution = spline.resolution ?? 50;
|
|
509
|
+
const samples = curve.getPoints(resolution);
|
|
510
|
+
|
|
511
|
+
const lineGeo = new THREE.BufferGeometry().setFromPoints(samples);
|
|
512
|
+
const lineMat = new THREE.LineBasicMaterial({
|
|
513
|
+
color: 0x00ccff,
|
|
514
|
+
depthTest: false,
|
|
515
|
+
transparent: true,
|
|
516
|
+
opacity: 0.8,
|
|
517
|
+
});
|
|
518
|
+
const line = new THREE.Line(lineGeo, lineMat);
|
|
519
|
+
line.renderOrder = 997;
|
|
520
|
+
group.add(line);
|
|
521
|
+
|
|
522
|
+
const dotGeo = new THREE.SphereGeometry(0.06, 8, 8);
|
|
523
|
+
const dotMat = new THREE.MeshBasicMaterial({
|
|
524
|
+
color: 0xffaa00,
|
|
525
|
+
depthTest: false,
|
|
526
|
+
transparent: true,
|
|
527
|
+
opacity: 0.9,
|
|
528
|
+
});
|
|
529
|
+
for (let i = 0; i < spline.points.length; i++) {
|
|
530
|
+
const dot = new THREE.Mesh(dotGeo, dotMat);
|
|
531
|
+
const p = spline.points[i]!;
|
|
532
|
+
dot.position.set(p[0], p[1], p[2]);
|
|
533
|
+
dot.renderOrder = 998;
|
|
534
|
+
setUserData(dot, 'splineControlPoint', i);
|
|
535
|
+
group.add(dot);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return group;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const splineGizmo: GizmoEntry = {
|
|
542
|
+
test: (entity) => !!entity.spline,
|
|
543
|
+
createIcon: () => makeIconSprite('〰', 'spline'),
|
|
544
|
+
createHelpers(entity) {
|
|
545
|
+
return [createSplineHelper(entity.spline!)];
|
|
546
|
+
},
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
// ---------------------------------------------------------------------------
|
|
550
|
+
// Registry
|
|
551
|
+
// ---------------------------------------------------------------------------
|
|
552
|
+
|
|
553
|
+
const GIZMO_REGISTRY: GizmoEntry[] = [
|
|
554
|
+
lightGizmo,
|
|
555
|
+
cameraGizmo,
|
|
556
|
+
audioGizmo,
|
|
557
|
+
colliderGizmo,
|
|
558
|
+
splineGizmo,
|
|
559
|
+
];
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Apply all matching gizmos to an entity object.
|
|
563
|
+
*
|
|
564
|
+
* Adds clickable icon billboards and non-clickable wireframe helpers.
|
|
565
|
+
* Idempotent — skips objects that already have gizmo children.
|
|
566
|
+
*/
|
|
567
|
+
export function applyEntityGizmos(obj: THREE.Object3D): void {
|
|
568
|
+
const entity = getUserData(obj, 'entity') as SceneEntity | undefined;
|
|
569
|
+
if (!entity) return;
|
|
570
|
+
|
|
571
|
+
// Idempotent: skip if gizmos already applied
|
|
572
|
+
if (obj.children.some((c) => getUserData(c, 'editorHelper') || getUserData(c, 'editorIcon')))
|
|
573
|
+
return;
|
|
574
|
+
|
|
575
|
+
let added = false;
|
|
576
|
+
for (const entry of GIZMO_REGISTRY) {
|
|
577
|
+
if (!entry.test(entity, obj)) continue;
|
|
578
|
+
|
|
579
|
+
// Icon billboard — clickable, on EDITOR_LAYER
|
|
580
|
+
const icon = entry.createIcon();
|
|
581
|
+
obj.add(icon);
|
|
582
|
+
|
|
583
|
+
// Helper wireframes — visual only, not clickable
|
|
584
|
+
const helpers = entry.createHelpers(entity, obj);
|
|
585
|
+
for (const helper of helpers) {
|
|
586
|
+
setUserData(helper, 'editorHelper', true);
|
|
587
|
+
setUserData(
|
|
588
|
+
helper,
|
|
589
|
+
'editorHelperType',
|
|
590
|
+
entry === lightGizmo
|
|
591
|
+
? 'lights'
|
|
592
|
+
: entry === cameraGizmo
|
|
593
|
+
? 'cameras'
|
|
594
|
+
: entry === audioGizmo
|
|
595
|
+
? 'audio'
|
|
596
|
+
: entry === colliderGizmo
|
|
597
|
+
? 'colliders'
|
|
598
|
+
: 'splines',
|
|
599
|
+
);
|
|
600
|
+
helper.traverse((child) => child.layers.set(EDITOR_LAYER));
|
|
601
|
+
obj.add(helper);
|
|
602
|
+
}
|
|
603
|
+
added = true;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Force matrix computation so helpers render at the correct position immediately
|
|
607
|
+
if (added) obj.updateMatrixWorld(true);
|
|
608
|
+
}
|