@volter/editor-core 0.5.57
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/BUNDLED_NOTICES +1527 -0
- package/LICENSE +686 -0
- package/LICENSE-APACHE +202 -0
- package/NOTICE +18 -0
- package/README.md +24 -0
- package/dist/build/chunk-JS4D4XZ3.js +175 -0
- package/dist/build/chunk-JS4D4XZ3.js.map +7 -0
- package/dist/build/vite-plugin-product-contributions.js +100 -0
- package/dist/build/vite-plugin-product-contributions.js.map +7 -0
- package/dist/build/vite-plugin-shared-react.js +25 -0
- package/dist/build/vite-plugin-shared-react.js.map +7 -0
- package/dist/build/vite-plugin-shared-three.js +86 -0
- package/dist/build/vite-plugin-shared-three.js.map +7 -0
- package/dist/server/chunk-KMJC7AGO.js +98 -0
- package/dist/server/chunk-KMJC7AGO.js.map +7 -0
- package/dist/server/chunk-PSILUMGS.js +46 -0
- package/dist/server/chunk-PSILUMGS.js.map +7 -0
- package/dist/server/frame-proxy.js +255 -0
- package/dist/server/frame-proxy.js.map +7 -0
- package/dist/server/open-browser.js +53 -0
- package/dist/server/open-browser.js.map +7 -0
- package/dist/server/process-shutdown.js +127 -0
- package/dist/server/process-shutdown.js.map +7 -0
- package/dist/server/session-registry.js +141 -0
- package/dist/server/session-registry.js.map +7 -0
- package/dist/server/spawn-opener.js +11 -0
- package/dist/server/spawn-opener.js.map +7 -0
- package/dist/server/worktree-identity.js +11 -0
- package/dist/server/worktree-identity.js.map +7 -0
- package/dist-server/packaged.mjs +88816 -0
- package/package.json +118 -0
- package/scripts/build-plugins.mjs +14 -0
- package/scripts/build-server.mjs +26 -0
- package/scripts/build-session.mjs +9 -0
- package/server/account-credentials.ts +60 -0
- package/server/account-service.ts +1434 -0
- package/server/adapter-region-includes.ts +47 -0
- package/server/asset-catalog-v2.ts +175 -0
- package/server/asset-history-snapshots.ts +196 -0
- package/server/asset-ledger-store.ts +195 -0
- package/server/asset-library-routes.ts +1595 -0
- package/server/blender-wasm-artifact.ts +430 -0
- package/server/boot-timings.ts +106 -0
- package/server/canonical-path.ts +50 -0
- package/server/checkout-workspace-preflight.ts +349 -0
- package/server/cloud-asset-catalog.ts +158 -0
- package/server/coding-inference-launch.ts +237 -0
- package/server/collaboration-account-client.ts +202 -0
- package/server/collaboration-attribution.ts +122 -0
- package/server/collaboration-session.ts +934 -0
- package/server/comfyui-bridge.ts +248 -0
- package/server/console-ledger.ts +499 -0
- package/server/creation-site-transform.ts +304 -0
- package/server/creation-site-write.ts +648 -0
- package/server/data-file-serialize.ts +158 -0
- package/server/editor-brand-html.ts +72 -0
- package/server/editor-control-socket.ts +366 -0
- package/server/editor-server-options.ts +190 -0
- package/server/editor-server.ts +1483 -0
- package/server/editor-sse.ts +352 -0
- package/server/engine-provenance.ts +169 -0
- package/server/engine-source-restart.ts +117 -0
- package/server/frame-bridge.ts +115 -0
- package/server/frame-proxy.ts +490 -0
- package/server/frame-workbench.ts +259 -0
- package/server/frontend-handoff.ts +229 -0
- package/server/game-globals-shadow.ts +113 -0
- package/server/gameplay-session-retention.ts +149 -0
- package/server/gameplay-sessions.ts +123 -0
- package/server/generation-jobs.ts +201 -0
- package/server/generation-reconciler.ts +137 -0
- package/server/generative-execution-context.ts +59 -0
- package/server/git-workflow.ts +612 -0
- package/server/harness-chat-caller.ts +36 -0
- package/server/harness-chat-service.ts +1681 -0
- package/server/idle-shutdown.ts +170 -0
- package/server/js-profiling-policy.ts +52 -0
- package/server/launcher-settings.ts +84 -0
- package/server/local-asset-catalog.ts +410 -0
- package/server/managed-account-defaults.ts +22 -0
- package/server/mock-control-plane.ts +540 -0
- package/server/model-import-conversion.ts +345 -0
- package/server/native-credential-store.ts +162 -0
- package/server/open-browser.ts +118 -0
- package/server/packaged.ts +1192 -0
- package/server/play-stall.ts +126 -0
- package/server/process-shutdown.ts +218 -0
- package/server/product-presets.ts +55 -0
- package/server/project-build-artifact.ts +95 -0
- package/server/project-components.ts +100 -0
- package/server/project-dependency-invalidation.ts +114 -0
- package/server/project-file-scan.ts +235 -0
- package/server/project-hmr-files.ts +215 -0
- package/server/project-install-roots.ts +210 -0
- package/server/project-kinds.ts +71 -0
- package/server/project-mcp-servers.ts +82 -0
- package/server/project-module-freshness.ts +109 -0
- package/server/project-module-instance.ts +375 -0
- package/server/project-optimize-deps-entries.ts +827 -0
- package/server/project-output-writer.ts +679 -0
- package/server/project-package-origin.ts +63 -0
- package/server/project-root-surface.ts +831 -0
- package/server/project-scratch-path.ts +47 -0
- package/server/project-script-hmr.ts +619 -0
- package/server/project-serving-plugins.ts +236 -0
- package/server/project-tools.ts +653 -0
- package/server/project-validation.ts +250 -0
- package/server/project-verbs.ts +127 -0
- package/server/project-view.ts +52 -0
- package/server/project-watch.ts +1134 -0
- package/server/project-work-coordinator.ts +288 -0
- package/server/provider-credentials.ts +331 -0
- package/server/recent-projects-store.ts +98 -0
- package/server/redact-secrets.ts +71 -0
- package/server/repository-presence.ts +267 -0
- package/server/routes/account.ts +357 -0
- package/server/routes/agents.ts +60 -0
- package/server/routes/assets.ts +154 -0
- package/server/routes/build.ts +13 -0
- package/server/routes/collaboration.ts +595 -0
- package/server/routes/configurations.ts +419 -0
- package/server/routes/context.ts +264 -0
- package/server/routes/control-plane.ts +1811 -0
- package/server/routes/logs.ts +272 -0
- package/server/routes/project-identity.ts +302 -0
- package/server/routes/project-open.ts +429 -0
- package/server/routes/project-source.ts +686 -0
- package/server/routes/project-state.ts +383 -0
- package/server/routes/relay.ts +1029 -0
- package/server/routes/route-helpers.ts +29 -0
- package/server/routes/served-modules.ts +138 -0
- package/server/routes/session-tabs.ts +196 -0
- package/server/routes/settings.ts +121 -0
- package/server/routes/share-control.ts +486 -0
- package/server/routes/themes.ts +116 -0
- package/server/routes/tools.ts +230 -0
- package/server/routes/worktrees.ts +595 -0
- package/server/scoped-game-css.ts +461 -0
- package/server/server-utils.ts +1194 -0
- package/server/session-product.ts +112 -0
- package/server/session-registry.ts +230 -0
- package/server/share-claims.ts +138 -0
- package/server/share-host.ts +526 -0
- package/server/share-session-gateway.ts +1362 -0
- package/server/share-tunnel.ts +123 -0
- package/server/spawn-opener.ts +74 -0
- package/server/support/play/log-format.ts +76 -0
- package/server/support/play/session-record.ts +199 -0
- package/server/support/project/build-discipline.ts +718 -0
- package/server/support/project/inspection-node.ts +119 -0
- package/server/support/project/log-naming.ts +54 -0
- package/server/support/project/provenance.ts +290 -0
- package/server/support/project/run-name.ts +18 -0
- package/server/support/project/session-journal.ts +830 -0
- package/server/support/project/shared.ts +327 -0
- package/server/tab-bootstrap.ts +62 -0
- package/server/tab-heartbeat.ts +477 -0
- package/server/tab-lifecycle.ts +550 -0
- package/server/tab-presence.ts +1508 -0
- package/server/team-agent-mirror.ts +95 -0
- package/server/twin-auth.ts +138 -0
- package/server/vendored-lock-recorder.ts +968 -0
- package/server/worktree-identity.ts +121 -0
- package/server/worktree-management.ts +398 -0
- package/server/worktree-retention.d.mts +13 -0
- package/server/worktree-retention.mjs +205 -0
- package/src/EditorContext.tsx +169 -0
- package/src/account.ts +333 -0
- package/src/action-registry.ts +532 -0
- package/src/active-product.ts +74 -0
- package/src/active-project.ts +155 -0
- package/src/adapter-editor-config.ts +25 -0
- package/src/adapter-observation.ts +49 -0
- package/src/animation/stage-transport.ts +379 -0
- package/src/animation/three-clips-subject.ts +172 -0
- package/src/api/asset-library-wire.ts +45 -0
- package/src/api/assets.ts +365 -0
- package/src/api/base.ts +10 -0
- package/src/api/build.ts +99 -0
- package/src/api/git-wire.ts +56 -0
- package/src/api/logs.ts +92 -0
- package/src/api/project-identity.ts +74 -0
- package/src/api/project-open.ts +355 -0
- package/src/api/project-source.ts +162 -0
- package/src/api/project-state.ts +107 -0
- package/src/api/relay.ts +268 -0
- package/src/api/settings.ts +36 -0
- package/src/api/themes.ts +45 -0
- package/src/api/worktrees.ts +257 -0
- package/src/asset-compare-core.ts +171 -0
- package/src/asset-compare.ts +294 -0
- package/src/asset-editor-context.tsx +100 -0
- package/src/asset-events.ts +97 -0
- package/src/asset-inspector-actions.ts +87 -0
- package/src/asset-preview-framing.ts +357 -0
- package/src/asset-preview.ts +2802 -0
- package/src/asset-selection-viewer-registry.ts +113 -0
- package/src/asset-selection.ts +133 -0
- package/src/asset-workflow/asset-capabilities.ts +344 -0
- package/src/asset-workflow/asset-import-jobs.ts +106 -0
- package/src/asset-workflow/asset-ledger-backend.ts +126 -0
- package/src/asset-workflow/asset-ledger.ts +156 -0
- package/src/asset-workflow/asset-materialization-report.ts +140 -0
- package/src/asset-workflow/asset-pack-manifest.ts +320 -0
- package/src/asset-workflow/asset-types.ts +142 -0
- package/src/asset-workflow/asset-workflow-quality.ts +61 -0
- package/src/asset-workflow/audio-preview-player.ts +193 -0
- package/src/asset-workflow/audio-waveform.ts +22 -0
- package/src/asset-workflow/cloud-asset-client.ts +263 -0
- package/src/asset-workflow/folder-preview.ts +345 -0
- package/src/asset-workflow/hosted-asset-materialization.ts +236 -0
- package/src/asset-workflow/image-view-scale.ts +32 -0
- package/src/asset-workflow/import-contract.ts +124 -0
- package/src/asset-workflow/ledger-write-lock.ts +244 -0
- package/src/asset-workflow/model-inspection.ts +818 -0
- package/src/asset-workflow/pixi-spritesheet.ts +197 -0
- package/src/asset-workflow/preview-resource-lifetime.ts +44 -0
- package/src/asset-workflow/project-asset-commands.ts +23 -0
- package/src/asset-workflow/project-asset-health.ts +139 -0
- package/src/asset-workflow/project-asset-operations.ts +154 -0
- package/src/asset-workflow/project-asset-roots.ts +68 -0
- package/src/asset-workflow/project-content.ts +288 -0
- package/src/asset-workflow/project-source-index.ts +550 -0
- package/src/asset-workflow/thumbnail-system.ts +256 -0
- package/src/authoring/active-adapter.ts +199 -0
- package/src/authoring/active-systems.ts +422 -0
- package/src/authoring/adapter-key.ts +18 -0
- package/src/authoring/authoring-asset-url.ts +27 -0
- package/src/authoring/bootstrap-state.ts +49 -0
- package/src/authoring/boundary-authoring-adapter.ts +184 -0
- package/src/authoring/breakpoint-state.ts +43 -0
- package/src/authoring/canvas-scene-guides.ts +76 -0
- package/src/authoring/component-instance-root.ts +171 -0
- package/src/authoring/composite-authoring-adapter.ts +2110 -0
- package/src/authoring/consumer-actions.ts +520 -0
- package/src/authoring/css-numeric-style.ts +97 -0
- package/src/authoring/design-time-layers.ts +849 -0
- package/src/authoring/design-time-mount-registry.ts +235 -0
- package/src/authoring/design-time-settle.ts +343 -0
- package/src/authoring/edit-mode-authoring.ts +619 -0
- package/src/authoring/eyedropper-session.ts +60 -0
- package/src/authoring/instance-source-menu-register.ts +13 -0
- package/src/authoring/instance-source-menu.ts +135 -0
- package/src/authoring/layered-pick.ts +183 -0
- package/src/authoring/live-gesture-lock.ts +50 -0
- package/src/authoring/live-object-transform.ts +62 -0
- package/src/authoring/mount-failure-report.ts +154 -0
- package/src/authoring/mounted-root-subjects.ts +144 -0
- package/src/authoring/no-authoring-adapter.ts +55 -0
- package/src/authoring/null-inspection-subjects.tsx +75 -0
- package/src/authoring/object3d-document-persistence.ts +106 -0
- package/src/authoring/object3d-document-session-registry.ts +120 -0
- package/src/authoring/object3d-document-session.ts +1326 -0
- package/src/authoring/object3d-gesture-controller.ts +113 -0
- package/src/authoring/panel-authoring.ts +121 -0
- package/src/authoring/prefab-instance-inspector-section.tsx +237 -0
- package/src/authoring/prefab-instance-section-model.ts +55 -0
- package/src/authoring/project-authoring-session.ts +105 -0
- package/src/authoring/provenance.ts +99 -0
- package/src/authoring/quarks-particle-systems.ts +19 -0
- package/src/authoring/react-canvas-navigation.ts +255 -0
- package/src/authoring/react-design-canvas-style.ts +20 -0
- package/src/authoring/react-story-board.ts +937 -0
- package/src/authoring/selection-scope.ts +195 -0
- package/src/authoring/shell-document-ops.ts +169 -0
- package/src/authoring/shell-object3d-document-write-policy.ts +108 -0
- package/src/authoring/shell-viewport-policy.ts +48 -0
- package/src/authoring/source-object3d-authoring-adapter.ts +526 -0
- package/src/authoring/stories-scope.ts +35 -0
- package/src/authoring/story-board-chrome-fit.ts +107 -0
- package/src/authoring/story-board-presentation.ts +111 -0
- package/src/authoring/three-projection-core.ts +226 -0
- package/src/authoring/viewport-pick-context.ts +40 -0
- package/src/authoring/viewport-raycast.ts +240 -0
- package/src/authoring/viewport-tool-context.ts +73 -0
- package/src/authoring/world-canvas-viewport-state.ts +35 -0
- package/src/authoring/world-hidden-viewport.ts +152 -0
- package/src/authoring/world-pan-state.ts +198 -0
- package/src/authoring/world-session-state.ts +101 -0
- package/src/authoring/write-pipe.ts +173 -0
- package/src/availability-tick.ts +66 -0
- package/src/bitmap-label.ts +120 -0
- package/src/blender-tab-metrics.ts +161 -0
- package/src/board-open-actions.ts +20 -0
- package/src/boot-routing.ts +391 -0
- package/src/build-identity.ts +15 -0
- package/src/bytes-codec.ts +62 -0
- package/src/camera-authoring.ts +175 -0
- package/src/cancellation-reason.ts +58 -0
- package/src/canvas-preview-frames.ts +482 -0
- package/src/capture-camera-pose.ts +77 -0
- package/src/chrome-registry.ts +159 -0
- package/src/chrome-slot-registry.ts +91 -0
- package/src/collaboration-client.ts +264 -0
- package/src/collaboration-presence.ts +41 -0
- package/src/command-dispatch.ts +19 -0
- package/src/command-listener.ts +2743 -0
- package/src/command-registry.ts +70 -0
- package/src/component-board-registry.ts +205 -0
- package/src/component-states-registry.ts +200 -0
- package/src/components/AgentPresentationNotice.tsx +75 -0
- package/src/components/AlignToolbar.tsx +204 -0
- package/src/components/AppRoot.tsx +394 -0
- package/src/components/ApplicationMenus.tsx +415 -0
- package/src/components/AssetBrowser.tsx +2843 -0
- package/src/components/AssetEditorShell.tsx +216 -0
- package/src/components/AssetInspectorToolSection.tsx +124 -0
- package/src/components/BoardRulers.tsx +354 -0
- package/src/components/CameraInfo.tsx +81 -0
- package/src/components/CanvasSceneViewport.tsx +556 -0
- package/src/components/CapabilityCoverageSection.tsx +185 -0
- package/src/components/CenterDocuments.tsx +22 -0
- package/src/components/ChromeSlot.tsx +20 -0
- package/src/components/CodeView.tsx +470 -0
- package/src/components/CollaborationHeaderControl.css +74 -0
- package/src/components/CompactInspectorCard.tsx +190 -0
- package/src/components/CompactInspectorShell.tsx +39 -0
- package/src/components/ConsolePanel.css +55 -0
- package/src/components/ConsolePanel.tsx +270 -0
- package/src/components/DefaultEditorLayout.tsx +106 -0
- package/src/components/DocumentHeaderStrip.tsx +96 -0
- package/src/components/DocumentShelfRail.tsx +45 -0
- package/src/components/DocumentThumbnail.tsx +345 -0
- package/src/components/EditorLeaseGuard.tsx +372 -0
- package/src/components/ErrorBanner.tsx +123 -0
- package/src/components/FolderPreviewTile.tsx +159 -0
- package/src/components/GameHierarchy.tsx +3842 -0
- package/src/components/GameplaySessionTimeline.tsx +295 -0
- package/src/components/InspectionProjection.tsx +968 -0
- package/src/components/Inspector.tsx +233 -0
- package/src/components/InspectorCanvasPreview.tsx +35 -0
- package/src/components/InspectorFieldsSection.tsx +290 -0
- package/src/components/InspectorObjectPreview.tsx +57 -0
- package/src/components/InspectorStoriesSection.tsx +92 -0
- package/src/components/InspectorToolSection.tsx +96 -0
- package/src/components/InspectorTransformSection.tsx +245 -0
- package/src/components/LightExplorerPanel.tsx +433 -0
- package/src/components/MediaProperties.tsx +145 -0
- package/src/components/NonThreeAuthoringBootstrap.tsx +19 -0
- package/src/components/Object3DDocumentToolbar.css +100 -0
- package/src/components/Object3DDocumentToolbar.tsx +454 -0
- package/src/components/Object3DDocumentViewport.tsx +58 -0
- package/src/components/ProjectHeader.css +617 -0
- package/src/components/ProjectHeader.tsx +328 -0
- package/src/components/ProjectLayout.tsx +249 -0
- package/src/components/ReactCanvasControls.tsx +284 -0
- package/src/components/RootSelectionOverlay.tsx +3162 -0
- package/src/components/RootTextEditor.tsx +79 -0
- package/src/components/SaveStatus.tsx +70 -0
- package/src/components/StageHost.tsx +1954 -0
- package/src/components/StageOverlays.tsx +21 -0
- package/src/components/StartupErrorScreen.tsx +76 -0
- package/src/components/StartupLoadingScreen.tsx +65 -0
- package/src/components/StatsOverlay.tsx +78 -0
- package/src/components/SurfaceStateOverlay.tsx +24 -0
- package/src/components/ToolContributionSurfaces.tsx +65 -0
- package/src/components/ToolHost.tsx +370 -0
- package/src/components/ToolObject3DPreview.tsx +39 -0
- package/src/components/Toolbar.tsx +595 -0
- package/src/components/TransientHint.tsx +44 -0
- package/src/components/TransportStrip.tsx +174 -0
- package/src/components/VersionControlSection.tsx +470 -0
- package/src/components/VgaiLogo.css +83 -0
- package/src/components/VgaiLogo.tsx +35 -0
- package/src/components/ViewportControlsHint.tsx +60 -0
- package/src/components/ViewportFurniture.tsx +401 -0
- package/src/components/ViewportOverlay.tsx +145 -0
- package/src/components/ViewportOverlaysMenu.tsx +153 -0
- package/src/components/ViewportShadingMenu.tsx +300 -0
- package/src/components/ViewportViewMenu.tsx +101 -0
- package/src/components/WorkspaceDocumentSurface.tsx +204 -0
- package/src/components/WorkspaceUtilitySurface.tsx +22 -0
- package/src/components/WorktreeSwitcher.css +239 -0
- package/src/components/WorktreeSwitcher.tsx +890 -0
- package/src/components/account-documents.tsx +1162 -0
- package/src/components/asset-documents.tsx +812 -0
- package/src/components/asset-editor-persistence.ts +216 -0
- package/src/components/asset-selection-section.tsx +535 -0
- package/src/components/asset-thumbnails.tsx +312 -0
- package/src/components/asset-viewers/AudioViewer.tsx +201 -0
- package/src/components/asset-viewers/EntityModelDocument.tsx +122 -0
- package/src/components/asset-viewers/EnvironmentAssetDocument.tsx +440 -0
- package/src/components/asset-viewers/GenericJsonViewer.tsx +102 -0
- package/src/components/asset-viewers/ImageViewer.tsx +300 -0
- package/src/components/asset-viewers/JsonAssetDocument.tsx +93 -0
- package/src/components/asset-viewers/LiveModuleDocument.tsx +417 -0
- package/src/components/asset-viewers/LutAssetDocument.tsx +444 -0
- package/src/components/asset-viewers/ModelAssetDocument.tsx +105 -0
- package/src/components/asset-viewers/Object3DPreview.tsx +359 -0
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +421 -0
- package/src/components/asset-viewers/PasteboardModuleDocument.tsx +67 -0
- package/src/components/asset-viewers/QuarksAssetDocument.tsx +526 -0
- package/src/components/asset-viewers/ShaderAssetDocument.tsx +743 -0
- package/src/components/asset-viewers/SourceAssetViewer.tsx +281 -0
- package/src/components/asset-viewers/SpritesheetSpriteView.tsx +102 -0
- package/src/components/asset-viewers/VideoViewer.tsx +101 -0
- package/src/components/asset-viewers/shader-source.ts +144 -0
- package/src/components/asset-workflow.css +731 -0
- package/src/components/board-guides.ts +150 -0
- package/src/components/compact-inspector.css +539 -0
- package/src/components/core-utilities.tsx +90 -0
- package/src/components/editor-notifications.css +40 -0
- package/src/components/engine-workspace.css +274 -0
- package/src/components/inspector-preview-section.tsx +238 -0
- package/src/components/inspector-property-grouping.ts +64 -0
- package/src/components/inspector-revert-label.ts +20 -0
- package/src/components/inspector-selection.ts +42 -0
- package/src/components/inspector-stories-gating.ts +171 -0
- package/src/components/inspector-transform-subject.ts +11 -0
- package/src/components/inspector-transform.ts +75 -0
- package/src/components/kind-documents.tsx +500 -0
- package/src/components/palette-action-publisher.tsx +140 -0
- package/src/components/primitives/DraftColorInput.tsx +74 -0
- package/src/components/product-shell.css +1829 -0
- package/src/components/project-tool-documents.tsx +410 -0
- package/src/components/scene-documents.tsx +223 -0
- package/src/components/stage-keyboard.tsx +37 -0
- package/src/components/stage-overlay-set.tsx +107 -0
- package/src/components/stage-presence-markers.ts +478 -0
- package/src/components/standard-viewport-dressing.ts +417 -0
- package/src/components/status-contributions.tsx +403 -0
- package/src/components/tool-documents.tsx +309 -0
- package/src/components/tool-schema-form.tsx +262 -0
- package/src/components/use-after-paint.ts +41 -0
- package/src/components/use-project-image-assets.ts +86 -0
- package/src/components/viewport-header-controls.css +90 -0
- package/src/components/viewport-surface-status.tsx +55 -0
- package/src/components/workspace-history.ts +32 -0
- package/src/components/workspace-static-panel-registry.tsx +152 -0
- package/src/components/workspace-surfaces.css +621 -0
- package/src/components/world-documents.tsx +586 -0
- package/src/components/world-overlay-gestures.ts +1694 -0
- package/src/composite-screenshot.ts +1641 -0
- package/src/console-sync.ts +131 -0
- package/src/constraint-helper.ts +338 -0
- package/src/content-entry-source-registry.ts +184 -0
- package/src/coverage/authoring-seam-evidence.ts +300 -0
- package/src/coverage/canvas-reveal.ts +192 -0
- package/src/coverage/design-time-surfaces.ts +101 -0
- package/src/coverage/live-seam-evidence.ts +11 -0
- package/src/coverage/ontology-invariants.ts +466 -0
- package/src/coverage/session-vitals.ts +501 -0
- package/src/coverage/system-seam-evidence.ts +72 -0
- package/src/crash-null-boundary.ts +36 -0
- package/src/creation-site-edit.ts +1479 -0
- package/src/creation-site-registry.ts +160 -0
- package/src/delegate-harness-registry.ts +143 -0
- package/src/document-context-registry.ts +177 -0
- package/src/document-open-registry.ts +200 -0
- package/src/document-preview-source.ts +20 -0
- package/src/document-renderer-session.ts +138 -0
- package/src/editor-api.ts +46 -0
- package/src/editor-chrome-capture.ts +139 -0
- package/src/editor-commands.ts +164 -0
- package/src/editor-console.ts +563 -0
- package/src/editor-current-view.ts +82 -0
- package/src/editor-document-probe.ts +881 -0
- package/src/editor-git-client.ts +115 -0
- package/src/editor-host-door.ts +462 -0
- package/src/editor-hotkeys.ts +842 -0
- package/src/editor-lease-view.ts +39 -0
- package/src/editor-lease.ts +415 -0
- package/src/editor-mode.ts +19 -0
- package/src/editor-notifications.ts +140 -0
- package/src/editor-presence.ts +563 -0
- package/src/editor-presentation-activity.ts +50 -0
- package/src/editor-presentation-notice.ts +42 -0
- package/src/editor-runtime.tsx +145 -0
- package/src/editor-server-response.ts +86 -0
- package/src/editor-session-attribution.ts +75 -0
- package/src/editor-session-mode.ts +54 -0
- package/src/editor-shell-store.ts +1304 -0
- package/src/editor-state-facets.ts +74 -0
- package/src/editor-styles.css +33 -0
- package/src/editor-view-presentation.ts +810 -0
- package/src/editor-viewport.ts +5302 -0
- package/src/entity-lod.ts +31 -0
- package/src/entity-object.ts +91 -0
- package/src/files/file-provider.ts +62 -0
- package/src/files/project-files.ts +264 -0
- package/src/finders/index.ts +136 -0
- package/src/finders/scenes-from-entrypoint-selection.ts +387 -0
- package/src/frame/bridge.tsx +1408 -0
- package/src/frame/product.ts +77 -0
- package/src/gameplay-dom-recording.ts +319 -0
- package/src/gameplay-export-state.ts +14 -0
- package/src/gameplay-replay.ts +417 -0
- package/src/gameplay-session-time.ts +9 -0
- package/src/gameplay-sessions.ts +204 -0
- package/src/harness-chat-types.ts +253 -0
- package/src/hierarchy-component-marks.ts +298 -0
- package/src/hierarchy-drop.ts +91 -0
- package/src/hierarchy-expansion-state.ts +80 -0
- package/src/hierarchy-header-slot.ts +52 -0
- package/src/hierarchy-internals.ts +197 -0
- package/src/hierarchy-kind-icon.ts +217 -0
- package/src/hierarchy-mark-reader.ts +73 -0
- package/src/hierarchy-menu-registry.ts +67 -0
- package/src/hierarchy-node-rows.ts +307 -0
- package/src/hierarchy-panel-view.ts +280 -0
- package/src/hierarchy-projection.ts +76 -0
- package/src/hierarchy-row-cache.ts +243 -0
- package/src/hierarchy-row-model.ts +308 -0
- package/src/hierarchy-rows.ts +11 -0
- package/src/hierarchy-walk.ts +86 -0
- package/src/history/editor-session.ts +25 -0
- package/src/history/history-commands.ts +147 -0
- package/src/history/history-delegate.ts +163 -0
- package/src/history/history-limit-notices.ts +43 -0
- package/src/history/history-service.ts +1173 -0
- package/src/history/persistence-coordinator.ts +35 -0
- package/src/history/project-file-history.ts +386 -0
- package/src/history/project-root-history-backends.ts +139 -0
- package/src/history/resource-registry.ts +209 -0
- package/src/history/snapshot-store.ts +103 -0
- package/src/history/source-history-backend.ts +546 -0
- package/src/history/types.ts +124 -0
- package/src/hmr-registration-group.ts +67 -0
- package/src/hmr-stable-react-context.ts +23 -0
- package/src/hotkeys.ts +188 -0
- package/src/inference-diagnostics.ts +69 -0
- package/src/initial-project.ts +80 -0
- package/src/inspection/active-subject.ts +578 -0
- package/src/inspection/active-surface.ts +142 -0
- package/src/inspection/compose.ts +1064 -0
- package/src/inspection/display.ts +170 -0
- package/src/inspection/document-subject.ts +109 -0
- package/src/inspection/game-subject.ts +85 -0
- package/src/inspection/model.ts +542 -0
- package/src/inspection/null-subject.ts +115 -0
- package/src/inspection/serialize.ts +357 -0
- package/src/inspection/use-active-inspection.ts +180 -0
- package/src/inspector-presentation.ts +201 -0
- package/src/inspector-section-registry.ts +221 -0
- package/src/instance-source-actions.ts +163 -0
- package/src/instanced-presentation.ts +164 -0
- package/src/js-heap.ts +71 -0
- package/src/key-actions.ts +91 -0
- package/src/keymap-presets.ts +401 -0
- package/src/layout-policy.ts +31 -0
- package/src/learn-links.ts +73 -0
- package/src/light-explorer-model.ts +134 -0
- package/src/live-canvas-frame.ts +55 -0
- package/src/live-document.ts +279 -0
- package/src/live-module-source.ts +230 -0
- package/src/live-session-registry.ts +220 -0
- package/src/live-transition.ts +633 -0
- package/src/manifest-project.ts +107 -0
- package/src/model-thumbnail.ts +523 -0
- package/src/native-selection-style.ts +202 -0
- package/src/object3d-document-write-policy.ts +137 -0
- package/src/packaged-runtime.ts +108 -0
- package/src/palettes/maya.palette.json +57 -0
- package/src/palettes/substance.palette.json +57 -0
- package/src/pasteboard-module.ts +81 -0
- package/src/performance-profiler.ts +367 -0
- package/src/performance-sources.ts +69 -0
- package/src/play-boot-phase.ts +145 -0
- package/src/presentation-surface.ts +248 -0
- package/src/project-adapter.ts +1143 -0
- package/src/project-asset-refresh.ts +26 -0
- package/src/project-declaration-refresh.ts +31 -0
- package/src/project-local-state.ts +118 -0
- package/src/project-manager.ts +243 -0
- package/src/project-module-changes.ts +163 -0
- package/src/project-module-split.ts +266 -0
- package/src/project-provenance.ts +115 -0
- package/src/project-ready.ts +42 -0
- package/src/project-session-reset.ts +67 -0
- package/src/project-shape.ts +68 -0
- package/src/project-tool-discovery.ts +66 -0
- package/src/project-tools.ts +107 -0
- package/src/project-work-types.ts +149 -0
- package/src/projection/three.ts +898 -0
- package/src/projection/types.ts +44 -0
- package/src/readiness.ts +113 -0
- package/src/reflection-probe-helper.ts +142 -0
- package/src/reported-play-state.ts +90 -0
- package/src/resolve-relative-specifier.ts +33 -0
- package/src/scene-document-plan.ts +315 -0
- package/src/scene-framing.ts +315 -0
- package/src/scene-live-open.ts +210 -0
- package/src/scene-view-fog.ts +89 -0
- package/src/scoped-game-css.ts +152 -0
- package/src/session-orphan-record.ts +193 -0
- package/src/session-tombstone.ts +126 -0
- package/src/settings/settings-provider.ts +82 -0
- package/src/settings-store.ts +345 -0
- package/src/shared-view-restore.ts +42 -0
- package/src/shell-store-door.ts +45 -0
- package/src/source-conflict.ts +122 -0
- package/src/spatial-handle-visuals.ts +332 -0
- package/src/stage-context.ts +341 -0
- package/src/stage-store-registry.ts +63 -0
- package/src/stale-chunk-recovery.ts +34 -0
- package/src/state-report-deferral.ts +73 -0
- package/src/storage/host-files-storage.ts +97 -0
- package/src/storage/http-storage.ts +174 -0
- package/src/storage/index.ts +75 -0
- package/src/storage/mem-storage.ts +149 -0
- package/src/storage/path-lock.ts +44 -0
- package/src/storage/paths.ts +26 -0
- package/src/storage/types.ts +116 -0
- package/src/stories/StoryComponentThumbnail.tsx +184 -0
- package/src/stories/StoryPreviewMount.tsx +306 -0
- package/src/stories/component-content-source.tsx +106 -0
- package/src/stories/component-states-source.ts +78 -0
- package/src/stories/compose-project-stories.ts +255 -0
- package/src/stories/pixi-story-model.ts +30 -0
- package/src/stories/prefabs-finder.ts +54 -0
- package/src/stories/prefabs-from-stories.ts +182 -0
- package/src/stories/project-story-discovery.ts +78 -0
- package/src/stories/project-story-regions.ts +24 -0
- package/src/stories/story-actions.ts +54 -0
- package/src/stories/story-capture-command.ts +124 -0
- package/src/stories/story-capture.ts +584 -0
- package/src/stories/story-declared-medium.ts +126 -0
- package/src/stories/story-discovery.ts +176 -0
- package/src/stories/story-document-openers.ts +36 -0
- package/src/stories/story-dom-runtime.ts +78 -0
- package/src/stories/story-grouping.ts +111 -0
- package/src/stories/story-lane.ts +88 -0
- package/src/stories/story-mount-turn.ts +27 -0
- package/src/stories/story-opener.ts +260 -0
- package/src/stories/story-pixi-preview.ts +408 -0
- package/src/stories/story-presentation.ts +215 -0
- package/src/stories/story-registry.ts +517 -0
- package/src/stories/story-three-preview.ts +807 -0
- package/src/stories/three-story-model.ts +96 -0
- package/src/story-three-preview-runtime.ts +56 -0
- package/src/surface-keyboard.ts +112 -0
- package/src/surface-state.ts +124 -0
- package/src/tab-bootstrap.js +628 -0
- package/src/tab-census.ts +198 -0
- package/src/tab-lifecycle-client.ts +196 -0
- package/src/theme-library.ts +844 -0
- package/src/theme-preference.ts +378 -0
- package/src/theme.css +3437 -0
- package/src/three-viewport/bone-selection-highlight.ts +119 -0
- package/src/three-viewport/camera-fit.ts +41 -0
- package/src/three-viewport/interactive-renderer.ts +132 -0
- package/src/three-viewport/selection-brackets.ts +310 -0
- package/src/three-viewport/selection-outline.ts +183 -0
- package/src/three-viewport/skeleton-helper.ts +61 -0
- package/src/three-viewport/source-color.ts +167 -0
- package/src/three-viewport/studio-environment.ts +96 -0
- package/src/three-viewport-presentation.ts +23 -0
- package/src/tool-contribution-play.ts +74 -0
- package/src/tool-loader.ts +1798 -0
- package/src/transform-mode-request.ts +57 -0
- package/src/transient-hint.ts +78 -0
- package/src/trigger-volume-helper.ts +116 -0
- package/src/ui-source/adapter-region-includes.ts +241 -0
- package/src/ui-source/ensure-import.ts +132 -0
- package/src/ui-source/file-region-resolver.ts +302 -0
- package/src/ui-source/inspect.ts +775 -0
- package/src/ui-source/oid-transform.ts +1845 -0
- package/src/ui-source/plan-csf-story.ts +196 -0
- package/src/ui-source/plan-extract-component.ts +421 -0
- package/src/ui-source/plan-fork-component.ts +621 -0
- package/src/ui-source/plan-named-style.ts +95 -0
- package/src/ui-source/plan-source-edit.ts +353 -0
- package/src/ui-source/r3f-contract-resolver.ts +327 -0
- package/src/ui-source/r3f-diagnostic-index.ts +165 -0
- package/src/ui-source/r3f-environment-binding.ts +133 -0
- package/src/ui-source/r3f-joint-binding.ts +251 -0
- package/src/ui-source/r3f-lod-binding.ts +91 -0
- package/src/ui-source/r3f-particle-binding.ts +235 -0
- package/src/ui-source/r3f-physics-binding.ts +483 -0
- package/src/ui-source/r3f-project-contracts.ts +444 -0
- package/src/ui-source/relative-import-specifier.ts +34 -0
- package/src/ui-source/reparent-guard.ts +544 -0
- package/src/ui-source/source-edit-request.ts +97 -0
- package/src/ui-source/source-write-backend.ts +618 -0
- package/src/ui-source/syntactic-prop-specs.ts +296 -0
- package/src/ui-source/tier-source-write-backend.ts +279 -0
- package/src/ui-source/ts-ast.ts +171 -0
- package/src/ui-source/utility-class-support.ts +169 -0
- package/src/ui-source/write-component-default.ts +133 -0
- package/src/ui-source/writer.ts +1989 -0
- package/src/viewport-activation-timings.ts +840 -0
- package/src/viewport-authoring-policy.ts +186 -0
- package/src/viewport-controls-hint.ts +57 -0
- package/src/viewport-door.ts +237 -0
- package/src/viewport-shading-boundary.ts +12 -0
- package/src/vite-error-surface.ts +141 -0
- package/src/wait-until.ts +37 -0
- package/src/workspace-areas.ts +156 -0
- package/src/workspace-aux-commands.ts +11 -0
- package/src/workspace-available-documents.ts +127 -0
- package/src/workspace-core-utilities.ts +31 -0
- package/src/workspace-document-ids.ts +59 -0
- package/src/workspace-document-registry.ts +612 -0
- package/src/workspace-document-restore.ts +145 -0
- package/src/workspace-host-commands.ts +141 -0
- package/src/workspace-persistence-gate.ts +40 -0
- package/src/workspace-play-utilities.ts +44 -0
- package/src/workspace-presets.ts +425 -0
- package/src/workspace-regions.ts +294 -0
- package/src/workspace-state-persistence.ts +536 -0
- package/src/workspace-static-panels.ts +73 -0
- package/src/workspace-status-registry.ts +121 -0
- package/src/workspace-style.ts +239 -0
- package/src/workspace-utility-commands.ts +74 -0
- package/src/workspace-utility-registry.ts +263 -0
- package/src/workspace-viewport-rect.ts +97 -0
- package/src/world-adoption.ts +115 -0
- package/src/world-document-routing.ts +104 -0
- package/vite-plugin-creation-site-write.ts +184 -0
- package/vite-plugin-creation-site.ts +80 -0
- package/vite-plugin-game-static.ts +303 -0
- package/vite-plugin-module-doorways.ts +336 -0
- package/vite-plugin-product-contributions.ts +197 -0
- package/vite-plugin-project-game-static.ts +125 -0
- package/vite-plugin-project-jsx-js.ts +65 -0
- package/vite-plugin-project-root-absolute-assets.ts +359 -0
- package/vite-plugin-shared-react.ts +420 -0
- package/vite-plugin-shared-three.ts +203 -0
- package/vite-plugin-ui-oid.ts +2106 -0
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE PROJECTOR FOR THE THREE SURFACE — one walk, one index, one set of derived
|
|
3
|
+
* views, for every authoring adapter that drives a live `Object3D` graph
|
|
4
|
+
* (ARCHITECTURE-CORE §The editor protocol, projection family).
|
|
5
|
+
*
|
|
6
|
+
* The projection unit is the RUNTIME OBJECT. Nothing here reads source, an AST
|
|
7
|
+
* or a document; a walk answers from the graph that is actually mounted, so an
|
|
8
|
+
* empty projection over a mounted world is a defect rather than a fact.
|
|
9
|
+
*
|
|
10
|
+
* ## What this owns, and what it deliberately does not
|
|
11
|
+
*
|
|
12
|
+
* OWNS — the four things all three three-surface authoring classes used to
|
|
13
|
+
* spell separately:
|
|
14
|
+
*
|
|
15
|
+
* - the DFS itself ({@link walkThreeGraph}), including the editor-furniture
|
|
16
|
+
* skip and the fold-into-the-nearest-admitted-ancestor rule,
|
|
17
|
+
* - IDENTITY MINTING, parameterized by scheme ({@link oidIdentity},
|
|
18
|
+
* {@link structuralIdentity}, {@link sourceOidIdentity},
|
|
19
|
+
* {@link documentPathIdentity}) — the id FORMATS are the schemes', the walk
|
|
20
|
+
* that applies them is one,
|
|
21
|
+
* - the INDEX both directions ({@link ThreeProjector.objects} /
|
|
22
|
+
* {@link ThreeProjector.idsByObject}) plus the `userData.entityId` stamp
|
|
23
|
+
* that makes the viewport's raycast, the gizmo and `store.objectMap` agree
|
|
24
|
+
* with the walk,
|
|
25
|
+
* - PICKING as a derived view over that index
|
|
26
|
+
* ({@link ThreeProjector.pick}).
|
|
27
|
+
*
|
|
28
|
+
* DOES NOT own the LANE-SPECIFIC hierarchy VIEW built on top. The R3F source
|
|
29
|
+
* lane collapses a component instance's interior into one row; the live lane
|
|
30
|
+
* resolves the same question over projected nodes with different top-level
|
|
31
|
+
* semantics. Both read this projection and derive their rows from it; merging
|
|
32
|
+
* the two collapse rules would be inventing a third, not extracting a shared
|
|
33
|
+
* one.
|
|
34
|
+
*
|
|
35
|
+
* BOUNDS are not here either, because they were never in the authoring classes:
|
|
36
|
+
* `@volter/editor-threejs/viewport/content-bounds` (`contentWorldBounds`) is the bounds walk
|
|
37
|
+
* every consumer uses, and it takes the `Object3D` this index resolves.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import type { EditorNode } from '@volter/editor-project/adapter';
|
|
41
|
+
import { isEditorOwnedObject } from '@volter/editor-threejs/viewport/editor-layers';
|
|
42
|
+
import { constraintsOf } from '@volter/editor-threejs/adapter/constraint';
|
|
43
|
+
import {
|
|
44
|
+
CAMERA_ID,
|
|
45
|
+
collectStructuralIds,
|
|
46
|
+
} from '@volter/editor-threejs/adapter/ingest/structural-ids';
|
|
47
|
+
import { object3DAuthoringSubjectOf } from '@volter/editor-threejs/adapter/object3d-authoring-subject';
|
|
48
|
+
import { reflectionProbeOf } from '@volter/editor-threejs/adapter/reflection-probe';
|
|
49
|
+
import { getUserData } from '@volter/editor-threejs/ecs/user-data';
|
|
50
|
+
import type * as THREE from 'three';
|
|
51
|
+
import { authoringOidOf, occurrenceId } from '../authoring/component-instance-root';
|
|
52
|
+
import { raycastCandidates, raycastPick } from '../authoring/viewport-raycast';
|
|
53
|
+
import type { EditorShellStore } from '../editor-shell-store';
|
|
54
|
+
import { entityIdOf, stampEntityId } from '../entity-object';
|
|
55
|
+
import type { ProjectedNode, Projection } from './types';
|
|
56
|
+
|
|
57
|
+
export type { ProjectedNode, Projection } from './types';
|
|
58
|
+
|
|
59
|
+
/** One projected authoring node over a live three graph. */
|
|
60
|
+
export type ThreeNode = ProjectedNode<THREE.Object3D>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Reflection stats for one walk. Surfaced by the adapters' `refresh()` for
|
|
64
|
+
* proof/telemetry (the ingest console line, the `__vgaiIngest.reflected` hook) —
|
|
65
|
+
* deliberately a fresh measurement each walk rather than a mount-time snapshot,
|
|
66
|
+
* because a running world keeps streaming objects in.
|
|
67
|
+
*/
|
|
68
|
+
export interface ThreeWalkStats {
|
|
69
|
+
readonly count: number;
|
|
70
|
+
readonly meshes: number;
|
|
71
|
+
readonly lights: number;
|
|
72
|
+
/**
|
|
73
|
+
* How many of `meshes` are `InstancedMesh`, and how many units those draw.
|
|
74
|
+
*
|
|
75
|
+
* Every other count here is keyed on OBJECTS, and an `InstancedMesh` is one
|
|
76
|
+
* object drawing N units — so `count`/`meshes` (and the hierarchy rows built
|
|
77
|
+
* from the same nodes) report 1 where a reader sees N, with no error
|
|
78
|
+
* anywhere, because both numbers are individually correct. These two fields
|
|
79
|
+
* are the only place the shortfall is expressible; `vgai doctor`'s
|
|
80
|
+
* instanced-content check reads exactly this pair
|
|
81
|
+
* (`packages/editor/src/doctor/detection-gaps.ts`).
|
|
82
|
+
*/
|
|
83
|
+
readonly instancedMeshes: number;
|
|
84
|
+
readonly instances: number;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type ThreeProjection = Projection<THREE.Object3D, ThreeWalkStats>;
|
|
88
|
+
|
|
89
|
+
/** Nothing walked (no scene mounted, or a scene with nothing in it). */
|
|
90
|
+
export const EMPTY_THREE_WALK_STATS: ThreeWalkStats = {
|
|
91
|
+
count: 0,
|
|
92
|
+
meshes: 0,
|
|
93
|
+
lights: 0,
|
|
94
|
+
instancedMeshes: 0,
|
|
95
|
+
instances: 0,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The IDENTITY seam — how a live `Object3D` tree is addressed: which objects
|
|
100
|
+
* are authoring nodes, what their ids are, and how they nest.
|
|
101
|
+
*
|
|
102
|
+
* An adapter never mints an id itself. It asks a scheme for a
|
|
103
|
+
* {@link ThreeProjection} and drives every provider (hierarchy, selection,
|
|
104
|
+
* transforms, inspector, picking) off that one walk.
|
|
105
|
+
*/
|
|
106
|
+
export interface ThreeIdentity {
|
|
107
|
+
/** (Re)walk `scene` and project its authoring identity. */
|
|
108
|
+
project(scene: THREE.Scene): ThreeProjection;
|
|
109
|
+
/** Live OID worlds can admit one changed native subtree without rewalking
|
|
110
|
+
* every survivor. Other identity schemes fall back to {@link project}. */
|
|
111
|
+
readonly incremental?: {
|
|
112
|
+
readonly kind: 'live-oid';
|
|
113
|
+
readonly worldId: string;
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Where the walk currently is, for a scheme that mints from position. */
|
|
118
|
+
export interface ThreeWalkContext {
|
|
119
|
+
/** Child-index path from the scene, e.g. `[0, 2, 1]`. Skipped objects still
|
|
120
|
+
* consume their index, so an id minted from it is stable against editor
|
|
121
|
+
* furniture appearing beside game content. */
|
|
122
|
+
readonly path: readonly number[];
|
|
123
|
+
/** The nearest ADMITTED ancestor's id — `null` ⇒ this subtree hangs under
|
|
124
|
+
* transparent containers only, so a node admitted here is a projection root. */
|
|
125
|
+
readonly ownerId: string | null;
|
|
126
|
+
/** Whether anything above already claims this subtree as its parts. */
|
|
127
|
+
readonly ownedByAncestor: boolean;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** The per-lane half of a walk: what to skip, what to admit, what to call it. */
|
|
131
|
+
export interface ThreeIdentityScheme {
|
|
132
|
+
/** Fresh per-walk state. */
|
|
133
|
+
begin?(scene: THREE.Scene): void;
|
|
134
|
+
/**
|
|
135
|
+
* Objects that never enter the projection, WITH their subtrees. Defaults to
|
|
136
|
+
* {@link isEditorOwnedObject} — the viewport's layer-31 furniture (grid,
|
|
137
|
+
* editor lights, BatchedRenderer, gizmo helper, pivot dummy, snap
|
|
138
|
+
* indicators) is parked in whatever scene is mounted, and projecting it
|
|
139
|
+
* offers the user the editor's own objects as if they were the world's.
|
|
140
|
+
*/
|
|
141
|
+
skip?(object: THREE.Object3D): boolean;
|
|
142
|
+
/** This object's id, or `null` when it is not an authoring node — in which
|
|
143
|
+
* case its children fold into the nearest admitted ancestor. Called for
|
|
144
|
+
* EVERY visited object, admitted or not, so a scheme counting occurrences
|
|
145
|
+
* counts the folded ones too. */
|
|
146
|
+
identify(object: THREE.Object3D, context: ThreeWalkContext): string | null;
|
|
147
|
+
/** DFS leave hook, after this object's children — for a scheme holding
|
|
148
|
+
* path-scoped state. */
|
|
149
|
+
leave?(object: THREE.Object3D, id: string | null): void;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Session-monotonic counter for minted ids. Module-level ON PURPOSE: the store
|
|
154
|
+
* (adoption) and the adapter both run the walk over the same scene, so a
|
|
155
|
+
* per-instance counter could mint `live:<w>:0` twice for two different objects.
|
|
156
|
+
* Stamped ids are reused on every later walk, so the counter only ever advances
|
|
157
|
+
* for genuinely new objects.
|
|
158
|
+
*/
|
|
159
|
+
let mintedIdCounter = 0;
|
|
160
|
+
|
|
161
|
+
/** The instanced half of {@link ThreeWalkStats}, over an already-projected node map. */
|
|
162
|
+
function instancedTally(nodes: ReadonlyMap<string, ThreeNode>): {
|
|
163
|
+
instancedMeshes: number;
|
|
164
|
+
instances: number;
|
|
165
|
+
} {
|
|
166
|
+
let instancedMeshes = 0;
|
|
167
|
+
let instances = 0;
|
|
168
|
+
for (const node of nodes.values()) {
|
|
169
|
+
const object = node.object as THREE.Object3D & {
|
|
170
|
+
isInstancedMesh?: boolean;
|
|
171
|
+
count?: number;
|
|
172
|
+
};
|
|
173
|
+
if (object.isInstancedMesh) {
|
|
174
|
+
instancedMeshes++;
|
|
175
|
+
instances += typeof object.count === 'number' ? object.count : 0;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return { instancedMeshes, instances };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function tally(nodes: ReadonlyMap<string, ThreeNode>): ThreeWalkStats {
|
|
182
|
+
let meshes = 0;
|
|
183
|
+
let lights = 0;
|
|
184
|
+
for (const node of nodes.values()) {
|
|
185
|
+
const object = node.object as THREE.Object3D & { isMesh?: boolean; isLight?: boolean };
|
|
186
|
+
if (object.isLight) lights++;
|
|
187
|
+
else if (object.isMesh) meshes++;
|
|
188
|
+
}
|
|
189
|
+
return { count: nodes.size, meshes, lights, ...instancedTally(nodes) };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* THE WALK. One DFS over a live scene, projecting it into authoring nodes and
|
|
194
|
+
* stamping `userData.entityId` on each, so the viewport raycast/gizmo and
|
|
195
|
+
* `EditorShellStore.objectMap` key off the same identity the walk minted.
|
|
196
|
+
*
|
|
197
|
+
* Every scheme that goes through here addresses a graph the editor is entitled
|
|
198
|
+
* to stamp. The one scheme that is not ({@link structuralIdentity}, over
|
|
199
|
+
* foreign objects) keeps identity in the projection instead and does not use
|
|
200
|
+
* this walk at all — see #1965.
|
|
201
|
+
*
|
|
202
|
+
* Idempotent for every scheme that reuses an existing stamp: re-walking a scene
|
|
203
|
+
* re-derives the same ids for the same objects.
|
|
204
|
+
*/
|
|
205
|
+
export function walkThreeGraph(scene: THREE.Scene, scheme: ThreeIdentityScheme): ThreeProjection {
|
|
206
|
+
const nodes = new Map<string, ThreeNode>();
|
|
207
|
+
const rootIds: string[] = [];
|
|
208
|
+
const skip = scheme.skip ?? isEditorOwnedObject;
|
|
209
|
+
scheme.begin?.(scene);
|
|
210
|
+
|
|
211
|
+
const visit = (
|
|
212
|
+
object: THREE.Object3D,
|
|
213
|
+
path: readonly number[],
|
|
214
|
+
ownerId: string | null,
|
|
215
|
+
ownedByAncestor: boolean,
|
|
216
|
+
): void => {
|
|
217
|
+
if (skip(object)) return;
|
|
218
|
+
const id = scheme.identify(object, { path, ownerId, ownedByAncestor });
|
|
219
|
+
if (id !== null) {
|
|
220
|
+
stampEntityId(object, id);
|
|
221
|
+
nodes.set(id, { object, id, parentId: ownerId, childIds: [] });
|
|
222
|
+
if (ownerId === null) rootIds.push(id);
|
|
223
|
+
else nodes.get(ownerId)?.childIds.push(id);
|
|
224
|
+
}
|
|
225
|
+
// `ownsParts` is the separate question of whether anything above (or this
|
|
226
|
+
// node itself) claims the subtree below as its implementation.
|
|
227
|
+
const ownsParts = ownedByAncestor || id !== null;
|
|
228
|
+
object.children.forEach((child, index) => {
|
|
229
|
+
visit(child, [...path, index], id ?? ownerId, ownsParts);
|
|
230
|
+
});
|
|
231
|
+
scheme.leave?.(object, id);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
scene.children.forEach((child, index) => {
|
|
235
|
+
visit(child, [index], null, false);
|
|
236
|
+
});
|
|
237
|
+
return { nodes, rootIds, stats: tally(nodes) };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* The scheme half of every three-lane entity id (`r3f:<worldId>:<oid>`).
|
|
242
|
+
*
|
|
243
|
+
* Spelled ONCE because it is read as well as written: the editor shell tests a
|
|
244
|
+
* focused id with `startsWith` to decide whether the three lane owns it
|
|
245
|
+
* (`components/DefaultEditorLayout.tsx`), so a literal changed at one of the
|
|
246
|
+
* mint sites and not at the reader is a silent loss of that decision rather
|
|
247
|
+
* than a compile error.
|
|
248
|
+
*/
|
|
249
|
+
export const THREE_ENTITY_ID_SCHEME = 'r3f:';
|
|
250
|
+
|
|
251
|
+
/** `r3f:<worldId>:<oid>` — the one place the three lane's id shape is built. */
|
|
252
|
+
export function threeEntityId(worldId: string, oid: string): string {
|
|
253
|
+
return `${THREE_ENTITY_ID_SCHEME}${worldId}:${oid}`;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* The LIVE OID scheme. A node stamped by the served TSX's `__vgaiOid` transform
|
|
258
|
+
* keeps `r3f:<worldId>:<oid>`, so selection survives Edit→Play; anything a
|
|
259
|
+
* source location cannot claim gets a session-stable `live:<worldId>:<n>`.
|
|
260
|
+
*/
|
|
261
|
+
function liveOidScheme(worldId: string): ThreeIdentityScheme {
|
|
262
|
+
const oidOccurrences = new Map<string, number>();
|
|
263
|
+
const reservedOccurrenceOwners = new Map<string, THREE.Object3D>();
|
|
264
|
+
const occurrenceBelongsTo = (id: string, base: string): boolean =>
|
|
265
|
+
id === base || (id.startsWith(`${base}#`) && /^\d+$/u.test(id.slice(base.length + 1)));
|
|
266
|
+
|
|
267
|
+
const reserveExistingOccurrences = (scene: THREE.Scene): void => {
|
|
268
|
+
reservedOccurrenceOwners.clear();
|
|
269
|
+
const visit = (object: THREE.Object3D): void => {
|
|
270
|
+
if (isEditorOwnedObject(object)) return;
|
|
271
|
+
const sourceOid = authoringOidOf(object);
|
|
272
|
+
const stamped = entityIdOf(object);
|
|
273
|
+
if (sourceOid !== undefined && stamped !== undefined) {
|
|
274
|
+
const base = threeEntityId(worldId, sourceOid);
|
|
275
|
+
if (occurrenceBelongsTo(stamped, base) && !reservedOccurrenceOwners.has(stamped)) {
|
|
276
|
+
reservedOccurrenceOwners.set(stamped, object);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
for (const child of object.children) visit(child);
|
|
280
|
+
};
|
|
281
|
+
for (const child of scene.children) visit(child);
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const reusableStampedIdentity = (
|
|
285
|
+
object: THREE.Object3D,
|
|
286
|
+
sourceOid: string | undefined,
|
|
287
|
+
): string | undefined => {
|
|
288
|
+
const stamped = entityIdOf(object);
|
|
289
|
+
if (stamped === undefined || sourceOid === undefined) return stamped;
|
|
290
|
+
const base = threeEntityId(worldId, sourceOid);
|
|
291
|
+
if (!occurrenceBelongsTo(stamped, base)) return stamped;
|
|
292
|
+
return reservedOccurrenceOwners.get(stamped) === object ? stamped : undefined;
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const claimSourceIdentity = (object: THREE.Object3D, sourceOid: string): string => {
|
|
296
|
+
const base = threeEntityId(worldId, sourceOid);
|
|
297
|
+
let occurrence = oidOccurrences.get(sourceOid) ?? 0;
|
|
298
|
+
let identity = occurrenceId(base, occurrence);
|
|
299
|
+
while (reservedOccurrenceOwners.has(identity)) {
|
|
300
|
+
occurrence += 1;
|
|
301
|
+
identity = occurrenceId(base, occurrence);
|
|
302
|
+
}
|
|
303
|
+
oidOccurrences.set(sourceOid, occurrence + 1);
|
|
304
|
+
reservedOccurrenceOwners.set(identity, object);
|
|
305
|
+
return identity;
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
begin: (scene) => {
|
|
310
|
+
oidOccurrences.clear();
|
|
311
|
+
reserveExistingOccurrences(scene);
|
|
312
|
+
},
|
|
313
|
+
identify: (object, context) => {
|
|
314
|
+
// Count every source-stamped runtime node in native traversal order,
|
|
315
|
+
// including projected-away wrappers/parts, so surviving authoring roots
|
|
316
|
+
// keep the same id across Edit and Play (mirrors the R3F design adapter's
|
|
317
|
+
// duplicate-OID suffixing exactly).
|
|
318
|
+
const sourceOid = authoringOidOf(object);
|
|
319
|
+
const stamped = reusableStampedIdentity(object, sourceOid);
|
|
320
|
+
if (stamped !== undefined) return stamped;
|
|
321
|
+
// Two live objects carrying one old occurrence stamp is the precise
|
|
322
|
+
// collision the reservation pass repairs. The first object in the
|
|
323
|
+
// current graph keeps the address; this later one receives the first
|
|
324
|
+
// free occurrence and is restamped by walkThreeGraph.
|
|
325
|
+
const sourceIdentity =
|
|
326
|
+
sourceOid === undefined ? undefined : claimSourceIdentity(object, sourceOid);
|
|
327
|
+
// ADMISSION. Three independent grounds, and the FIRST is what makes a
|
|
328
|
+
// play mount project the world the author wrote:
|
|
329
|
+
//
|
|
330
|
+
// - A SOURCE IDENTITY. The served TSX stamped this element, so it is a
|
|
331
|
+
// node the author authored — `<object3D name='Coins'>` as much as the
|
|
332
|
+
// `<Coin/>` instances inside it. Ownership by an ancestor is beside the
|
|
333
|
+
// point: an authored node is not its owner's implementation detail.
|
|
334
|
+
// Without this, a fresh play mount (where nothing is pre-stamped with
|
|
335
|
+
// an `entityId` yet) admitted only the nodes whose `authoringInstance`
|
|
336
|
+
// differed from their parent's — so `Stage`'s whole interior collapsed
|
|
337
|
+
// and its 49 component instances rendered FLAT, with the `Coins` and
|
|
338
|
+
// `Enemies` groups that really parent them gone from the panel.
|
|
339
|
+
// - NO OWNER ABOVE. An unstamped world (an ingest, an adopted graph) has
|
|
340
|
+
// no source identities at all; its outermost objects are the rows, and
|
|
341
|
+
// everything under them stays folded in as parts.
|
|
342
|
+
// - AN EXPLICIT OPT-IN. `userData.authoringRoot` is how a runtime
|
|
343
|
+
// descendant of such a world asks to be a row of its own.
|
|
344
|
+
//
|
|
345
|
+
// What stays folded is exactly what has none of the three: the parts a
|
|
346
|
+
// library builds under an authored node (GLTF meshes, bones, a light's
|
|
347
|
+
// `target`), which the mark convention folds by classification instead.
|
|
348
|
+
if (
|
|
349
|
+
sourceIdentity !== undefined ||
|
|
350
|
+
!context.ownedByAncestor ||
|
|
351
|
+
getUserData(object, 'authoringRoot') === true
|
|
352
|
+
) {
|
|
353
|
+
return sourceIdentity ?? `live:${worldId}:${mintedIdCounter++}`;
|
|
354
|
+
}
|
|
355
|
+
return null;
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Walk a live scene under the OID scheme. Exported because scene ADOPTION runs
|
|
362
|
+
* the same walk the adapter does (`viewport-root-presentation.ts`), and the two must
|
|
363
|
+
* agree about every id.
|
|
364
|
+
*/
|
|
365
|
+
export function projectThreeScene(scene: THREE.Scene, worldId: string): ThreeProjection {
|
|
366
|
+
return walkThreeGraph(scene, liveOidScheme(worldId));
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Stamp identities on a live scene and return the id → Object3D map the store
|
|
371
|
+
* adopts as its `objectMap`. The adapter's own index is built by the same walk,
|
|
372
|
+
* so the two can never disagree about an id.
|
|
373
|
+
*/
|
|
374
|
+
export function stampThreeIdentities(
|
|
375
|
+
scene: THREE.Scene,
|
|
376
|
+
worldId: string,
|
|
377
|
+
): Map<string, THREE.Object3D> {
|
|
378
|
+
const { nodes } = projectThreeScene(scene, worldId);
|
|
379
|
+
const map = new Map<string, THREE.Object3D>();
|
|
380
|
+
for (const [id, node] of nodes) map.set(id, node.object);
|
|
381
|
+
return map;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* The OID identity (see {@link liveOidScheme}), for a LIVE world.
|
|
386
|
+
*
|
|
387
|
+
* `camera` is the SAME allowance {@link structuralIdentity} makes and for the
|
|
388
|
+
* same reason: a captured world's render camera often is not a child of the
|
|
389
|
+
* scene it renders, and a camera that is not a node is not selectable,
|
|
390
|
+
* inspectable, or reachable by the viewport's framing. It joins as an extra
|
|
391
|
+
* root under the fixed {@link CAMERA_ID} only when the walk did not already
|
|
392
|
+
* meet it as a scene child — an R3F world usually authors its camera inside the
|
|
393
|
+
* tree, and admitting it twice would put the same object on two rows.
|
|
394
|
+
*/
|
|
395
|
+
export function oidIdentity(
|
|
396
|
+
worldId: string,
|
|
397
|
+
options: { readonly camera?: THREE.Camera | undefined } = {},
|
|
398
|
+
): ThreeIdentity {
|
|
399
|
+
const camera = options.camera;
|
|
400
|
+
return {
|
|
401
|
+
incremental: { kind: 'live-oid', worldId },
|
|
402
|
+
project: (scene) => {
|
|
403
|
+
const projection = projectThreeScene(scene, worldId);
|
|
404
|
+
if (!camera || [...projection.nodes.values()].some((node) => node.object === camera)) {
|
|
405
|
+
return projection;
|
|
406
|
+
}
|
|
407
|
+
stampEntityId(camera, CAMERA_ID);
|
|
408
|
+
projection.nodes.set(CAMERA_ID, {
|
|
409
|
+
object: camera,
|
|
410
|
+
id: CAMERA_ID,
|
|
411
|
+
parentId: null,
|
|
412
|
+
childIds: [],
|
|
413
|
+
});
|
|
414
|
+
projection.rootIds.push(CAMERA_ID);
|
|
415
|
+
return { ...projection, stats: { ...projection.stats, count: projection.nodes.size } };
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export interface StructuralIdentityOptions {
|
|
421
|
+
/**
|
|
422
|
+
* A render camera that is NOT a scene child — surfaced as an extra root node
|
|
423
|
+
* under the fixed {@link CAMERA_ID} so it is selectable and inspectable. A
|
|
424
|
+
* captured world's camera usually lives outside the tree it renders.
|
|
425
|
+
*/
|
|
426
|
+
readonly camera?: THREE.Camera | undefined;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* The STRUCTURAL-PATH scheme. Deterministic from the scene's shape (position in
|
|
431
|
+
* the tree + three.js type + name), so the same id re-binds to the same object
|
|
432
|
+
* after the world rebuilds part of its graph within a session. This is the
|
|
433
|
+
* scheme for a graph whose source was never stamped, and it pairs with the
|
|
434
|
+
* creation-site anchors the persistence backend writes through.
|
|
435
|
+
*
|
|
436
|
+
* The walk itself lives in `@volter/editor-threejs/adapter/ingest/structural-ids` — it is
|
|
437
|
+
* the engine's own ingest walk and stays there; this provider is the projection
|
|
438
|
+
* hop, turning its pure flat id→object map into the parent/child node graph
|
|
439
|
+
* every authoring provider reads, WITHOUT stamping the foreign objects.
|
|
440
|
+
*/
|
|
441
|
+
export function structuralIdentity(options: StructuralIdentityOptions = {}): ThreeIdentity {
|
|
442
|
+
const camera = options.camera;
|
|
443
|
+
return {
|
|
444
|
+
project: (scene) => {
|
|
445
|
+
const walk = collectStructuralIds(scene, camera);
|
|
446
|
+
// Reverse the walk's own map rather than storing identity on the foreign
|
|
447
|
+
// objects. A re-walk after a rebuild therefore has no stale stamps to
|
|
448
|
+
// collide with live ids or parent under the wrong object.
|
|
449
|
+
const idByObject = new Map<THREE.Object3D, string>();
|
|
450
|
+
for (const [id, object] of walk.byId) idByObject.set(object, id);
|
|
451
|
+
const nodes = new Map<string, ThreeNode>();
|
|
452
|
+
const rootIds: string[] = [];
|
|
453
|
+
for (const [id, object] of walk.byId) {
|
|
454
|
+
const parent = object.parent;
|
|
455
|
+
const parentId = (parent && parent !== scene ? idByObject.get(parent) : undefined) ?? null;
|
|
456
|
+
// `childIds` is filtered to the walk's own membership: editor furniture
|
|
457
|
+
// parked in the world's scene is skipped by `assignStructuralIds`, and
|
|
458
|
+
// it must not reappear as a hierarchy row through a parent's children.
|
|
459
|
+
const childIds = object.children
|
|
460
|
+
.map((child) => idByObject.get(child))
|
|
461
|
+
.filter((childId): childId is string => childId !== undefined);
|
|
462
|
+
nodes.set(id, { object, id, parentId, childIds });
|
|
463
|
+
if (parentId === null) rootIds.push(id);
|
|
464
|
+
}
|
|
465
|
+
return {
|
|
466
|
+
nodes,
|
|
467
|
+
rootIds,
|
|
468
|
+
stats: {
|
|
469
|
+
count: walk.count,
|
|
470
|
+
meshes: walk.meshes,
|
|
471
|
+
lights: walk.lights,
|
|
472
|
+
...instancedTally(nodes),
|
|
473
|
+
},
|
|
474
|
+
};
|
|
475
|
+
},
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* The SOURCE OID identity, for a first-party R3F world whose TSX the editor
|
|
481
|
+
* serves: every object is a node (the lane's hierarchy view does its own
|
|
482
|
+
* collapsing on top), addressed by its authored callsite where it has one and
|
|
483
|
+
* by its structural path where it does not.
|
|
484
|
+
*
|
|
485
|
+
* It also reports the two OID-lane facts the walk is the only place able to
|
|
486
|
+
* measure, refreshed on every `project`:
|
|
487
|
+
*
|
|
488
|
+
* - `instanceRoots` — how many times each authored element RENDERS, counted at
|
|
489
|
+
* instance ROOTS only (an object with no ancestor carrying the same
|
|
490
|
+
* identity). Every host element inside a component definition carries that
|
|
491
|
+
* instance's stamp, so counting stamped objects would report a component as
|
|
492
|
+
* rendering ~50 times instead of once.
|
|
493
|
+
* - `representatives` — the FIRST live object that resolved to each oid: the
|
|
494
|
+
* occurrence that keeps the bare address, and the one every `…#n`
|
|
495
|
+
* occurrence's write authority is decided against.
|
|
496
|
+
*/
|
|
497
|
+
export interface SourceOidIdentity extends ThreeIdentity {
|
|
498
|
+
readonly instanceRoots: ReadonlyMap<string, number>;
|
|
499
|
+
readonly representatives: ReadonlyMap<string, THREE.Object3D>;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
export function sourceOidIdentity(worldId: string): SourceOidIdentity {
|
|
503
|
+
let instanceRoots: ReadonlyMap<string, number> = new Map();
|
|
504
|
+
let representatives: ReadonlyMap<string, THREE.Object3D> = new Map();
|
|
505
|
+
return {
|
|
506
|
+
get instanceRoots() {
|
|
507
|
+
return instanceRoots;
|
|
508
|
+
},
|
|
509
|
+
get representatives() {
|
|
510
|
+
return representatives;
|
|
511
|
+
},
|
|
512
|
+
project: (scene) => {
|
|
513
|
+
const oidOccurrence = new Map<string, number>();
|
|
514
|
+
const roots = new Map<string, number>();
|
|
515
|
+
const reps = new Map<string, THREE.Object3D>();
|
|
516
|
+
/** Authoring identities of the objects ON the current DFS path. */
|
|
517
|
+
const ancestorIdentities = new Set<string>();
|
|
518
|
+
const openedBy = new Map<THREE.Object3D, string>();
|
|
519
|
+
const projection = walkThreeGraph(scene, {
|
|
520
|
+
identify: (object, context) => {
|
|
521
|
+
const oid = authoringOidOf(object);
|
|
522
|
+
if (oid === undefined) return threeEntityId(worldId, `@${context.path.join('.')}`);
|
|
523
|
+
const n = oidOccurrence.get(oid) ?? 0;
|
|
524
|
+
oidOccurrence.set(oid, n + 1);
|
|
525
|
+
if (n === 0) reps.set(oid, object);
|
|
526
|
+
if (!ancestorIdentities.has(oid)) {
|
|
527
|
+
roots.set(oid, (roots.get(oid) ?? 0) + 1);
|
|
528
|
+
ancestorIdentities.add(oid);
|
|
529
|
+
openedBy.set(object, oid);
|
|
530
|
+
}
|
|
531
|
+
return occurrenceId(threeEntityId(worldId, oid), n);
|
|
532
|
+
},
|
|
533
|
+
leave: (object) => {
|
|
534
|
+
const opened = openedBy.get(object);
|
|
535
|
+
if (opened === undefined) return;
|
|
536
|
+
ancestorIdentities.delete(opened);
|
|
537
|
+
openedBy.delete(object);
|
|
538
|
+
},
|
|
539
|
+
});
|
|
540
|
+
instanceRoots = roots;
|
|
541
|
+
representatives = reps;
|
|
542
|
+
return projection;
|
|
543
|
+
},
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* The DOCUMENT-PATH identity, for a native Object3D graph opened as a document
|
|
549
|
+
* (a loaded model, a project source graph): document-scoped structural ids over
|
|
550
|
+
* every object the document owns.
|
|
551
|
+
*
|
|
552
|
+
* Its skip is the DOCUMENT's, not the viewport's: a document graph is built by
|
|
553
|
+
* its own session rather than parked in the editor's scene, and the one piece
|
|
554
|
+
* of furniture it does add is a `SkeletonHelper`, which carries no editor mark
|
|
555
|
+
* of its own.
|
|
556
|
+
*/
|
|
557
|
+
export function documentPathIdentity(documentId: string): ThreeIdentity {
|
|
558
|
+
return {
|
|
559
|
+
project: (scene) =>
|
|
560
|
+
walkThreeGraph(scene, {
|
|
561
|
+
skip: (object) =>
|
|
562
|
+
Boolean(getUserData(object, 'editorHelper')) ||
|
|
563
|
+
(object as THREE.SkeletonHelper).isSkeletonHelper === true,
|
|
564
|
+
identify: (_object, context) => `source-object:${documentId}:${context.path.join('.')}`,
|
|
565
|
+
}),
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* What KIND of thing a projected node is, for the hierarchy row's icon and the
|
|
571
|
+
* inspector's header. Reflected from the live object plus the marks a world
|
|
572
|
+
* puts on its own nodes — no schema, no name convention.
|
|
573
|
+
*/
|
|
574
|
+
export function nativeKindOf(object: THREE.Object3D): EditorNode['kind'] {
|
|
575
|
+
const authoringSubject = object3DAuthoringSubjectOf(object);
|
|
576
|
+
if (authoringSubject) return authoringSubject.kind;
|
|
577
|
+
if (constraintsOf(object).length > 0) return 'constraint-owner';
|
|
578
|
+
if (reflectionProbeOf(object)) return 'reflection-probe';
|
|
579
|
+
if ((object as THREE.SkinnedMesh).isSkinnedMesh) return 'skinned-mesh';
|
|
580
|
+
if ((object as THREE.Bone).isBone) return 'bone';
|
|
581
|
+
if ((object as THREE.Light).isLight) return 'light';
|
|
582
|
+
if ((object as THREE.Camera).isCamera) return 'camera';
|
|
583
|
+
// Three tags its audio nodes only through `type` — there is no `isAudio`
|
|
584
|
+
// flag to read like the lines above, and an `Audio`/`AudioListener` carries
|
|
585
|
+
// no geometry, so without this it fell through to the generic `object`
|
|
586
|
+
// circle and a world's sound was the one thing the hierarchy could not name.
|
|
587
|
+
if (
|
|
588
|
+
object.type === 'Audio' ||
|
|
589
|
+
object.type === 'PositionalAudio' ||
|
|
590
|
+
object.type === 'AudioListener'
|
|
591
|
+
)
|
|
592
|
+
return 'audio';
|
|
593
|
+
if ((object as THREE.Mesh).isMesh) return 'mesh';
|
|
594
|
+
if (object.children.length > 0) return 'group';
|
|
595
|
+
return 'object';
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/** Where {@link ThreeProjector.pick} raycasts. */
|
|
599
|
+
export type ThreePickScope =
|
|
600
|
+
/** This projector's own objects, resolved through its own reverse index — the
|
|
601
|
+
* answer for a surface whose objects are not (or not only) the shell's. */
|
|
602
|
+
| 'projection'
|
|
603
|
+
/** `EditorShellStore.objectMap` and the `entityId` stamps, the shell's own
|
|
604
|
+
* index — the answer for a surface that owns that map. */
|
|
605
|
+
| 'store-object-map';
|
|
606
|
+
|
|
607
|
+
export interface ThreeProjectorOptions {
|
|
608
|
+
/** Default `'projection'`. */
|
|
609
|
+
readonly pickScope?: ThreePickScope;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function isDescendant(object: THREE.Object3D, ancestor: THREE.Object3D): boolean {
|
|
613
|
+
for (let current = object.parent; current !== null; current = current.parent) {
|
|
614
|
+
if (current === ancestor) return true;
|
|
615
|
+
}
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function isAttachedTo(object: THREE.Object3D, scene: THREE.Scene): boolean {
|
|
620
|
+
if (object === scene) return true;
|
|
621
|
+
for (let current = object.parent; current !== null; current = current.parent) {
|
|
622
|
+
if (current === scene) return true;
|
|
623
|
+
}
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
export interface ThreeProjectionDelta {
|
|
628
|
+
readonly removedIds: ReadonlySet<string>;
|
|
629
|
+
readonly addedIds: ReadonlySet<string>;
|
|
630
|
+
/** Projected parents whose direct child order/membership changed. */
|
|
631
|
+
readonly changedParentIds: ReadonlySet<string>;
|
|
632
|
+
/** True when the projection forest's own root order/membership changed. */
|
|
633
|
+
readonly rootsChanged: boolean;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* The projector: one identity scheme, the index its walk produces, and the
|
|
638
|
+
* derived views every three authoring adapter reads instead of re-walking.
|
|
639
|
+
*/
|
|
640
|
+
export class ThreeProjector {
|
|
641
|
+
private nodesById = new Map<string, ThreeNode>();
|
|
642
|
+
private objectsById = new Map<string, THREE.Object3D>();
|
|
643
|
+
private idsByObjectIndex = new Map<THREE.Object3D, string>();
|
|
644
|
+
private roots: string[] = [];
|
|
645
|
+
private walkStats: ThreeWalkStats = EMPTY_THREE_WALK_STATS;
|
|
646
|
+
|
|
647
|
+
constructor(
|
|
648
|
+
private readonly identity: ThreeIdentity,
|
|
649
|
+
private readonly options: ThreeProjectorOptions = {},
|
|
650
|
+
) {}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* (Re)walk. `null` is an honest "nothing is mounted" — the projection empties
|
|
654
|
+
* rather than keeping a stale index, because a hierarchy over a torn-down
|
|
655
|
+
* world is worse than no hierarchy.
|
|
656
|
+
*/
|
|
657
|
+
project(scene: THREE.Scene | null): ThreeWalkStats {
|
|
658
|
+
if (!scene) {
|
|
659
|
+
this.nodesById = new Map();
|
|
660
|
+
this.objectsById = new Map();
|
|
661
|
+
this.idsByObjectIndex = new Map();
|
|
662
|
+
this.roots = [];
|
|
663
|
+
this.walkStats = EMPTY_THREE_WALK_STATS;
|
|
664
|
+
return this.walkStats;
|
|
665
|
+
}
|
|
666
|
+
const { nodes, rootIds, stats } = this.identity.project(scene);
|
|
667
|
+
this.nodesById = nodes;
|
|
668
|
+
this.objectsById = new Map([...nodes].map(([id, node]) => [id, node.object] as const));
|
|
669
|
+
this.idsByObjectIndex = new Map([...nodes].map(([id, node]) => [node.object, id] as const));
|
|
670
|
+
this.roots = rootIds;
|
|
671
|
+
this.walkStats = stats;
|
|
672
|
+
return stats;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/** Apply final-state childadded/childremoved roots to a live OID projection.
|
|
676
|
+
* Survivors retain their ids; only the changed subtrees are withdrawn and
|
|
677
|
+
* re-admitted. `null` asks the caller to use the identity's full rebuild. */
|
|
678
|
+
projectSubtrees(
|
|
679
|
+
scene: THREE.Scene | null,
|
|
680
|
+
changedRoots: readonly THREE.Object3D[],
|
|
681
|
+
): ThreeProjectionDelta | null {
|
|
682
|
+
const incremental = this.identity.incremental;
|
|
683
|
+
if (scene === null || incremental?.kind !== 'live-oid') return null;
|
|
684
|
+
|
|
685
|
+
// A reparent emits remove+add in one task, and a parent/child pair can both
|
|
686
|
+
// appear in the burst. Process only the outermost changed roots.
|
|
687
|
+
const changed = changedRoots.filter(
|
|
688
|
+
(candidate, index) =>
|
|
689
|
+
changedRoots.indexOf(candidate) === index &&
|
|
690
|
+
!changedRoots.some((other) => other !== candidate && isDescendant(candidate, other)),
|
|
691
|
+
);
|
|
692
|
+
const previousRoots = this.roots;
|
|
693
|
+
const removedIds = new Set<string>();
|
|
694
|
+
const addedIds = new Set<string>();
|
|
695
|
+
const affectedParents = new Set<string>();
|
|
696
|
+
|
|
697
|
+
for (const root of changed) {
|
|
698
|
+
root.traverse((object) => {
|
|
699
|
+
const id = this.idsByObjectIndex.get(object);
|
|
700
|
+
if (id === undefined) return;
|
|
701
|
+
const node = this.nodesById.get(id);
|
|
702
|
+
if (node?.parentId !== null && node?.parentId !== undefined) {
|
|
703
|
+
affectedParents.add(node.parentId);
|
|
704
|
+
}
|
|
705
|
+
removedIds.add(id);
|
|
706
|
+
this.nodesById.delete(id);
|
|
707
|
+
this.objectsById.delete(id);
|
|
708
|
+
this.idsByObjectIndex.delete(object);
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const skip = isEditorOwnedObject;
|
|
713
|
+
const idAvailable = (id: string, object: THREE.Object3D): boolean => {
|
|
714
|
+
const owner = this.objectsById.get(id);
|
|
715
|
+
return owner === undefined || owner === object;
|
|
716
|
+
};
|
|
717
|
+
const identify = (object: THREE.Object3D, ownedByAncestor: boolean): string | null => {
|
|
718
|
+
const stamped = entityIdOf(object);
|
|
719
|
+
if (stamped !== undefined && idAvailable(stamped, object)) return stamped;
|
|
720
|
+
const sourceOid = authoringOidOf(object);
|
|
721
|
+
if (sourceOid !== undefined) {
|
|
722
|
+
const base = threeEntityId(incremental.worldId, sourceOid);
|
|
723
|
+
let occurrence = 0;
|
|
724
|
+
let id = occurrenceId(base, occurrence);
|
|
725
|
+
while (!idAvailable(id, object)) id = occurrenceId(base, ++occurrence);
|
|
726
|
+
return id;
|
|
727
|
+
}
|
|
728
|
+
if (!ownedByAncestor || getUserData(object, 'authoringRoot') === true) {
|
|
729
|
+
let id = `live:${incremental.worldId}:${mintedIdCounter++}`;
|
|
730
|
+
while (!idAvailable(id, object)) id = `live:${incremental.worldId}:${mintedIdCounter++}`;
|
|
731
|
+
return id;
|
|
732
|
+
}
|
|
733
|
+
return null;
|
|
734
|
+
};
|
|
735
|
+
const visit = (
|
|
736
|
+
object: THREE.Object3D,
|
|
737
|
+
ownerId: string | null,
|
|
738
|
+
ownedByAncestor: boolean,
|
|
739
|
+
): void => {
|
|
740
|
+
if (skip(object)) return;
|
|
741
|
+
const id = identify(object, ownedByAncestor);
|
|
742
|
+
if (id !== null) {
|
|
743
|
+
stampEntityId(object, id);
|
|
744
|
+
this.nodesById.set(id, { object, id, parentId: ownerId, childIds: [] });
|
|
745
|
+
this.objectsById.set(id, object);
|
|
746
|
+
this.idsByObjectIndex.set(object, id);
|
|
747
|
+
addedIds.add(id);
|
|
748
|
+
if (ownerId !== null) affectedParents.add(ownerId);
|
|
749
|
+
}
|
|
750
|
+
const nextOwner = id ?? ownerId;
|
|
751
|
+
const ownsParts = ownedByAncestor || id !== null;
|
|
752
|
+
for (const child of object.children) visit(child, nextOwner, ownsParts);
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
for (const root of changed) {
|
|
756
|
+
if (!isAttachedTo(root, scene)) continue;
|
|
757
|
+
let parent = root.parent;
|
|
758
|
+
let ownerId: string | null = null;
|
|
759
|
+
while (parent !== null && parent !== scene) {
|
|
760
|
+
const candidate = this.idsByObjectIndex.get(parent);
|
|
761
|
+
if (candidate !== undefined) {
|
|
762
|
+
ownerId = candidate;
|
|
763
|
+
break;
|
|
764
|
+
}
|
|
765
|
+
parent = parent.parent;
|
|
766
|
+
}
|
|
767
|
+
visit(root, ownerId, ownerId !== null);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
const projectedChildren = (container: THREE.Object3D): string[] => {
|
|
771
|
+
const ids: string[] = [];
|
|
772
|
+
const collect = (object: THREE.Object3D): void => {
|
|
773
|
+
if (skip(object)) return;
|
|
774
|
+
const id = this.idsByObjectIndex.get(object);
|
|
775
|
+
if (id !== undefined) {
|
|
776
|
+
ids.push(id);
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
for (const child of object.children) collect(child);
|
|
780
|
+
};
|
|
781
|
+
for (const child of container.children) collect(child);
|
|
782
|
+
return ids;
|
|
783
|
+
};
|
|
784
|
+
for (const id of affectedParents) {
|
|
785
|
+
const parent = this.nodesById.get(id);
|
|
786
|
+
if (parent !== undefined) {
|
|
787
|
+
parent.childIds.splice(0, parent.childIds.length, ...projectedChildren(parent.object));
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
for (const id of addedIds) {
|
|
791
|
+
const node = this.nodesById.get(id);
|
|
792
|
+
if (node !== undefined) {
|
|
793
|
+
node.childIds.splice(0, node.childIds.length, ...projectedChildren(node.object));
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
const sceneRoots = projectedChildren(scene);
|
|
797
|
+
const detachedRoots = this.roots.filter((id) => {
|
|
798
|
+
const object = this.objectsById.get(id);
|
|
799
|
+
return object !== undefined && !isAttachedTo(object, scene) && !removedIds.has(id);
|
|
800
|
+
});
|
|
801
|
+
this.roots = [...sceneRoots, ...detachedRoots];
|
|
802
|
+
this.walkStats = tally(this.nodesById);
|
|
803
|
+
const rootsChanged =
|
|
804
|
+
previousRoots.length !== this.roots.length ||
|
|
805
|
+
previousRoots.some((id, index) => id !== this.roots[index]);
|
|
806
|
+
return { removedIds, addedIds, changedParentIds: affectedParents, rootsChanged };
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
get nodes(): ReadonlyMap<string, ThreeNode> {
|
|
810
|
+
return this.nodesById;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/** id → live object. */
|
|
814
|
+
get objects(): ReadonlyMap<string, THREE.Object3D> {
|
|
815
|
+
return this.objectsById;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/** live object → id. */
|
|
819
|
+
get idsByObject(): ReadonlyMap<THREE.Object3D, string> {
|
|
820
|
+
return this.idsByObjectIndex;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
get rootIds(): readonly string[] {
|
|
824
|
+
return this.roots;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
get stats(): ThreeWalkStats {
|
|
828
|
+
return this.walkStats;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
node(id: string): ThreeNode | null {
|
|
832
|
+
return this.nodesById.get(id) ?? null;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
rootNodes(): ThreeNode[] {
|
|
836
|
+
return this.roots
|
|
837
|
+
.map((id) => this.nodesById.get(id))
|
|
838
|
+
.filter((node): node is ThreeNode => node !== undefined);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
objectOf(id: string): THREE.Object3D | null {
|
|
842
|
+
return this.objectsById.get(id) ?? null;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
idOf(object: THREE.Object3D): string | null {
|
|
846
|
+
return this.idsByObjectIndex.get(object) ?? null;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/** The ids of whichever objects are in this projection — the one place the
|
|
850
|
+
* "map through the reverse index and drop what the walk skipped" step is
|
|
851
|
+
* written. */
|
|
852
|
+
childIdsOf(objects: Iterable<THREE.Object3D>): string[] {
|
|
853
|
+
const ids: string[] = [];
|
|
854
|
+
for (const object of objects) {
|
|
855
|
+
const id = this.idsByObjectIndex.get(object);
|
|
856
|
+
if (id !== undefined) ids.push(id);
|
|
857
|
+
}
|
|
858
|
+
return ids;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/** A copy for the shell viewport's native-object index. */
|
|
862
|
+
objectMapSnapshot(): Map<string, THREE.Object3D> {
|
|
863
|
+
return new Map(this.objectsById);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/** Frontmost authorable subject under the pointer. */
|
|
867
|
+
pick(
|
|
868
|
+
store: EditorShellStore,
|
|
869
|
+
clientX: number,
|
|
870
|
+
clientY: number,
|
|
871
|
+
isLocked?: (id: string) => boolean,
|
|
872
|
+
): string | null {
|
|
873
|
+
return raycastPick(store, clientX, clientY, isLocked, this.pickProjection());
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/** Every authorable subject under the pointer, front to back. */
|
|
877
|
+
candidates(
|
|
878
|
+
store: EditorShellStore,
|
|
879
|
+
clientX: number,
|
|
880
|
+
clientY: number,
|
|
881
|
+
isLocked?: (id: string) => boolean,
|
|
882
|
+
): string[] {
|
|
883
|
+
return raycastCandidates(store, clientX, clientY, isLocked, this.pickProjection());
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
private pickProjection():
|
|
887
|
+
| {
|
|
888
|
+
objects: Iterable<THREE.Object3D>;
|
|
889
|
+
idForObject3D: (object: THREE.Object3D) => string | null;
|
|
890
|
+
}
|
|
891
|
+
| undefined {
|
|
892
|
+
if (this.options.pickScope === 'store-object-map') return undefined;
|
|
893
|
+
return {
|
|
894
|
+
objects: this.objectsById.values(),
|
|
895
|
+
idForObject3D: (object) => this.idOf(object),
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
}
|