@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,818 @@
|
|
|
1
|
+
import { getUserData } from '@volter/editor-threejs/ecs/user-data';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
|
|
4
|
+
export interface ModelInspection {
|
|
5
|
+
nodes: number;
|
|
6
|
+
meshes: number;
|
|
7
|
+
primitives: number;
|
|
8
|
+
vertices: number;
|
|
9
|
+
triangles: number;
|
|
10
|
+
materials: number;
|
|
11
|
+
textures: number;
|
|
12
|
+
bones: number;
|
|
13
|
+
clips: number;
|
|
14
|
+
morphTargets: number;
|
|
15
|
+
memoryBytes: number;
|
|
16
|
+
bounds: { x: number; y: number; z: number } | null;
|
|
17
|
+
geometry: {
|
|
18
|
+
readonly attributes: Array<{
|
|
19
|
+
readonly name: string;
|
|
20
|
+
readonly itemSize: number;
|
|
21
|
+
readonly meshCount: number;
|
|
22
|
+
}>;
|
|
23
|
+
readonly uvChannels: readonly string[];
|
|
24
|
+
readonly indexedMeshes: number;
|
|
25
|
+
readonly nonIndexedMeshes: number;
|
|
26
|
+
readonly instancedMeshes: number;
|
|
27
|
+
readonly skinnedMeshes: number;
|
|
28
|
+
readonly nonFiniteAttributeValues: number;
|
|
29
|
+
readonly lodGroups: number;
|
|
30
|
+
readonly lodLevels: number;
|
|
31
|
+
};
|
|
32
|
+
animationClips: Array<{
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly name: string;
|
|
35
|
+
readonly duration: number;
|
|
36
|
+
readonly tracks: number;
|
|
37
|
+
readonly keyframes: number;
|
|
38
|
+
readonly targets: readonly string[];
|
|
39
|
+
readonly rootMotion: boolean;
|
|
40
|
+
}>;
|
|
41
|
+
hierarchy: Array<{ id: string; name: string; type: string; depth: number }>;
|
|
42
|
+
materialNames: string[];
|
|
43
|
+
textureNames: string[];
|
|
44
|
+
materialSlots: Array<{
|
|
45
|
+
id: string;
|
|
46
|
+
name: string;
|
|
47
|
+
type: string;
|
|
48
|
+
textureSlots: string[];
|
|
49
|
+
usageCount: number;
|
|
50
|
+
meshNames: string[];
|
|
51
|
+
color: string | null;
|
|
52
|
+
transparent: boolean;
|
|
53
|
+
}>;
|
|
54
|
+
textureBindings: Array<{
|
|
55
|
+
materialId: string;
|
|
56
|
+
materialName: string;
|
|
57
|
+
slot: string;
|
|
58
|
+
textureName: string;
|
|
59
|
+
width: number | null;
|
|
60
|
+
height: number | null;
|
|
61
|
+
format: number;
|
|
62
|
+
colorSpace: string;
|
|
63
|
+
}>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface ModelRigInspection {
|
|
67
|
+
readonly kind: 'none' | 'rigid' | 'skinned';
|
|
68
|
+
readonly skinnedMeshes: number;
|
|
69
|
+
readonly skeletons: number;
|
|
70
|
+
readonly uniqueBones: number;
|
|
71
|
+
readonly weightedVertices: number;
|
|
72
|
+
readonly nonNormalizedVertices: number;
|
|
73
|
+
readonly zeroWeightVertices: number;
|
|
74
|
+
readonly invalidJointIndices: number;
|
|
75
|
+
readonly inverseBindMismatches: number;
|
|
76
|
+
readonly externalBones: number;
|
|
77
|
+
readonly nonFiniteAnimationValues: number;
|
|
78
|
+
readonly unresolvedTrackTargets: string[];
|
|
79
|
+
readonly rootBones: readonly string[];
|
|
80
|
+
readonly bindModes: readonly string[];
|
|
81
|
+
readonly valid: boolean;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type ModelStructureKind = 'static' | 'animated' | 'rigged' | 'skinned';
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Classify the native graph without treating every animated object as a rig or
|
|
88
|
+
* every non-skinned model as static. Rigid armatures are native Bone trees;
|
|
89
|
+
* skinning is the separate mesh-deformation case.
|
|
90
|
+
*/
|
|
91
|
+
export function classifyModelStructure(
|
|
92
|
+
inspection: Pick<ModelInspection, 'clips'>,
|
|
93
|
+
rig: Pick<ModelRigInspection, 'uniqueBones' | 'skinnedMeshes'>,
|
|
94
|
+
): ModelStructureKind {
|
|
95
|
+
if (rig.skinnedMeshes > 0) return 'skinned';
|
|
96
|
+
if (rig.uniqueBones > 0) return 'rigged';
|
|
97
|
+
if (inspection.clips > 0) return 'animated';
|
|
98
|
+
return 'static';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface Object3DNodeInspection {
|
|
102
|
+
readonly type: string;
|
|
103
|
+
readonly visible: boolean;
|
|
104
|
+
readonly children: number;
|
|
105
|
+
readonly descendants: number;
|
|
106
|
+
readonly renderOrder: number;
|
|
107
|
+
readonly layers: number;
|
|
108
|
+
readonly geometry: null | {
|
|
109
|
+
readonly vertices: number;
|
|
110
|
+
readonly triangles: number;
|
|
111
|
+
readonly indexed: boolean;
|
|
112
|
+
readonly attributes: readonly string[];
|
|
113
|
+
readonly uvChannels: readonly string[];
|
|
114
|
+
readonly morphTargets: number;
|
|
115
|
+
readonly nonFiniteAttributeValues: number;
|
|
116
|
+
readonly bounds: { x: number; y: number; z: number } | null;
|
|
117
|
+
};
|
|
118
|
+
readonly materials: Array<{
|
|
119
|
+
readonly id: string;
|
|
120
|
+
readonly name: string;
|
|
121
|
+
readonly type: string;
|
|
122
|
+
readonly color: string | null;
|
|
123
|
+
readonly textureSlots: readonly string[];
|
|
124
|
+
}>;
|
|
125
|
+
readonly skin: null | {
|
|
126
|
+
readonly bones: number;
|
|
127
|
+
readonly rootBones: readonly string[];
|
|
128
|
+
readonly bindMode: string;
|
|
129
|
+
};
|
|
130
|
+
readonly bone: null | {
|
|
131
|
+
readonly parent: string | null;
|
|
132
|
+
readonly children: readonly string[];
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
interface SkinWeightInspection {
|
|
137
|
+
readonly weightedVertices: number;
|
|
138
|
+
readonly nonNormalizedVertices: number;
|
|
139
|
+
readonly zeroWeightVertices: number;
|
|
140
|
+
readonly invalidJointIndices: number;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const textureKeys = [
|
|
144
|
+
'map',
|
|
145
|
+
'alphaMap',
|
|
146
|
+
'aoMap',
|
|
147
|
+
'bumpMap',
|
|
148
|
+
'displacementMap',
|
|
149
|
+
'emissiveMap',
|
|
150
|
+
'envMap',
|
|
151
|
+
'lightMap',
|
|
152
|
+
'metalnessMap',
|
|
153
|
+
'normalMap',
|
|
154
|
+
'roughnessMap',
|
|
155
|
+
'specularMap',
|
|
156
|
+
] as const;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* A material's base colour as hex, or `null` when it has none.
|
|
160
|
+
*
|
|
161
|
+
* `.isColor` rather than `instanceof THREE.Color`: these roots arrive from
|
|
162
|
+
* `../authoring/source-object3d-authoring-adapter.ts` and the selection
|
|
163
|
+
* inspector, so under the packaged runtime the material was constructed by the
|
|
164
|
+
* PROJECT's own `three` module and its `Color` is not the shell's constructor.
|
|
165
|
+
* `instanceof` there reports every authored colour as absent. Same rule and
|
|
166
|
+
* same reason as `../authoring/three-scene-identity.ts`.
|
|
167
|
+
*/
|
|
168
|
+
function materialColorHex(material: THREE.Material): string | null {
|
|
169
|
+
if (!('color' in material)) return null;
|
|
170
|
+
const color = (material as THREE.Material & { color?: THREE.Color }).color;
|
|
171
|
+
return color?.isColor === true ? `#${color.getHexString()}` : null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function countNonFiniteAttributeValues(geometry: THREE.BufferGeometry): number {
|
|
175
|
+
let count = 0;
|
|
176
|
+
for (const attribute of Object.values(geometry.attributes)) {
|
|
177
|
+
const values = attribute.array;
|
|
178
|
+
for (let index = 0; index < values.length; index++) {
|
|
179
|
+
if (!Number.isFinite(Number(values[index]))) count++;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return count;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function attributeBounds(
|
|
186
|
+
attribute: THREE.BufferAttribute | THREE.InterleavedBufferAttribute | undefined,
|
|
187
|
+
): THREE.Box3 | null {
|
|
188
|
+
if (!attribute) return null;
|
|
189
|
+
const bounds = new THREE.Box3();
|
|
190
|
+
const point = new THREE.Vector3();
|
|
191
|
+
for (let index = 0; index < attribute.count; index++) {
|
|
192
|
+
point.set(attribute.getX(index), attribute.getY(index), attribute.getZ(index));
|
|
193
|
+
if (Number.isFinite(point.x) && Number.isFinite(point.y) && Number.isFinite(point.z)) {
|
|
194
|
+
bounds.expandByPoint(point);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return bounds.isEmpty() ? null : bounds;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Compute geometry bounds without writing BufferGeometry.boundingBox. */
|
|
201
|
+
function geometryBounds(geometry: THREE.BufferGeometry): THREE.Box3 | null {
|
|
202
|
+
const bounds = attributeBounds(geometry.getAttribute('position'));
|
|
203
|
+
if (!bounds) return null;
|
|
204
|
+
for (const attribute of geometry.morphAttributes['position'] ?? []) {
|
|
205
|
+
const morphBounds = attributeBounds(attribute);
|
|
206
|
+
if (!morphBounds) continue;
|
|
207
|
+
if (geometry.morphTargetsRelative) {
|
|
208
|
+
bounds.expandByPoint(new THREE.Vector3().addVectors(bounds.min, morphBounds.min));
|
|
209
|
+
bounds.expandByPoint(new THREE.Vector3().addVectors(bounds.max, morphBounds.max));
|
|
210
|
+
} else {
|
|
211
|
+
bounds.union(morphBounds);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return bounds;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function localObjectMatrix(object: THREE.Object3D): THREE.Matrix4 {
|
|
218
|
+
return object.matrixAutoUpdate
|
|
219
|
+
? new THREE.Matrix4().compose(object.position, object.quaternion, object.scale)
|
|
220
|
+
: object.matrix.clone();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function expandDrawableBounds(
|
|
224
|
+
result: THREE.Box3,
|
|
225
|
+
object: THREE.Object3D,
|
|
226
|
+
world: THREE.Matrix4,
|
|
227
|
+
): void {
|
|
228
|
+
if (getUserData(object, 'editorHelper') || (object as THREE.SkeletonHelper).isSkeletonHelper)
|
|
229
|
+
return;
|
|
230
|
+
const drawable = object as THREE.Object3D & {
|
|
231
|
+
isMesh?: boolean;
|
|
232
|
+
isLine?: boolean;
|
|
233
|
+
isPoints?: boolean;
|
|
234
|
+
geometry?: THREE.BufferGeometry;
|
|
235
|
+
};
|
|
236
|
+
if ((!drawable.isMesh && !drawable.isLine && !drawable.isPoints) || !drawable.geometry) return;
|
|
237
|
+
const localBounds = geometryBounds(drawable.geometry);
|
|
238
|
+
if (!localBounds) return;
|
|
239
|
+
const instanced = object as THREE.InstancedMesh;
|
|
240
|
+
if (!instanced.isInstancedMesh) {
|
|
241
|
+
result.union(localBounds.clone().applyMatrix4(world));
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const instanceMatrix = new THREE.Matrix4();
|
|
245
|
+
for (let index = 0; index < instanced.count; index++) {
|
|
246
|
+
instanced.getMatrixAt(index, instanceMatrix);
|
|
247
|
+
result.union(
|
|
248
|
+
localBounds.clone().applyMatrix4(new THREE.Matrix4().multiplyMatrices(world, instanceMatrix)),
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Compute native graph bounds without updating Object3D matrices or geometry caches. */
|
|
254
|
+
function modelBounds(root: THREE.Object3D): THREE.Box3 | null {
|
|
255
|
+
const result = new THREE.Box3();
|
|
256
|
+
const ancestors: THREE.Object3D[] = [];
|
|
257
|
+
for (let parent = root.parent; parent; parent = parent.parent) ancestors.push(parent);
|
|
258
|
+
const rootParentWorld = new THREE.Matrix4();
|
|
259
|
+
for (let index = ancestors.length - 1; index >= 0; index--) {
|
|
260
|
+
rootParentWorld.multiply(localObjectMatrix(ancestors[index]!));
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const visit = (object: THREE.Object3D, parentWorld: THREE.Matrix4) => {
|
|
264
|
+
const world = new THREE.Matrix4().multiplyMatrices(parentWorld, localObjectMatrix(object));
|
|
265
|
+
expandDrawableBounds(result, object, world);
|
|
266
|
+
for (const child of object.children) visit(child, world);
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
visit(root, rootParentWorld);
|
|
270
|
+
return result.isEmpty() ? null : result;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** Exact, side-effect-free statistics used by the Asset Editor and import validation. */
|
|
274
|
+
export function inspectModel(root: THREE.Object3D): ModelInspection {
|
|
275
|
+
const materials = new Set<THREE.Material>();
|
|
276
|
+
const textures = new Set<THREE.Texture>();
|
|
277
|
+
const materialSlots = new Map<THREE.Material, string[]>();
|
|
278
|
+
const materialUsers = new Map<THREE.Material, Set<THREE.Object3D>>();
|
|
279
|
+
const attributeUsage = new Map<string, { itemSize: number; meshes: number }>();
|
|
280
|
+
const uvChannels = new Set<string>();
|
|
281
|
+
const textureBindings: ModelInspection['textureBindings'] = [];
|
|
282
|
+
const hierarchy: ModelInspection['hierarchy'] = [];
|
|
283
|
+
let nodes = 0;
|
|
284
|
+
let meshes = 0;
|
|
285
|
+
let primitives = 0;
|
|
286
|
+
let vertices = 0;
|
|
287
|
+
let triangles = 0;
|
|
288
|
+
let bones = 0;
|
|
289
|
+
let morphTargets = 0;
|
|
290
|
+
let memoryBytes = 0;
|
|
291
|
+
let indexedMeshes = 0;
|
|
292
|
+
let nonIndexedMeshes = 0;
|
|
293
|
+
let instancedMeshes = 0;
|
|
294
|
+
let skinnedMeshes = 0;
|
|
295
|
+
let nonFiniteAttributeValues = 0;
|
|
296
|
+
let lodGroups = 0;
|
|
297
|
+
let lodLevels = 0;
|
|
298
|
+
|
|
299
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: one traversal intentionally reconciles geometry, material, texture, animation, and hierarchy statistics.
|
|
300
|
+
root.traverse((object) => {
|
|
301
|
+
if (getUserData(object, 'editorHelper') || (object as THREE.SkeletonHelper).isSkeletonHelper)
|
|
302
|
+
return;
|
|
303
|
+
nodes++;
|
|
304
|
+
let depth = 0;
|
|
305
|
+
for (let parent = object.parent; parent && parent !== root.parent; parent = parent.parent)
|
|
306
|
+
depth++;
|
|
307
|
+
hierarchy.push({ id: object.uuid, name: object.name || object.type, type: object.type, depth });
|
|
308
|
+
if ((object as THREE.Bone).isBone) bones++;
|
|
309
|
+
if ((object as THREE.LOD).isLOD) {
|
|
310
|
+
lodGroups++;
|
|
311
|
+
lodLevels += (object as THREE.LOD).levels.length;
|
|
312
|
+
}
|
|
313
|
+
const renderable = object as THREE.Mesh;
|
|
314
|
+
const drawable = object as THREE.Object3D & {
|
|
315
|
+
isMesh?: boolean;
|
|
316
|
+
isLine?: boolean;
|
|
317
|
+
isPoints?: boolean;
|
|
318
|
+
};
|
|
319
|
+
if (!drawable.isMesh && !drawable.isLine && !drawable.isPoints) return;
|
|
320
|
+
meshes++;
|
|
321
|
+
if ((renderable as THREE.InstancedMesh).isInstancedMesh) instancedMeshes++;
|
|
322
|
+
if ((renderable as THREE.SkinnedMesh).isSkinnedMesh) skinnedMeshes++;
|
|
323
|
+
const geometry = renderable.geometry;
|
|
324
|
+
if (geometry) {
|
|
325
|
+
nonFiniteAttributeValues += countNonFiniteAttributeValues(geometry);
|
|
326
|
+
const position = geometry.getAttribute('position');
|
|
327
|
+
vertices += position?.count ?? 0;
|
|
328
|
+
triangles += geometry.index
|
|
329
|
+
? Math.floor(geometry.index.count / 3)
|
|
330
|
+
: Math.floor((position?.count ?? 0) / 3);
|
|
331
|
+
primitives += Math.max(geometry.groups.length, 1);
|
|
332
|
+
morphTargets += geometry.morphAttributes['position']?.length ?? 0;
|
|
333
|
+
if (geometry.index) {
|
|
334
|
+
indexedMeshes++;
|
|
335
|
+
memoryBytes += geometry.index.array.byteLength;
|
|
336
|
+
} else nonIndexedMeshes++;
|
|
337
|
+
for (const [name, attribute] of Object.entries(geometry.attributes)) {
|
|
338
|
+
memoryBytes += attribute.array.byteLength;
|
|
339
|
+
const usage = attributeUsage.get(name) ?? { itemSize: attribute.itemSize, meshes: 0 };
|
|
340
|
+
usage.meshes++;
|
|
341
|
+
attributeUsage.set(name, usage);
|
|
342
|
+
if (/^uv\d*$/.test(name)) uvChannels.add(name);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
const objectMaterials = Array.isArray(renderable.material)
|
|
346
|
+
? renderable.material
|
|
347
|
+
: renderable.material
|
|
348
|
+
? [renderable.material]
|
|
349
|
+
: [];
|
|
350
|
+
for (const material of objectMaterials) {
|
|
351
|
+
materials.add(material);
|
|
352
|
+
const users = materialUsers.get(material) ?? new Set<THREE.Object3D>();
|
|
353
|
+
users.add(object);
|
|
354
|
+
materialUsers.set(material, users);
|
|
355
|
+
const slots = materialSlots.get(material) ?? [];
|
|
356
|
+
for (const key of textureKeys) {
|
|
357
|
+
// Structural brand, not `instanceof`: these roots arrive from
|
|
358
|
+
// `source-object3d-authoring-adapter.ts` and the selection inspector,
|
|
359
|
+
// so under the packaged runtime the material was built by the
|
|
360
|
+
// PROJECT's `three`. See `../authoring/three-scene-identity.ts`.
|
|
361
|
+
const texture = (material as THREE.Material & Record<string, unknown>)[key] as
|
|
362
|
+
| THREE.Texture
|
|
363
|
+
| undefined;
|
|
364
|
+
if (texture?.isTexture !== true) continue;
|
|
365
|
+
textures.add(texture);
|
|
366
|
+
if (!slots.includes(key)) slots.push(key);
|
|
367
|
+
const image = texture.image as { width?: number; height?: number } | undefined;
|
|
368
|
+
if (
|
|
369
|
+
!textureBindings.some(
|
|
370
|
+
(binding) => binding.materialId === material.uuid && binding.slot === key,
|
|
371
|
+
)
|
|
372
|
+
) {
|
|
373
|
+
textureBindings.push({
|
|
374
|
+
materialId: material.uuid,
|
|
375
|
+
materialName: material.name || material.type,
|
|
376
|
+
slot: key,
|
|
377
|
+
textureName: texture.name || texture.source.data?.src || 'Texture',
|
|
378
|
+
width: image?.width ?? null,
|
|
379
|
+
height: image?.height ?? null,
|
|
380
|
+
format: texture.format,
|
|
381
|
+
colorSpace: texture.colorSpace || 'none',
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
materialSlots.set(material, slots);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
for (const texture of textures) {
|
|
389
|
+
const image = texture.image as { width?: number; height?: number } | undefined;
|
|
390
|
+
memoryBytes += (image?.width ?? 0) * (image?.height ?? 0) * 4;
|
|
391
|
+
}
|
|
392
|
+
const box = modelBounds(root);
|
|
393
|
+
const size = box?.getSize(new THREE.Vector3()) ?? null;
|
|
394
|
+
return {
|
|
395
|
+
nodes,
|
|
396
|
+
meshes,
|
|
397
|
+
primitives,
|
|
398
|
+
vertices,
|
|
399
|
+
triangles,
|
|
400
|
+
materials: materials.size,
|
|
401
|
+
textures: textures.size,
|
|
402
|
+
bones,
|
|
403
|
+
clips: root.animations.length,
|
|
404
|
+
morphTargets,
|
|
405
|
+
memoryBytes,
|
|
406
|
+
bounds: size ? { x: size.x, y: size.y, z: size.z } : null,
|
|
407
|
+
geometry: {
|
|
408
|
+
attributes: [...attributeUsage.entries()]
|
|
409
|
+
.map(([name, usage]) => ({ name, itemSize: usage.itemSize, meshCount: usage.meshes }))
|
|
410
|
+
.sort((left, right) => left.name.localeCompare(right.name)),
|
|
411
|
+
uvChannels: [...uvChannels].sort(),
|
|
412
|
+
indexedMeshes,
|
|
413
|
+
nonIndexedMeshes,
|
|
414
|
+
instancedMeshes,
|
|
415
|
+
skinnedMeshes,
|
|
416
|
+
nonFiniteAttributeValues,
|
|
417
|
+
lodGroups,
|
|
418
|
+
lodLevels,
|
|
419
|
+
},
|
|
420
|
+
animationClips: root.animations.map((clip, index) => {
|
|
421
|
+
const targets = new Set<string>();
|
|
422
|
+
let keyframes = 0;
|
|
423
|
+
for (const track of clip.tracks) {
|
|
424
|
+
keyframes += track.times.length;
|
|
425
|
+
const parsed = THREE.PropertyBinding.parseTrackName(track.name);
|
|
426
|
+
if (parsed.nodeName) targets.add(parsed.nodeName);
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
id: `${index}:${clip.uuid}`,
|
|
430
|
+
name: clip.name || `Clip ${index + 1}`,
|
|
431
|
+
duration: clip.duration,
|
|
432
|
+
tracks: clip.tracks.length,
|
|
433
|
+
keyframes,
|
|
434
|
+
targets: [...targets].sort(),
|
|
435
|
+
rootMotion: rootMotionTrailPoints(clip).length > 1,
|
|
436
|
+
};
|
|
437
|
+
}),
|
|
438
|
+
hierarchy,
|
|
439
|
+
materialNames: [...materials].map((material) => material.name || material.type),
|
|
440
|
+
textureNames: [...textures].map(
|
|
441
|
+
(texture) => texture.name || texture.source.data?.src || 'Texture',
|
|
442
|
+
),
|
|
443
|
+
materialSlots: [...materials].map((material) => ({
|
|
444
|
+
id: material.uuid,
|
|
445
|
+
name: material.name || material.type,
|
|
446
|
+
type: material.type,
|
|
447
|
+
textureSlots: materialSlots.get(material) ?? [],
|
|
448
|
+
usageCount: materialUsers.get(material)?.size ?? 0,
|
|
449
|
+
meshNames: [...(materialUsers.get(material) ?? [])]
|
|
450
|
+
.map((object) => object.name || object.type)
|
|
451
|
+
.sort(),
|
|
452
|
+
color: materialColorHex(material),
|
|
453
|
+
transparent: material.transparent,
|
|
454
|
+
})),
|
|
455
|
+
textureBindings,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function inspectSkinWeights(mesh: THREE.SkinnedMesh): SkinWeightInspection {
|
|
460
|
+
const indices = mesh.geometry.getAttribute('skinIndex');
|
|
461
|
+
const weights = mesh.geometry.getAttribute('skinWeight');
|
|
462
|
+
if (!indices || !weights) {
|
|
463
|
+
return {
|
|
464
|
+
weightedVertices: 0,
|
|
465
|
+
nonNormalizedVertices: 0,
|
|
466
|
+
zeroWeightVertices: 0,
|
|
467
|
+
invalidJointIndices: 0,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
const weightedVertices = Math.min(indices.count, weights.count);
|
|
471
|
+
let nonNormalizedVertices = 0;
|
|
472
|
+
let zeroWeightVertices = 0;
|
|
473
|
+
let invalidJointIndices = 0;
|
|
474
|
+
for (let vertex = 0; vertex < weightedVertices; vertex++) {
|
|
475
|
+
const values = [
|
|
476
|
+
weights.getX(vertex),
|
|
477
|
+
weights.getY(vertex),
|
|
478
|
+
weights.getZ(vertex),
|
|
479
|
+
weights.getW(vertex),
|
|
480
|
+
];
|
|
481
|
+
const joints = [
|
|
482
|
+
indices.getX(vertex),
|
|
483
|
+
indices.getY(vertex),
|
|
484
|
+
indices.getZ(vertex),
|
|
485
|
+
indices.getW(vertex),
|
|
486
|
+
];
|
|
487
|
+
const total = values.reduce((sum, value) => sum + value, 0);
|
|
488
|
+
if (total <= 1e-6) zeroWeightVertices++;
|
|
489
|
+
else if (Math.abs(total - 1) > 1e-3) nonNormalizedVertices++;
|
|
490
|
+
invalidJointIndices += values.filter(
|
|
491
|
+
(value, slot) =>
|
|
492
|
+
value > 1e-6 && (joints[slot]! < 0 || joints[slot]! >= mesh.skeleton.bones.length),
|
|
493
|
+
).length;
|
|
494
|
+
}
|
|
495
|
+
return {
|
|
496
|
+
weightedVertices,
|
|
497
|
+
nonNormalizedVertices,
|
|
498
|
+
zeroWeightVertices,
|
|
499
|
+
invalidJointIndices,
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function nonFiniteTrackValueCount(track: THREE.KeyframeTrack): number {
|
|
504
|
+
let count = 0;
|
|
505
|
+
for (const value of track.times) if (!Number.isFinite(value)) count++;
|
|
506
|
+
for (const value of track.values) if (!Number.isFinite(value)) count++;
|
|
507
|
+
return count;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function unresolvedTrackTarget(root: THREE.Object3D, track: THREE.KeyframeTrack): string | null {
|
|
511
|
+
const nodeName = THREE.PropertyBinding.parseTrackName(track.name).nodeName;
|
|
512
|
+
return nodeName && !THREE.PropertyBinding.findNode(root, nodeName) ? nodeName : null;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function animationIntegrity(root: THREE.Object3D): {
|
|
516
|
+
readonly nonFiniteValues: number;
|
|
517
|
+
readonly unresolvedTargets: Set<string>;
|
|
518
|
+
} {
|
|
519
|
+
let nonFiniteValues = 0;
|
|
520
|
+
const unresolvedTargets = new Set<string>();
|
|
521
|
+
for (const clip of root.animations) {
|
|
522
|
+
for (const track of clip.tracks) {
|
|
523
|
+
nonFiniteValues += nonFiniteTrackValueCount(track);
|
|
524
|
+
const unresolved = unresolvedTrackTarget(root, track);
|
|
525
|
+
if (unresolved) unresolvedTargets.add(unresolved);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return { nonFiniteValues, unresolvedTargets };
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/** Read-only skin/clip integrity facts for the model Inspector. */
|
|
532
|
+
export function inspectModelRig(root: THREE.Object3D): ModelRigInspection {
|
|
533
|
+
const sourceNodes = new Set<THREE.Object3D>();
|
|
534
|
+
const skeletons = new Set<THREE.Skeleton>();
|
|
535
|
+
const bones = new Set<THREE.Bone>();
|
|
536
|
+
let skinnedMeshes = 0;
|
|
537
|
+
let weightedVertices = 0;
|
|
538
|
+
let nonNormalizedVertices = 0;
|
|
539
|
+
let zeroWeightVertices = 0;
|
|
540
|
+
let invalidJointIndices = 0;
|
|
541
|
+
let inverseBindMismatches = 0;
|
|
542
|
+
const externalBones = new Set<THREE.Bone>();
|
|
543
|
+
const rootBones = new Set<string>();
|
|
544
|
+
const bindModes = new Set<string>();
|
|
545
|
+
root.traverse((object) => {
|
|
546
|
+
sourceNodes.add(object);
|
|
547
|
+
const bone = object as THREE.Bone;
|
|
548
|
+
if (!bone.isBone) return;
|
|
549
|
+
bones.add(bone);
|
|
550
|
+
});
|
|
551
|
+
root.traverse((object) => {
|
|
552
|
+
const mesh = object as THREE.SkinnedMesh;
|
|
553
|
+
if (!mesh.isSkinnedMesh) return;
|
|
554
|
+
skinnedMeshes++;
|
|
555
|
+
bindModes.add(mesh.bindMode);
|
|
556
|
+
skeletons.add(mesh.skeleton);
|
|
557
|
+
if (mesh.skeleton.bones.length !== mesh.skeleton.boneInverses.length) inverseBindMismatches++;
|
|
558
|
+
for (const bone of mesh.skeleton.bones) {
|
|
559
|
+
bones.add(bone);
|
|
560
|
+
if (!sourceNodes.has(bone)) externalBones.add(bone);
|
|
561
|
+
}
|
|
562
|
+
const weights = inspectSkinWeights(mesh);
|
|
563
|
+
weightedVertices += weights.weightedVertices;
|
|
564
|
+
nonNormalizedVertices += weights.nonNormalizedVertices;
|
|
565
|
+
zeroWeightVertices += weights.zeroWeightVertices;
|
|
566
|
+
invalidJointIndices += weights.invalidJointIndices;
|
|
567
|
+
});
|
|
568
|
+
const animation = animationIntegrity(root);
|
|
569
|
+
for (const bone of bones) {
|
|
570
|
+
if (!(bone.parent as THREE.Bone | null)?.isBone || !bones.has(bone.parent as THREE.Bone)) {
|
|
571
|
+
rootBones.add(bone.name || bone.uuid);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
const nonFiniteAnimationValues = animation.nonFiniteValues;
|
|
575
|
+
const unresolvedTrackTargets = animation.unresolvedTargets;
|
|
576
|
+
const valid =
|
|
577
|
+
nonNormalizedVertices === 0 &&
|
|
578
|
+
zeroWeightVertices === 0 &&
|
|
579
|
+
invalidJointIndices === 0 &&
|
|
580
|
+
inverseBindMismatches === 0 &&
|
|
581
|
+
externalBones.size === 0 &&
|
|
582
|
+
nonFiniteAnimationValues === 0 &&
|
|
583
|
+
unresolvedTrackTargets.size === 0;
|
|
584
|
+
return {
|
|
585
|
+
kind: skinnedMeshes > 0 ? 'skinned' : bones.size > 0 ? 'rigid' : 'none',
|
|
586
|
+
skinnedMeshes,
|
|
587
|
+
skeletons: skeletons.size,
|
|
588
|
+
uniqueBones: bones.size,
|
|
589
|
+
weightedVertices,
|
|
590
|
+
nonNormalizedVertices,
|
|
591
|
+
zeroWeightVertices,
|
|
592
|
+
invalidJointIndices,
|
|
593
|
+
inverseBindMismatches,
|
|
594
|
+
externalBones: externalBones.size,
|
|
595
|
+
nonFiniteAnimationValues,
|
|
596
|
+
unresolvedTrackTargets: [...unresolvedTrackTargets].sort(),
|
|
597
|
+
rootBones: [...rootBones].sort(),
|
|
598
|
+
bindModes: [...bindModes].sort(),
|
|
599
|
+
valid,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function inspectNodeGeometry(geometry: THREE.BufferGeometry | undefined) {
|
|
604
|
+
if (!geometry) return null;
|
|
605
|
+
const size = geometryBounds(geometry)?.getSize(new THREE.Vector3());
|
|
606
|
+
const position = geometry.getAttribute('position');
|
|
607
|
+
return {
|
|
608
|
+
vertices: position?.count ?? 0,
|
|
609
|
+
triangles: Math.floor((geometry.index?.count ?? position?.count ?? 0) / 3),
|
|
610
|
+
indexed: geometry.index !== null,
|
|
611
|
+
attributes: Object.keys(geometry.attributes).sort(),
|
|
612
|
+
uvChannels: Object.keys(geometry.attributes)
|
|
613
|
+
.filter((name) => /^uv\d*$/.test(name))
|
|
614
|
+
.sort(),
|
|
615
|
+
morphTargets: geometry.morphAttributes['position']?.length ?? 0,
|
|
616
|
+
nonFiniteAttributeValues: countNonFiniteAttributeValues(geometry),
|
|
617
|
+
bounds: size ? { x: size.x, y: size.y, z: size.z } : null,
|
|
618
|
+
} satisfies NonNullable<Object3DNodeInspection['geometry']>;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function inspectNodeMaterials(mesh: THREE.Mesh): Object3DNodeInspection['materials'] {
|
|
622
|
+
const objectMaterials = Array.isArray(mesh.material)
|
|
623
|
+
? mesh.material
|
|
624
|
+
: mesh.material
|
|
625
|
+
? [mesh.material]
|
|
626
|
+
: [];
|
|
627
|
+
return objectMaterials.map((material) => ({
|
|
628
|
+
id: material.uuid,
|
|
629
|
+
name: material.name || material.type,
|
|
630
|
+
type: material.type,
|
|
631
|
+
color: materialColorHex(material),
|
|
632
|
+
textureSlots: textureKeys.filter(
|
|
633
|
+
(key) =>
|
|
634
|
+
((material as THREE.Material & Record<string, unknown>)[key] as THREE.Texture | undefined)
|
|
635
|
+
?.isTexture === true,
|
|
636
|
+
),
|
|
637
|
+
}));
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
function inspectNodeSkin(object: THREE.Object3D): Object3DNodeInspection['skin'] {
|
|
641
|
+
const mesh = object as THREE.SkinnedMesh;
|
|
642
|
+
if (!mesh.isSkinnedMesh) return null;
|
|
643
|
+
return {
|
|
644
|
+
bones: mesh.skeleton.bones.length,
|
|
645
|
+
rootBones: mesh.skeleton.bones
|
|
646
|
+
.filter((bone) => !(bone.parent as THREE.Bone | null)?.isBone)
|
|
647
|
+
.map((bone) => bone.name || bone.uuid),
|
|
648
|
+
bindMode: mesh.bindMode,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function inspectNodeBone(object: THREE.Object3D): Object3DNodeInspection['bone'] {
|
|
653
|
+
const bone = object as THREE.Bone;
|
|
654
|
+
if (!bone.isBone) return null;
|
|
655
|
+
return {
|
|
656
|
+
parent: (bone.parent as THREE.Bone | null)?.isBone
|
|
657
|
+
? bone.parent?.name || bone.parent?.uuid || null
|
|
658
|
+
: null,
|
|
659
|
+
children: bone.children
|
|
660
|
+
.filter((child): child is THREE.Bone => (child as THREE.Bone).isBone)
|
|
661
|
+
.map((child) => child.name || child.uuid),
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/** Inspect one selected native node without manufacturing an authored descriptor. */
|
|
666
|
+
export function inspectObject3DNode(object: THREE.Object3D): Object3DNodeInspection {
|
|
667
|
+
let descendants = -1;
|
|
668
|
+
object.traverse(() => descendants++);
|
|
669
|
+
const mesh = object as THREE.Mesh;
|
|
670
|
+
return {
|
|
671
|
+
type: object.type,
|
|
672
|
+
visible: object.visible,
|
|
673
|
+
children: object.children.length,
|
|
674
|
+
descendants,
|
|
675
|
+
renderOrder: object.renderOrder,
|
|
676
|
+
layers: object.layers.mask,
|
|
677
|
+
geometry: inspectNodeGeometry(mesh.geometry),
|
|
678
|
+
materials: inspectNodeMaterials(mesh),
|
|
679
|
+
skin: inspectNodeSkin(object),
|
|
680
|
+
bone: inspectNodeBone(object),
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
export type ModelDiagnosticMode =
|
|
685
|
+
| 'lit'
|
|
686
|
+
| 'unlit'
|
|
687
|
+
| 'wireframe'
|
|
688
|
+
| 'normals'
|
|
689
|
+
| 'uv'
|
|
690
|
+
| 'vertex-colors'
|
|
691
|
+
| 'bounds'
|
|
692
|
+
| 'skeleton'
|
|
693
|
+
| 'collision'
|
|
694
|
+
| 'lod';
|
|
695
|
+
|
|
696
|
+
export type ModelCameraPreset =
|
|
697
|
+
| 'front'
|
|
698
|
+
| 'back'
|
|
699
|
+
| 'left'
|
|
700
|
+
| 'right'
|
|
701
|
+
| 'top'
|
|
702
|
+
| 'bottom'
|
|
703
|
+
| 'isometric';
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* What the viewport's own view text calls the direction it is looking FROM —
|
|
707
|
+
* Blender's "Front Orthographic" / "Top Orthographic", and "User" the moment
|
|
708
|
+
* the view is orbited off an axis. DERIVED from the camera every frame, never
|
|
709
|
+
* stored: Blender's label reverts to User on the first drag, and a remembered
|
|
710
|
+
* preset would keep claiming an axis the view had already left.
|
|
711
|
+
*
|
|
712
|
+
* `null` means no axis — the caller says "User". `isometric` is deliberately
|
|
713
|
+
* absent: it is a 3/4 direction, which Blender calls User too.
|
|
714
|
+
*/
|
|
715
|
+
export function axisViewName(direction: THREE.Vector3): string | null {
|
|
716
|
+
const unit = direction.clone().normalize();
|
|
717
|
+
for (const preset of ['front', 'back', 'left', 'right', 'top', 'bottom'] as const) {
|
|
718
|
+
// Half a degree: enough that a settled preset reads as its axis and any
|
|
719
|
+
// deliberate orbit does not.
|
|
720
|
+
if (cameraPresetDirection(preset).angleTo(unit) < 0.0087)
|
|
721
|
+
return `${preset[0]?.toUpperCase()}${preset.slice(1)}`;
|
|
722
|
+
}
|
|
723
|
+
return null;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
export function cameraPresetDirection(preset: ModelCameraPreset): THREE.Vector3 {
|
|
727
|
+
const directions: Record<ModelCameraPreset, [number, number, number]> = {
|
|
728
|
+
front: [0, 0, 1],
|
|
729
|
+
back: [0, 0, -1],
|
|
730
|
+
left: [-1, 0, 0],
|
|
731
|
+
right: [1, 0, 0],
|
|
732
|
+
top: [0, 1, 0],
|
|
733
|
+
bottom: [0, -1, 0],
|
|
734
|
+
isometric: [1, 0.72, 1],
|
|
735
|
+
};
|
|
736
|
+
return new THREE.Vector3(...directions[preset]).normalize();
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
export function supportedModelDiagnosticModes(root: THREE.Object3D): Array<{
|
|
740
|
+
mode: ModelDiagnosticMode;
|
|
741
|
+
available: boolean;
|
|
742
|
+
reason?: string;
|
|
743
|
+
}> {
|
|
744
|
+
let uv = false;
|
|
745
|
+
let colors = false;
|
|
746
|
+
let bones = false;
|
|
747
|
+
let lod = false;
|
|
748
|
+
root.traverse((object) => {
|
|
749
|
+
const mesh = object as THREE.Mesh;
|
|
750
|
+
uv ||= Boolean(mesh.geometry?.getAttribute('uv'));
|
|
751
|
+
colors ||= Boolean(mesh.geometry?.getAttribute('color'));
|
|
752
|
+
bones ||= Boolean((object as THREE.Bone).isBone || (mesh as THREE.SkinnedMesh).isSkinnedMesh);
|
|
753
|
+
lod ||= (object as THREE.LOD).isLOD === true;
|
|
754
|
+
});
|
|
755
|
+
return [
|
|
756
|
+
{ mode: 'lit', available: true },
|
|
757
|
+
{ mode: 'unlit', available: true },
|
|
758
|
+
{ mode: 'wireframe', available: true },
|
|
759
|
+
{ mode: 'normals', available: true },
|
|
760
|
+
{ mode: 'uv', available: uv, ...(!uv ? { reason: 'No UV channel' } : {}) },
|
|
761
|
+
{
|
|
762
|
+
mode: 'vertex-colors',
|
|
763
|
+
available: colors,
|
|
764
|
+
...(!colors ? { reason: 'No vertex colors' } : {}),
|
|
765
|
+
},
|
|
766
|
+
{ mode: 'bounds', available: true },
|
|
767
|
+
{ mode: 'skeleton', available: bones, ...(!bones ? { reason: 'No skeleton' } : {}) },
|
|
768
|
+
{ mode: 'collision', available: false, reason: 'No collision metadata' },
|
|
769
|
+
{ mode: 'lod', available: lod, ...(!lod ? { reason: 'No LOD groups' } : {}) },
|
|
770
|
+
];
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/** Extract an animation clip's authored root translation samples for diagnostics. */
|
|
774
|
+
export function rootMotionTrailPoints(clip: THREE.AnimationClip): THREE.Vector3[] {
|
|
775
|
+
const positionTracks = clip.tracks.filter(
|
|
776
|
+
// `ValueTypeName` is KeyframeTrack's own public discriminator, and the
|
|
777
|
+
// clip may come from the PROJECT's `three` under the packaged runtime.
|
|
778
|
+
(track): track is THREE.VectorKeyframeTrack =>
|
|
779
|
+
track.ValueTypeName === 'vector' && track.name.toLowerCase().endsWith('.position'),
|
|
780
|
+
);
|
|
781
|
+
const track =
|
|
782
|
+
positionTracks.find((candidate) => /(^|[./])(root|hips|armature)[./]/i.test(candidate.name)) ??
|
|
783
|
+
positionTracks[0];
|
|
784
|
+
if (!track || track.values.length < 6) return [];
|
|
785
|
+
const points: THREE.Vector3[] = [];
|
|
786
|
+
for (let offset = 0; offset + 2 < track.values.length; offset += 3) {
|
|
787
|
+
points.push(
|
|
788
|
+
new THREE.Vector3(
|
|
789
|
+
track.values[offset]!,
|
|
790
|
+
track.values[offset + 1]!,
|
|
791
|
+
track.values[offset + 2]!,
|
|
792
|
+
),
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
return points;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/** Resolve a hierarchy/material row to the preview objects it represents. */
|
|
799
|
+
export function modelSelectionObjects(
|
|
800
|
+
root: THREE.Object3D,
|
|
801
|
+
selection: { kind: 'node' | 'material'; id: string },
|
|
802
|
+
): THREE.Object3D[] {
|
|
803
|
+
if (selection.kind === 'node') {
|
|
804
|
+
const object = root.getObjectByProperty('uuid', selection.id);
|
|
805
|
+
return object ? [object] : [];
|
|
806
|
+
}
|
|
807
|
+
const selected: THREE.Object3D[] = [];
|
|
808
|
+
root.traverse((object) => {
|
|
809
|
+
const mesh = object as THREE.Mesh;
|
|
810
|
+
const materials = Array.isArray(mesh.material)
|
|
811
|
+
? mesh.material
|
|
812
|
+
: mesh.material
|
|
813
|
+
? [mesh.material]
|
|
814
|
+
: [];
|
|
815
|
+
if (materials.some((material) => material.uuid === selection.id)) selected.push(mesh);
|
|
816
|
+
});
|
|
817
|
+
return selected;
|
|
818
|
+
}
|