@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
package/src/kit/scene-framing.ts
CHANGED
|
@@ -1,64 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* What the editor camera should look at when it adopts a world nobody authored
|
|
3
|
-
* here — the two answers behind `EditorViewport.focusOnScene`.
|
|
4
|
-
*
|
|
5
|
-
* ## The measured defect
|
|
6
|
-
*
|
|
7
|
-
* Auto-frame framed the game's ENTIRE content AABB. Measured on a Cuberun
|
|
8
|
-
* mount: a 2,000-radius BackSide skybox sphere, a sun mesh at `z = -2000` and a
|
|
9
|
-
* hyperspace prop at `z ≈ -5365` stretched that AABB to ~8,400 units deep, so
|
|
10
|
-
* the fit distance landed the camera 12,870 units away from a ~1,000-unit
|
|
11
|
-
* gameplay corridor. 93% of the Scene viewport was black, and the one visible
|
|
12
|
-
* thing was the OUTSIDE of the skybox. The same world framed at working
|
|
13
|
-
* distance is vivid and correct. (Descent measured the same looseness at a
|
|
14
|
-
* smaller scale: a camera at ~2,580 for an ~800-unit world.)
|
|
15
|
-
*
|
|
16
|
-
* The union is the wrong instrument because a backdrop is not content the
|
|
17
|
-
* reader is looking FOR — it is content that exists to be far away. One prop
|
|
18
|
-
* 5,000 units out costs the whole framing, and no amount of care in the fit
|
|
19
|
-
* math can recover it.
|
|
20
|
-
*
|
|
21
|
-
* ## The two answers, in order
|
|
22
|
-
*
|
|
23
|
-
* 1. {@link viewFromGameCamera} — a game's author already decided the
|
|
24
|
-
* interesting view, so seeding the orbit camera from the world matrix of a
|
|
25
|
-
* camera the game itself placed opens the Scene tab where the game looks.
|
|
26
|
-
* {@link pickGameCamera} decides which camera that is.
|
|
27
|
-
* 2. {@link frameableContentBounds} — the fallback for a game whose camera we
|
|
28
|
-
* cannot name. It trims outliers off the union before framing.
|
|
29
|
-
*
|
|
30
|
-
* ## Why gap-trimming and not a percentile
|
|
31
|
-
*
|
|
32
|
-
* A blanket percentile (frame the inner 90% of content) would tighten the
|
|
33
|
-
* framing of EVERY world, including the uniform ones that are framed correctly
|
|
34
|
-
* today. This trims only across a spatial GAP: a face of the bounding box moves
|
|
35
|
-
* inward only when a small share of boxes sits beyond a void spanning a large
|
|
36
|
-
* fraction of the current diagonal. A uniformly-filled world has no such gap,
|
|
37
|
-
* so it comes back byte-identical to the plain union — that identity is the
|
|
38
|
-
* first property the tests pin.
|
|
39
|
-
*/
|
|
1
|
+
/** Frame adopted worlds from useful content bounds: trim enclosing backdrops and isolated distant clusters while retaining uniformly distributed content. */
|
|
40
2
|
|
|
41
|
-
import { isEditorOwnedObject } from '@volter/editor-threejs/viewport/editor-layers';
|
|
42
3
|
import * as THREE from 'three';
|
|
43
4
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
/** A gap must span this share of the current content diagonal to be a gap. */
|
|
49
|
-
minGapFraction?: number;
|
|
50
|
-
/** Below this many boxes there is no distribution to judge; nothing trims. */
|
|
51
|
-
minSampleSize?: number;
|
|
52
|
-
/** Cap on trim passes, so cost stays bounded on a huge world. */
|
|
53
|
-
maxPasses?: number;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const DEFAULTS: Required<FrameableBoundsOptions> = {
|
|
57
|
-
maxOutlierFraction: 0.1,
|
|
58
|
-
minGapFraction: 0.2,
|
|
59
|
-
minSampleSize: 8,
|
|
60
|
-
maxPasses: 8,
|
|
61
|
-
};
|
|
5
|
+
const MAX_OUTLIER_FRACTION = 0.1;
|
|
6
|
+
const MIN_GAP_FRACTION = 0.2;
|
|
7
|
+
const MIN_SAMPLE_SIZE = 8;
|
|
8
|
+
const MAX_TRIM_PASSES = 8;
|
|
62
9
|
|
|
63
10
|
/** Orbit pivot distance when there is no content to measure one from. */
|
|
64
11
|
const EMPTY_CONTENT_PIVOT_DISTANCE = 10;
|
|
@@ -122,25 +69,23 @@ function widestOuterGap(
|
|
|
122
69
|
*/
|
|
123
70
|
export function frameableContentBounds(
|
|
124
71
|
boxes: readonly THREE.Box3[],
|
|
125
|
-
options: FrameableBoundsOptions = {},
|
|
126
72
|
): THREE.Box3 {
|
|
127
|
-
const opts = { ...DEFAULTS, ...options };
|
|
128
73
|
const content = boxes.filter((b) => !b.isEmpty());
|
|
129
74
|
const all = content.map((_, i) => i);
|
|
130
75
|
const full = unionOf(content, all);
|
|
131
|
-
if (content.length <
|
|
76
|
+
if (content.length < MIN_SAMPLE_SIZE) return full;
|
|
132
77
|
|
|
133
|
-
let budget = Math.floor(content.length *
|
|
78
|
+
let budget = Math.floor(content.length * MAX_OUTLIER_FRACTION);
|
|
134
79
|
if (budget < 1) return full;
|
|
135
80
|
|
|
136
81
|
const kept = new Set(all);
|
|
137
82
|
let bounds = full;
|
|
138
83
|
const size = new THREE.Vector3();
|
|
139
|
-
for (let pass = 0; pass <
|
|
84
|
+
for (let pass = 0; pass < MAX_TRIM_PASSES && budget > 0; pass++) {
|
|
140
85
|
const diagonal = bounds.getSize(size).length();
|
|
141
86
|
if (!(diagonal > 0)) break;
|
|
142
87
|
const cut = widestOuterGap(content, kept, budget);
|
|
143
|
-
if (!cut || cut.gap <=
|
|
88
|
+
if (!cut || cut.gap <= MIN_GAP_FRACTION * diagonal) break;
|
|
144
89
|
for (const i of cut.discard) kept.delete(i);
|
|
145
90
|
budget -= cut.discard.length;
|
|
146
91
|
bounds = unionOf(content, kept);
|
|
@@ -154,21 +99,7 @@ export interface SeededView {
|
|
|
154
99
|
target: THREE.Vector3;
|
|
155
100
|
}
|
|
156
101
|
|
|
157
|
-
/**
|
|
158
|
-
* The editor orbit pose that reproduces `camera`'s own view.
|
|
159
|
-
*
|
|
160
|
-
* The eye is the game camera's world position and the view direction is its
|
|
161
|
-
* own; the only thing invented is the orbit PIVOT, which is placed at the depth
|
|
162
|
-
* of the content along that view direction — the point the reader is already
|
|
163
|
-
* looking at, so the first orbit drag turns around the world rather than
|
|
164
|
-
* around a point behind their head.
|
|
165
|
-
*
|
|
166
|
-
* Returns `null` when that pivot cannot be placed: the world sits BEHIND the
|
|
167
|
-
* camera (a view showing none of the game is not worth adopting), or it sits
|
|
168
|
-
* ON it, which leaves an orbit pivot at the eye — measured as a target
|
|
169
|
-
* 4.6e-14 units out, an orbit control that can only spin in place. Both fall
|
|
170
|
-
* back to {@link frameableContentBounds}.
|
|
171
|
-
*/
|
|
102
|
+
/** Adopt a camera’s world pose with an orbit pivot at visible content depth. Fall back to frameableContentBounds if the content is behind or at the eye. */
|
|
172
103
|
export function viewFromGameCamera(camera: THREE.Camera, content: THREE.Box3): SeededView | null {
|
|
173
104
|
const forward = camera.getWorldDirection(new THREE.Vector3());
|
|
174
105
|
const position = new THREE.Vector3().setFromMatrixPosition(camera.matrixWorld);
|
|
@@ -223,43 +154,7 @@ function isVisibleInWorld(object: THREE.Object3D): boolean {
|
|
|
223
154
|
return true;
|
|
224
155
|
}
|
|
225
156
|
|
|
226
|
-
/**
|
|
227
|
-
* Does a view posed HERE show the world, or a surface pressed against the lens?
|
|
228
|
-
*
|
|
229
|
-
* The measured defect: a game's own camera is placed for the game's own moment
|
|
230
|
-
* — inside a cockpit, behind a wall it is about to drive through, under
|
|
231
|
-
* terrain that has not settled — and adopting it as the editor's opening view
|
|
232
|
-
* can hand the reader a full-bleed rectangle of rock. The view is a perfectly
|
|
233
|
-
* good gameplay camera and a useless first look, and nothing in the pose
|
|
234
|
-
* itself says which.
|
|
235
|
-
*
|
|
236
|
-
* The instrument is geometric, not pixels: nine rays through the frustum, and
|
|
237
|
-
* a view SHOWS THE WORLD when at least one of them either escapes (sky,
|
|
238
|
-
* backdrop, open space) or reaches something farther than
|
|
239
|
-
* {@link NEAR_FIELD_FRACTION} of what this view claims to be looking at. The
|
|
240
|
-
* threshold is relative to the view's own pivot distance, so it needs no
|
|
241
|
-
* world-scale constant and reads the same on a 10-unit room and a 10,000-unit
|
|
242
|
-
* canyon.
|
|
243
|
-
*
|
|
244
|
-
* Deliberately conservative: EVERY sample must be blocked in the near field
|
|
245
|
-
* before the view is refused, so an ordinary view with a prop in the middle or
|
|
246
|
-
* ground filling its lower half still passes. A refusal here costs the reader
|
|
247
|
-
* only the game's own camera — the caller falls back to
|
|
248
|
-
* {@link frameableContentBounds}, which always shows something.
|
|
249
|
-
*
|
|
250
|
-
* Three's raycast honours `material.side` exactly as the renderer does, and
|
|
251
|
-
* that alignment is the reason this instrument is honest rather than merely
|
|
252
|
-
* cheap: a camera sealed inside a front-faced solid is invisible to BOTH — the
|
|
253
|
-
* reader sees through the shell, the rays escape through it, and the view is
|
|
254
|
-
* correctly not called blocked. The failure this catches is the one that is
|
|
255
|
-
* actually on screen: front faces, filling the frame, right at the lens.
|
|
256
|
-
*
|
|
257
|
-
* (The screenshot lane measures the same failure from the other side, on
|
|
258
|
-
* PIXELS, in `composite-screenshot.ts`. That instrument needs a drawn frame
|
|
259
|
-
* and a GPU readback, and it cannot tell a legitimately flat picture from a
|
|
260
|
-
* blocked one — which is why the decision to adopt a pose is made here, before
|
|
261
|
-
* anything is drawn.)
|
|
262
|
-
*/
|
|
157
|
+
/** Check nine geometric frustum rays before adopting a camera pose, rejecting views blocked by a surface near the lens. */
|
|
263
158
|
export function seededViewShowsWorld(
|
|
264
159
|
camera: THREE.Camera,
|
|
265
160
|
content: readonly THREE.Object3D[],
|
|
@@ -277,39 +172,3 @@ export function seededViewShowsWorld(
|
|
|
277
172
|
return false;
|
|
278
173
|
}
|
|
279
174
|
|
|
280
|
-
/** Every camera in the game's own scene, in scene order, editor furniture and
|
|
281
|
-
* its subtrees excluded. */
|
|
282
|
-
function gameCameras(node: THREE.Object3D, out: THREE.Camera[] = []): THREE.Camera[] {
|
|
283
|
-
if (isEditorOwnedObject(node)) return out;
|
|
284
|
-
if ((node as THREE.Camera).isCamera) out.push(node as THREE.Camera);
|
|
285
|
-
for (const child of node.children) gameCameras(child, out);
|
|
286
|
-
return out;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* The camera whose view an ingest mount should adopt — one the game itself
|
|
291
|
-
* CREATED AND PLACED, which means one that is in the game's own scene graph.
|
|
292
|
-
*
|
|
293
|
-
* The capture's own camera (the one the first `render(scene, camera)` used) is
|
|
294
|
-
* preferred WHEN it is one of those, and is otherwise ignored, because on the
|
|
295
|
-
* first rendered frame it can be a reconciler's placeholder rather than the
|
|
296
|
-
* author's camera. Measured on Cuberun: the captured camera came back with an
|
|
297
|
-
* identity world matrix at the origin while the game's own
|
|
298
|
-
* `<PerspectiveCamera position={[0, 10, -10]}>` was sitting in the scene at
|
|
299
|
-
* (0, 8, 3.5) looking down the corridor — seeding from the capture opened on a
|
|
300
|
-
* flat wall of ground plane, seeding from the scene's camera opened on the
|
|
301
|
-
* game.
|
|
302
|
-
*
|
|
303
|
-
* Editor furniture is never adopted: the capture trap sits on the shared
|
|
304
|
-
* `WebGLRenderer.prototype`, so the editor's own cameras are the one thing
|
|
305
|
-
* that must never come back from here.
|
|
306
|
-
*/
|
|
307
|
-
export function pickGameCamera(
|
|
308
|
-
captured: THREE.Camera | null | undefined,
|
|
309
|
-
scene: THREE.Object3D,
|
|
310
|
-
): THREE.Camera | null {
|
|
311
|
-
const cameras = gameCameras(scene);
|
|
312
|
-
if (cameras.length === 0) return null;
|
|
313
|
-
if (captured && cameras.includes(captured)) return captured;
|
|
314
|
-
return cameras[0] as THREE.Camera;
|
|
315
|
-
}
|
|
@@ -1,51 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
/** Neutralize scene fog while rendering the authoring view because its camera can sit outside the project camera’s intended fog range. Retain the fog object and shader variant. */
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
* Why the Scene viewport draws a world's fog OUT.
|
|
5
|
-
*
|
|
6
|
-
* ## The measured defect
|
|
7
|
-
*
|
|
8
|
-
* Fog is calibrated in metres from THE GAME'S OWN CAMERA. The racing-game
|
|
9
|
-
* mount declares `<fog args={['white', 0, 500]}>`, which is exactly right at
|
|
10
|
-
* its chase camera 20 units behind the car and meaningless anywhere else: its
|
|
11
|
-
* world is ~800 units across, so the editor framing that shows the whole track
|
|
12
|
-
* sits ~700 units back, and every visible surface is past the fog's far plane.
|
|
13
|
-
* Measured on that mount (2026-08-15): the framed Scene view is a featureless
|
|
14
|
-
* white blob — the track, the canyon and the horizon are all the same white.
|
|
15
|
-
*
|
|
16
|
-
* The Scene camera is not the game's camera. It is placed by framing, by a
|
|
17
|
-
* seed, or by the reader's own orbit, at whatever distance the world needs —
|
|
18
|
-
* so a distance-calibrated effect authored for another camera is not a look,
|
|
19
|
-
* it is an erasure.
|
|
20
|
-
*
|
|
21
|
-
* ## Why a view policy and not fog-aware framing
|
|
22
|
-
*
|
|
23
|
-
* The alternative was to have the framing respect the fog range — pick a
|
|
24
|
-
* distance inside it. It cannot work, and the numbers say so rather than an
|
|
25
|
-
* argument: this world's content diagonal is ~1,100 units against a fog far
|
|
26
|
-
* plane of 500, so no distance both shows the track and stays readable. Any
|
|
27
|
-
* fog-aware framing would have to stop showing the world to keep the fog,
|
|
28
|
-
* which is backwards — the first look exists to show the world.
|
|
29
|
-
*
|
|
30
|
-
* So the fog goes, as a VIEW POLICY, in the same class as the shading modes:
|
|
31
|
-
* presentation-only, restored before anything else can observe it, and applied
|
|
32
|
-
* only to the editor's own draw. This is also the established answer in the
|
|
33
|
-
* genre — Unity's Scene view renders fog OFF by default and offers it as a
|
|
34
|
-
* scene-view effect toggle, for exactly this reason.
|
|
35
|
-
*
|
|
36
|
-
* The game's own look is not touched anywhere it belongs: the Game tab, play
|
|
37
|
-
* mode and every exported build render the game's camera through the game's
|
|
38
|
-
* fog. Only the authoring camera is exempted, and there is deliberately no
|
|
39
|
-
* toggle for it — a Scene view that can be washed white by a setting is a
|
|
40
|
-
* fault the reader has to diagnose, and nothing here is worth that.
|
|
41
|
-
*
|
|
42
|
-
* ## Why the parameters and not `scene.fog = null`
|
|
43
|
-
*
|
|
44
|
-
* `!!scene.fog` is part of three's shader-program cache key, so nulling it for
|
|
45
|
-
* one draw and restoring it for the next compiles and keeps a SECOND program
|
|
46
|
-
* variant for every material in the world. Neutralising the fog's own numbers
|
|
47
|
-
* leaves the program identical and the fog term at zero.
|
|
48
|
-
*/
|
|
3
|
+
import type * as THREE from 'three';
|
|
49
4
|
|
|
50
5
|
/** Far enough that no real depth reaches it; `near < far` keeps the linear
|
|
51
6
|
* fog's `smoothstep(near, far, depth)` well-defined. */
|
|
@@ -13,11 +13,9 @@ export interface SpatialHandleBinding {
|
|
|
13
13
|
readonly handle: SpatialDragHandle;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export
|
|
17
|
-
readonly isSpatialHandleMesh: true;
|
|
18
|
-
}
|
|
16
|
+
export type SpatialHandleMesh = THREE.Mesh<THREE.SphereGeometry, THREE.MeshBasicMaterial>;
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
interface SpatialHandleVisualSet {
|
|
21
19
|
readonly roots: THREE.Object3D[];
|
|
22
20
|
readonly handles: SpatialHandleMesh[];
|
|
23
21
|
}
|
|
@@ -47,10 +45,9 @@ function addLine(
|
|
|
47
45
|
material: THREE.LineBasicMaterial,
|
|
48
46
|
closed = false,
|
|
49
47
|
): void {
|
|
50
|
-
const linePoints = closed
|
|
48
|
+
const linePoints = closed ? [...points, points[0]!] : [...points];
|
|
51
49
|
const line = new THREE.Line(new THREE.BufferGeometry().setFromPoints(linePoints), material);
|
|
52
50
|
line.renderOrder = 997;
|
|
53
|
-
line.layers.set(EDITOR_LAYER);
|
|
54
51
|
if (material instanceof THREE.LineDashedMaterial) line.computeLineDistances();
|
|
55
52
|
root.add(line);
|
|
56
53
|
}
|
|
@@ -60,8 +57,8 @@ function circlePoints(
|
|
|
60
57
|
radius: number,
|
|
61
58
|
axisA: THREE.Vector3,
|
|
62
59
|
axisB: THREE.Vector3,
|
|
63
|
-
segments = 64,
|
|
64
60
|
): THREE.Vector3[] {
|
|
61
|
+
const segments = 64;
|
|
65
62
|
const points: THREE.Vector3[] = [];
|
|
66
63
|
for (let i = 0; i < segments; i++) {
|
|
67
64
|
const angle = (i / segments) * Math.PI * 2;
|
|
@@ -244,7 +241,6 @@ function guideObject(guide: SpatialHandleGuide): THREE.Object3D {
|
|
|
244
241
|
: guide.kind === 'box'
|
|
245
242
|
? boxGuide(guide)
|
|
246
243
|
: capsuleGuide(guide);
|
|
247
|
-
root.layers.set(EDITOR_LAYER);
|
|
248
244
|
root.traverse((child) => child.layers.set(EDITOR_LAYER));
|
|
249
245
|
return root;
|
|
250
246
|
}
|
|
@@ -256,8 +252,7 @@ function handleMesh(nodeId: string, handle: SpatialDragHandle): SpatialHandleMes
|
|
|
256
252
|
transparent: true,
|
|
257
253
|
opacity: handle.writable ? 0.92 : 0.35,
|
|
258
254
|
});
|
|
259
|
-
const mesh = new THREE.Mesh(HANDLE_GEOMETRY, material)
|
|
260
|
-
Object.defineProperty(mesh, 'isSpatialHandleMesh', { value: true });
|
|
255
|
+
const mesh = new THREE.Mesh(HANDLE_GEOMETRY, material);
|
|
261
256
|
mesh.position.copy(vector(handle.position));
|
|
262
257
|
mesh.renderOrder = 998;
|
|
263
258
|
mesh.layers.set(EDITOR_LAYER);
|
|
@@ -282,15 +277,15 @@ export function createSpatialHandleVisuals(
|
|
|
282
277
|
return { roots, handles };
|
|
283
278
|
}
|
|
284
279
|
|
|
285
|
-
export function spatialHandleBinding(mesh: SpatialHandleMesh): SpatialHandleBinding
|
|
286
|
-
return bindingByMesh.get(mesh)
|
|
280
|
+
export function spatialHandleBinding(mesh: SpatialHandleMesh): SpatialHandleBinding {
|
|
281
|
+
return bindingByMesh.get(mesh)!;
|
|
287
282
|
}
|
|
288
283
|
|
|
289
284
|
export function setSpatialHandleHovered(mesh: SpatialHandleMesh, hovered: boolean): void {
|
|
290
|
-
const binding =
|
|
291
|
-
const material = mesh.material
|
|
285
|
+
const binding = spatialHandleBinding(mesh);
|
|
286
|
+
const material = mesh.material;
|
|
292
287
|
material.color.set(
|
|
293
|
-
hovered && binding
|
|
288
|
+
hovered && binding.handle.writable ? '#ffffff' : binding.handle.color,
|
|
294
289
|
);
|
|
295
290
|
}
|
|
296
291
|
|
|
@@ -301,32 +296,28 @@ export function scaleSpatialHandle(mesh: SpatialHandleMesh, camera: THREE.Camera
|
|
|
301
296
|
|
|
302
297
|
function disposeRenderable(
|
|
303
298
|
child: THREE.Object3D,
|
|
304
|
-
disposedGeometries: Set<THREE.BufferGeometry>,
|
|
305
299
|
disposedMaterials: Set<THREE.Material>,
|
|
306
300
|
): void {
|
|
307
|
-
const renderable = child as THREE.
|
|
301
|
+
const renderable = child as THREE.Object3D & {
|
|
302
|
+
geometry?: THREE.BufferGeometry;
|
|
303
|
+
material?: THREE.LineBasicMaterial | THREE.MeshBasicMaterial;
|
|
304
|
+
};
|
|
308
305
|
const geometry = renderable.geometry;
|
|
309
|
-
if (geometry && geometry !== HANDLE_GEOMETRY
|
|
306
|
+
if (geometry && geometry !== HANDLE_GEOMETRY) {
|
|
310
307
|
geometry.dispose();
|
|
311
|
-
disposedGeometries.add(geometry);
|
|
312
308
|
}
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
? [renderable.material]
|
|
317
|
-
: [];
|
|
318
|
-
for (const material of materials) {
|
|
319
|
-
if (disposedMaterials.has(material)) continue;
|
|
309
|
+
const material = renderable.material;
|
|
310
|
+
if (material) {
|
|
311
|
+
if (disposedMaterials.has(material)) return;
|
|
320
312
|
material.dispose();
|
|
321
313
|
disposedMaterials.add(material);
|
|
322
314
|
}
|
|
323
315
|
}
|
|
324
316
|
|
|
325
317
|
export function disposeSpatialHandleVisuals(roots: readonly THREE.Object3D[]): void {
|
|
326
|
-
const disposedGeometries = new Set<THREE.BufferGeometry>();
|
|
327
318
|
const disposedMaterials = new Set<THREE.Material>();
|
|
328
319
|
for (const root of roots) {
|
|
329
320
|
root.removeFromParent();
|
|
330
|
-
root.traverse((child) => disposeRenderable(child,
|
|
321
|
+
root.traverse((child) => disposeRenderable(child, disposedMaterials));
|
|
331
322
|
}
|
|
332
323
|
}
|
|
@@ -1,33 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* three content, and how a story's preview thumbnail is keyed in an in-memory
|
|
4
|
-
* cache. Kept pure and off the React tree so every one of them is
|
|
5
|
-
* unit-testable with no renderer.
|
|
6
|
-
*
|
|
7
|
-
* Membership belongs to the CONSUMER, never to the registry, and it is
|
|
8
|
-
* DECLARED (`story-declared-medium.ts`). The `3D Components` board admits
|
|
9
|
-
* stories whose medium is `three`, the canvas board admits `canvas`, the
|
|
10
|
-
* DOM design board admits `dom` ({@link domStoryBoardMembers}). An
|
|
11
|
-
* undeclared story is a named gap and is not a candidate on any board —
|
|
12
|
-
* nothing here mounts to guess. The project's story registry stays
|
|
13
|
-
* deliberately unfiltered.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import type * as THREE from 'three';
|
|
17
|
-
import { getProjectStoryRegions } from '@volter/sdk/kit/stories/project-story-regions';
|
|
18
|
-
import { declaredStoryMedium, reportUndeclaredStoryMedium } from '@volter/sdk/kit/stories/story-declared-medium';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Whether a mounted story's root wrapper actually holds `three` content.
|
|
22
|
-
*
|
|
23
|
-
* A story that renders nothing (or renders only React fragments/null) commits
|
|
24
|
-
* successfully into the wrapper group and yields an EMPTY `Object3D` — a
|
|
25
|
-
* successful mount with nothing in it. A surface shows a story that rendered
|
|
26
|
-
* something; "the mount succeeded" alone is not the test.
|
|
27
|
-
*/
|
|
28
|
-
export function mountedStoryHasThreeContent(root: THREE.Object3D): boolean {
|
|
29
|
-
return root.children.length > 0;
|
|
30
|
-
}
|
|
1
|
+
import { editorHost } from '@volter/sdk/host';
|
|
2
|
+
import { declaredStoryMedium } from '@volter/sdk/stories/story-declared-medium';
|
|
31
3
|
|
|
32
4
|
/**
|
|
33
5
|
* The DOM design board's membership — every story DECLARED as `dom`.
|
|
@@ -36,7 +8,7 @@ export function mountedStoryHasThreeContent(root: THREE.Object3D): boolean {
|
|
|
36
8
|
* mounts: medium is a declaration, and a last-resort mount would make
|
|
37
9
|
* "residue 0" unfalsifiable (the orphan would land on this board).
|
|
38
10
|
*
|
|
39
|
-
* The
|
|
11
|
+
* The DOM design board renders each member through
|
|
40
12
|
* `react-dom`, so a three story landing on it renders `<group>`/`<primitive>`
|
|
41
13
|
* as unknown DOM tags. Declaration is what keeps those stories off this
|
|
42
14
|
* board; the project's story registry stays deliberately unfiltered.
|
|
@@ -49,7 +21,7 @@ export function domStoryBoardMembers<
|
|
|
49
21
|
readonly tags?: readonly string[];
|
|
50
22
|
},
|
|
51
23
|
>(stories: readonly T[]): T[] {
|
|
52
|
-
const regions =
|
|
24
|
+
const regions = editorHost().stories.regions();
|
|
53
25
|
const members: T[] = [];
|
|
54
26
|
for (const story of stories) {
|
|
55
27
|
const declared = declaredStoryMedium({ modulePath: story.modulePath, regions });
|
|
@@ -58,7 +30,7 @@ export function domStoryBoardMembers<
|
|
|
58
30
|
continue;
|
|
59
31
|
}
|
|
60
32
|
if (declared.via === 'undeclared' && story.modulePath) {
|
|
61
|
-
|
|
33
|
+
editorHost().stories.reportUndeclaredMedium(story.modulePath, declared.reason);
|
|
62
34
|
}
|
|
63
35
|
}
|
|
64
36
|
return members;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The Three set's canvases, rendered at a caller's scale
|
|
3
|
-
* (`@volter/sdk/
|
|
3
|
+
* (`@volter/sdk/canvas-frames`): the Scene viewport's presented frame
|
|
4
4
|
* or a fresh render of it, and an Object3D document's presented frame or an
|
|
5
5
|
* offscreen render of its subject. The editor-chrome photograph asks here, so a
|
|
6
6
|
* 2x capture of a viewport gets a 2x render rather than a 1x one stretched.
|
|
7
7
|
*/
|
|
8
|
-
import { registerCanvasRender } from '@volter/sdk/
|
|
8
|
+
import { presentedCanvasFrame, registerCanvasRender } from '@volter/sdk/canvas-frames';
|
|
9
9
|
import { allObject3DDocumentSessions } from './authoring/object3d-document-session-registry';
|
|
10
|
-
import { liveCanvasFrame } from '@volter/sdk/kit/live-canvas-frame';
|
|
11
10
|
import { threeStoreForHost } from './three-state';
|
|
12
11
|
|
|
13
12
|
async function imageOf(dataUrl: string): Promise<HTMLImageElement> {
|
|
@@ -22,7 +21,7 @@ export function registerThreeCanvasRender(): () => void {
|
|
|
22
21
|
const store = threeStoreForHost();
|
|
23
22
|
if (store && store.viewportCanvas() === canvas) {
|
|
24
23
|
return (async () => {
|
|
25
|
-
const presented = await
|
|
24
|
+
const presented = await presentedCanvasFrame(canvas);
|
|
26
25
|
if (presented) return presented;
|
|
27
26
|
const dataUrl = store.captureViewportImage(size);
|
|
28
27
|
return dataUrl ? imageOf(dataUrl) : null;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The Three integration's answers to the hierarchy panel's row questions
|
|
3
|
-
* (`@volter/sdk/
|
|
4
|
-
* draws, a node's component marks come off its `Object3D
|
|
5
|
-
* projected parents its object map changed so the panel can patch rows.
|
|
3
|
+
* (`@volter/sdk/hierarchy-row-media`): an instanced mesh's row names the units it
|
|
4
|
+
* draws, and a node's component marks come off its `Object3D`.
|
|
6
5
|
*/
|
|
7
|
-
import { registerHierarchyRowMedia } from '@volter/sdk/
|
|
6
|
+
import { registerHierarchyRowMedia } from '@volter/sdk/hierarchy-row-media';
|
|
8
7
|
import { entityObject3D } from './entity-object';
|
|
9
8
|
import { markReaderFor } from './hierarchy-mark-reader';
|
|
10
9
|
import { instancedRowDetail } from './instanced-presentation';
|
|
@@ -15,12 +14,5 @@ export function registerThreeHierarchyRowMedia(): () => void {
|
|
|
15
14
|
detail: (adapter, nodeId, store) =>
|
|
16
15
|
instancedRowDetail(entityObject3D(adapter, threeStateOf(store).objectMap, nodeId)),
|
|
17
16
|
marks: (hierarchy) => markReaderFor(hierarchy),
|
|
18
|
-
structureLog: (store) => {
|
|
19
|
-
const three = threeStateOf(store);
|
|
20
|
-
return {
|
|
21
|
-
epoch: () => three.gizmoEpoch,
|
|
22
|
-
parentChangesSince: (epoch) => three.ingestObjectMapChangesSince(epoch),
|
|
23
|
-
};
|
|
24
|
-
},
|
|
25
17
|
});
|
|
26
18
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THE THREE.JS ANSWER TO "what does this node look like"
|
|
3
|
-
* (`@volter/sdk/
|
|
3
|
+
* (`@volter/sdk/inspection-node-media`): a selected node the shell's
|
|
4
4
|
* Three store resolves to a live `Object3D` is previewed as an isolated live
|
|
5
5
|
* view of that object, a camera has no Preview section (its view is the
|
|
6
6
|
* viewport's), an instanced mesh names its units, and a node in the object map
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
import {
|
|
11
11
|
type InspectionNodeMedia,
|
|
12
12
|
registerInspectionNodeMedia,
|
|
13
|
-
} from '@volter/sdk/
|
|
13
|
+
} from '@volter/sdk/inspection-node-media';
|
|
14
14
|
import { contentWorldBounds } from '@volter/editor-threejs/viewport/content-bounds';
|
|
15
15
|
import { createElement, lazy } from 'react';
|
|
16
16
|
import type * as THREE from 'three';
|
|
17
|
-
import { openRegisteredDocument } from '@volter/sdk/
|
|
18
|
-
import { InspectorPreviewBody } from '@volter/sdk/
|
|
17
|
+
import { openRegisteredDocument } from '@volter/sdk/document-open-registry';
|
|
18
|
+
import { InspectorPreviewBody } from '@volter/sdk/components/inspector-preview-section';
|
|
19
19
|
import { entityObject3D } from './entity-object';
|
|
20
20
|
import { describeInstancedPresentation } from './instanced-presentation';
|
|
21
21
|
import { threeStoreForHost } from './three-state';
|
|
@@ -38,7 +38,7 @@ function objectMedia(
|
|
|
38
38
|
(mode) =>
|
|
39
39
|
createElement(InspectorPreviewBody, {
|
|
40
40
|
picture: () =>
|
|
41
|
-
createElement(InspectorObjectPreview, { previewKey, object, displayName
|
|
41
|
+
createElement(InspectorObjectPreview, { previewKey, object, displayName }),
|
|
42
42
|
previewKey,
|
|
43
43
|
actions,
|
|
44
44
|
...(mode ? { mode } : {}),
|
|
@@ -62,7 +62,7 @@ export function registerThreeInspectionMedia(): () => void {
|
|
|
62
62
|
? {
|
|
63
63
|
// The live `Object3D.name` is the only name there is.
|
|
64
64
|
open: () =>
|
|
65
|
-
openRegisteredDocument('asset-editor:entity',
|
|
65
|
+
openRegisteredDocument('asset-editor:entity', {
|
|
66
66
|
entityId: nodeId,
|
|
67
67
|
title: object.name || nodeId,
|
|
68
68
|
}),
|
|
@@ -1,34 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THE THREE INTEGRATION, INSTALLED — every registration the kit's Three viewport answers
|
|
3
|
-
* through: the Object3D surfaces contributions mount,
|
|
3
|
+
* through: the Object3D surfaces contributions mount, its Asset Lab
|
|
4
4
|
* viewers, the inspector's node media, model thumbnails, the viewport status facet,
|
|
5
5
|
* canvas captures, the gizmo viewport's authoring policy, and the view state each session store's
|
|
6
6
|
* Three half saves. The kit constructs none of it; the integration installs it once, and every
|
|
7
7
|
* Three surface asks for it before it mounts (`ensureThreeIntegration`).
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { registerRendererResourceCounts } from '@volter/sdk/kit/renderer-resource-counts';
|
|
9
|
+
import { saveThumbnail } from '@volter/sdk/api/project-open';
|
|
10
|
+
import { OUTLINER_DATA, OUTLINER_OBJECT, registerHierarchyKinds } from '@volter/sdk/hierarchy-kind-icon';
|
|
11
|
+
import { registerContributionSurfaces } from '@volter/sdk/contribution-surfaces';
|
|
12
|
+
import { registerRendererResourceCounts } from '@volter/sdk/renderer-resource-counts';
|
|
14
13
|
import { interactiveViewportRendererCounts } from './three-viewport/interactive-renderer';
|
|
15
14
|
import { inspectorPreviewRendererCounts } from '../viewport/preview-renderer';
|
|
16
15
|
import { liveHostRendererCount } from '../viewport/renderer-ownership';
|
|
17
|
-
import type * as THREE from 'three';
|
|
18
16
|
import { SHELL_VIEWPORT_AUTHORING_POLICY } from './authoring/shell-viewport-policy';
|
|
19
17
|
import { Object3DAuthoringSurface, Object3DPreviewSurface } from './components/object3d-contribution-surfaces';
|
|
20
18
|
import { registerThreeAssetViewers } from './components/asset-viewers/three-asset-viewers';
|
|
21
19
|
import type { EditorStatePersistence } from './editor-shell-store';
|
|
22
20
|
import { registerModelThumbnails } from './model-thumbnail';
|
|
23
|
-
import {
|
|
24
|
-
import { onShellStore } from '@volter/sdk/
|
|
21
|
+
import { editorHost } from '@volter/sdk/host';
|
|
22
|
+
import { onShellStore } from '@volter/sdk/shell-store-door';
|
|
25
23
|
import { registerThreeCanvasRender } from './three-canvas-render';
|
|
26
24
|
import { registerThreeHierarchyRowMedia } from './three-hierarchy-row-media';
|
|
27
25
|
import { registerThreePlayCameraFlight } from './play-camera-flight';
|
|
28
26
|
import { registerThreeInspectionMedia } from './three-inspection-media';
|
|
29
|
-
import { threeStateOf
|
|
27
|
+
import { threeStateOf } from './three-state';
|
|
30
28
|
import { installViewportAuthoringPolicy } from './viewport-authoring-policy';
|
|
31
|
-
import { viewportCommands } from './viewport-commands';
|
|
32
29
|
import { reportViewportStatus } from './viewport-status-facet';
|
|
33
30
|
|
|
34
31
|
/**
|
|
@@ -40,24 +37,31 @@ const hotData = (import.meta as ImportMeta & { hot?: { data: Record<string, unkn
|
|
|
40
37
|
const VIEW_STATE_PERSISTENCE: EditorStatePersistence = (hotData?.['viewStatePersistence'] as
|
|
41
38
|
| EditorStatePersistence
|
|
42
39
|
| undefined) ?? {
|
|
43
|
-
save: (state) =>
|
|
40
|
+
save: (state) => editorHost().projectLocalState.write('view', state),
|
|
44
41
|
saveThumbnail,
|
|
45
42
|
};
|
|
46
43
|
if (hotData) hotData['viewStatePersistence'] = VIEW_STATE_PERSISTENCE;
|
|
47
44
|
|
|
48
|
-
const NO_OBJECTS: ReadonlyMap<string, THREE.Object3D> = new Map();
|
|
49
45
|
|
|
50
46
|
let installed: (() => void) | null = null;
|
|
51
47
|
|
|
52
48
|
/** Install the integration; idempotent. Returns the teardown of the first install. */
|
|
53
49
|
export function ensureThreeIntegration(): () => void {
|
|
54
50
|
if (installed) return installed;
|
|
51
|
+
// The hierarchy glyphs, in place before the first hierarchy render a viewport brings (its rows are memoized). The
|
|
52
|
+
// core composite gives a `stage` world's group row the manifest surface as its kind (`groupNode`); that row draws
|
|
53
|
+
// the outliner's object glyph. A model document's root is a datablock, not an object carrying one `mesh-data` row
|
|
54
|
+
// off it, so the two kinds `source-object3d-authoring-adapter.ts` mints for it, `three-source` and `model-asset`,
|
|
55
|
+
// draw the data glyph, as `mesh-data` does: the Properties breadcrumb takes its glyph from the datablock, never
|
|
56
|
+
// from the open tab.
|
|
57
|
+
registerHierarchyKinds({
|
|
58
|
+
glyphs: { stage: OUTLINER_OBJECT, 'three-source': OUTLINER_DATA, 'model-asset': OUTLINER_DATA },
|
|
59
|
+
});
|
|
55
60
|
const stops = [
|
|
56
61
|
registerContributionSurfaces({
|
|
57
62
|
Object3DPreview: Object3DPreviewSurface,
|
|
58
63
|
Object3DAuthoring: Object3DAuthoringSurface,
|
|
59
64
|
}),
|
|
60
|
-
registerContributedCommands('three-viewport', viewportCommands),
|
|
61
65
|
registerThreeAssetViewers(),
|
|
62
66
|
registerThreeInspectionMedia(),
|
|
63
67
|
registerModelThumbnails(),
|
|
@@ -65,10 +69,6 @@ export function ensureThreeIntegration(): () => void {
|
|
|
65
69
|
registerThreeCanvasRender(),
|
|
66
70
|
registerThreeHierarchyRowMedia(),
|
|
67
71
|
registerThreePlayCameraFlight(),
|
|
68
|
-
registerHostHierarchyObjects({
|
|
69
|
-
object: (id) => threeStoreForHost()?.objectMap.get(id) ?? null,
|
|
70
|
-
objects: () => threeStoreForHost()?.objectMap ?? NO_OBJECTS,
|
|
71
|
-
}),
|
|
72
72
|
registerRendererResourceCounts('hostLive', liveHostRendererCount),
|
|
73
73
|
registerRendererResourceCounts('interactive', interactiveViewportRendererCounts),
|
|
74
74
|
registerRendererResourceCounts('inspectorPreview', inspectorPreviewRendererCounts),
|
package/src/kit/three-state.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/** A shell store's Three half (`threeStateOf`, beside the half in `editor-shell-store.ts`). */
|
|
2
|
-
import type { ShellStore } from '@volter/sdk/
|
|
2
|
+
import type { ShellStore } from '@volter/sdk/shell-store';
|
|
3
3
|
import { type EditorShellStore, threeStateOf } from './editor-shell-store';
|
|
4
|
-
import {
|
|
5
|
-
import { onShellStore, shellStoreForHost } from '@volter/sdk/kit/shell-store-door';
|
|
4
|
+
import { shellStoreForHost } from '@volter/sdk/shell-store-door';
|
|
6
5
|
|
|
7
6
|
export { threeStateOf };
|
|
8
7
|
|
|
@@ -11,23 +10,7 @@ export function optionalThreeStateOf(store: ShellStore | null): EditorShellStore
|
|
|
11
10
|
return store === null ? null : threeStateOf(store);
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
/** The session store's Three half (`threeStateOf`), for a component that reads the scene,
|
|
15
|
-
* object map, camera or viewport tools. */
|
|
16
|
-
export function useThreeEditorStore(): EditorShellStore {
|
|
17
|
-
return threeStateOf(useEditorStore());
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** {@link useThreeEditorStore} outside a session answers `null`. */
|
|
21
|
-
export function useOptionalThreeEditorStore(): EditorShellStore | null {
|
|
22
|
-
return optionalThreeStateOf(useOptionalEditorStore());
|
|
23
|
-
}
|
|
24
|
-
|
|
25
13
|
/** The session store's Three half (`three-state.ts`), or `null` before a session opens. */
|
|
26
14
|
export function threeStoreForHost(): EditorShellStore | null {
|
|
27
15
|
return optionalThreeStateOf(shellStoreForHost());
|
|
28
16
|
}
|
|
29
|
-
|
|
30
|
-
/** {@link onShellStore} for a lane that works on the store's Three half. */
|
|
31
|
-
export function onThreeStore(listener: (store: EditorShellStore) => void): () => void {
|
|
32
|
-
return onShellStore((store) => listener(threeStateOf(store)));
|
|
33
|
-
}
|