@volter/editor-threejs 0.5.211 → 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
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { editorHost } from '@volter/sdk/host';
|
|
2
|
+
import type { CollaborationPresence } from '@volter/sdk/collaboration-types';
|
|
3
|
+
|
|
4
|
+
let presence: CollaborationPresence = {
|
|
5
|
+
document: null,
|
|
6
|
+
file: null,
|
|
7
|
+
oid: null,
|
|
8
|
+
selection: [],
|
|
9
|
+
camera: null,
|
|
10
|
+
gesture: null,
|
|
11
|
+
};
|
|
12
|
+
let sent = '';
|
|
13
|
+
let scheduled = false;
|
|
14
|
+
let inFlight = false;
|
|
15
|
+
|
|
16
|
+
const PRESENCE_INTERVAL_MS = 100;
|
|
17
|
+
|
|
18
|
+
function schedulePresence(): void {
|
|
19
|
+
if (scheduled || inFlight) return;
|
|
20
|
+
scheduled = true;
|
|
21
|
+
globalThis.setTimeout(() => {
|
|
22
|
+
scheduled = false;
|
|
23
|
+
const serialized = JSON.stringify(presence);
|
|
24
|
+
if (serialized === sent) return;
|
|
25
|
+
sent = serialized;
|
|
26
|
+
inFlight = true;
|
|
27
|
+
void editorHost().collaboration.updatePresence(presence)
|
|
28
|
+
.catch(() => {
|
|
29
|
+
sent = '';
|
|
30
|
+
})
|
|
31
|
+
.finally(() => {
|
|
32
|
+
inFlight = false;
|
|
33
|
+
if (JSON.stringify(presence) !== sent) schedulePresence();
|
|
34
|
+
});
|
|
35
|
+
}, PRESENCE_INTERVAL_MS);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function reportCollaborationPresence(patch: Partial<CollaborationPresence>): void {
|
|
39
|
+
presence = { ...presence, ...patch };
|
|
40
|
+
schedulePresence();
|
|
41
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EditorSurface, Inline, Text } from '@volter/sdk/widgets';
|
|
2
2
|
import { useEffect, useRef, useState } from 'react';
|
|
3
|
-
import { useEditorStats } from '@volter/sdk/
|
|
3
|
+
import { useEditorStats } from '@volter/sdk/editor-runtime';
|
|
4
4
|
|
|
5
5
|
function fmt(n: number): string {
|
|
6
6
|
return n.toFixed(2);
|
|
@@ -33,14 +33,12 @@ export function CameraInfo() {
|
|
|
33
33
|
}, [stats]);
|
|
34
34
|
|
|
35
35
|
return (
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
// (compact tier; pointer-events stays none — it is a readout, not a
|
|
39
|
-
// control). Bars chrome keeps the pre-U6 overlay surface + blur.
|
|
36
|
+
// HUD overlays use the compact island material under islands chrome and the overlay surface
|
|
37
|
+
// under bars chrome. The readout keeps pointer events disabled.
|
|
40
38
|
<EditorSurface
|
|
41
39
|
variant="overlay"
|
|
42
40
|
border
|
|
43
|
-
className="volter-camera-info volter-chrome-island
|
|
41
|
+
className="volter-camera-info volter-chrome-island"
|
|
44
42
|
data-island-scale="compact"
|
|
45
43
|
>
|
|
46
44
|
<Inline gap={8} align="center" style={{ whiteSpace: 'nowrap' }}>
|
|
@@ -1,49 +1,22 @@
|
|
|
1
1
|
import type * as THREE from 'three';
|
|
2
2
|
import { Object3DSnapshotDocument } from './asset-viewers/EntityModelDocument';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
*
|
|
6
|
-
* `Object3D` the caller already has in hand.
|
|
7
|
-
*
|
|
8
|
-
* It takes the OBJECT, not an id to look one up by: a subject's preview is
|
|
9
|
-
* composed only when the thing is genuinely previewable
|
|
10
|
-
* (`inspection/compose.ts`), and the object the composer proved exists is the
|
|
11
|
-
* one rendered here. A scene node and a part inside an asset document are
|
|
12
|
-
* therefore the same case — the part is not in the shell's object map, and
|
|
13
|
-
* with an id this component could only have shown a "no longer available"
|
|
14
|
-
* alert for a thing that is right there on screen.
|
|
15
|
-
*
|
|
16
|
-
* Every host passes `fill`: the section body's square box, the compact card's
|
|
17
|
-
* round disc, and the minimized card's round thumb all own their own geometry
|
|
18
|
-
* and this component just fills it.
|
|
19
|
-
*/
|
|
4
|
+
/** Fill the Inspector media slot with a preview of its already-resolved Object3D. The caller
|
|
5
|
+
* supplies the document identity, source object and display name. */
|
|
20
6
|
export function InspectorObjectPreview({
|
|
21
7
|
previewKey,
|
|
22
8
|
object,
|
|
23
9
|
displayName,
|
|
24
|
-
fill = false,
|
|
25
10
|
}: {
|
|
26
11
|
/** Stable identity for the isolated preview document. */
|
|
27
12
|
previewKey: string;
|
|
28
13
|
object: THREE.Object3D;
|
|
29
14
|
displayName: string;
|
|
30
|
-
fill?: boolean;
|
|
31
15
|
}) {
|
|
32
16
|
return (
|
|
33
17
|
<div
|
|
34
18
|
data-testid="inspector-object-preview"
|
|
35
|
-
style={
|
|
36
|
-
fill
|
|
37
|
-
? { position: 'absolute', inset: 0, overflow: 'hidden' }
|
|
38
|
-
: {
|
|
39
|
-
width: '100%',
|
|
40
|
-
height: 220,
|
|
41
|
-
minHeight: 220,
|
|
42
|
-
borderBottom: '1px solid var(--volter-structural-divider)',
|
|
43
|
-
overflow: 'hidden',
|
|
44
|
-
position: 'relative',
|
|
45
|
-
}
|
|
46
|
-
}
|
|
19
|
+
style={{ position: 'absolute', inset: 0, overflow: 'hidden' }}
|
|
47
20
|
>
|
|
48
21
|
<Object3DSnapshotDocument
|
|
49
22
|
documentId={`inspector-preview:${previewKey}`}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/* Imported by `Object3DDocumentToolbar.tsx`, so this sheet loads with the toolbar's module, after the
|
|
2
|
+
* host's theme.css, and a declaration here wins over one theme.css makes at the same specificity. The
|
|
3
|
+
* toolbar's border and its labelled control's gap are therefore left to `.volter-toolbar` and
|
|
4
|
+
* `.volter-btn`. `padding: 0` and `min-height: auto` below differ from `.volter-toolbar`'s, and compute
|
|
5
|
+
* the recipe's values only because `.volter-toolbar[data-compact="true"]` (theme.css) outranks them:
|
|
6
|
+
* both toolbars are `EditorToolbar compact`, which emits that attribute. */
|
|
7
|
+
/* THE 3D DOCUMENT'S CONTROLS LIVE IN THE HOST'S HEADER STRIP now
|
|
8
|
+
(`WorkspaceDocumentSurface`), not floating over the stage: one row, the
|
|
9
|
+
view group first and the preview group pushed to the trailing edge. The
|
|
10
|
+
strip supplies the divider and height; these groups are plain in-flow
|
|
11
|
+
toolbars inside it. */
|
|
12
|
+
.volter-model-document-header {
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
gap: var(--volter-space-2);
|
|
16
|
+
flex: 1 1 auto;
|
|
17
|
+
min-width: 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.volter-model-viewport-toolbar {
|
|
21
|
+
position: static;
|
|
22
|
+
min-height: auto;
|
|
23
|
+
padding: 0;
|
|
24
|
+
background: transparent;
|
|
25
|
+
box-shadow: none;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.volter-model-viewport-toolbar--preview {
|
|
29
|
+
margin-left: auto;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.volter-model-viewport-toolbar > * {
|
|
33
|
+
flex: 0 0 auto;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.volter-model-toolbar-labelled-control {
|
|
37
|
+
display: inline-flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
white-space: nowrap;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.volter-model-capture-preview {
|
|
43
|
+
position: absolute;
|
|
44
|
+
z-index: var(--volter-z-sticky);
|
|
45
|
+
top: calc(var(--volter-space-3) + var(--volter-control-default-height) + var(--volter-space-2));
|
|
46
|
+
right: var(--volter-space-3);
|
|
47
|
+
width: min(420px, calc(100% - 2 * var(--volter-space-3)));
|
|
48
|
+
padding: var(--volter-space-3);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.volter-model-capture-preview > div {
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
justify-content: space-between;
|
|
55
|
+
margin-bottom: var(--volter-space-3);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.volter-model-capture-preview img {
|
|
59
|
+
display: block;
|
|
60
|
+
width: 100%;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.volter-model-toolbar-error {
|
|
64
|
+
position: absolute;
|
|
65
|
+
z-index: var(--volter-z-sticky);
|
|
66
|
+
right: var(--volter-space-3);
|
|
67
|
+
bottom: var(--volter-space-3);
|
|
68
|
+
max-width: 420px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@media (width <= 720px) {
|
|
72
|
+
.volter-model-toolbar-labelled-control > span {
|
|
73
|
+
display: none;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -36,18 +36,12 @@ import {
|
|
|
36
36
|
object3DDocumentSessionsVersion,
|
|
37
37
|
subscribeObject3DDocumentSessions,
|
|
38
38
|
} from '../authoring/object3d-document-session-registry';
|
|
39
|
-
import {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
subscribeContributedChrome,
|
|
43
|
-
} from '@volter/sdk/kit/chrome-registry';
|
|
44
|
-
import type { HelperVisibility } from '@volter/sdk/kit/shell-store';
|
|
45
|
-
import { object3DDocumentWritePolicy } from '@volter/sdk/kit/object3d-document-write-policy';
|
|
46
|
-
import { stageStore, stageStoresVersion, subscribeStageStores } from '@volter/sdk/kit/stage-store-registry';
|
|
39
|
+
import type { HelperVisibility } from '@volter/sdk/shell-store';
|
|
40
|
+
import { assetDocumentWritePolicy } from '@volter/sdk/asset-document-write-policy';
|
|
41
|
+
import { stageStore, stageStoresVersion, subscribeStageStores } from '@volter/sdk/stage-store-registry';
|
|
47
42
|
import { viewportStageHelperKinds } from '../../viewport-door';
|
|
48
43
|
import {
|
|
49
44
|
type PresentationLayer,
|
|
50
|
-
applyViewPreset,
|
|
51
45
|
DOCUMENT_STUDIO_PRESET,
|
|
52
46
|
setViewGridVisible,
|
|
53
47
|
viewGridVisible,
|
|
@@ -55,13 +49,12 @@ import {
|
|
|
55
49
|
stageLightsPerMode,
|
|
56
50
|
startingPresentation,
|
|
57
51
|
studioPresets,
|
|
58
|
-
viewPresets,
|
|
59
52
|
viewPresentationBinding,
|
|
60
53
|
subscribeViewportPresentation,
|
|
61
54
|
viewPresentation,
|
|
62
55
|
viewportPresentationVersion,
|
|
63
|
-
} from '@volter/sdk/
|
|
64
|
-
import { ViewportOverlaysGlyph, ViewportOverlaysMenu } from '
|
|
56
|
+
} from '@volter/sdk/viewport-presentation';
|
|
57
|
+
import { ViewportOverlaysGlyph, ViewportOverlaysMenu } from './ViewportOverlaysMenu';
|
|
65
58
|
import {
|
|
66
59
|
type ViewportDisplayModeChoice,
|
|
67
60
|
ViewportDisplayModeMenu,
|
|
@@ -69,6 +62,7 @@ import {
|
|
|
69
62
|
viewportShadingModes,
|
|
70
63
|
viewportShadingSegments,
|
|
71
64
|
} from './ViewportShadingMenu';
|
|
65
|
+
import './Object3DDocumentToolbar.css';
|
|
72
66
|
|
|
73
67
|
const subscribeToNothing = () => () => undefined;
|
|
74
68
|
const noSessionVersion = () => 0;
|
|
@@ -80,11 +74,9 @@ const noSessionVersion = () => 0;
|
|
|
80
74
|
* (`View3DOverlay.show_bones`, `makesrna/intern/rna_space.cc:5125-5129`, drawn
|
|
81
75
|
* by `scripts/startup/bl_ui/space_view3d.py:7161` with `text="Bones"`), and it
|
|
82
76
|
* is Blender's noun that is used here rather than the Helpers vocabulary's
|
|
83
|
-
* `Skeletons`, because this menu
|
|
84
|
-
* says so. `Weights` has no Blender checkbox at all: Blender reaches weight
|
|
77
|
+
* `Skeletons`, because this menu uses Blender's overlay vocabulary. `Weights` has no Blender checkbox at all: Blender reaches weight
|
|
85
78
|
* colours through Weight Paint MODE, and an inspection surface has no brushes
|
|
86
|
-
* to enter one, so it is a display toggle here
|
|
87
|
-
* Blender, "Inspection parity", I4).
|
|
79
|
+
* to enter one, so it is a display toggle here.
|
|
88
80
|
*
|
|
89
81
|
* A ROW STANDS ONLY WHERE THE DOCUMENT PUT A HELPER OF THAT KIND on this stage
|
|
90
82
|
* (`viewportStageHelperKinds`) — the same rule the Outliner's restriction
|
|
@@ -114,19 +106,13 @@ export function Object3DDocumentToolbar({
|
|
|
114
106
|
object3DDocumentSessionsVersion,
|
|
115
107
|
);
|
|
116
108
|
const session = object3DDocumentSession(documentId);
|
|
117
|
-
useSyncExternalStore(
|
|
118
|
-
subscribeContributedChrome,
|
|
119
|
-
contributedChromeVersion,
|
|
120
|
-
contributedChromeVersion,
|
|
121
|
-
);
|
|
122
109
|
useSyncExternalStore(
|
|
123
110
|
session?.subscribe ?? subscribeToNothing,
|
|
124
111
|
session?.getSnapshot ?? noSessionVersion,
|
|
125
112
|
session?.getSnapshot ?? noSessionVersion,
|
|
126
113
|
);
|
|
127
114
|
// THIS STAGE's own store and its own helpers: the shared panels read
|
|
128
|
-
// whichever store the FOCUSED document's stage runs on
|
|
129
|
-
// §One stage), and a toolbar is a control over ITS document, never over the
|
|
115
|
+
// whichever store the FOCUSED document's stage runs on , and a toolbar is a control over ITS document, never over the
|
|
130
116
|
// world root's.
|
|
131
117
|
useSyncExternalStore(subscribeStageStores, stageStoresVersion, stageStoresVersion);
|
|
132
118
|
const stage = stageStore(documentId);
|
|
@@ -206,8 +192,8 @@ export function Object3DDocumentToolbar({
|
|
|
206
192
|
try {
|
|
207
193
|
// The shell owns where a framing is RECORDED (`.volter/thumbnails.json`,
|
|
208
194
|
// written through the project-file route); this surface only knows the
|
|
209
|
-
// asset and the pose. See `
|
|
210
|
-
await
|
|
195
|
+
// asset and the pose. See `asset-document-write-policy.ts`.
|
|
196
|
+
await assetDocumentWritePolicy().saveThumbnailFraming(
|
|
211
197
|
assetPath,
|
|
212
198
|
reset ? undefined : session.cameraPose(),
|
|
213
199
|
);
|
|
@@ -237,13 +223,7 @@ export function Object3DDocumentToolbar({
|
|
|
237
223
|
label="Viewport view controls"
|
|
238
224
|
className="volter-model-viewport-toolbar volter-model-viewport-toolbar--view"
|
|
239
225
|
>
|
|
240
|
-
{/*
|
|
241
|
-
selection when there is one and the whole subject when there is
|
|
242
|
-
not, but it was labelled "Frame selection" unconditionally — so a
|
|
243
|
-
tester hunting a fit-the-board control on the 3D board reported
|
|
244
|
-
finding none, then discovered this button does exactly that
|
|
245
|
-
(runhuman passes 87 and 89). One control, named for the job it is
|
|
246
|
-
about to do. */}
|
|
226
|
+
{/* Name the framing control for the selection, or the whole subject when selection is empty. */}
|
|
247
227
|
<Tooltip text={frameLabel}>
|
|
248
228
|
<IconButton aria-label={frameLabel} onClick={() => session.frame()}>
|
|
249
229
|
<EditorIcon icon={faExpand} size="md" />
|
|
@@ -255,11 +235,7 @@ export function Object3DDocumentToolbar({
|
|
|
255
235
|
ref={viewRef}
|
|
256
236
|
type="button"
|
|
257
237
|
size="compact"
|
|
258
|
-
//
|
|
259
|
-
// `Object Mode v` in the same row of chrome is a 20px well
|
|
260
|
-
// (#272727 in a 1px #3c3c3c border) while its plain menu words
|
|
261
|
-
// — View/Select/Add — carry no chrome at all. `ghost` said this
|
|
262
|
-
// was the second kind; it is the first.
|
|
238
|
+
// Use the select-like trigger variant for the mode dropdown.
|
|
263
239
|
variant="secondary"
|
|
264
240
|
aria-label={`Camera view: ${presentation.projection}`}
|
|
265
241
|
aria-haspopup="menu"
|
|
@@ -329,12 +305,6 @@ export function Object3DDocumentToolbar({
|
|
|
329
305
|
</IconButton>
|
|
330
306
|
</Tooltip>
|
|
331
307
|
) : null}
|
|
332
|
-
{/* A package's own controls on this document (`@volter/sdk/chrome`,
|
|
333
|
-
`placement: 'object3d-document'`): handed the document's id; the
|
|
334
|
-
contribution resolves its subject through its own integration. */}
|
|
335
|
-
{contributedHeaderItems('object3d-document').map(({ id, Component }) => (
|
|
336
|
-
<Component key={id} documentId={documentId} />
|
|
337
|
-
))}
|
|
338
308
|
</EditorToolbar>
|
|
339
309
|
<EditorToolbar
|
|
340
310
|
compact
|
|
@@ -344,46 +314,15 @@ export function Object3DDocumentToolbar({
|
|
|
344
314
|
{/* THE DISPLAY CONTROLS SIT AT THE TRAILING EDGE, which is where
|
|
345
315
|
Blender's are: its 3D View header packs Visibility, Gizmo,
|
|
346
316
|
Overlays, X-Ray and Shading hard against the region's right border
|
|
347
|
-
and leaves the middle empty
|
|
348
|
-
|
|
349
|
-
right and this group's `...` alone at the edge. */}
|
|
350
|
-
{/* BLENDER'S SHADING CONTROL, one widget: the segments it has, and the
|
|
351
|
-
chevron's popover carrying every other mode AND the lighting rows
|
|
352
|
-
that used to be a second `Studio v` dropdown further along the row.
|
|
353
|
-
Blender's own shading popover is where Lighting lives (Studio /
|
|
354
|
-
MatCap / Flat), so the two controls were always one control drawn
|
|
355
|
-
twice — measured in `modeling-edit-none.png`, its header's trailing
|
|
356
|
-
cluster is five icon groups and no words at all. */}
|
|
317
|
+
and leaves the middle empty, and so does ours. */}
|
|
318
|
+
{/* The shading segments and popover form one widget; the popover contains the remaining modes and lighting settings. */}
|
|
357
319
|
<ViewportDisplayModeMenu
|
|
358
320
|
mode={presentation.mode}
|
|
359
321
|
onChange={(mode) => session.setMode(mode)}
|
|
360
322
|
choices={modes}
|
|
361
323
|
segments={perMode ? perModeShadingSegments(declaredModes) : viewportShadingSegments}
|
|
362
324
|
>
|
|
363
|
-
{/*
|
|
364
|
-
its light, backdrop, overlays and tool) that the packages here contribute
|
|
365
|
-
(`*.view.ts`). Choosing one replaces the view's own choices; the rows below then
|
|
366
|
-
adjust it. Shown only where a package contributes one. */}
|
|
367
|
-
{viewPresets().length > 0 ? (
|
|
368
|
-
<label>
|
|
369
|
-
<span>View</span>
|
|
370
|
-
<Select
|
|
371
|
-
aria-label="View preset"
|
|
372
|
-
value=""
|
|
373
|
-
onChange={(event) => {
|
|
374
|
-
if (event.target.value) applyViewPreset(documentId, event.target.value);
|
|
375
|
-
}}
|
|
376
|
-
>
|
|
377
|
-
<option value="">Choose…</option>
|
|
378
|
-
{viewPresets().map((preset) => (
|
|
379
|
-
<option key={preset.id} value={preset.id}>
|
|
380
|
-
{preset.title}
|
|
381
|
-
</option>
|
|
382
|
-
))}
|
|
383
|
-
</Select>
|
|
384
|
-
</label>
|
|
385
|
-
) : null}
|
|
386
|
-
{/* LIGHTING AND EXPOSURE ARE THE VIEW'S PRESENTATION (`kit/viewport-presentation`):
|
|
325
|
+
{/* LIGHTING AND EXPOSURE ARE THE VIEW'S PRESENTATION (`@volter/sdk/viewport-presentation`):
|
|
387
326
|
a studio preset, or the scene's own lights, and the tone's exposure. */}
|
|
388
327
|
<label>
|
|
389
328
|
<span>Lighting</span>
|
|
@@ -445,12 +384,7 @@ export function Object3DDocumentToolbar({
|
|
|
445
384
|
</IconButton>
|
|
446
385
|
</Tooltip>
|
|
447
386
|
)}
|
|
448
|
-
{/*
|
|
449
|
-
toggles, which is Grid and Bounds, exactly Blender's Show Overlays
|
|
450
|
-
subject. It takes Blender's mark, and Blender's split toggle+chevron:
|
|
451
|
-
the toggle is the MASTER flag, this stage's own helpers switch
|
|
452
|
-
(`StageHost` takes the grid, axis lines and selection marks down
|
|
453
|
-
with it), and each overlay below keeps its own choice under it. */}
|
|
387
|
+
{/* The master helpers toggle controls overlays; individual overlay choices remain stored underneath it. */}
|
|
454
388
|
<ViewportOverlaysMenu
|
|
455
389
|
glyph={<ViewportOverlaysGlyph />}
|
|
456
390
|
{...(stage === null
|
|
@@ -460,7 +394,7 @@ export function Object3DDocumentToolbar({
|
|
|
460
394
|
{
|
|
461
395
|
id: 'grid',
|
|
462
396
|
label: 'Grid',
|
|
463
|
-
// The VIEW's grid switch (
|
|
397
|
+
// The VIEW's grid switch (`@volter/sdk/viewport-presentation`), one per view.
|
|
464
398
|
enabled: viewGridVisible(documentId),
|
|
465
399
|
onToggle: () => setViewGridVisible(documentId, !viewGridVisible(documentId)),
|
|
466
400
|
},
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Lazy document boundary for the shared Object3D authoring surface.
|
|
3
3
|
*
|
|
4
|
-
* Registries and open-document planners
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* merely to register unopened document kinds put it on Scene's cold path.
|
|
8
|
-
* Keep the public component shape here and evaluate the implementation only
|
|
9
|
-
* when React actually renders an Object3D document.
|
|
4
|
+
* Registries and open-document planners can name this surface without importing its animation,
|
|
5
|
+
* capture, model-inspection and source-authoring implementation. React loads that implementation
|
|
6
|
+
* when it renders an Object3D document.
|
|
10
7
|
*/
|
|
11
8
|
|
|
12
9
|
import { lazy, Suspense, useEffect } from 'react';
|
|
13
|
-
import { announceDocumentStage } from '@volter/sdk/
|
|
10
|
+
import { announceDocumentStage } from '@volter/sdk/document-viewports';
|
|
14
11
|
import type { Object3DDocumentViewportProps } from './StageHost';
|
|
15
|
-
import { OBJECT3D_SURFACE_BUILDING, ViewportSurfaceStatus } from '
|
|
12
|
+
import { OBJECT3D_SURFACE_BUILDING, ViewportSurfaceStatus } from '../viewport-surface-status';
|
|
16
13
|
|
|
17
14
|
const LazyObject3DDocumentViewport = lazy(async () => {
|
|
18
15
|
const module = await import('./StageHost');
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* The stage's studio floor and the asset workspace's box, applied by `StageHost.tsx`. They read only the
|
|
2
|
+
design system's shared tokens (`--volter-*`). */
|
|
3
|
+
.volter-object3d-studio-stage {
|
|
4
|
+
/* Canonical Asset Lab presentation floor: a cool key-light bloom behind
|
|
5
|
+
the subject, a faint counter-light low in frame, and a restrained edge
|
|
6
|
+
vignette. The Three canvas is alpha-backed, so the same stage appears in
|
|
7
|
+
the full Asset Editor document and the inspector's preview section. */
|
|
8
|
+
background-color: var(--volter-object3d-stage-base, #1d232b);
|
|
9
|
+
background-image:
|
|
10
|
+
radial-gradient(
|
|
11
|
+
90% 72% at 50% 36%,
|
|
12
|
+
color-mix(in srgb, var(--volter-accent) 16%, rgb(122 148 177 / 22%)),
|
|
13
|
+
transparent 62%
|
|
14
|
+
),
|
|
15
|
+
radial-gradient(70% 42% at 50% 92%, rgb(124 151 181 / 10%), transparent 72%),
|
|
16
|
+
radial-gradient(140% 112% at 50% 44%, transparent 42%, rgb(0 0 0 / 58%) 100%),
|
|
17
|
+
linear-gradient(
|
|
18
|
+
180deg,
|
|
19
|
+
var(--volter-object3d-stage-top, color-mix(in srgb, var(--volter-surface-raised) 82%, #394451)) 0%,
|
|
20
|
+
var(--volter-object3d-stage-bottom, color-mix(in srgb, var(--volter-surface-shell) 76%, #11151b))
|
|
21
|
+
100%
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Inspector previews sit over the authored world, so retain some of that
|
|
26
|
+
context beneath the studio lighting. The full Asset Lab remains opaque. */
|
|
27
|
+
.volter-object3d-studio-stage[data-volter-chromeless="true"] {
|
|
28
|
+
--volter-object3d-stage-base: rgb(29 35 43 / 34%);
|
|
29
|
+
--volter-object3d-stage-top: rgb(57 68 81 / 40%);
|
|
30
|
+
--volter-object3d-stage-bottom: rgb(17 21 27 / 52%);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.volter-object3d-asset-workspace {
|
|
34
|
+
position: absolute;
|
|
35
|
+
inset: 0;
|
|
36
|
+
display: flex;
|
|
37
|
+
min-width: 0;
|
|
38
|
+
min-height: 0;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
}
|