@volter/editor-threejs 0.5.66 → 0.5.68
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/contributions/animation-timeline.utility.tsx +44 -0
- package/contributions/three-integration.service.ts +13 -0
- package/package.json +96 -6
- package/src/adapter/renderer-config.ts +3 -4
- package/src/adapter/three-contract.ts +72 -0
- package/src/ecs/object-marks.ts +1 -1
- package/src/ecs/user-data.ts +0 -9
- 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-api.ts +92 -0
- package/src/viewport-door.ts +237 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE PLAY ENTRY CAMERA FLIGHT — the Three integration's half of Play's immersive entry
|
|
3
|
+
* (`@volter/editor-sdk/kit/play-camera-flight`). The kit's transition owns the phases and the
|
|
4
|
+
* chrome; this owns the camera: the editor viewport flies along a Catmull-Rom spline from its
|
|
5
|
+
* preview pose to the AUTHORED game camera's pose, retargets to the game's live camera once it
|
|
6
|
+
* boots so the cross-fade is pixel-continuous, and restores the preview pose when Play ends. The
|
|
7
|
+
* flight reads the authored viewport's rig through the viewport door and rides its frame hook, so
|
|
8
|
+
* flight pacing rides the editor's own frame clock.
|
|
9
|
+
*/
|
|
10
|
+
import { registerPlayCameraFlight, type LiveCameraLookup } from '@volter/editor-sdk/kit/play-camera-flight';
|
|
11
|
+
import { onViewportFrame, viewportRig } from '../viewport-door';
|
|
12
|
+
import * as THREE from 'three';
|
|
13
|
+
import { threeStoreForHost } from './three-state';
|
|
14
|
+
|
|
15
|
+
/** Flight time scales mildly with distance so short hops feel snappy and
|
|
16
|
+
* cross-scene flights stay graceful, clamped to a tight band. */
|
|
17
|
+
export function flightDurationMs(distance: number): number {
|
|
18
|
+
return Math.min(1600, Math.max(800, 900 + distance * 25));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function easeInOutCubic(t: number): number {
|
|
22
|
+
return t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Control points for the camera flight spline: start, two interior points
|
|
27
|
+
* forming a gentle arc, end. The arc lifts along world-up and bows slightly
|
|
28
|
+
* sideways, both proportional to (but clamped against) the travel distance —
|
|
29
|
+
* a short hop barely arcs, a long flight gets a visible swoop, and a
|
|
30
|
+
* degenerate zero-length flight returns a straight (stationary) curve.
|
|
31
|
+
*/
|
|
32
|
+
export function computeFlightControlPoints(
|
|
33
|
+
start: THREE.Vector3,
|
|
34
|
+
end: THREE.Vector3,
|
|
35
|
+
up: THREE.Vector3 = new THREE.Vector3(0, 1, 0),
|
|
36
|
+
): THREE.Vector3[] {
|
|
37
|
+
const travel = new THREE.Vector3().subVectors(end, start);
|
|
38
|
+
const distance = travel.length();
|
|
39
|
+
if (distance < 1e-6) {
|
|
40
|
+
return [start.clone(), start.clone(), end.clone(), end.clone()];
|
|
41
|
+
}
|
|
42
|
+
const lift = Math.min(distance * 0.18, 6);
|
|
43
|
+
// Side bias perpendicular to travel and up — gives the arc a slight bank
|
|
44
|
+
// instead of a purely vertical hump. Falls back to zero when travel is
|
|
45
|
+
// parallel to up (straight vertical flights just lift).
|
|
46
|
+
const side = new THREE.Vector3().crossVectors(travel, up);
|
|
47
|
+
if (side.lengthSq() > 1e-9) side.normalize().multiplyScalar(Math.min(distance * 0.08, 2.5));
|
|
48
|
+
else side.set(0, 0, 0);
|
|
49
|
+
const liftVec = up.clone().normalize().multiplyScalar(lift);
|
|
50
|
+
const p1 = start.clone().addScaledVector(travel, 0.3).add(liftVec).add(side);
|
|
51
|
+
const p2 = end
|
|
52
|
+
.clone()
|
|
53
|
+
.addScaledVector(travel, -0.3)
|
|
54
|
+
.addScaledVector(liftVec, 0.6)
|
|
55
|
+
.addScaledVector(side, 0.5);
|
|
56
|
+
return [start.clone(), p1, p2, end.clone()];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The narrow store surface `resolveAuthoredCameraTarget` reads. */
|
|
60
|
+
export interface PlayCameraSourceStore {
|
|
61
|
+
readonly objectMap: ReadonlyMap<string, THREE.Object3D>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface AuthoredCameraTarget {
|
|
65
|
+
entityId: string;
|
|
66
|
+
position: [number, number, number];
|
|
67
|
+
quaternion: [number, number, number, number];
|
|
68
|
+
fov: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Editor-only overlays (helpers, gizmos) live on layer 31 — never flight targets. */
|
|
72
|
+
const EDITOR_ONLY_LAYER_MASK = 1 << 31;
|
|
73
|
+
|
|
74
|
+
function isEditorOnlyOrGizmo(obj: THREE.Object3D): boolean {
|
|
75
|
+
return (obj.layers.mask & EDITOR_ONLY_LAYER_MASK) !== 0 || /gizmo/i.test(obj.name);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Depth-first search for the first real camera in a subtree, skipping
|
|
79
|
+
* editor-only/gizmo branches wholesale. */
|
|
80
|
+
function findCameraInSubtree(obj: THREE.Object3D): THREE.Camera | null {
|
|
81
|
+
if (isEditorOnlyOrGizmo(obj)) return null;
|
|
82
|
+
if ((obj as Partial<THREE.Camera>).isCamera) return obj as THREE.Camera;
|
|
83
|
+
for (const child of obj.children) {
|
|
84
|
+
const found = findCameraInSubtree(child);
|
|
85
|
+
if (found) return found;
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function targetFromObject(
|
|
91
|
+
entityId: string,
|
|
92
|
+
obj: THREE.Object3D,
|
|
93
|
+
fov: number,
|
|
94
|
+
): AuthoredCameraTarget {
|
|
95
|
+
const position = new THREE.Vector3();
|
|
96
|
+
const quaternion = new THREE.Quaternion();
|
|
97
|
+
obj.getWorldPosition(position);
|
|
98
|
+
obj.getWorldQuaternion(quaternion);
|
|
99
|
+
return {
|
|
100
|
+
entityId,
|
|
101
|
+
position: position.toArray() as [number, number, number],
|
|
102
|
+
quaternion: quaternion.toArray() as [number, number, number, number],
|
|
103
|
+
fov,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Find the authored game camera in the CURRENT (pre-play) editor scene.
|
|
109
|
+
*
|
|
110
|
+
* A world's source IS its document, so there is no descriptor to read a
|
|
111
|
+
* declared camera out of: the design session adopts the mounted fiber scene
|
|
112
|
+
* into the store, which makes the authored camera (e.g. drei
|
|
113
|
+
* `<PerspectiveCamera makeDefault>`) a live `THREE.Camera` in the mapped
|
|
114
|
+
* object graphs. Take the first one outside editor-only (layer-31) and gizmo
|
|
115
|
+
* branches, at its live world transform.
|
|
116
|
+
*
|
|
117
|
+
* `null` when no camera is found — play then keeps today's behavior (game
|
|
118
|
+
* starts at the preview pose, no flight).
|
|
119
|
+
*/
|
|
120
|
+
export function resolveAuthoredCameraTarget(
|
|
121
|
+
store: PlayCameraSourceStore,
|
|
122
|
+
): AuthoredCameraTarget | null {
|
|
123
|
+
for (const [id, obj] of store.objectMap) {
|
|
124
|
+
const camera = findCameraInSubtree(obj);
|
|
125
|
+
if (!camera) continue;
|
|
126
|
+
const perspective = camera as Partial<THREE.PerspectiveCamera>;
|
|
127
|
+
return targetFromObject(id, camera, perspective.isPerspectiveCamera ? perspective.fov! : 60);
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
interface FlightRuntime {
|
|
133
|
+
startPos: THREE.Vector3;
|
|
134
|
+
startQuat: THREE.Quaternion;
|
|
135
|
+
startFov: number;
|
|
136
|
+
endPos: THREE.Vector3;
|
|
137
|
+
endQuat: THREE.Quaternion;
|
|
138
|
+
endFov: number;
|
|
139
|
+
curve: THREE.CatmullRomCurve3;
|
|
140
|
+
startedAt: number;
|
|
141
|
+
durationMs: number;
|
|
142
|
+
landed: boolean;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function rebuildCurve(flight: FlightRuntime): void {
|
|
146
|
+
flight.curve = new THREE.CatmullRomCurve3(
|
|
147
|
+
computeFlightControlPoints(flight.startPos, flight.endPos),
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
interface ActiveFlight {
|
|
152
|
+
readonly runtime: FlightRuntime;
|
|
153
|
+
readonly snapshot: { position: THREE.Vector3; target: THREE.Vector3; up: THREE.Vector3; fov: number };
|
|
154
|
+
readonly onLanded: () => void;
|
|
155
|
+
/** Live game camera, registered at game-ready — the flight retargets to it
|
|
156
|
+
* each frame so the hand-off converges on whatever the game actually does
|
|
157
|
+
* with its camera during boot (follow rigs, SceneCamera components…). */
|
|
158
|
+
getLiveCamera: LiveCameraLookup | null;
|
|
159
|
+
/** Driving the camera: from `begin` until the game is on screen (`settle`) or Play ends. */
|
|
160
|
+
driving: boolean;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let _flight: ActiveFlight | null = null;
|
|
164
|
+
|
|
165
|
+
function begin(onLanded: () => void): boolean {
|
|
166
|
+
const viewport = viewportRig();
|
|
167
|
+
const objects = threeStoreForHost()?.objectMap;
|
|
168
|
+
const target = objects ? resolveAuthoredCameraTarget({ objectMap: objects }) : null;
|
|
169
|
+
if (!target || !viewport) return false;
|
|
170
|
+
const startPos = viewport.camera.position.clone();
|
|
171
|
+
const endPos = new THREE.Vector3().fromArray(target.position);
|
|
172
|
+
_flight = {
|
|
173
|
+
runtime: {
|
|
174
|
+
startPos,
|
|
175
|
+
startQuat: viewport.camera.quaternion.clone(),
|
|
176
|
+
startFov: viewport.camera.fov,
|
|
177
|
+
endPos,
|
|
178
|
+
endQuat: new THREE.Quaternion().fromArray(target.quaternion),
|
|
179
|
+
endFov: target.fov,
|
|
180
|
+
curve: new THREE.CatmullRomCurve3(computeFlightControlPoints(startPos, endPos)),
|
|
181
|
+
startedAt: performance.now(),
|
|
182
|
+
durationMs: flightDurationMs(startPos.distanceTo(endPos)),
|
|
183
|
+
landed: false,
|
|
184
|
+
},
|
|
185
|
+
snapshot: {
|
|
186
|
+
position: viewport.camera.position.clone(),
|
|
187
|
+
target: viewport.orbit.target.clone(),
|
|
188
|
+
up: viewport.camera.up.clone(),
|
|
189
|
+
fov: viewport.camera.fov,
|
|
190
|
+
},
|
|
191
|
+
onLanded,
|
|
192
|
+
getLiveCamera: null,
|
|
193
|
+
driving: true,
|
|
194
|
+
};
|
|
195
|
+
viewport.orbit.enabled = false;
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** After the flight, leave OrbitControls in a sane state at the landed pose
|
|
200
|
+
* so a `set-viewport-tab scene` during play still orbits correctly. */
|
|
201
|
+
function settle(): void {
|
|
202
|
+
const viewport = viewportRig();
|
|
203
|
+
const active = _flight;
|
|
204
|
+
if (!active) return;
|
|
205
|
+
active.driving = false;
|
|
206
|
+
if (!viewport) return;
|
|
207
|
+
const camera = viewport.camera;
|
|
208
|
+
const forward = new THREE.Vector3(0, 0, -1).applyQuaternion(camera.quaternion);
|
|
209
|
+
const distance = Math.max(1, active.snapshot.position.distanceTo(active.snapshot.target));
|
|
210
|
+
viewport.orbit.target.copy(camera.position).addScaledVector(forward, distance);
|
|
211
|
+
// The orbit re-aims with the camera's up: the landed pose's own, so its roll stands.
|
|
212
|
+
camera.up.set(0, 1, 0).applyQuaternion(camera.quaternion);
|
|
213
|
+
viewport.orbit.enabled = true;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Restore the pre-play camera pose. */
|
|
217
|
+
function end(): void {
|
|
218
|
+
const active = _flight;
|
|
219
|
+
if (!active) return;
|
|
220
|
+
_flight = null;
|
|
221
|
+
const viewport = viewportRig();
|
|
222
|
+
if (!viewport) return;
|
|
223
|
+
const snap = active.snapshot;
|
|
224
|
+
viewport.camera.position.copy(snap.position);
|
|
225
|
+
viewport.orbit.target.copy(snap.target);
|
|
226
|
+
viewport.camera.up.copy(snap.up);
|
|
227
|
+
viewport.camera.lookAt(snap.target);
|
|
228
|
+
if (viewport.camera.fov !== snap.fov) {
|
|
229
|
+
viewport.camera.fov = snap.fov;
|
|
230
|
+
viewport.camera.updateProjectionMatrix();
|
|
231
|
+
}
|
|
232
|
+
viewport.orbit.enabled = true;
|
|
233
|
+
viewport.orbit.update();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const _liveCamPos = new THREE.Vector3();
|
|
237
|
+
const _liveCamQuat = new THREE.Quaternion();
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Per-frame flight driver — called from the world root's stage's RAF loop right after
|
|
241
|
+
* `viewport.update(dt)` so the flight has final say over the camera pose.
|
|
242
|
+
*/
|
|
243
|
+
onViewportFrame(playTransitionFrame);
|
|
244
|
+
|
|
245
|
+
function playTransitionFrame(): void {
|
|
246
|
+
const active = _flight;
|
|
247
|
+
const viewport = viewportRig();
|
|
248
|
+
if (!active || !viewport || !active.driving) return;
|
|
249
|
+
const flight = active.runtime;
|
|
250
|
+
|
|
251
|
+
// Retarget to the live game camera once it exists (`track`).
|
|
252
|
+
// The kit hands the lane's lookup over unread; only an `Object3D` is a camera to converge on.
|
|
253
|
+
const found = active.getLiveCamera?.() ?? null;
|
|
254
|
+
const liveCamera = (found as Partial<THREE.Object3D> | null)?.isObject3D ? (found as THREE.Object3D) : null;
|
|
255
|
+
if (liveCamera) {
|
|
256
|
+
liveCamera.getWorldPosition(_liveCamPos);
|
|
257
|
+
liveCamera.getWorldQuaternion(_liveCamQuat);
|
|
258
|
+
if (_liveCamPos.distanceToSquared(flight.endPos) > 1e-8) {
|
|
259
|
+
flight.endPos.copy(_liveCamPos);
|
|
260
|
+
rebuildCurve(flight);
|
|
261
|
+
}
|
|
262
|
+
flight.endQuat.copy(_liveCamQuat);
|
|
263
|
+
const liveFov = (liveCamera as THREE.PerspectiveCamera).fov;
|
|
264
|
+
if (typeof liveFov === 'number') flight.endFov = liveFov;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const camera = viewport.camera;
|
|
268
|
+
if (flight.landed) {
|
|
269
|
+
// Holding/cross-fading: pin to the (possibly live-tracked) end pose.
|
|
270
|
+
camera.position.copy(flight.endPos);
|
|
271
|
+
camera.quaternion.copy(flight.endQuat);
|
|
272
|
+
if (camera.fov !== flight.endFov) {
|
|
273
|
+
camera.fov = flight.endFov;
|
|
274
|
+
camera.updateProjectionMatrix();
|
|
275
|
+
}
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const t = Math.min(1, (performance.now() - flight.startedAt) / flight.durationMs);
|
|
280
|
+
const eased = easeInOutCubic(t);
|
|
281
|
+
camera.position.copy(flight.curve.getPoint(eased));
|
|
282
|
+
camera.quaternion.slerpQuaternions(flight.startQuat, flight.endQuat, eased);
|
|
283
|
+
camera.fov = flight.startFov + (flight.endFov - flight.startFov) * eased;
|
|
284
|
+
camera.updateProjectionMatrix();
|
|
285
|
+
if (t >= 1) {
|
|
286
|
+
flight.landed = true;
|
|
287
|
+
active.onLanded();
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function registerThreePlayCameraFlight(): () => void {
|
|
292
|
+
return registerPlayCameraFlight({
|
|
293
|
+
begin,
|
|
294
|
+
track: (liveCamera) => {
|
|
295
|
+
if (_flight) _flight.getLiveCamera = liveCamera;
|
|
296
|
+
},
|
|
297
|
+
settle,
|
|
298
|
+
end,
|
|
299
|
+
});
|
|
300
|
+
}
|