@volter/editor-threejs 0.5.65 → 0.5.67
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 +2 -0
- package/contributions/animation-mixers.service.ts +20 -0
- package/contributions/animation-timeline.utility.tsx +44 -0
- package/contributions/three-integration.service.ts +13 -0
- package/dist-node/serving.mjs +405 -0
- package/package.json +113 -5
- package/serving/animation-live-module.ts +70 -0
- package/serving/animation-stamp.ts +88 -0
- package/serving/index.ts +14 -0
- package/serving/model-import-conversion.ts +344 -0
- package/src/adapter/ingest/scene-capture.ts +1 -23
- package/src/adapter/renderer-config.ts +3 -4
- package/src/adapter/three-contract.ts +72 -0
- package/src/animation/live-mixers.ts +55 -0
- package/src/ecs/object-marks.ts +1 -1
- package/src/ecs/user-data.ts +0 -16
- package/src/host-hierarchy-objects.ts +31 -0
- package/src/kit/animation/three-clips-subject.ts +190 -0
- package/src/kit/asset-compare.ts +294 -0
- package/src/kit/asset-preview-command.ts +265 -0
- package/src/kit/asset-preview-framing.ts +357 -0
- package/src/kit/asset-preview.ts +2802 -0
- package/src/kit/asset-workflow/model-inspection.ts +830 -0
- package/src/kit/authoring/component-instance-root.ts +171 -0
- package/src/kit/authoring/design-time-settle.ts +343 -0
- package/src/kit/authoring/live-object-transform.ts +62 -0
- package/src/kit/authoring/object3d-document-session-registry.ts +154 -0
- package/src/kit/authoring/object3d-document-session.ts +1965 -0
- package/src/kit/authoring/object3d-gesture-controller.ts +113 -0
- package/src/kit/authoring/quarks-particle-systems.ts +19 -0
- package/src/kit/authoring/shell-viewport-policy.ts +48 -0
- package/src/kit/authoring/source-object3d-authoring-adapter.ts +526 -0
- package/src/kit/authoring/three-projection-core.ts +226 -0
- package/src/kit/authoring/viewport-pick-context.ts +39 -0
- package/src/kit/authoring/viewport-raycast.ts +240 -0
- package/src/kit/authoring/world-hidden-viewport.ts +95 -0
- package/src/kit/camera-authoring.ts +175 -0
- package/src/kit/components/CameraInfo.tsx +56 -0
- package/src/kit/components/InspectorObjectPreview.tsx +57 -0
- package/src/kit/components/Object3DDocumentToolbar.tsx +549 -0
- package/src/kit/components/Object3DDocumentViewport.tsx +58 -0
- package/src/kit/components/StageHost.tsx +2547 -0
- package/src/kit/components/StageOverlays.tsx +21 -0
- package/src/kit/components/StatsOverlay.tsx +78 -0
- package/src/kit/components/ToolObject3DPreview.tsx +39 -0
- package/src/kit/components/ViewportFurniture.tsx +655 -0
- package/src/kit/components/ViewportOverlay.tsx +215 -0
- package/src/kit/components/ViewportShadingMenu.tsx +340 -0
- package/src/kit/components/ViewportViewMenu.tsx +155 -0
- package/src/kit/components/asset-viewers/EntityModelDocument.tsx +121 -0
- package/src/kit/components/asset-viewers/EnvironmentAssetDocument.tsx +440 -0
- package/src/kit/components/asset-viewers/LiveModuleDocument.tsx +395 -0
- package/src/kit/components/asset-viewers/LutAssetDocument.tsx +444 -0
- package/src/kit/components/asset-viewers/ModelAssetDocument.tsx +105 -0
- package/src/kit/components/asset-viewers/Object3DPreview.tsx +356 -0
- package/src/kit/components/asset-viewers/QuarksAssetDocument.tsx +527 -0
- package/src/kit/components/asset-viewers/ShaderAssetDocument.tsx +743 -0
- package/src/kit/components/asset-viewers/three-asset-viewers.tsx +132 -0
- package/src/kit/components/object3d-contribution-surfaces.tsx +33 -0
- package/src/kit/components/stage-keyboard.tsx +40 -0
- package/src/kit/components/stage-overlay-set.tsx +105 -0
- package/src/kit/components/stage-presence-markers.ts +482 -0
- package/src/kit/components/stage-transform-chrome.ts +30 -0
- package/src/kit/components/stage-transform-tools.tsx +73 -0
- package/src/kit/components/stage-view-name.ts +30 -0
- package/src/kit/components/standard-viewport-dressing.ts +1042 -0
- package/src/kit/components/world-root-binding.ts +64 -0
- package/src/kit/constraint-helper.ts +338 -0
- package/src/kit/editor-shell-store.ts +814 -0
- package/src/kit/editor-viewport.ts +6621 -0
- package/src/kit/entity-lod.ts +31 -0
- package/src/kit/entity-object.ts +92 -0
- package/src/kit/hierarchy-mark-reader.ts +74 -0
- package/src/kit/instanced-presentation.ts +164 -0
- package/src/kit/live-module-source.ts +230 -0
- package/src/kit/model-thumbnail.ts +539 -0
- package/src/kit/play-camera-flight.ts +300 -0
- package/src/kit/projection/three.ts +898 -0
- package/src/kit/reflection-probe-helper.ts +142 -0
- package/src/kit/scene-document-viewport.ts +51 -0
- package/src/kit/scene-framing.ts +315 -0
- package/src/kit/scene-view-fog.ts +89 -0
- package/src/kit/spatial-handle-visuals.ts +332 -0
- package/src/kit/stories/three-story-model.ts +66 -0
- package/src/kit/three-canvas-render.ts +44 -0
- package/src/kit/three-hierarchy-row-media.ts +26 -0
- package/src/kit/three-inspection-media.ts +73 -0
- package/src/kit/three-integration.ts +86 -0
- package/src/kit/three-state.ts +33 -0
- package/src/kit/three-viewport/bone-selection-highlight.ts +119 -0
- package/src/kit/three-viewport/camera-fit.ts +41 -0
- package/src/kit/three-viewport/interactive-renderer.ts +132 -0
- package/src/kit/three-viewport/selection-brackets.ts +355 -0
- package/src/kit/three-viewport/selection-outline.ts +333 -0
- package/src/kit/three-viewport/skeleton-helper.ts +61 -0
- package/src/kit/three-viewport/source-color.ts +197 -0
- package/src/kit/three-viewport/studio-environment.ts +96 -0
- package/src/kit/trigger-volume-helper.ts +116 -0
- package/src/kit/viewport-actions.ts +128 -0
- package/src/kit/viewport-authoring-policy.ts +154 -0
- package/src/kit/viewport-commands.ts +318 -0
- package/src/kit/viewport-hotkeys.ts +119 -0
- package/src/kit/viewport-shading-boundary.ts +12 -0
- package/src/kit/viewport-status-facet.ts +53 -0
- package/src/object3d-contributions.ts +494 -0
- package/src/render/viewport-shading.ts +6 -2
- package/src/viewport/content-bounds.ts +38 -4
- package/src/viewport/environment.ts +16 -0
- package/src/viewport-api.ts +92 -0
- package/src/viewport-door.ts +237 -0
- package/src/animation/animation-clock.ts +0 -479
- package/src/animation/runtime-inspection.ts +0 -45
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { EDITOR_LAYER } from '@volter/editor-threejs/viewport/editor-layers';
|
|
2
|
+
import { triggerVolumeOf } from '@volter/editor-threejs/adapter/trigger-volume';
|
|
3
|
+
import { setUserData } from '@volter/editor-threejs/ecs/user-data';
|
|
4
|
+
import * as THREE from 'three';
|
|
5
|
+
|
|
6
|
+
const RING = new THREE.Color(0x4fd1c5);
|
|
7
|
+
const RING_SELECTED = new THREE.Color(0x9ff0e8);
|
|
8
|
+
const SEGMENTS = 64;
|
|
9
|
+
/** How far the corner ticks rise off the ring, in world units. */
|
|
10
|
+
const TICK_HEIGHT = 0.6;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Editor-only visualization for a project-owned trigger volume — the group
|
|
14
|
+
* whose radius decides where a hazard fires or a quest arrives. The game's
|
|
15
|
+
* source says nothing about being authored; it just carries the radius in
|
|
16
|
+
* `userData`, and this helper draws it: a ground circle in the source's local
|
|
17
|
+
* XZ plane that extends upward at ±X/±Z, which is what such a volume tests.
|
|
18
|
+
*
|
|
19
|
+
* Same contract as `ReflectionProbeHelper`: it follows the live Object3D but
|
|
20
|
+
* owns every line and material it draws, sits on EDITOR_LAYER, and therefore
|
|
21
|
+
* never enters the game's tree or its runtime camera.
|
|
22
|
+
*/
|
|
23
|
+
export class TriggerVolumeHelper extends THREE.Group {
|
|
24
|
+
private signature = '';
|
|
25
|
+
private selected = false;
|
|
26
|
+
|
|
27
|
+
constructor(readonly source: THREE.Object3D) {
|
|
28
|
+
super();
|
|
29
|
+
this.name = 'Trigger Volume Helper';
|
|
30
|
+
this.matrixAutoUpdate = false;
|
|
31
|
+
this.renderOrder = 998;
|
|
32
|
+
setUserData(this, 'editorHelper', true);
|
|
33
|
+
setUserData(this, 'editorHelperType', 'trigger-volumes');
|
|
34
|
+
this.layers.set(EDITOR_LAYER);
|
|
35
|
+
this.refresh(true);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
setSelected(selected: boolean): void {
|
|
39
|
+
if (this.selected === selected) return;
|
|
40
|
+
this.selected = selected;
|
|
41
|
+
this.refresh(true);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
update(): void {
|
|
45
|
+
this.refresh(false);
|
|
46
|
+
this.source.updateWorldMatrix(true, false);
|
|
47
|
+
this.matrix.copy(this.source.matrixWorld);
|
|
48
|
+
this.matrixWorldNeedsUpdate = true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
dispose(): void {
|
|
52
|
+
this.clearVisuals();
|
|
53
|
+
this.removeFromParent();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private refresh(force: boolean): void {
|
|
57
|
+
const volume = triggerVolumeOf(this.source);
|
|
58
|
+
if (!volume) return;
|
|
59
|
+
const radius = volume.radius;
|
|
60
|
+
const next = String(radius);
|
|
61
|
+
if (!force && next === this.signature) return;
|
|
62
|
+
this.signature = next;
|
|
63
|
+
this.clearVisuals();
|
|
64
|
+
|
|
65
|
+
const color = this.selected ? RING_SELECTED : RING;
|
|
66
|
+
const opacity = this.selected ? 1 : 0.62;
|
|
67
|
+
|
|
68
|
+
const ring: number[] = [];
|
|
69
|
+
for (let i = 0; i < SEGMENTS; i += 1) {
|
|
70
|
+
const angle = (i / SEGMENTS) * Math.PI * 2;
|
|
71
|
+
ring.push(Math.cos(angle) * radius, 0, Math.sin(angle) * radius);
|
|
72
|
+
}
|
|
73
|
+
const ringGeometry = new THREE.BufferGeometry();
|
|
74
|
+
ringGeometry.setAttribute('position', new THREE.Float32BufferAttribute(ring, 3));
|
|
75
|
+
const loop = new THREE.LineLoop(ringGeometry, this.lineMaterial(color, opacity));
|
|
76
|
+
loop.renderOrder = this.renderOrder;
|
|
77
|
+
this.add(loop);
|
|
78
|
+
|
|
79
|
+
const ticks: number[] = [];
|
|
80
|
+
for (const [x, z] of [
|
|
81
|
+
[radius, 0],
|
|
82
|
+
[-radius, 0],
|
|
83
|
+
[0, radius],
|
|
84
|
+
[0, -radius],
|
|
85
|
+
] as const) {
|
|
86
|
+
ticks.push(x, 0, z, x, TICK_HEIGHT, z);
|
|
87
|
+
}
|
|
88
|
+
const tickGeometry = new THREE.BufferGeometry();
|
|
89
|
+
tickGeometry.setAttribute('position', new THREE.Float32BufferAttribute(ticks, 3));
|
|
90
|
+
const tickLines = new THREE.LineSegments(tickGeometry, this.lineMaterial(color, opacity));
|
|
91
|
+
tickLines.renderOrder = this.renderOrder;
|
|
92
|
+
this.add(tickLines);
|
|
93
|
+
|
|
94
|
+
this.traverse((object) => object.layers.set(EDITOR_LAYER));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private lineMaterial(color: THREE.Color, opacity: number): THREE.LineBasicMaterial {
|
|
98
|
+
return new THREE.LineBasicMaterial({
|
|
99
|
+
color,
|
|
100
|
+
depthTest: false,
|
|
101
|
+
transparent: true,
|
|
102
|
+
opacity,
|
|
103
|
+
toneMapped: false,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private clearVisuals(): void {
|
|
108
|
+
for (const child of [...this.children]) {
|
|
109
|
+
child.removeFromParent();
|
|
110
|
+
const line = child as THREE.LineSegments;
|
|
111
|
+
line.geometry?.dispose();
|
|
112
|
+
const materials = Array.isArray(line.material) ? line.material : [line.material];
|
|
113
|
+
for (const material of materials) material?.dispose();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE THREE VIEWPORT'S PALETTE ENTRIES — tool and space modes, grid, helpers,
|
|
3
|
+
* stats, snap, shading, the view presets, Frame and the camera verbs. They are
|
|
4
|
+
* the active three stage's, published as a palette contribution while that
|
|
5
|
+
* stage is bound (`components/stage-keyboard.tsx`), and they act on that
|
|
6
|
+
* stage's own store: the same stage its keys drive (`viewport-hotkeys.ts`).
|
|
7
|
+
*/
|
|
8
|
+
import type { ActionContribution, ContributedAction } from '@volter/editor-sdk/chrome';
|
|
9
|
+
import {
|
|
10
|
+
alignCameraToViewport,
|
|
11
|
+
cameraAuthoringPresentation,
|
|
12
|
+
currentCameraAuthoringSubject,
|
|
13
|
+
leaveCameraView,
|
|
14
|
+
pilotCamera,
|
|
15
|
+
subscribeCameraAuthoring,
|
|
16
|
+
toggleCameraPreviewPin,
|
|
17
|
+
viewThroughCamera,
|
|
18
|
+
} from './camera-authoring';
|
|
19
|
+
import { registerContributedActions } from '@volter/editor-sdk/kit/chrome-registry';
|
|
20
|
+
import type { EditorShellStore } from './editor-shell-store';
|
|
21
|
+
import { object3DDocumentSession } from './authoring/object3d-document-session-registry';
|
|
22
|
+
import { requestTransformMode } from '@volter/editor-sdk/kit/transform-mode-request';
|
|
23
|
+
import { activeWorkspaceDocumentId } from '@volter/editor-sdk/kit/workspace-document-registry';
|
|
24
|
+
import {
|
|
25
|
+
setViewGridVisible,
|
|
26
|
+
viewGridVisible,
|
|
27
|
+
viewPresentationBinding,
|
|
28
|
+
} from '@volter/editor-sdk/kit/viewport-presentation';
|
|
29
|
+
|
|
30
|
+
function cameraActions(): ContributedAction[] {
|
|
31
|
+
const presentation = cameraAuthoringPresentation();
|
|
32
|
+
if (presentation.view) {
|
|
33
|
+
return [{ id: 'camera.exitView', label: 'Exit Camera View', execute: leaveCameraView }];
|
|
34
|
+
}
|
|
35
|
+
const subject = currentCameraAuthoringSubject();
|
|
36
|
+
if (!subject) return [];
|
|
37
|
+
return [
|
|
38
|
+
{
|
|
39
|
+
id: 'camera.viewThrough',
|
|
40
|
+
label: `View Through ${subject.name}`,
|
|
41
|
+
execute: () => viewThroughCamera(subject),
|
|
42
|
+
},
|
|
43
|
+
...(subject.canAuthorPose
|
|
44
|
+
? [
|
|
45
|
+
{ id: 'camera.pilot', label: `Pilot ${subject.name}`, execute: () => pilotCamera(subject) },
|
|
46
|
+
{
|
|
47
|
+
id: 'camera.alignToView',
|
|
48
|
+
label: `Align ${subject.name} to Current View`,
|
|
49
|
+
execute: () => alignCameraToViewport(subject),
|
|
50
|
+
},
|
|
51
|
+
]
|
|
52
|
+
: []),
|
|
53
|
+
{
|
|
54
|
+
id: 'camera.pinPreview',
|
|
55
|
+
label: presentation.previewPinned ? 'Unpin Camera Preview' : 'Pin Camera Preview',
|
|
56
|
+
execute: toggleCameraPreviewPin,
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function activeObject3DDocumentSession() {
|
|
62
|
+
const documentId = activeWorkspaceDocumentId();
|
|
63
|
+
return documentId ? object3DDocumentSession(documentId) : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** A document stage paints the mode its SESSION holds; the store's is only what the menu shows
|
|
67
|
+
* where there is no session (`set-shading-mode` and the shading menu route the same way). */
|
|
68
|
+
function setShading(store: EditorShellStore, mode: Parameters<EditorShellStore['setShadingMode']>[0]): void {
|
|
69
|
+
const session = activeObject3DDocumentSession();
|
|
70
|
+
if (session) session.setMode(mode);
|
|
71
|
+
else store.setShadingMode(mode);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function registerViewportActions(store: EditorShellStore): () => void {
|
|
75
|
+
const fixed: ContributedAction[] = [
|
|
76
|
+
{ id: 'mode.select', label: 'Select Tool (no gizmo)', shortcut: 'transform.select', execute: () => requestTransformMode(store.shell, 'select') },
|
|
77
|
+
{ id: 'mode.combined', label: 'Transform Mode (all handles)', shortcut: 'transform.combined', execute: () => requestTransformMode(store.shell, 'combined') },
|
|
78
|
+
{ id: 'mode.translate', label: 'Translate Mode', shortcut: 'transform.translate', execute: () => requestTransformMode(store.shell, 'translate') },
|
|
79
|
+
{ id: 'mode.rotate', label: 'Rotate Mode', shortcut: 'transform.rotate', execute: () => requestTransformMode(store.shell, 'rotate') },
|
|
80
|
+
{ id: 'mode.scale', label: 'Scale Mode', shortcut: 'transform.scale', execute: () => requestTransformMode(store.shell, 'scale') },
|
|
81
|
+
{ id: 'mode.world', label: 'World Space', execute: () => store.shell.setTransformSpace('world') },
|
|
82
|
+
{ id: 'mode.local', label: 'Local Space', execute: () => store.shell.setTransformSpace('local') },
|
|
83
|
+
{
|
|
84
|
+
id: 'toggle.grid',
|
|
85
|
+
label: 'Toggle Grid',
|
|
86
|
+
execute: () => {
|
|
87
|
+
// The ACTIVE view's grid switch, one per view (`kit/viewport-presentation`).
|
|
88
|
+
const viewId = activeWorkspaceDocumentId();
|
|
89
|
+
if (viewId && viewPresentationBinding(viewId)) setViewGridVisible(viewId, !viewGridVisible(viewId));
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
{ id: 'toggle.helpers', label: 'Toggle Helpers', execute: () => store.shell.toggleHelpers() },
|
|
93
|
+
{ id: 'toggle.stats', label: 'Toggle Stats Overlay', execute: () => store.toggleStats() },
|
|
94
|
+
{ id: 'toggle.snap', label: 'Toggle Snap', shortcut: 'viewport.toggleSnap', execute: () => store.shell.toggleSnap() },
|
|
95
|
+
{ id: 'toggle.surface-snap', label: 'Toggle Surface Snap', execute: () => store.shell.toggleSnapToSurface() },
|
|
96
|
+
{ id: 'shading.solid', label: 'Material Shading', execute: () => setShading(store, 'solid') },
|
|
97
|
+
{ id: 'shading.clay', label: 'Solid Shading', execute: () => setShading(store, 'clay') },
|
|
98
|
+
{ id: 'shading.wireframe', label: 'Wireframe Shading', execute: () => setShading(store, 'wireframe') },
|
|
99
|
+
{ id: 'shading.unlit', label: 'Unlit Shading', execute: () => setShading(store, 'unlit') },
|
|
100
|
+
{ id: 'shading.normals', label: 'Normal Shading', execute: () => setShading(store, 'normals') },
|
|
101
|
+
{ id: 'shading.overdraw', label: 'Overdraw Shading', execute: () => setShading(store, 'overdraw') },
|
|
102
|
+
{ id: 'view.top', label: 'Top View', shortcut: 'view.top', execute: () => store.shell.setViewPreset('top') },
|
|
103
|
+
{ id: 'view.front', label: 'Front View', shortcut: 'view.front', execute: () => store.shell.setViewPreset('front') },
|
|
104
|
+
{ id: 'view.right', label: 'Right View', shortcut: 'view.right', execute: () => store.shell.setViewPreset('right') },
|
|
105
|
+
{ id: 'view.bottom', label: 'Bottom View', shortcut: 'view.bottom', execute: () => store.shell.setViewPreset('bottom') },
|
|
106
|
+
{ id: 'view.back', label: 'Back View', shortcut: 'view.back', execute: () => store.shell.setViewPreset('back') },
|
|
107
|
+
{ id: 'view.left', label: 'Left View', shortcut: 'view.left', execute: () => store.shell.setViewPreset('left') },
|
|
108
|
+
{ id: 'view.orbitLeft', label: 'Orbit Left', shortcut: 'view.orbitLeft', execute: () => store.shell.stepView('orbit-left') },
|
|
109
|
+
{ id: 'view.orbitRight', label: 'Orbit Right', shortcut: 'view.orbitRight', execute: () => store.shell.stepView('orbit-right') },
|
|
110
|
+
{ id: 'view.orbitUp', label: 'Orbit Up', shortcut: 'view.orbitUp', execute: () => store.shell.stepView('orbit-up') },
|
|
111
|
+
{ id: 'view.orbitDown', label: 'Orbit Down', shortcut: 'view.orbitDown', execute: () => store.shell.stepView('orbit-down') },
|
|
112
|
+
{ id: 'view.opposite', label: 'Opposite View', shortcut: 'view.opposite', execute: () => store.shell.stepView('opposite') },
|
|
113
|
+
{ id: 'view.rollLeft', label: 'Roll Left', shortcut: 'view.rollLeft', execute: () => store.shell.stepView('roll-left') },
|
|
114
|
+
{ id: 'view.rollRight', label: 'Roll Right', shortcut: 'view.rollRight', execute: () => store.shell.stepView('roll-right') },
|
|
115
|
+
{ id: 'view.all', label: 'Frame All', shortcut: 'view.all', execute: () => store.shell.frameAll() },
|
|
116
|
+
{ id: 'view.zoomIn', label: 'Zoom In', shortcut: 'view.zoomIn', execute: () => store.shell.zoomView(1) },
|
|
117
|
+
{ id: 'view.zoomOut', label: 'Zoom Out', shortcut: 'view.zoomOut', execute: () => store.shell.zoomView(-1) },
|
|
118
|
+
{ id: 'view.perspective', label: 'Perspective View', shortcut: 'view.perspective', execute: () => store.shell.setViewPreset('perspective') },
|
|
119
|
+
{ id: 'view.projection', label: 'Perspective/Orthographic', shortcut: 'view.projection', execute: () => store.shell.toggleProjection() },
|
|
120
|
+
{ id: 'view.camera', label: 'Camera View', shortcut: 'view.camera', execute: () => store.shell.toggleCameraView() },
|
|
121
|
+
{ id: 'focus.selected', label: 'Focus Selected', shortcut: 'viewport.frameSelection', execute: () => store.shell.focusOnSelection() },
|
|
122
|
+
];
|
|
123
|
+
const contribution: ActionContribution = {
|
|
124
|
+
actions: () => [...fixed, ...cameraActions()],
|
|
125
|
+
subscribe: subscribeCameraAuthoring,
|
|
126
|
+
};
|
|
127
|
+
return registerContributedActions(contribution);
|
|
128
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
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
|
+
*/
|
|
42
|
+
|
|
43
|
+
import type { AuthoringAdapter } from '@volter/editor-project/adapter';
|
|
44
|
+
import { adapterOnlyToolOwner, type ViewportToolOwner } from '@volter/editor-sdk/kit/authoring/viewport-tool-owner';
|
|
45
|
+
|
|
46
|
+
export { adapterOnlyToolOwner, type ViewportToolOwner };
|
|
47
|
+
import type * as THREE from 'three';
|
|
48
|
+
import { makeNoAuthoringAdapter } from '@volter/editor-sdk/kit/authoring/no-authoring-adapter';
|
|
49
|
+
import type { ShellStore } from '@volter/editor-sdk/kit/shell-store';
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
export interface ViewportAuthoringPolicy {
|
|
53
|
+
/** The adapter this viewport drives when its host injected none. */
|
|
54
|
+
activeAuthoring(store: ShellStore): AuthoringAdapter;
|
|
55
|
+
/**
|
|
56
|
+
* The adapter a selection SCOPE is keyed on — the Hierarchy's own resolution,
|
|
57
|
+
* so a viewport drill-in and the panel breadcrumb name the same scope.
|
|
58
|
+
*/
|
|
59
|
+
panelAuthoring(store: ShellStore): AuthoringAdapter;
|
|
60
|
+
/** Topmost-first hit test across every visible, pick-unlocked layer. */
|
|
61
|
+
pick(
|
|
62
|
+
store: ShellStore,
|
|
63
|
+
clientX: number,
|
|
64
|
+
clientY: number,
|
|
65
|
+
intent: 'normal' | 'deep',
|
|
66
|
+
): string | null;
|
|
67
|
+
/** Which native root the selection resolves to, or `null` for empty,
|
|
68
|
+
* organizational and cross-root selections. */
|
|
69
|
+
toolOwner(adapter: AuthoringAdapter, selectedIds: Iterable<string>): ViewportToolOwner | null;
|
|
70
|
+
/** Is that owner's surface actually PAINTED in this viewport right now? */
|
|
71
|
+
toolOwnerPainted(store: ShellStore, owner: ViewportToolOwner | null): boolean;
|
|
72
|
+
/** Is a THREE stage painted here at all, selection or not — the gate for
|
|
73
|
+
* the grid and axis lines (Blender's floor is persistent; only the gizmos
|
|
74
|
+
* need an owner). */
|
|
75
|
+
threeSurfaceShowing(store: ShellStore, adapter: AuthoringAdapter): boolean;
|
|
76
|
+
/** The one three world rendered here, for the world-hidden eye. */
|
|
77
|
+
threeViewportRootId(store: ShellStore): string | null;
|
|
78
|
+
/** Apply the eye to every entity object (a no-op unless that root is hidden). */
|
|
79
|
+
applyRootHiddenVisibility(
|
|
80
|
+
objectMap: ReadonlyMap<string, THREE.Object3D>,
|
|
81
|
+
threeRootId: string | null,
|
|
82
|
+
): void;
|
|
83
|
+
/** The scene-level half of the eye: background, fog, IBL, env objects. */
|
|
84
|
+
suppressRootEnvironment(scene: THREE.Scene): void;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
/** Memoized per store, because the viewport calls `activeAuthoring` per gesture
|
|
89
|
+
* and adapter IDENTITY keys the selection scope and the handle cache. Mirrors
|
|
90
|
+
* `authoring/active-adapter.ts`'s own per-store memo. */
|
|
91
|
+
const _noAuthoringByStore = new WeakMap<ShellStore, AuthoringAdapter>();
|
|
92
|
+
|
|
93
|
+
export const DEFAULT_VIEWPORT_AUTHORING_POLICY: ViewportAuthoringPolicy = {
|
|
94
|
+
activeAuthoring(store) {
|
|
95
|
+
let adapter = _noAuthoringByStore.get(store);
|
|
96
|
+
if (!adapter) {
|
|
97
|
+
adapter = makeNoAuthoringAdapter(store, 'No authoring adapter');
|
|
98
|
+
_noAuthoringByStore.set(store, adapter);
|
|
99
|
+
}
|
|
100
|
+
return adapter;
|
|
101
|
+
},
|
|
102
|
+
panelAuthoring(store) {
|
|
103
|
+
// No shell means no shared panels to agree with.
|
|
104
|
+
return DEFAULT_VIEWPORT_AUTHORING_POLICY.activeAuthoring(store);
|
|
105
|
+
},
|
|
106
|
+
pick() {
|
|
107
|
+
// No shell means no layer stack to walk. A host that mounts this viewport
|
|
108
|
+
// for one document supplies its own hit test (`EditorViewportOptions.pick`),
|
|
109
|
+
// which is what the Asset Lab document does.
|
|
110
|
+
return null;
|
|
111
|
+
},
|
|
112
|
+
toolOwner: adapterOnlyToolOwner,
|
|
113
|
+
toolOwnerPainted() {
|
|
114
|
+
// No world-group eyes exist without a composite, so nothing can be hidden.
|
|
115
|
+
return true;
|
|
116
|
+
},
|
|
117
|
+
threeSurfaceShowing(_store, adapter) {
|
|
118
|
+
return !adapter.rects && typeof adapter.hierarchy.object3D === 'function';
|
|
119
|
+
},
|
|
120
|
+
threeViewportRootId() {
|
|
121
|
+
return null;
|
|
122
|
+
},
|
|
123
|
+
applyRootHiddenVisibility() {
|
|
124
|
+
// Unreachable while `threeViewportRootId` is null — stated rather than
|
|
125
|
+
// implemented, so the no-shell story is one story.
|
|
126
|
+
},
|
|
127
|
+
suppressRootEnvironment() {},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
let _policy: ViewportAuthoringPolicy = DEFAULT_VIEWPORT_AUTHORING_POLICY;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Install the shell's composition. One owner, same rule as the store's
|
|
134
|
+
* `attachHistory`/`attachStatePersistence`: re-installing the SAME policy is a
|
|
135
|
+
* no-op (React strict-mode double-invokes the provider body), a DIFFERENT one
|
|
136
|
+
* throws rather than letting two shells disagree about what is under a pointer.
|
|
137
|
+
*/
|
|
138
|
+
export function installViewportAuthoringPolicy(policy: ViewportAuthoringPolicy): void {
|
|
139
|
+
if (_policy === policy) return;
|
|
140
|
+
if (_policy !== DEFAULT_VIEWPORT_AUTHORING_POLICY) {
|
|
141
|
+
throw new Error('A viewport authoring policy is already installed.');
|
|
142
|
+
}
|
|
143
|
+
_policy = policy;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** The installed policy, or the no-shell default. */
|
|
147
|
+
export function viewportAuthoringPolicy(): ViewportAuthoringPolicy {
|
|
148
|
+
return _policy;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Test seam: drop the installed policy. */
|
|
152
|
+
export function __resetViewportAuthoringPolicyForTest(): void {
|
|
153
|
+
_policy = DEFAULT_VIEWPORT_AUTHORING_POLICY;
|
|
154
|
+
}
|