@volter/editor-threejs 0.5.210 → 0.5.212
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/NOTICE +7 -2
- package/README.md +8 -13
- package/contributions/animation-mixers.service.ts +2 -4
- package/contributions/animation-timeline.utility.tsx +1 -2
- package/contributions/three.page.ts +50 -0
- package/dist-node/serving.mjs +84 -3
- package/dist-node/shared-three.mjs +145 -0
- package/package.json +296 -16
- package/serving/animation-stamp.ts +1 -1
- package/serving/index.ts +22 -5
- package/serving/model-import-conversion.ts +6 -3
- package/serving/shared-three.ts +176 -0
- package/src/adapter/constraint.ts +1 -18
- package/src/adapter/object3d-authoring-subject.ts +1 -12
- package/src/adapter/reflection-probe.ts +1 -9
- package/src/adapter/three-contract.ts +6 -60
- package/src/animation/live-mixers.ts +0 -4
- package/src/api.ts +7 -0
- package/src/asset-loaders.ts +2 -178
- package/src/capture/photograph-sampling.ts +333 -0
- package/src/capture/scene.ts +1 -1
- package/src/ecs/object-marks.ts +6 -14
- package/src/ecs/user-data.ts +3 -138
- package/src/kit/animation/three-clips-subject.ts +19 -42
- package/src/kit/asset-compare-core.ts +161 -0
- package/src/kit/asset-compare.ts +8 -27
- package/src/kit/asset-preview-command.ts +14 -19
- package/src/kit/asset-preview-framing.ts +8 -67
- package/src/kit/asset-preview.ts +109 -342
- package/src/kit/asset-workflow/model-inspection.ts +11 -154
- package/src/kit/asset-workflow/preview-resource-lifetime.ts +25 -0
- package/src/kit/authoring/component-instance-root.ts +3 -39
- package/src/kit/authoring/live-object-transform.ts +1 -30
- package/src/kit/authoring/object3d-document-session-registry.ts +7 -15
- package/src/kit/authoring/object3d-document-session.ts +471 -208
- package/src/kit/authoring/shell-viewport-policy.ts +18 -24
- package/src/kit/authoring/source-object3d-authoring-adapter.ts +6 -48
- package/src/kit/authoring/three-projection-core.ts +11 -40
- package/src/kit/authoring/viewport-pick-context.ts +5 -12
- package/src/kit/authoring/viewport-raycast.ts +15 -93
- package/src/kit/authoring/world-hidden-viewport.ts +8 -61
- package/src/kit/capture-camera-pose.ts +68 -0
- package/src/kit/collaboration-presence.ts +41 -0
- package/src/kit/components/CameraInfo.tsx +4 -6
- package/src/kit/components/InspectorObjectPreview.tsx +3 -30
- package/src/kit/components/Object3DDocumentToolbar.css +75 -0
- package/src/kit/components/Object3DDocumentToolbar.tsx +18 -84
- package/src/kit/components/Object3DDocumentViewport.tsx +5 -8
- package/src/kit/components/StageHost.css +40 -0
- package/src/kit/components/StageHost.tsx +181 -404
- package/src/kit/components/StageOverlays.tsx +1 -2
- package/src/kit/components/StatsOverlay.tsx +5 -17
- package/src/kit/components/ViewportFurniture.tsx +27 -154
- package/src/kit/components/ViewportOverlay.tsx +13 -17
- package/src/kit/components/ViewportOverlaysMenu.tsx +177 -0
- package/src/kit/components/ViewportShadingMenu.tsx +2 -42
- package/src/kit/components/ViewportViewMenu.tsx +7 -18
- package/src/kit/components/asset-viewers/EntityModelDocument.tsx +4 -13
- package/src/kit/components/asset-viewers/EnvironmentAssetDocument.tsx +5 -5
- package/src/kit/components/asset-viewers/LiveModuleDocument.tsx +24 -102
- package/src/kit/components/asset-viewers/LutAssetDocument.tsx +6 -6
- package/src/kit/components/asset-viewers/Object3DPreview.tsx +10 -45
- package/src/kit/components/asset-viewers/QuarksAssetDocument.tsx +2 -2
- package/src/kit/components/asset-viewers/ShaderAssetDocument.tsx +11 -10
- package/src/kit/components/asset-viewers/shader-source.ts +144 -0
- package/src/kit/components/asset-viewers/three-asset-viewers.tsx +2 -14
- package/src/kit/components/object3d-contribution-surfaces.tsx +1 -1
- package/src/kit/components/stage-keyboard.tsx +6 -12
- package/src/kit/components/stage-overlay-set.tsx +11 -20
- package/src/kit/components/stage-presence-markers.ts +9 -32
- package/src/kit/components/stage-transform-chrome.ts +4 -4
- package/src/kit/components/stage-transform-tools.tsx +7 -14
- package/src/kit/components/standard-viewport-dressing.ts +47 -167
- package/src/kit/components/world-root-binding.ts +2 -7
- package/src/kit/constraint-helper.ts +1 -1
- package/src/kit/document-renderer-session.ts +114 -0
- package/src/kit/editor-presentation-activity.ts +58 -0
- package/src/kit/editor-shell-store.ts +31 -403
- package/src/kit/editor-viewport.ts +476 -1762
- package/src/kit/entity-lod.ts +3 -16
- package/src/kit/entity-object.ts +1 -39
- package/src/kit/hierarchy-mark-reader.ts +13 -30
- package/src/kit/instanced-presentation.ts +1 -47
- package/src/kit/live-gesture-lock.ts +35 -0
- package/src/kit/live-module-source.ts +12 -52
- package/src/kit/model-thumbnail.ts +7 -35
- package/src/kit/play-camera-flight.ts +7 -16
- package/src/kit/projection/three.ts +13 -366
- package/src/kit/retained-document-states.ts +91 -0
- package/src/kit/scene-document-viewport.ts +3 -3
- package/src/kit/scene-framing.ts +11 -152
- package/src/kit/scene-view-fog.ts +2 -47
- package/src/kit/spatial-handle-visuals.ts +19 -28
- package/src/kit/stories/three-story-model.ts +5 -33
- package/src/kit/three-canvas-render.ts +3 -4
- package/src/kit/three-hierarchy-row-media.ts +3 -11
- package/src/kit/three-inspection-media.ts +6 -6
- package/src/kit/three-integration.ts +18 -18
- package/src/kit/three-state.ts +2 -19
- package/src/kit/three-viewport/camera-fit.ts +2 -6
- package/src/kit/three-viewport/selection-brackets.ts +5 -20
- package/src/kit/three-viewport/selection-outline.ts +4 -56
- package/src/kit/three-viewport/skeleton-helper.ts +1 -32
- package/src/kit/three-viewport/source-color.ts +1 -33
- package/src/kit/three-viewport/studio-environment.ts +4 -55
- package/src/kit/three-viewport-presentation.ts +23 -0
- package/src/kit/transport-strip.tsx +147 -0
- package/src/kit/viewport-actions.ts +8 -50
- package/src/kit/viewport-authoring-policy.ts +5 -52
- package/src/kit/viewport-commands.ts +28 -57
- package/src/kit/viewport-hotkeys.ts +29 -31
- package/src/kit/viewport-status-facet.ts +4 -5
- package/src/kit/viewport-surface-status.tsx +44 -0
- package/src/loader.ts +8 -71
- package/src/object3d-contributions.ts +18 -34
- package/src/viewport/clip-planes.ts +4 -32
- package/src/viewport/content-bounds.ts +13 -142
- package/src/viewport/editor-layers.ts +4 -26
- package/src/viewport/environment.ts +1 -1
- package/src/viewport/preview-renderer.ts +7 -45
- package/src/viewport/renderer-ownership.ts +14 -62
- package/src/viewport-api.ts +14 -33
- package/src/viewport-door.ts +16 -86
- package/contributions/three-integration.service.ts +0 -13
- package/src/adapter/hierarchy-marks.ts +0 -156
- package/src/adapter/ingest/scene-capture.ts +0 -843
- package/src/adapter/ingest/structural-ids.ts +0 -139
- package/src/adapter/ingest/visible-capture-window.ts +0 -302
- package/src/adapter/renderer-config.ts +0 -115
- package/src/asset-parse-error.ts +0 -29
- package/src/host-hierarchy-objects.ts +0 -31
- package/src/kit/authoring/design-time-settle.ts +0 -343
- package/src/kit/camera-authoring.ts +0 -175
|
@@ -1,53 +1,12 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WHAT THE GIZMO VIEWPORT ASKS THE SHELL — the one collaborator
|
|
3
|
-
* `editor-viewport.ts` declares and the shell installs at boot.
|
|
4
|
-
*
|
|
5
|
-
* ARCHITECTURE-CORE §Editor chrome, "The viewport stack is separable": a
|
|
6
|
-
* surface below the shell never imports the shell's transport, a world
|
|
7
|
-
* authoring adapter, or project discovery. The viewport was importing six
|
|
8
|
-
* `authoring/` modules to ask four questions —
|
|
9
|
-
*
|
|
10
|
-
* · which adapter am I driving, and which one do the shared panels drive?
|
|
11
|
-
* · what is under the pointer?
|
|
12
|
-
* · which native root owns the current selection, and is it painted here?
|
|
13
|
-
* · which three root is world-hidden, and how do I apply that?
|
|
14
|
-
*
|
|
15
|
-
* — and every one of those answers is a COMPOSITE fact. `layered-pick.ts`
|
|
16
|
-
* walks `CompositeAuthoringAdapter.childAdapters()` in manifest z-order;
|
|
17
|
-
* `panel-authoring.ts` reaches the workspace-document registry;
|
|
18
|
-
* `world-hidden-viewport.ts` and `viewport-tool-context.ts` resolve the
|
|
19
|
-
* composite's live three child. The viewport itself knows about none of that:
|
|
20
|
-
* it raycasts a scene and drags a gizmo. Measured, those six edges were 96 of
|
|
21
|
-
* its 193 files.
|
|
22
|
-
*
|
|
23
|
-
* ## The default is the NO-SHELL answer, and it is deliberate
|
|
24
|
-
*
|
|
25
|
-
* {@link DEFAULT_VIEWPORT_AUTHORING_POLICY} is what a viewport mounted with no
|
|
26
|
-
* shell above it (a bounded host; a fixture) gets: no composite exists,
|
|
27
|
-
* so there are no layers to walk, no other panel to agree with, no world-group
|
|
28
|
-
* eye to honour. Each default states that outright rather than half-answering.
|
|
29
|
-
* The ONE default that is real logic is {@link adapterOnlyToolOwner} — the
|
|
30
|
-
* structural single-adapter fallback `resolveViewportToolContext` already used
|
|
31
|
-
* for non-composite adapters, which lives here now and is imported BACK by
|
|
32
|
-
* `authoring/viewport-tool-context.ts` so there is one copy of it.
|
|
33
|
-
*
|
|
34
|
-
* In the editor the shell policy is installed once by `EditorProvider`
|
|
35
|
-
* (`EditorContext.tsx`), before any viewport mounts — including the Asset Lab
|
|
36
|
-
* document's own, which is why that document keeps today's behaviour exactly.
|
|
37
|
-
* The install site is the PROVIDER rather than `the world root's stage` because a
|
|
38
|
-
* workspace can have an Asset Lab document open with no scene the world root's stage
|
|
39
|
-
* mounted at all, and that document's viewport must not silently fall to the
|
|
40
|
-
* defaults.
|
|
41
|
-
*/
|
|
1
|
+
/** The shell installs the viewport’s authoring policy: adapter resolution, picking, selection ownership and world-hidden roots. Asset documents can use this seam without importing shell discovery or transport modules. */
|
|
42
2
|
|
|
43
3
|
import type { AuthoringAdapter } from '@volter/project/adapter';
|
|
44
|
-
import { adapterOnlyToolOwner, type ViewportToolOwner } from '@volter/sdk/
|
|
4
|
+
import { adapterOnlyToolOwner, type ViewportToolOwner } from '@volter/sdk/authoring/viewport-tool-owner';
|
|
45
5
|
|
|
46
6
|
export { adapterOnlyToolOwner, type ViewportToolOwner };
|
|
47
7
|
import type * as THREE from 'three';
|
|
48
|
-
import { makeNoAuthoringAdapter } from '@volter/sdk/
|
|
49
|
-
import type { ShellStore } from '@volter/sdk/
|
|
50
|
-
|
|
8
|
+
import { makeNoAuthoringAdapter } from '@volter/sdk/authoring/no-authoring-adapter';
|
|
9
|
+
import type { ShellStore } from '@volter/sdk/shell-store';
|
|
51
10
|
|
|
52
11
|
export interface ViewportAuthoringPolicy {
|
|
53
12
|
/** The adapter this viewport drives when its host injected none. */
|
|
@@ -84,7 +43,6 @@ export interface ViewportAuthoringPolicy {
|
|
|
84
43
|
suppressRootEnvironment(scene: THREE.Scene): void;
|
|
85
44
|
}
|
|
86
45
|
|
|
87
|
-
|
|
88
46
|
/** Memoized per store, because the viewport calls `activeAuthoring` per gesture
|
|
89
47
|
* and adapter IDENTITY keys the selection scope and the handle cache. Mirrors
|
|
90
48
|
* `authoring/active-adapter.ts`'s own per-store memo. */
|
|
@@ -115,7 +73,7 @@ export const DEFAULT_VIEWPORT_AUTHORING_POLICY: ViewportAuthoringPolicy = {
|
|
|
115
73
|
return true;
|
|
116
74
|
},
|
|
117
75
|
threeSurfaceShowing(_store, adapter) {
|
|
118
|
-
return !adapter.rects && typeof adapter.hierarchy.
|
|
76
|
+
return !adapter.rects && typeof adapter.hierarchy.nativeObject === 'function';
|
|
119
77
|
},
|
|
120
78
|
threeViewportRootId() {
|
|
121
79
|
return null;
|
|
@@ -147,8 +105,3 @@ export function installViewportAuthoringPolicy(policy: ViewportAuthoringPolicy):
|
|
|
147
105
|
export function viewportAuthoringPolicy(): ViewportAuthoringPolicy {
|
|
148
106
|
return _policy;
|
|
149
107
|
}
|
|
150
|
-
|
|
151
|
-
/** Test seam: drop the installed policy. */
|
|
152
|
-
export function __resetViewportAuthoringPolicyForTest(): void {
|
|
153
|
-
_policy = DEFAULT_VIEWPORT_AUTHORING_POLICY;
|
|
154
|
-
}
|
|
@@ -2,34 +2,26 @@
|
|
|
2
2
|
* THE THREE VIEWPORT'S RELAY VERBS — framing, the camera, view presets, the
|
|
3
3
|
* open Object3D document's orbit/turntable/frame, the viewport photograph and
|
|
4
4
|
* the display and transform toggles. They are the Three viewport's, so they
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* with its verb from `command-table.ts`.
|
|
5
|
+
* are editor-threejs's Code-OSS commands `volter.<verb>` (`@volter/sdk/commands`),
|
|
6
|
+
* registered by its page half (`../../contributions/three.page.ts`), rather than
|
|
7
|
+
* rows of the kit's own table. Each command declares its budget in contributes.commands.
|
|
9
8
|
*/
|
|
10
|
-
import type {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
CommandSpec,
|
|
14
|
-
EditorCommandMessage,
|
|
15
|
-
EditorCommandResult,
|
|
16
|
-
} from '@volter/sdk/commands';
|
|
17
|
-
import { commandLine } from '@volter/sdk/kit/product-command';
|
|
18
|
-
import { activeWorkspaceDocumentId } from '@volter/sdk/kit/workspace-document-registry';
|
|
9
|
+
import type { EditorCommandMessage, EditorCommandResult, VerbHandler } from '@volter/sdk/commands';
|
|
10
|
+
import { commandLine } from '@volter/sdk/product-command';
|
|
11
|
+
import { activeWorkspaceDocumentId } from '@volter/sdk/workspace-document-registry';
|
|
19
12
|
import { object3DDocumentSession } from './authoring/object3d-document-session-registry';
|
|
20
|
-
import { activeDocumentAuthoring } from '@volter/sdk/
|
|
13
|
+
import { activeDocumentAuthoring } from '@volter/sdk/authoring/shell-document-ops';
|
|
21
14
|
import { handleAssetPreviewCommand } from './asset-preview-command';
|
|
22
|
-
import { captureSizeFromCommand } from '@volter/sdk/
|
|
15
|
+
import { captureSizeFromCommand } from '@volter/sdk/capture-size';
|
|
23
16
|
import type { EditorShellStore, HelperVisibility } from './editor-shell-store';
|
|
24
|
-
import { editorHost } from '@volter/sdk/host';
|
|
25
17
|
import { entityObject3D } from './entity-object';
|
|
26
18
|
import { VIEWPORT_SHADING_MODES } from '../render/viewport-shading';
|
|
27
|
-
import { focusedStageStore } from '@volter/sdk/
|
|
19
|
+
import { focusedStageStore } from '@volter/sdk/stage-context';
|
|
28
20
|
import {
|
|
29
21
|
setViewGridVisible,
|
|
30
22
|
startingPresentation,
|
|
31
23
|
viewPresentationBinding,
|
|
32
|
-
} from '@volter/sdk/
|
|
24
|
+
} from '@volter/sdk/viewport-presentation';
|
|
33
25
|
import { threeStoreForHost } from './three-state';
|
|
34
26
|
import { viewportStages } from '../viewport-door';
|
|
35
27
|
import { threeStateOf } from './three-state';
|
|
@@ -39,20 +31,14 @@ function activeObject3DDocumentSession() {
|
|
|
39
31
|
return documentId ? object3DDocumentSession(documentId) : null;
|
|
40
32
|
}
|
|
41
33
|
|
|
42
|
-
/** One verb over the session's Three store. */
|
|
34
|
+
/** One verb over the session's Three store. Its budget and derivation are its `contributes.commands` entry. */
|
|
43
35
|
function verb(
|
|
44
36
|
run: (store: EditorShellStore, cmd: EditorCommandMessage) => EditorCommandResult | Promise<EditorCommandResult>,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
derivedRefresh,
|
|
51
|
-
handle: (cmd) => {
|
|
52
|
-
const store = threeStoreForHost();
|
|
53
|
-
if (!store) return { ok: false, error: `${cmd.type} needs an editor session; none has opened yet.` };
|
|
54
|
-
return run(store, cmd);
|
|
55
|
-
},
|
|
37
|
+
): VerbHandler {
|
|
38
|
+
return (cmd) => {
|
|
39
|
+
const store = threeStoreForHost();
|
|
40
|
+
if (!store) return { ok: false, error: `${cmd.type} needs an editor session; none has opened yet.` };
|
|
41
|
+
return run(store, cmd);
|
|
56
42
|
};
|
|
57
43
|
}
|
|
58
44
|
|
|
@@ -60,7 +46,7 @@ const OK: EditorCommandResult = { ok: true };
|
|
|
60
46
|
|
|
61
47
|
type ShadingMode = Parameters<EditorShellStore['setShadingMode']>[0];
|
|
62
48
|
|
|
63
|
-
export const
|
|
49
|
+
export const viewportVerbs: Readonly<Record<string, VerbHandler>> = {
|
|
64
50
|
'focus-entity': verb((store, cmd) => {
|
|
65
51
|
if (!activeObject3DDocumentSession()?.frameIds([cmd['id'] as string])) {
|
|
66
52
|
store.shell.focusOnEntity(cmd['id'] as string);
|
|
@@ -107,9 +93,7 @@ export const viewportCommands: CommandContribution['commands'] = {
|
|
|
107
93
|
return OK;
|
|
108
94
|
}),
|
|
109
95
|
'view-preset': verb((store, cmd) => {
|
|
110
|
-
//
|
|
111
|
-
// `cameraPresetDirection` and threw a TypeError that named neither the
|
|
112
|
-
// command nor the vocabulary (measured live, 2026-09-18).
|
|
96
|
+
// Refuse absent or unknown camera presets with the accepted vocabulary.
|
|
113
97
|
const requested = cmd['preset'];
|
|
114
98
|
const presets = ['top', 'front', 'right', 'perspective'] as const;
|
|
115
99
|
if (typeof requested !== 'string' || !(presets as readonly string[]).includes(requested)) {
|
|
@@ -154,8 +138,8 @@ export const viewportCommands: CommandContribution['commands'] = {
|
|
|
154
138
|
// the move's own wall clock. Both refuse a length past 60 s themselves. There
|
|
155
139
|
// is no Scene fallback: the Scene viewport's camera answers to
|
|
156
140
|
// `view-preset`/`set-camera` and has no framed subject to circle.
|
|
157
|
-
'document-orbit': verb((_store, cmd) => documentMove(cmd)
|
|
158
|
-
'document-turntable': verb((_store, cmd) => documentMove(cmd)
|
|
141
|
+
'document-orbit': verb((_store, cmd) => documentMove(cmd)),
|
|
142
|
+
'document-turntable': verb((_store, cmd) => documentMove(cmd)),
|
|
159
143
|
'document-frame': verb((_store, cmd) => {
|
|
160
144
|
const documentSession = activeObject3DDocumentSession();
|
|
161
145
|
if (!documentSession) {
|
|
@@ -210,22 +194,11 @@ export const viewportCommands: CommandContribution['commands'] = {
|
|
|
210
194
|
if (!measure)
|
|
211
195
|
return { ok: false, error: `document-frame-cost: the stage for ${JSON.stringify(stage.documentId)} has no frame of its own to measure.` };
|
|
212
196
|
return { ok: true, data: { stage: stage.documentId, ...measure(frames, quality) } };
|
|
213
|
-
}
|
|
197
|
+
}),
|
|
214
198
|
'capture-viewport': verb(async (store, cmd) => {
|
|
215
199
|
// Fresh, unthrottled on-demand capture (see EditorShellStore.captureViewportImage).
|
|
216
200
|
const requested = captureSizeFromCommand(cmd);
|
|
217
201
|
if ('error' in requested) return { ok: false, error: requested.error };
|
|
218
|
-
// AN ADOPTED SCENE IS PRESENTED BY ITS ADOPTER. The store's renderer and
|
|
219
|
-
// camera are its own viewport's; re-rendering a scene another document
|
|
220
|
-
// adopted through them came back white.
|
|
221
|
-
if (store.hasAdoptedScene) {
|
|
222
|
-
try {
|
|
223
|
-
const capture = await editorHost().documents.captureActive(requested.size);
|
|
224
|
-
return { ok: true, data: { base64: capture.base64, mimeType: capture.mimeType } };
|
|
225
|
-
} catch (error) {
|
|
226
|
-
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
202
|
const dataUrl = store.captureViewportImage(requested.size);
|
|
230
203
|
if (!dataUrl) {
|
|
231
204
|
// The refusal names the MECHANISM and the door that does answer: this door
|
|
@@ -235,21 +208,20 @@ export const viewportCommands: CommandContribution['commands'] = {
|
|
|
235
208
|
ok: false,
|
|
236
209
|
error:
|
|
237
210
|
"This door photographs the editor's own three.js Scene viewport, and nothing has bound one " +
|
|
238
|
-
'(no renderer/scene/camera).
|
|
239
|
-
'
|
|
240
|
-
|
|
241
|
-
`or the running game through \`bridge-screenshot\` (${commandLine('screenshot')}).`,
|
|
211
|
+
'(no renderer/scene/camera). Capture a document-owned canvas through ' +
|
|
212
|
+
'`capture-active-document` (`editor.captureActiveDocument()`) ' +
|
|
213
|
+
`or the running game through ${commandLine('capture --region play')}.`,
|
|
242
214
|
};
|
|
243
215
|
}
|
|
244
216
|
const comma = dataUrl.indexOf(',');
|
|
245
217
|
const base64 = comma >= 0 ? dataUrl.slice(comma + 1) : dataUrl;
|
|
246
218
|
return { ok: true, data: { base64, mimeType: 'image/png' } };
|
|
247
|
-
}
|
|
219
|
+
}),
|
|
248
220
|
// The Asset Lab's photographs; the budget covers a multi-shot set's renders.
|
|
249
|
-
'capture-asset-preview': verb((store, cmd) => handleAssetPreviewCommand(store, cmd)
|
|
221
|
+
'capture-asset-preview': verb((store, cmd) => handleAssetPreviewCommand(store, cmd)),
|
|
250
222
|
// Display — set semantics (only toggle when the value differs).
|
|
251
223
|
'set-grid': verb((_store, cmd) => {
|
|
252
|
-
// The ACTIVE view's grid switch (
|
|
224
|
+
// The ACTIVE view's grid switch (`@volter/sdk/viewport-presentation`), one per view.
|
|
253
225
|
const viewId = activeWorkspaceDocumentId();
|
|
254
226
|
if (!viewId || !viewPresentationBinding(viewId)) {
|
|
255
227
|
return { ok: false, error: 'set-grid: the active document draws no grid.' };
|
|
@@ -272,8 +244,7 @@ export const viewportCommands: CommandContribution['commands'] = {
|
|
|
272
244
|
}),
|
|
273
245
|
'set-shading-mode': verb((store, cmd) => {
|
|
274
246
|
const documentSession = activeObject3DDocumentSession();
|
|
275
|
-
//
|
|
276
|
-
// and every frame after it threw (`editor.shading('material')`).
|
|
247
|
+
// Refuse modes the active viewport does not support before changing renderer state.
|
|
277
248
|
const mode = cmd['mode'];
|
|
278
249
|
const known: readonly string[] = documentSession
|
|
279
250
|
? [...VIEWPORT_SHADING_MODES, 'uv', 'vertex-colors']
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
* W/E/R under `volter`, G/R/S under `blender`.
|
|
8
8
|
*/
|
|
9
9
|
import { editorHost } from '@volter/sdk/host';
|
|
10
|
-
import { getActiveScope, installHotkeys, setActiveScope } from '@volter/sdk/
|
|
11
|
-
import { showTransientHint } from '@volter/sdk/
|
|
10
|
+
import { getActiveScope, installHotkeys, setActiveScope } from '@volter/sdk/hotkeys';
|
|
11
|
+
import { showTransientHint } from '@volter/sdk/transient-hint';
|
|
12
12
|
import type { EditorShellStore, TransformMode } from './editor-shell-store';
|
|
13
13
|
import type { EditorViewport } from './editor-viewport';
|
|
14
|
-
import { requestTransformMode } from '@volter/sdk/
|
|
14
|
+
import { requestTransformMode } from '@volter/sdk/transform-mode-request';
|
|
15
15
|
|
|
16
16
|
export function registerViewportHotkeys(
|
|
17
17
|
store: EditorShellStore,
|
|
@@ -23,18 +23,17 @@ export function registerViewportHotkeys(
|
|
|
23
23
|
if (!viewport.isFlying) requestTransformMode(store.shell, mode);
|
|
24
24
|
};
|
|
25
25
|
const unbind = keyboard.bindActions([
|
|
26
|
-
{ id: 'transform.select',
|
|
27
|
-
{ id: 'transform.combined',
|
|
28
|
-
{ id: 'transform.translate',
|
|
29
|
-
{ id: 'transform.rotate',
|
|
30
|
-
{ id: 'transform.scale',
|
|
26
|
+
{ id: 'transform.select', run: transformMode('select') },
|
|
27
|
+
{ id: 'transform.combined', run: transformMode('combined') },
|
|
28
|
+
{ id: 'transform.translate', run: transformMode('translate') },
|
|
29
|
+
{ id: 'transform.rotate', run: transformMode('rotate') },
|
|
30
|
+
{ id: 'transform.scale', run: transformMode('scale') },
|
|
31
31
|
// Snap has a key and says what it did: a human build session asked "how
|
|
32
32
|
// do you toggle the snap" with the magnet button on screen the whole time.
|
|
33
33
|
// The hint names the ACTIVE keymap's key, never a remembered literal.
|
|
34
34
|
// Holding Ctrl/⌘ during a drag snaps temporarily (editor-viewport.ts).
|
|
35
35
|
{
|
|
36
36
|
id: 'viewport.toggleSnap',
|
|
37
|
-
scope: 'stage',
|
|
38
37
|
run: () => {
|
|
39
38
|
store.shell.toggleSnap();
|
|
40
39
|
const { translate, rotate, scale } = store.shell.snapValues;
|
|
@@ -46,36 +45,35 @@ export function registerViewportHotkeys(
|
|
|
46
45
|
);
|
|
47
46
|
},
|
|
48
47
|
},
|
|
49
|
-
{ id: 'viewport.frameSelection',
|
|
48
|
+
{ id: 'viewport.frameSelection', run: () => store.shell.focusOnSelection() },
|
|
50
49
|
{
|
|
51
50
|
id: 'viewport.cyclePivot',
|
|
52
|
-
scope: 'stage',
|
|
53
51
|
run: () => {
|
|
54
52
|
const pivotModes = ['active-element', 'median-point', 'individual-origins'] as const;
|
|
55
53
|
const idx = pivotModes.indexOf(store.shell.pivotMode);
|
|
56
54
|
store.shell.setPivotMode(pivotModes[(idx + 1) % pivotModes.length]!);
|
|
57
55
|
},
|
|
58
56
|
},
|
|
59
|
-
{ id: 'view.top',
|
|
60
|
-
{ id: 'view.front',
|
|
61
|
-
{ id: 'view.right',
|
|
62
|
-
{ id: 'view.bottom',
|
|
63
|
-
{ id: 'view.back',
|
|
64
|
-
{ id: 'view.left',
|
|
65
|
-
{ id: 'view.orbitLeft',
|
|
66
|
-
{ id: 'view.orbitRight',
|
|
67
|
-
{ id: 'view.orbitUp',
|
|
68
|
-
{ id: 'view.orbitDown',
|
|
69
|
-
{ id: 'view.opposite',
|
|
70
|
-
{ id: 'view.rollLeft',
|
|
71
|
-
{ id: 'view.rollRight',
|
|
72
|
-
{ id: 'view.all',
|
|
73
|
-
{ id: 'view.zoomIn',
|
|
74
|
-
{ id: 'view.zoomOut',
|
|
75
|
-
{ id: 'view.perspective',
|
|
76
|
-
{ id: 'view.projection',
|
|
77
|
-
{ id: 'view.camera',
|
|
78
|
-
{ id: 'viewport.snapToFloor',
|
|
57
|
+
{ id: 'view.top', run: () => store.shell.setViewPreset('top') },
|
|
58
|
+
{ id: 'view.front', run: () => store.shell.setViewPreset('front') },
|
|
59
|
+
{ id: 'view.right', run: () => store.shell.setViewPreset('right') },
|
|
60
|
+
{ id: 'view.bottom', run: () => store.shell.setViewPreset('bottom') },
|
|
61
|
+
{ id: 'view.back', run: () => store.shell.setViewPreset('back') },
|
|
62
|
+
{ id: 'view.left', run: () => store.shell.setViewPreset('left') },
|
|
63
|
+
{ id: 'view.orbitLeft', run: () => store.shell.stepView('orbit-left') },
|
|
64
|
+
{ id: 'view.orbitRight', run: () => store.shell.stepView('orbit-right') },
|
|
65
|
+
{ id: 'view.orbitUp', run: () => store.shell.stepView('orbit-up') },
|
|
66
|
+
{ id: 'view.orbitDown', run: () => store.shell.stepView('orbit-down') },
|
|
67
|
+
{ id: 'view.opposite', run: () => store.shell.stepView('opposite') },
|
|
68
|
+
{ id: 'view.rollLeft', run: () => store.shell.stepView('roll-left') },
|
|
69
|
+
{ id: 'view.rollRight', run: () => store.shell.stepView('roll-right') },
|
|
70
|
+
{ id: 'view.all', run: () => store.shell.frameAll() },
|
|
71
|
+
{ id: 'view.zoomIn', run: () => store.shell.zoomView(1) },
|
|
72
|
+
{ id: 'view.zoomOut', run: () => store.shell.zoomView(-1) },
|
|
73
|
+
{ id: 'view.perspective', run: () => store.shell.setViewPreset('perspective') },
|
|
74
|
+
{ id: 'view.projection', run: () => store.shell.toggleProjection() },
|
|
75
|
+
{ id: 'view.camera', run: () => store.shell.toggleCameraView() },
|
|
76
|
+
{ id: 'viewport.snapToFloor', run: () => viewport.snapSelectionToFloor() },
|
|
79
77
|
]);
|
|
80
78
|
|
|
81
79
|
const onPointerDown = () => setActiveScope('viewport');
|
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
* THE VIEWPORT'S STATUS KEYS, reported by the Three set as a status facet
|
|
3
3
|
* (`host.session.reportFacet`): grid, helpers, stats, shading, helper visibility,
|
|
4
4
|
* the armed transform tool, space and snap, and the camera — each for the stage
|
|
5
|
-
* the person is looking at
|
|
6
|
-
* unit 4), with an Object3D document's own presentation where it has one.
|
|
5
|
+
* the person is looking at , with an Object3D document's own presentation where it has one.
|
|
7
6
|
*/
|
|
8
7
|
import { editorHost } from '@volter/sdk/host';
|
|
9
|
-
import { activeWorkspaceDocumentId } from '@volter/sdk/
|
|
8
|
+
import { activeWorkspaceDocumentId } from '@volter/sdk/workspace-document-registry';
|
|
10
9
|
import { object3DDocumentSession } from './authoring/object3d-document-session-registry';
|
|
11
|
-
import { focusedStageStore } from '@volter/sdk/
|
|
12
|
-
import { viewGridVisible } from '@volter/sdk/
|
|
10
|
+
import { focusedStageStore } from '@volter/sdk/stage-context';
|
|
11
|
+
import { viewGridVisible } from '@volter/sdk/viewport-presentation';
|
|
13
12
|
import { threeStoreForHost } from './three-state';
|
|
14
13
|
import { threeStateOf } from './three-state';
|
|
15
14
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The 3D board's empty-viewport vocabulary, reused by every other design
|
|
3
|
+
* surface that would otherwise sit blank while it builds. Scene's root-aware
|
|
4
|
+
* loading/failure/empty states live in `SurfaceStateOverlay`; this compact
|
|
5
|
+
* label remains for object/board construction whose subject is already known.
|
|
6
|
+
*
|
|
7
|
+
* Idle / building / ready is viewport STATE, not a new overlay channel —
|
|
8
|
+
* the board already owns this shape (`ThreeBoardDocument`: idle "Open this
|
|
9
|
+
* tab…", building "Settling N stories…"). Scene's first design
|
|
10
|
+
* mount and `Object3DDocumentViewport`'s first construct are the same wait
|
|
11
|
+
* with different subjects. Copy names the subject being prepared; it does not
|
|
12
|
+
* claim a narrower phase (such as physics settling) while indexing, mounting,
|
|
13
|
+
* bounds/layout, or renderer setup is actually in flight.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { themeVars } from '@volter/sdk/widgets';
|
|
17
|
+
import type { ReactNode } from 'react';
|
|
18
|
+
|
|
19
|
+
/** Fixed construct tail after the board scene exists — viewport-bind, not settle. */
|
|
20
|
+
export const OBJECT3D_SURFACE_BUILDING = 'Preparing the viewport…';
|
|
21
|
+
|
|
22
|
+
export function ViewportSurfaceStatus({
|
|
23
|
+
testId,
|
|
24
|
+
children,
|
|
25
|
+
}: {
|
|
26
|
+
readonly testId: string;
|
|
27
|
+
readonly children: ReactNode;
|
|
28
|
+
}) {
|
|
29
|
+
return (
|
|
30
|
+
<div
|
|
31
|
+
data-testid={testId}
|
|
32
|
+
style={{
|
|
33
|
+
position: 'absolute',
|
|
34
|
+
inset: 0,
|
|
35
|
+
padding: 16,
|
|
36
|
+
fontSize: 12,
|
|
37
|
+
color: themeVars.content.muted,
|
|
38
|
+
pointerEvents: 'none',
|
|
39
|
+
}}
|
|
40
|
+
>
|
|
41
|
+
{children}
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
package/src/loader.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides a single LoadingManager (for Three.js loaders) and resolveUrl()
|
|
5
5
|
* (for raw fetch calls) so that all engine asset requests go through one
|
|
6
|
-
* configurable URL prefix.
|
|
6
|
+
* configurable URL prefix.
|
|
7
7
|
*
|
|
8
8
|
* Three.js loaders constructed with `loadingManager` automatically rewrite
|
|
9
9
|
* URLs via setURLModifier(). For raw fetch() calls, wrap the URL with
|
|
@@ -34,18 +34,12 @@ function rewriteUrl(url: string): string {
|
|
|
34
34
|
export const loadingManager = new THREE.LoadingManager();
|
|
35
35
|
loadingManager.setURLModifier(rewriteUrl);
|
|
36
36
|
|
|
37
|
-
/** Pre-configured TextureLoader using the shared manager. */
|
|
38
|
-
export const textureLoader = new THREE.TextureLoader(loadingManager);
|
|
39
|
-
|
|
40
37
|
/**
|
|
41
38
|
* Shared DRACOLoader for decoding DRACO-compressed GLTF/GLB meshes.
|
|
42
39
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* draco_wasm_wrapper.js / draco_decoder.wasm to this path). This is an
|
|
47
|
-
* absolute path, so it deliberately does NOT go through the asset prefix —
|
|
48
|
-
* the decoder is engine infrastructure, not a scene asset.
|
|
40
|
+
* Decoder wasm/js is served at '/jsm/libs/draco/gltf/'. DRACOLoader
|
|
41
|
+
* appends draco_wasm_wrapper.js / draco_decoder.wasm to this absolute path,
|
|
42
|
+
* independently of the scene asset prefix.
|
|
49
43
|
*/
|
|
50
44
|
export const dracoLoader = new DRACOLoader(loadingManager);
|
|
51
45
|
dracoLoader.setDecoderPath('/jsm/libs/draco/gltf/');
|
|
@@ -53,77 +47,20 @@ dracoLoader.setDecoderPath('/jsm/libs/draco/gltf/');
|
|
|
53
47
|
/**
|
|
54
48
|
* Shared KTX2Loader for decoding KHR_texture_basisu (KTX2/Basis) textures.
|
|
55
49
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* and `packages/game-editor/template/public/jsm/libs/basis/` (a scaffolded game's
|
|
59
|
-
* own build), pinned in `vendor/upstream-assets.lock.json`, and KTX2Loader
|
|
60
|
-
* appends `basis_transcoder.js` / `.wasm` to this absolute path.
|
|
61
|
-
*
|
|
62
|
-
* Without this wiring, the Asset Budget's "Compress textures · KTX2" output
|
|
63
|
-
* is a GLB nothing in the engine can load — which is why the op and this
|
|
64
|
-
* loader landed together.
|
|
50
|
+
* The host serves basis_transcoder.js and basis_transcoder.wasm at
|
|
51
|
+
* '/jsm/libs/basis/'. KTX2Loader appends the filenames to this absolute path.
|
|
65
52
|
*/
|
|
66
53
|
export const ktx2Loader = new KTX2Loader(loadingManager);
|
|
67
54
|
ktx2Loader.setTranscoderPath('/jsm/libs/basis/');
|
|
68
55
|
|
|
69
|
-
let ktx2SupportDetected = false;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Teach the shared KTX2Loader which compressed formats THIS GPU accepts.
|
|
73
|
-
*
|
|
74
|
-
* KTX2Loader refuses to transcode until it has seen a renderer, and the
|
|
75
|
-
* answer is a property of the page's GPU rather than of any one renderer —
|
|
76
|
-
* so the FIRST renderer to exist supplies it and every later one is a no-op.
|
|
77
|
-
* Called by the engine's own renderer setup (`setup/setup-renderer.ts`) and
|
|
78
|
-
* by the editor's viewport, which between them cover every surface that
|
|
79
|
-
* parses a GLB.
|
|
80
|
-
*
|
|
81
|
-
* This sits on the MOUNT path, so it must never throw for a renderer it
|
|
82
|
-
* cannot question. `detectSupport` reads `renderer.extensions.has/get` (or
|
|
83
|
-
* `renderer.hasFeature` on a WebGPU renderer) — surface a headless stand-in
|
|
84
|
-
* (the test harnesses' fake renderers, any non-WebGL host) does not have.
|
|
85
|
-
* Such a renderer is skipped WITHOUT latching, so the first renderer that
|
|
86
|
-
* can actually answer still configures the loader for the whole page.
|
|
87
|
-
*/
|
|
88
|
-
export function detectKtx2Support(renderer: THREE.WebGLRenderer): void {
|
|
89
|
-
if (ktx2SupportDetected) return;
|
|
90
|
-
const probe = renderer as unknown as
|
|
91
|
-
| {
|
|
92
|
-
isWebGPURenderer?: boolean;
|
|
93
|
-
hasFeature?: unknown;
|
|
94
|
-
extensions?: { has?: unknown; get?: unknown };
|
|
95
|
-
}
|
|
96
|
-
| null
|
|
97
|
-
| undefined;
|
|
98
|
-
const canAnswer =
|
|
99
|
-
probe != null &&
|
|
100
|
-
(probe.isWebGPURenderer === true
|
|
101
|
-
? typeof probe.hasFeature === 'function'
|
|
102
|
-
: typeof probe.extensions?.has === 'function' && typeof probe.extensions?.get === 'function');
|
|
103
|
-
if (!canAnswer) return;
|
|
104
|
-
ktx2Loader.detectSupport(renderer);
|
|
105
|
-
ktx2SupportDetected = true;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
56
|
/** Pre-configured GLTFLoader using the shared manager, with DRACO decoding wired in. */
|
|
109
57
|
export const gltfLoader = new GLTFLoader(loadingManager);
|
|
110
58
|
gltfLoader.setDRACOLoader(dracoLoader);
|
|
111
59
|
gltfLoader.setKTX2Loader(ktx2Loader);
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
// output of the editor's Asset Budget "Compress · meshopt" action (W4a M3) —
|
|
115
|
-
// throws at load time in every runtime and the editor viewport. Symmetric
|
|
116
|
-
// with the DRACO wiring above (CB2): the ONE shared loader owns codec setup.
|
|
60
|
+
// Configure the shared loader’s bundled meshopt decoder for EXT_meshopt_compression, alongside its
|
|
61
|
+
// DRACO decoder.
|
|
117
62
|
gltfLoader.setMeshoptDecoder(MeshoptDecoder);
|
|
118
63
|
|
|
119
|
-
/**
|
|
120
|
-
* Set the URL prefix prepended to relative asset paths.
|
|
121
|
-
* Called once by createGameRuntime(). Defaults to '/'.
|
|
122
|
-
*/
|
|
123
|
-
export function setAssetPrefix(prefix: string): void {
|
|
124
|
-
_prefix = prefix.endsWith('/') ? prefix : prefix ? `${prefix}/` : '/';
|
|
125
|
-
}
|
|
126
|
-
|
|
127
64
|
/**
|
|
128
65
|
* Resolve a relative asset URL using the current prefix.
|
|
129
66
|
* Use this for raw fetch() calls — Three.js loaders using
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { AuthoringAdapter } from '@volter/project/adapter';
|
|
11
|
+
import type { SourceObject3DAuthoringAdapter } from './kit/authoring/source-object3d-authoring-adapter';
|
|
11
12
|
import type {
|
|
12
13
|
DocumentPersistenceBinding,
|
|
13
|
-
|
|
14
|
-
} from '@volter/sdk/
|
|
15
|
-
import type { PresentationLayer } from '@volter/sdk/kit/viewport-presentation';
|
|
14
|
+
} from '@volter/sdk/authoring/document-persistence-types';
|
|
15
|
+
import type { PresentationLayer } from '@volter/sdk/viewport-presentation';
|
|
16
16
|
import type { ComponentType } from 'react';
|
|
17
17
|
import type {
|
|
18
18
|
AnimationClip,
|
|
@@ -44,22 +44,23 @@ export interface ToolObject3DPreviewSource {
|
|
|
44
44
|
readonly hierarchyRoots?: readonly Object3D[];
|
|
45
45
|
readonly animations?: readonly AnimationClip[];
|
|
46
46
|
/**
|
|
47
|
-
* The source's OWN tick
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* loop: content time does not advance on a design-time surface, so a source
|
|
51
|
-
* that animates from here shows its settled pose and then holds it. Play is
|
|
52
|
-
* where a world runs.
|
|
47
|
+
* The source's OWN tick: the stage's transport calls it as content time
|
|
48
|
+
* advances (`StageHost`'s `advanceContent`). Content time does not advance on
|
|
49
|
+
* its own on a design-time surface; Play is where a world runs.
|
|
53
50
|
*/
|
|
54
51
|
update?(deltaSeconds: number): void;
|
|
55
52
|
/** Prepare camera-dependent draw buffers immediately before rendering.
|
|
56
53
|
* Does not advance content time or mutate the authored document. An optional
|
|
57
54
|
* returned function restores temporary draw state, including after errors.
|
|
58
55
|
* Interactive draws may use disposable navigation geometry; omitted/false
|
|
59
|
-
* always means full detail (captures and default frame-cost readings).
|
|
60
|
-
|
|
56
|
+
* always means full detail (captures and default frame-cost readings).
|
|
57
|
+
* `drawMode` is the stage's draw mode for this draw, `pixelScale` its target pixels per
|
|
58
|
+
* output pixel (the device pixel ratio on screen, the supersampling factor in a photograph),
|
|
59
|
+
* and `photograph` says the draw is a capture, which gets one chance: nothing in it may
|
|
60
|
+
* settle on a later frame. */
|
|
61
|
+
prepareDraw?(camera: Camera, options?: {interactive: boolean; height: number; multiDraw?: boolean; renderer?: WebGLRenderer; drawMode?: string; pixelScale?: number; photograph?: boolean}): void | (() => void);
|
|
61
62
|
/**
|
|
62
|
-
* EVERY CHANGE TO WHAT THE SOURCE DRAWS, announced: the
|
|
63
|
+
* EVERY CHANGE TO WHAT THE SOURCE DRAWS, announced: the source calls
|
|
63
64
|
* `listener` whenever the source's graph, materials, textures or poses may
|
|
64
65
|
* look different, and returns nothing from it. A source that offers this is
|
|
65
66
|
* drawn only when something changed -- this, the camera, input on the
|
|
@@ -153,16 +154,8 @@ export interface ToolViewportDressing {
|
|
|
153
154
|
*/
|
|
154
155
|
readonly viewLocked?: Object3D;
|
|
155
156
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* A document whose source system states its own transform passes that one
|
|
160
|
-
* instead: Blender's factory scene is AgX (`view_settings.view_transform`),
|
|
161
|
-
* and this product's Blender RENDER path already photographs through
|
|
162
|
-
* `AgXToneMapping` — so before this existed the viewport was the one
|
|
163
|
-
* surface in the chain running a different curve from the thing it frames.
|
|
164
|
-
* It is not a look preference: the curve decides how a shading range lands
|
|
165
|
-
* on screen, and two curves over one radiance are two different pictures.
|
|
157
|
+
* The stage’s three.js tone-mapping transform; omitted uses ACES. Documents can supply their
|
|
158
|
+
* source system’s transform so viewport and render use the same curve, such as Blender’s AgX.
|
|
166
159
|
*/
|
|
167
160
|
readonly toneMapping?: ToneMapping;
|
|
168
161
|
}
|
|
@@ -213,7 +206,7 @@ export interface ToolObject3DAuthoringProps {
|
|
|
213
206
|
/** Per-document overrides of the standard viewport dressing. */
|
|
214
207
|
readonly dressing?: ToolViewportDressing;
|
|
215
208
|
/** The kind of stage this view is, for its starting presentation
|
|
216
|
-
* (`@volter/sdk/
|
|
209
|
+
* (`@volter/sdk/viewport-presentation`): the document's own kind (`'model'`).
|
|
217
210
|
* Without it the kind is read off the document id's prefix. */
|
|
218
211
|
readonly stageKind?: string;
|
|
219
212
|
/** How far the OPENING view stands back from a fit of the content: `1` fills the view (the
|
|
@@ -236,16 +229,10 @@ export interface ToolObject3DAuthoringProps {
|
|
|
236
229
|
} | null;
|
|
237
230
|
/**
|
|
238
231
|
* THE DOCUMENT'S OWN PRESENTATION LAYER, over the stage's starting values and under a person's
|
|
239
|
-
* choices (
|
|
232
|
+
* choices (`@volter/sdk/viewport-presentation`): what the file itself says about how it is seen — a
|
|
240
233
|
* `.blend`'s saved lens as `camera.fov`. Read when the document binds.
|
|
241
234
|
*/
|
|
242
235
|
readonly presentation?: PresentationLayer | null;
|
|
243
|
-
/**
|
|
244
|
-
* Optional binding from live native clips back to ordinary project source.
|
|
245
|
-
* Project code only serializes its own TypeScript shape; the editor owns the
|
|
246
|
-
* checksum-guarded write and records it in the canonical project history.
|
|
247
|
-
* Without this binding the animation workspace remains honestly read-only.
|
|
248
|
-
*/
|
|
249
236
|
/**
|
|
250
237
|
* Project-owned serialization for this authored document. Each serializer
|
|
251
238
|
* returns the exact bytes of one ordinary project file; the host writes all
|
|
@@ -387,16 +374,13 @@ export interface ToolObject3DDocumentState {
|
|
|
387
374
|
readonly animations: readonly AnimationClip[];
|
|
388
375
|
}
|
|
389
376
|
|
|
390
|
-
/** One project-owned artifact participating in an Asset Lab commit. */
|
|
391
|
-
export type ToolObject3DDocumentResource = DocumentPersistenceResource<ToolObject3DDocumentState>;
|
|
392
|
-
|
|
393
377
|
export type ToolObject3DDocumentPersistence = DocumentPersistenceBinding<ToolObject3DDocumentState>;
|
|
394
378
|
|
|
395
379
|
export interface ToolObject3DDocumentAuthoringContext extends ToolObject3DDocumentState {
|
|
396
380
|
readonly documentId: string;
|
|
397
381
|
readonly sourcePath: string;
|
|
398
382
|
readonly scene: Scene;
|
|
399
|
-
readonly defaultAdapter:
|
|
383
|
+
readonly defaultAdapter: SourceObject3DAuthoringAdapter;
|
|
400
384
|
/**
|
|
401
385
|
* Serialize this project-owned document through its declared persistence
|
|
402
386
|
* resources and record the change in canonical history. Rejects when the
|