@giro3d/giro3d
Version:
A JS/WebGL framework for 3D geospatial data visualization
133 lines (112 loc) • 4.86 kB
text/typescript
/*
* Copyright (c) 2015-2018, IGN France.
* Copyright (c) 2018-2026, Giro3D team.
* SPDX-License-Identifier: MIT
*/
import type GUI from 'lil-gui';
import {
CameraHelper,
Mesh,
MeshBasicMaterial,
SphereGeometry,
type OrthographicCamera,
type PerspectiveCamera,
type Vector3,
} from 'three';
import type Instance from '../core/Instance';
import type View from '../renderer/View';
import Ellipsoid from '../core/geographic/Ellipsoid';
import { isOrthographicCamera, isPerspectiveCamera } from '../utils/predicates';
import Panel from './Panel';
class CameraInspector extends Panel {
public view: View;
public camera: PerspectiveCamera | OrthographicCamera;
public snapshots: CameraHelper[] = [];
public horizonDistance = 'N/A';
/**
* @param gui - The GUI.
* @param instance - The Giro3D instance.
*/
public constructor(gui: GUI, instance: Instance) {
super(gui, instance, 'View');
this.view = this.instance.view;
this.camera = this.view.camera;
const notify = this.notify.bind(this);
this.addController(this.camera, 'type').name('Type');
if (isPerspectiveCamera(this.camera)) {
this.addController(this.camera, 'fov').min(25).max(150).name('FOV');
} else if (isOrthographicCamera(this.camera)) {
this.addController(this.camera, 'zoom');
this.addController(this.camera, 'left');
this.addController(this.camera, 'right');
this.addController(this.camera, 'top');
this.addController(this.camera, 'bottom');
}
this.addController(instance.mainLoop, 'automaticCameraPlaneComputation')
.name('Automatic plane computation')
.onChange(notify);
this.addController(this.camera, 'far').name('Far plane').onChange(notify);
this.addController(this.camera, 'near').name('Near plane').onChange(notify);
this.addController(this.view, 'maxFarPlane').name('Max far plane').onChange(notify);
this.addController(this.view, 'minNearPlane').name('Min near plane').onChange(notify);
this.addController(this.view, 'width').name('Width (pixels)');
this.addController(this.view, 'height').name('Height (pixels)');
if (instance.coordinateSystem.isEpsg(4978)) {
this.addController(this, 'horizonDistance');
instance.addEventListener('after-camera-update', () => {
const distance = Ellipsoid.WGS84.getOpticalHorizon(instance.view.camera.position);
this.horizonDistance =
distance != null
? distance > 10000
? (distance / 1000).toFixed(0) + ' km'
: distance.toFixed(0) + 'm'
: 'N/A';
});
}
this.addController(this, 'createFrustumSnapshot').name('Create frustum snapshot');
this.addController(this, 'deleteSnapshots').name('Delete frustum snapshots');
const position = this.gui.addFolder('Position');
position.close();
this._controllers.push(position.add(this.camera.position, 'x'));
this._controllers.push(position.add(this.camera.position, 'y'));
this._controllers.push(position.add(this.camera.position, 'z'));
if (this.view.controls && 'target' in this.view.controls) {
const target = this.gui.addFolder('Target');
target.close();
const targetObj = this.view.controls.target as Vector3;
this._controllers.push(target.add(targetObj, 'x'));
this._controllers.push(target.add(targetObj, 'y'));
this._controllers.push(target.add(targetObj, 'z'));
}
}
public deleteSnapshots(): void {
this.snapshots.forEach(helper => {
helper.dispose();
this.instance.remove(helper);
});
this.snapshots.length = 0;
}
public createFrustumSnapshot(): void {
const helper = new CameraHelper(this.instance.view.camera);
this.instance.add(helper);
helper.update();
this.instance.notifyChange();
if (
this.instance.coordinateSystem.isEpsg(4978) &&
isPerspectiveCamera(this.instance.view.camera)
) {
const distance = Ellipsoid.WGS84.getOpticalHorizon(this.instance.view.camera.position);
if (distance != null) {
helper.add(
new Mesh(
new SphereGeometry(distance, 32, 16),
new MeshBasicMaterial({ wireframe: true, color: 'green' }),
),
);
}
}
helper.updateMatrixWorld(true);
this.snapshots.push(helper);
}
}
export default CameraInspector;