itowns 2.45.1-next.0 → 2.45.1-next.1
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/dist/455.js +2 -0
- package/dist/455.js.map +1 -0
- package/dist/debug.js +3 -0
- package/dist/debug.js.LICENSE.txt +13 -0
- package/dist/debug.js.map +1 -0
- package/dist/itowns.js +3 -0
- package/dist/itowns.js.LICENSE.txt +5 -0
- package/dist/itowns.js.map +1 -0
- package/dist/itowns_lasparser.js +2 -0
- package/dist/itowns_lasparser.js.map +1 -0
- package/dist/itowns_lasworker.js +2 -0
- package/dist/itowns_lasworker.js.map +1 -0
- package/dist/itowns_potree2worker.js +2 -0
- package/dist/itowns_potree2worker.js.map +1 -0
- package/dist/itowns_widgets.js +2 -0
- package/dist/itowns_widgets.js.map +1 -0
- package/lib/Controls/FirstPersonControls.js +308 -0
- package/lib/Controls/FlyControls.js +175 -0
- package/lib/Controls/GlobeControls.js +1178 -0
- package/lib/Controls/PlanarControls.js +1025 -0
- package/lib/Controls/StateControl.js +432 -0
- package/lib/Controls/StreetControls.js +392 -0
- package/lib/Converter/Feature2Mesh.js +612 -0
- package/lib/Converter/Feature2Texture.js +174 -0
- package/lib/Converter/convertToTile.js +70 -0
- package/lib/Converter/textureConverter.js +43 -0
- package/lib/Core/3DTiles/C3DTBatchTable.js +131 -0
- package/lib/Core/3DTiles/C3DTBatchTableHierarchyExtension.js +96 -0
- package/lib/Core/3DTiles/C3DTBoundingVolume.js +156 -0
- package/lib/Core/3DTiles/C3DTExtensions.js +97 -0
- package/lib/Core/3DTiles/C3DTFeature.js +110 -0
- package/lib/Core/3DTiles/C3DTilesEnums.js +20 -0
- package/lib/Core/3DTiles/C3DTileset.js +99 -0
- package/lib/Core/3DTiles/utils/BinaryPropertyAccessor.js +100 -0
- package/lib/Core/AnimationPlayer.js +142 -0
- package/lib/Core/CopcNode.js +174 -0
- package/lib/Core/Deprecated/Undeprecator.js +74 -0
- package/lib/Core/EntwinePointTileNode.js +126 -0
- package/lib/Core/Feature.js +488 -0
- package/lib/Core/Geographic/GeoidGrid.js +108 -0
- package/lib/Core/Label.js +222 -0
- package/lib/Core/MainLoop.js +209 -0
- package/lib/Core/Picking.js +255 -0
- package/lib/Core/PointCloudNode.js +42 -0
- package/lib/Core/Potree2Node.js +206 -0
- package/lib/Core/Potree2PointAttributes.js +139 -0
- package/lib/Core/PotreeNode.js +101 -0
- package/lib/Core/Prefab/Globe/Atmosphere.js +293 -0
- package/lib/Core/Prefab/Globe/GlobeLayer.js +152 -0
- package/lib/Core/Prefab/Globe/GlobeTileBuilder.js +110 -0
- package/lib/Core/Prefab/Globe/SkyShader.js +78 -0
- package/lib/Core/Prefab/GlobeView.js +155 -0
- package/lib/Core/Prefab/Planar/PlanarLayer.js +59 -0
- package/lib/Core/Prefab/Planar/PlanarTileBuilder.js +71 -0
- package/lib/Core/Prefab/PlanarView.js +62 -0
- package/lib/Core/Prefab/TileBuilder.js +82 -0
- package/lib/Core/Prefab/computeBufferTileGeometry.js +248 -0
- package/lib/Core/Scheduler/Cache.js +17 -0
- package/lib/Core/Scheduler/CancelledCommandException.js +15 -0
- package/lib/Core/Scheduler/Scheduler.js +294 -0
- package/lib/Core/Style.js +660 -0
- package/lib/Core/StyleOptions.js +486 -0
- package/lib/Core/System/Capabilities.js +63 -0
- package/lib/Core/Tile/Tile.js +205 -0
- package/lib/Core/Tile/TileGrid.js +49 -0
- package/lib/Core/TileGeometry.js +124 -0
- package/lib/Core/TileMesh.js +108 -0
- package/lib/Core/View.js +1115 -0
- package/lib/Layer/C3DTilesLayer.js +459 -0
- package/lib/Layer/ColorLayer.js +154 -0
- package/lib/Layer/CopcLayer.js +63 -0
- package/lib/Layer/ElevationLayer.js +139 -0
- package/lib/Layer/EntwinePointTileLayer.js +71 -0
- package/lib/Layer/FeatureGeometryLayer.js +77 -0
- package/lib/Layer/GeoidLayer.js +80 -0
- package/lib/Layer/GeometryLayer.js +233 -0
- package/lib/Layer/InfoLayer.js +64 -0
- package/lib/Layer/LabelLayer.js +469 -0
- package/lib/Layer/Layer.js +335 -0
- package/lib/Layer/LayerUpdateState.js +89 -0
- package/lib/Layer/LayerUpdateStrategy.js +80 -0
- package/lib/Layer/OGC3DTilesLayer.js +543 -0
- package/lib/Layer/OrientedImageLayer.js +227 -0
- package/lib/Layer/PointCloudLayer.js +405 -0
- package/lib/Layer/Potree2Layer.js +171 -0
- package/lib/Layer/PotreeLayer.js +72 -0
- package/lib/Layer/RasterLayer.js +37 -0
- package/lib/Layer/ReferencingLayerProperties.js +62 -0
- package/lib/Layer/TiledGeometryLayer.js +459 -0
- package/lib/Loader/LASLoader.js +193 -0
- package/lib/Loader/Potree2BrotliLoader.js +261 -0
- package/lib/Loader/Potree2Loader.js +207 -0
- package/lib/Main.js +113 -0
- package/lib/MainBundle.js +4 -0
- package/lib/Parser/B3dmParser.js +174 -0
- package/lib/Parser/CameraCalibrationParser.js +94 -0
- package/lib/Parser/GDFParser.js +72 -0
- package/lib/Parser/GTXParser.js +75 -0
- package/lib/Parser/GeoJsonParser.js +212 -0
- package/lib/Parser/GpxParser.js +25 -0
- package/lib/Parser/ISGParser.js +71 -0
- package/lib/Parser/KMLParser.js +25 -0
- package/lib/Parser/LASParser.js +137 -0
- package/lib/Parser/MapBoxUrlParser.js +83 -0
- package/lib/Parser/PntsParser.js +131 -0
- package/lib/Parser/Potree2BinParser.js +92 -0
- package/lib/Parser/PotreeBinParser.js +106 -0
- package/lib/Parser/PotreeCinParser.js +29 -0
- package/lib/Parser/ShapefileParser.js +78 -0
- package/lib/Parser/VectorTileParser.js +215 -0
- package/lib/Parser/XbilParser.js +120 -0
- package/lib/Parser/deprecated/LegacyGLTFLoader.js +1386 -0
- package/lib/Parser/iGLTFLoader.js +168 -0
- package/lib/Process/3dTilesProcessing.js +304 -0
- package/lib/Process/FeatureProcessing.js +76 -0
- package/lib/Process/LayeredMaterialNodeProcessing.js +229 -0
- package/lib/Process/ObjectRemovalHelper.js +97 -0
- package/lib/Process/handlerNodeError.js +23 -0
- package/lib/Provider/3dTilesProvider.js +149 -0
- package/lib/Provider/DataSourceProvider.js +24 -0
- package/lib/Provider/Fetcher.js +233 -0
- package/lib/Provider/PointCloudProvider.js +45 -0
- package/lib/Provider/TileProvider.js +16 -0
- package/lib/Provider/URLBuilder.js +116 -0
- package/lib/Renderer/Camera.js +281 -0
- package/lib/Renderer/Color.js +56 -0
- package/lib/Renderer/ColorLayersOrdering.js +115 -0
- package/lib/Renderer/CommonMaterial.js +31 -0
- package/lib/Renderer/Label2DRenderer.js +192 -0
- package/lib/Renderer/LayeredMaterial.js +243 -0
- package/lib/Renderer/OBB.js +150 -0
- package/lib/Renderer/OrientedImageCamera.js +118 -0
- package/lib/Renderer/OrientedImageMaterial.js +167 -0
- package/lib/Renderer/PointsMaterial.js +485 -0
- package/lib/Renderer/RasterTile.js +243 -0
- package/lib/Renderer/RenderMode.js +31 -0
- package/lib/Renderer/Shader/ShaderChunk.js +160 -0
- package/lib/Renderer/Shader/ShaderUtils.js +47 -0
- package/lib/Renderer/SphereHelper.js +17 -0
- package/lib/Renderer/WebXR.js +51 -0
- package/lib/Renderer/c3DEngine.js +214 -0
- package/lib/Source/C3DTilesGoogleSource.js +74 -0
- package/lib/Source/C3DTilesIonSource.js +54 -0
- package/lib/Source/C3DTilesSource.js +30 -0
- package/lib/Source/CopcSource.js +126 -0
- package/lib/Source/EntwinePointTileSource.js +72 -0
- package/lib/Source/FileSource.js +188 -0
- package/lib/Source/OGC3DTilesGoogleSource.js +29 -0
- package/lib/Source/OGC3DTilesIonSource.js +34 -0
- package/lib/Source/OGC3DTilesSource.js +21 -0
- package/lib/Source/OrientedImageSource.js +59 -0
- package/lib/Source/Potree2Source.js +167 -0
- package/lib/Source/PotreeSource.js +82 -0
- package/lib/Source/Source.js +202 -0
- package/lib/Source/TMSSource.js +144 -0
- package/lib/Source/VectorTilesSource.js +182 -0
- package/lib/Source/WFSSource.js +170 -0
- package/lib/Source/WMSSource.js +167 -0
- package/lib/Source/WMTSSource.js +92 -0
- package/lib/ThreeExtended/capabilities/WebGL.js +69 -0
- package/lib/ThreeExtended/libs/ktx-parse.module.js +506 -0
- package/lib/ThreeExtended/libs/zstddec.module.js +29 -0
- package/lib/ThreeExtended/loaders/DDSLoader.js +200 -0
- package/lib/ThreeExtended/loaders/DRACOLoader.js +400 -0
- package/lib/ThreeExtended/loaders/GLTFLoader.js +2879 -0
- package/lib/ThreeExtended/loaders/KTX2Loader.js +709 -0
- package/lib/ThreeExtended/math/ColorSpaces.js +59 -0
- package/lib/ThreeExtended/utils/BufferGeometryUtils.js +846 -0
- package/lib/ThreeExtended/utils/WorkerPool.js +70 -0
- package/lib/Utils/CameraUtils.js +554 -0
- package/lib/Utils/DEMUtils.js +350 -0
- package/lib/Utils/FeaturesUtils.js +156 -0
- package/lib/Utils/Gradients.js +16 -0
- package/lib/Utils/ThreeUtils.js +115 -0
- package/lib/Utils/gui/C3DTilesStyle.js +218 -0
- package/lib/Utils/gui/Main.js +7 -0
- package/lib/Utils/gui/Minimap.js +152 -0
- package/lib/Utils/gui/Navigation.js +245 -0
- package/lib/Utils/gui/Scale.js +104 -0
- package/lib/Utils/gui/Searchbar.js +234 -0
- package/lib/Utils/gui/Widget.js +80 -0
- package/lib/Utils/placeObjectOnGround.js +136 -0
- package/lib/Worker/LASLoaderWorker.js +19 -0
- package/lib/Worker/Potree2Worker.js +21 -0
- package/package.json +2 -2
package/lib/Core/View.js
ADDED
|
@@ -0,0 +1,1115 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { CRS, Coordinates } from '@itowns/geographic';
|
|
3
|
+
import Camera from "../Renderer/Camera.js";
|
|
4
|
+
import initializeWebXR from "../Renderer/WebXR.js";
|
|
5
|
+
import MainLoop, { MAIN_LOOP_EVENTS, RENDERING_PAUSED } from "./MainLoop.js";
|
|
6
|
+
import Capabilities from "./System/Capabilities.js";
|
|
7
|
+
import { COLOR_LAYERS_ORDER_CHANGED } from "../Renderer/ColorLayersOrdering.js";
|
|
8
|
+
import c3DEngine from "../Renderer/c3DEngine.js";
|
|
9
|
+
import RenderMode from "../Renderer/RenderMode.js";
|
|
10
|
+
import FeaturesUtils from "../Utils/FeaturesUtils.js";
|
|
11
|
+
import { getMaxColorSamplerUnitsCount } from "../Renderer/LayeredMaterial.js";
|
|
12
|
+
import Scheduler from "./Scheduler/Scheduler.js";
|
|
13
|
+
import Picking from "./Picking.js";
|
|
14
|
+
import LabelLayer from "../Layer/LabelLayer.js";
|
|
15
|
+
import ObjectRemovalHelper from "../Process/ObjectRemovalHelper.js";
|
|
16
|
+
export const VIEW_EVENTS = {
|
|
17
|
+
/**
|
|
18
|
+
* Fires when all the layers of the view are considered initialized.
|
|
19
|
+
* Initialized in this context means: all layers are ready to be
|
|
20
|
+
* displayed (no pending network access, no visual improvement to be
|
|
21
|
+
* expected, ...).
|
|
22
|
+
* If you add new layers, the event will be fired again when all
|
|
23
|
+
* layers are ready.
|
|
24
|
+
* @event View#layers-initialized
|
|
25
|
+
* @property type {string} layers-initialized
|
|
26
|
+
*/
|
|
27
|
+
LAYERS_INITIALIZED: 'layers-initialized',
|
|
28
|
+
LAYER_REMOVED: 'layer-removed',
|
|
29
|
+
LAYER_ADDED: 'layer-added',
|
|
30
|
+
INITIALIZED: 'initialized',
|
|
31
|
+
COLOR_LAYERS_ORDER_CHANGED,
|
|
32
|
+
CAMERA_MOVED: 'camera-moved',
|
|
33
|
+
DISPOSED: 'disposed'
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Fired on current view's domElement when double right-clicking it. Copies all properties of the second right-click
|
|
38
|
+
* MouseEvent (such as cursor position).
|
|
39
|
+
* @event View#dblclick-right
|
|
40
|
+
* @property {string} type dblclick-right
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
function _preprocessLayer(view, layer, parentLayer) {
|
|
44
|
+
const source = layer.source;
|
|
45
|
+
if (parentLayer && !layer.extent) {
|
|
46
|
+
layer.extent = parentLayer.extent;
|
|
47
|
+
if (source && !source.extent) {
|
|
48
|
+
source.extent = parentLayer.extent;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (layer.isGeometryLayer && !layer.isLabelLayer) {
|
|
52
|
+
// Find crs projection layer, this is projection destination
|
|
53
|
+
layer.crs = view.referenceCrs;
|
|
54
|
+
} else if (!layer.crs) {
|
|
55
|
+
if (parentLayer && parentLayer.tileMatrixSets && parentLayer.tileMatrixSets.includes(source.crs)) {
|
|
56
|
+
layer.crs = source.crs;
|
|
57
|
+
} else {
|
|
58
|
+
layer.crs = parentLayer && parentLayer.extent.crs;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (layer.isLabelLayer) {
|
|
62
|
+
view.mainLoop.gfxEngine.label2dRenderer.registerLayer(layer);
|
|
63
|
+
} else if (layer.labelEnabled || layer.addLabelLayer) {
|
|
64
|
+
if (layer.labelEnabled) {
|
|
65
|
+
// eslint-disable-next-line no-console
|
|
66
|
+
console.info('layer.labelEnabled is deprecated use addLabelLayer, instead of');
|
|
67
|
+
}
|
|
68
|
+
// Because the features are shared between layer and labelLayer.
|
|
69
|
+
layer.buildExtent = true;
|
|
70
|
+
// label layer needs 3d data structure.
|
|
71
|
+
layer.structure = '3d';
|
|
72
|
+
const labelLayer = new LabelLayer(`${layer.id}-label`, {
|
|
73
|
+
source,
|
|
74
|
+
style: layer.style,
|
|
75
|
+
zoom: layer.zoom,
|
|
76
|
+
performance: layer.addLabelLayer.performance,
|
|
77
|
+
crs: source.crs,
|
|
78
|
+
visible: layer.visible,
|
|
79
|
+
margin: 15,
|
|
80
|
+
forceClampToTerrain: layer.addLabelLayer.forceClampToTerrain
|
|
81
|
+
});
|
|
82
|
+
layer.addEventListener('visible-property-changed', () => {
|
|
83
|
+
labelLayer.visible = layer.visible;
|
|
84
|
+
});
|
|
85
|
+
const removeLabelLayer = e => {
|
|
86
|
+
if (e.layerId === layer.id) {
|
|
87
|
+
view.removeLayer(labelLayer.id);
|
|
88
|
+
}
|
|
89
|
+
view.removeEventListener(VIEW_EVENTS.LAYER_REMOVED, removeLabelLayer);
|
|
90
|
+
};
|
|
91
|
+
view.addEventListener(VIEW_EVENTS.LAYER_REMOVED, removeLabelLayer);
|
|
92
|
+
layer.whenReady = layer.whenReady.then(() => {
|
|
93
|
+
view.addLayer(labelLayer);
|
|
94
|
+
return layer;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (layer.isOGC3DTilesLayer) {
|
|
98
|
+
layer._setup(view);
|
|
99
|
+
}
|
|
100
|
+
return layer;
|
|
101
|
+
}
|
|
102
|
+
const _eventCoords = new THREE.Vector2();
|
|
103
|
+
const matrix = new THREE.Matrix4();
|
|
104
|
+
const screen = new THREE.Vector2();
|
|
105
|
+
const ray = new THREE.Ray();
|
|
106
|
+
const direction = new THREE.Vector3();
|
|
107
|
+
const positionVector = new THREE.Vector3();
|
|
108
|
+
const coordinates = new Coordinates('EPSG:4326');
|
|
109
|
+
const viewers = [];
|
|
110
|
+
// Size of the camera frustrum, in meters
|
|
111
|
+
let screenMeters;
|
|
112
|
+
let id = 0;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @property {number} id - The id of the view. It's incremented at each new view instance, starting at 0.
|
|
116
|
+
* @property {HTMLElement} domElement - The domElement holding the canvas where the view is displayed
|
|
117
|
+
* @property {String} referenceCrs - The coordinate reference system of the view
|
|
118
|
+
* @property {MainLoop} mainLoop - itowns mainloop scheduling the operations
|
|
119
|
+
* @property {THREE.Scene} scene - threejs scene of the view
|
|
120
|
+
* @property {Camera} camera - itowns camera (that holds a threejs camera that is directly accessible with View.camera3D)
|
|
121
|
+
* @property {THREE.Camera} camera3D - threejs camera that is stored in itowns camera
|
|
122
|
+
* @property {THREE.WebGLRenderer} renderer - threejs webglrenderer rendering this view
|
|
123
|
+
*/
|
|
124
|
+
class View extends THREE.EventDispatcher {
|
|
125
|
+
#layers = [];
|
|
126
|
+
#pixelDepthBuffer = (() => new Uint8Array(4))();
|
|
127
|
+
#fullSizeDepthBuffer;
|
|
128
|
+
/**
|
|
129
|
+
* Constructs an Itowns View instance
|
|
130
|
+
*
|
|
131
|
+
* @example <caption><b>Create a view with a custom Three.js camera.</b></caption>
|
|
132
|
+
* var viewerDiv = document.getElementById('viewerDiv');
|
|
133
|
+
* var customCamera = itowns.THREE.PerspectiveCamera();
|
|
134
|
+
* var view = itowns.View('EPSG:4326', viewerDiv, { camera: { cameraThree: customCamera } });
|
|
135
|
+
*
|
|
136
|
+
* @example <caption><b>Create a view with an orthographic camera, and grant it with Three.js custom controls.</b></caption>
|
|
137
|
+
* var viewerDiv = document.getElementById('viewerDiv');
|
|
138
|
+
* var view = itowns.View('EPSG:4326', viewerDiv, { camera: { type: itowns.CAMERA_TYPE.ORTHOGRAPHIC } });
|
|
139
|
+
* var customControls = itowns.THREE.OrbitControls(view.camera3D, viewerDiv);
|
|
140
|
+
*
|
|
141
|
+
* @param {String} crs - The default CRS of Three.js coordinates. Should be a cartesian CRS.
|
|
142
|
+
* @param {HTMLElement} viewerDiv - Where to instanciate the Three.js scene in the DOM
|
|
143
|
+
* @param {Object} [options] - Optional properties.
|
|
144
|
+
* @param {Object} [options.camera] - Options for the camera associated to the view. See {@link Camera} options.
|
|
145
|
+
* @param {MainLoop} [options.mainLoop] - {@link MainLoop} instance to use, otherwise a default one will be constructed
|
|
146
|
+
* @param {WebGLRenderer|Object} [options.renderer] - {@link WebGLRenderer} instance to use, otherwise
|
|
147
|
+
* a default one will be constructed. In this case, if options.renderer is an object, it will be used to
|
|
148
|
+
* configure the renderer (see {@link c3DEngine}. If not present, a new <canvas> will be created and
|
|
149
|
+
* added to viewerDiv (mutually exclusive with mainLoop)
|
|
150
|
+
* @param {Object} [options.webXR] - enable webxr button to switch on VR visualization.
|
|
151
|
+
* @param {number} [options.webXR.scale=1.0] - apply webxr scale tranformation.
|
|
152
|
+
* @param {Scene} [options.scene3D] - [THREE.Scene](https://threejs.org/docs/#api/en/scenes/Scene) instance to use, otherwise a default one will be constructed
|
|
153
|
+
* @param {Color} [options.diffuse] - [THREE.Color](https://threejs.org/docs/?q=color#api/en/math/Color) Diffuse color terrain material.
|
|
154
|
+
* This color is applied to terrain if there isn't color layer on terrain extent (by example on pole).
|
|
155
|
+
* @param {boolean} [options.enableFocusOnStart=true] - enable focus on dom element on start.
|
|
156
|
+
*/
|
|
157
|
+
constructor(crs, viewerDiv) {
|
|
158
|
+
let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
159
|
+
if (!viewerDiv) {
|
|
160
|
+
throw new Error('Invalid viewerDiv parameter (must non be null/undefined)');
|
|
161
|
+
}
|
|
162
|
+
super();
|
|
163
|
+
this.domElement = viewerDiv;
|
|
164
|
+
this.id = id++;
|
|
165
|
+
this.referenceCrs = crs;
|
|
166
|
+
let engine;
|
|
167
|
+
// options.renderer can be 2 separate things:
|
|
168
|
+
// - an actual renderer (in this case we don't use viewerDiv)
|
|
169
|
+
// - options for the renderer to be created
|
|
170
|
+
if (options.renderer && options.renderer.domElement) {
|
|
171
|
+
engine = new c3DEngine(options.renderer);
|
|
172
|
+
} else {
|
|
173
|
+
engine = new c3DEngine(viewerDiv, options.renderer);
|
|
174
|
+
}
|
|
175
|
+
this.mainLoop = options.mainLoop || new MainLoop(new Scheduler(), engine);
|
|
176
|
+
this.scene = options.scene3D || new THREE.Scene();
|
|
177
|
+
if (!options.scene3D) {
|
|
178
|
+
this.scene.matrixWorldAutoUpdate = false;
|
|
179
|
+
}
|
|
180
|
+
this.camera = new Camera(this.referenceCrs, this.mainLoop.gfxEngine.getWindowSize().x, this.mainLoop.gfxEngine.getWindowSize().y, options.camera);
|
|
181
|
+
this._frameRequesters = {};
|
|
182
|
+
this._resizeListener = () => this.resize();
|
|
183
|
+
window.addEventListener('resize', this._resizeListener, false);
|
|
184
|
+
this._changeSources = new Set();
|
|
185
|
+
this._delayedFrameRequesterRemoval = [];
|
|
186
|
+
this._allLayersAreReadyCallback = () => {
|
|
187
|
+
// all layers must be ready
|
|
188
|
+
const allReady = this.getLayers().every(layer => layer.ready);
|
|
189
|
+
if (allReady && this.mainLoop.scheduler.commandsWaitingExecutionCount() == 0 && this.mainLoop.renderingState == RENDERING_PAUSED) {
|
|
190
|
+
this.dispatchEvent({
|
|
191
|
+
type: VIEW_EVENTS.LAYERS_INITIALIZED
|
|
192
|
+
});
|
|
193
|
+
this.removeFrameRequester(MAIN_LOOP_EVENTS.UPDATE_END, this._allLayersAreReadyCallback);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
this.camera.resize(this.domElement.clientWidth, this.domElement.clientHeight);
|
|
197
|
+
const fn = () => {
|
|
198
|
+
this.removeEventListener(VIEW_EVENTS.LAYERS_INITIALIZED, fn);
|
|
199
|
+
this.dispatchEvent({
|
|
200
|
+
type: VIEW_EVENTS.INITIALIZED
|
|
201
|
+
});
|
|
202
|
+
};
|
|
203
|
+
this.addEventListener(VIEW_EVENTS.LAYERS_INITIALIZED, fn);
|
|
204
|
+
this.#fullSizeDepthBuffer = new Uint8Array(4 * this.camera.width * this.camera.height);
|
|
205
|
+
|
|
206
|
+
// Indicates that view's domElement can be focused (the negative value indicates that domElement can't be
|
|
207
|
+
// focused sequentially using tab key). Focus is needed to capture some key events.
|
|
208
|
+
this.domElement.tabIndex = -1;
|
|
209
|
+
// Set focus on view's domElement.
|
|
210
|
+
if (!options.disableFocusOnStart) {
|
|
211
|
+
this.domElement.focus();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Create a custom `dblclick-right` event that is triggered when double right-clicking
|
|
215
|
+
let rightClickTimeStamp;
|
|
216
|
+
this.domElement.addEventListener('mouseup', event => {
|
|
217
|
+
if (event.button === 2) {
|
|
218
|
+
// If pressed mouse button is right button
|
|
219
|
+
// If time between two right-clicks is bellow 500 ms, triggers a `dblclick-right` event
|
|
220
|
+
if (rightClickTimeStamp && event.timeStamp - rightClickTimeStamp < 500) {
|
|
221
|
+
this.domElement.dispatchEvent(new MouseEvent('dblclick-right', event));
|
|
222
|
+
}
|
|
223
|
+
rightClickTimeStamp = event.timeStamp;
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// push all viewer to keep source.cache
|
|
228
|
+
viewers.push(this);
|
|
229
|
+
if (options.webXR) {
|
|
230
|
+
initializeWebXR(this, options.webXR);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Get the Threejs renderer used to render this view.
|
|
236
|
+
* @returns {THREE.WebGLRenderer} the WebGLRenderer used to render this view.
|
|
237
|
+
*/
|
|
238
|
+
get renderer() {
|
|
239
|
+
return this.mainLoop?.gfxEngine?.getRenderer();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Get the threejs Camera of this view
|
|
244
|
+
* @returns {THREE.Camera} the threejs camera of this view
|
|
245
|
+
*/
|
|
246
|
+
get camera3D() {
|
|
247
|
+
return this.camera?.camera3D;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Dispose viewer before delete it.
|
|
252
|
+
*
|
|
253
|
+
* Method dispose all viewer objects
|
|
254
|
+
* - remove control
|
|
255
|
+
* - remove all layers
|
|
256
|
+
* - remove all frame requester
|
|
257
|
+
* - remove all events
|
|
258
|
+
* @param {boolean} [clearCache=false] Whether to clear all the caches or not (layers cache, style cache, tilesCache)
|
|
259
|
+
*/
|
|
260
|
+
dispose() {
|
|
261
|
+
let clearCache = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
|
262
|
+
const id = viewers.indexOf(this);
|
|
263
|
+
if (id == -1) {
|
|
264
|
+
console.warn('View already disposed');
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
window.removeEventListener('resize', this._resizeListener);
|
|
268
|
+
|
|
269
|
+
// controls dispose
|
|
270
|
+
if (this.controls) {
|
|
271
|
+
if (typeof this.controls.dispose === 'function') {
|
|
272
|
+
this.controls.dispose();
|
|
273
|
+
}
|
|
274
|
+
delete this.controls;
|
|
275
|
+
}
|
|
276
|
+
// remove alls frameRequester
|
|
277
|
+
this.removeAllFrameRequesters();
|
|
278
|
+
// remove all layers
|
|
279
|
+
const layers = this.getLayers(l => !l.isTiledGeometryLayer && !l.isAtmosphere);
|
|
280
|
+
for (const layer of layers) {
|
|
281
|
+
this.removeLayer(layer.id, clearCache);
|
|
282
|
+
}
|
|
283
|
+
const atmospheres = this.getLayers(l => l.isAtmosphere);
|
|
284
|
+
for (const atmosphere of atmospheres) {
|
|
285
|
+
this.removeLayer(atmosphere.id, clearCache);
|
|
286
|
+
}
|
|
287
|
+
const tileLayers = this.getLayers(l => l.isTiledGeometryLayer);
|
|
288
|
+
for (const tileLayer of tileLayers) {
|
|
289
|
+
this.removeLayer(tileLayer.id, clearCache);
|
|
290
|
+
}
|
|
291
|
+
viewers.splice(id, 1);
|
|
292
|
+
// Remove remaining objects in the scene (e.g. helpers, debug, etc.)
|
|
293
|
+
this.scene.traverse(ObjectRemovalHelper.cleanup);
|
|
294
|
+
this.dispatchEvent({
|
|
295
|
+
type: VIEW_EVENTS.DISPOSED
|
|
296
|
+
});
|
|
297
|
+
// remove alls events
|
|
298
|
+
this.removeAllEvents();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Add layer in viewer.
|
|
303
|
+
* The layer id must be unique.
|
|
304
|
+
*
|
|
305
|
+
* The `layer.whenReady` is a promise that resolves when
|
|
306
|
+
* the layer is done. This promise is also returned by
|
|
307
|
+
* `addLayer` allowing to chain call.
|
|
308
|
+
*
|
|
309
|
+
* @param {LayerOptions|Layer|GeometryLayer} layer The layer to add in view.
|
|
310
|
+
* @param {Layer=} parentLayer it's the layer to which the layer will be attached.
|
|
311
|
+
* @return {Promise} a promise resolved with the new layer object when it is fully initialized or rejected if any error occurred.
|
|
312
|
+
*/
|
|
313
|
+
addLayer(layer, parentLayer) {
|
|
314
|
+
if (!layer || !layer.isLayer) {
|
|
315
|
+
return Promise.reject(new Error('Add Layer type object'));
|
|
316
|
+
}
|
|
317
|
+
const duplicate = this.getLayerById(layer.id);
|
|
318
|
+
if (duplicate) {
|
|
319
|
+
return layer._reject(new Error(`Invalid id '${layer.id}': id already used`));
|
|
320
|
+
}
|
|
321
|
+
layer = _preprocessLayer(this, layer, parentLayer);
|
|
322
|
+
if (parentLayer) {
|
|
323
|
+
if (layer.isColorLayer) {
|
|
324
|
+
const layerColors = this.getLayers(l => l.isColorLayer);
|
|
325
|
+
layer.sequence = layerColors.length;
|
|
326
|
+
const sumColorLayers = parentLayer.countColorLayersTextures(...layerColors, layer);
|
|
327
|
+
if (sumColorLayers <= getMaxColorSamplerUnitsCount()) {
|
|
328
|
+
parentLayer.attach(layer);
|
|
329
|
+
} else {
|
|
330
|
+
return layer._reject(new Error(`Cant add color layer ${layer.id}: the maximum layer is reached`));
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
parentLayer.attach(layer);
|
|
334
|
+
}
|
|
335
|
+
} else {
|
|
336
|
+
if (typeof layer.update !== 'function') {
|
|
337
|
+
return layer._reject(new Error('Cant add GeometryLayer: missing a update function'));
|
|
338
|
+
}
|
|
339
|
+
if (typeof layer.preUpdate !== 'function') {
|
|
340
|
+
return layer._reject(new Error('Cant add GeometryLayer: missing a preUpdate function'));
|
|
341
|
+
}
|
|
342
|
+
this.#layers.push(layer);
|
|
343
|
+
}
|
|
344
|
+
if (layer.object3d && !layer.object3d.parent && layer.object3d !== this.scene) {
|
|
345
|
+
this.scene.add(layer.object3d);
|
|
346
|
+
}
|
|
347
|
+
Promise.all(layer._promises).then(() => {
|
|
348
|
+
layer._resolve();
|
|
349
|
+
this.notifyChange(parentLayer || layer, false);
|
|
350
|
+
if (!this._frameRequesters[MAIN_LOOP_EVENTS.UPDATE_END] || !this._frameRequesters[MAIN_LOOP_EVENTS.UPDATE_END].includes(this._allLayersAreReadyCallback)) {
|
|
351
|
+
this.addFrameRequester(MAIN_LOOP_EVENTS.UPDATE_END, this._allLayersAreReadyCallback);
|
|
352
|
+
}
|
|
353
|
+
this.dispatchEvent({
|
|
354
|
+
type: VIEW_EVENTS.LAYER_ADDED,
|
|
355
|
+
layerId: layer.id
|
|
356
|
+
});
|
|
357
|
+
}, layer._reject);
|
|
358
|
+
return layer.whenReady;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Removes a specific imagery layer from the current layer list. This removes layers inserted with attach().
|
|
363
|
+
* @example
|
|
364
|
+
* view.removeLayer('layerId');
|
|
365
|
+
* @param {string} layerId The identifier
|
|
366
|
+
* @param {boolean} [clearCache=false] Whether to clear all the layer cache or not
|
|
367
|
+
* @return {boolean}
|
|
368
|
+
*/
|
|
369
|
+
removeLayer(layerId, clearCache) {
|
|
370
|
+
const layer = this.getLayerById(layerId);
|
|
371
|
+
if (layer) {
|
|
372
|
+
const parentLayer = layer.parent;
|
|
373
|
+
|
|
374
|
+
// Remove and dispose all nodes
|
|
375
|
+
layer.delete(clearCache);
|
|
376
|
+
|
|
377
|
+
// Detach layer if it's attached
|
|
378
|
+
if (parentLayer && !parentLayer.detach(layer)) {
|
|
379
|
+
throw new Error(`Error to detach ${layerId} from ${parentLayer.id}`);
|
|
380
|
+
} else if (parentLayer == undefined) {
|
|
381
|
+
// Remove layer from viewer
|
|
382
|
+
this.#layers.splice(this.#layers.findIndex(l => l.id == layerId), 1);
|
|
383
|
+
}
|
|
384
|
+
if (layer.isColorLayer) {
|
|
385
|
+
// Update color layers sequence
|
|
386
|
+
const imageryLayers = this.getLayers(l => l.isColorLayer);
|
|
387
|
+
for (const color of imageryLayers) {
|
|
388
|
+
if (color.sequence > layer.sequence) {
|
|
389
|
+
color.sequence--;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Remove unused cache in all viewers
|
|
395
|
+
|
|
396
|
+
// count of times the source is used in all viewer
|
|
397
|
+
let sharedSourceCount = 0;
|
|
398
|
+
for (const view of viewers) {
|
|
399
|
+
// add count of times the source is used in other layers
|
|
400
|
+
sharedSourceCount += view.getLayers(l => l.source.uid == layer.source.uid && l.crs == layer.crs).length;
|
|
401
|
+
}
|
|
402
|
+
// if sharedSourceCount equals to 0 so remove unused cache for this CRS
|
|
403
|
+
layer.source.onLayerRemoved({
|
|
404
|
+
unusedCrs: sharedSourceCount == 0 ? layer.crs : undefined
|
|
405
|
+
});
|
|
406
|
+
this.notifyChange(this.camera);
|
|
407
|
+
this.dispatchEvent({
|
|
408
|
+
type: VIEW_EVENTS.LAYER_REMOVED,
|
|
409
|
+
layerId
|
|
410
|
+
});
|
|
411
|
+
return true;
|
|
412
|
+
} else {
|
|
413
|
+
throw new Error(`${layerId} doesn't exist`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Notifies the scene it needs to be updated due to changes exterior to the
|
|
419
|
+
* scene itself (e.g. camera movement).
|
|
420
|
+
* non-interactive events (e.g: texture loaded)
|
|
421
|
+
* @param {*} changeSource
|
|
422
|
+
* @param {boolean} needsRedraw - indicates if notified change requires a full scene redraw.
|
|
423
|
+
*/
|
|
424
|
+
notifyChange() {
|
|
425
|
+
let changeSource = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
|
|
426
|
+
let needsRedraw = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
|
427
|
+
if (changeSource) {
|
|
428
|
+
this._changeSources.add(changeSource);
|
|
429
|
+
if (!this.mainLoop.gfxEngine.renderer.xr.isPresenting && (changeSource.isTileMesh || changeSource.isCamera)) {
|
|
430
|
+
this.#fullSizeDepthBuffer.needsUpdate = true;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
this.mainLoop.scheduleViewUpdate(this, needsRedraw);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Get all layers, with an optionnal filter applied.
|
|
438
|
+
* The filter method will be called with 2 args:
|
|
439
|
+
* - 1st: current layer
|
|
440
|
+
* - 2nd: (optional) the geometry layer to which the current layer is attached
|
|
441
|
+
* @example
|
|
442
|
+
* // get all layers
|
|
443
|
+
* view.getLayers();
|
|
444
|
+
* // get all color layers
|
|
445
|
+
* view.getLayers(layer => layer.isColorLayer);
|
|
446
|
+
* // get all elevation layers
|
|
447
|
+
* view.getLayers(layer => layer.isElevationLayer);
|
|
448
|
+
* // get all geometry layers
|
|
449
|
+
* view.getLayers(layer => layer.isGeometryLayer);
|
|
450
|
+
* // get one layer with id
|
|
451
|
+
* view.getLayers(layer => layer.id === 'itt');
|
|
452
|
+
* @param {function(Layer):boolean} filter
|
|
453
|
+
* @returns {Array<Layer>}
|
|
454
|
+
*/
|
|
455
|
+
getLayers(filter) {
|
|
456
|
+
const result = [];
|
|
457
|
+
for (const layer of this.#layers) {
|
|
458
|
+
if (!filter || filter(layer)) {
|
|
459
|
+
result.push(layer);
|
|
460
|
+
}
|
|
461
|
+
if (layer.attachedLayers) {
|
|
462
|
+
for (const attached of layer.attachedLayers) {
|
|
463
|
+
if (!filter || filter(attached, layer)) {
|
|
464
|
+
result.push(attached);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return result;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Gets the layer by identifier.
|
|
474
|
+
*
|
|
475
|
+
* @param {String} layerId The layer identifier
|
|
476
|
+
* @return {Layer} The layer by identifier.
|
|
477
|
+
*/
|
|
478
|
+
|
|
479
|
+
getLayerById(layerId) {
|
|
480
|
+
return this.getLayers(l => l.id === layerId)[0];
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* @name FrameRequester
|
|
485
|
+
* @function
|
|
486
|
+
*
|
|
487
|
+
* @description
|
|
488
|
+
* Method that will be called each time the `MainLoop` updates. This function
|
|
489
|
+
* will be given as parameter the delta (in ms) between this update and the
|
|
490
|
+
* previous one, and whether or not we just started to render again. This update
|
|
491
|
+
* is considered as the "next" update if `view.notifyChange` was called during a
|
|
492
|
+
* precedent update. If `view.notifyChange` has been called by something else
|
|
493
|
+
* (other micro/macrotask, UI events etc...), then this update is considered as
|
|
494
|
+
* being the "first". It can also receive optional arguments, depending on the
|
|
495
|
+
* attach point of this function. Currently only `BEFORE_LAYER_UPDATE /
|
|
496
|
+
* AFTER_LAYER_UPDATE` attach points provide an additional argument: the layer
|
|
497
|
+
* being updated.
|
|
498
|
+
* <br><br>
|
|
499
|
+
*
|
|
500
|
+
* This means that if a `frameRequester` function wants to animate something, it
|
|
501
|
+
* should keep on calling `view.notifyChange` until its task is done.
|
|
502
|
+
* <br><br>
|
|
503
|
+
*
|
|
504
|
+
* Implementors of `frameRequester` should keep in mind that this function will
|
|
505
|
+
* be potentially called at each frame, thus care should be given about
|
|
506
|
+
* performance.
|
|
507
|
+
* <br><br>
|
|
508
|
+
*
|
|
509
|
+
* Typical frameRequesters are controls, module wanting to animate moves or UI
|
|
510
|
+
* elements etc... Basically anything that would want to call
|
|
511
|
+
* requestAnimationFrame.
|
|
512
|
+
*
|
|
513
|
+
* @param {number} dt
|
|
514
|
+
* @param {boolean} updateLoopRestarted
|
|
515
|
+
* @param {...*} args
|
|
516
|
+
*/
|
|
517
|
+
/**
|
|
518
|
+
* Add a frame requester to this view.
|
|
519
|
+
*
|
|
520
|
+
* FrameRequesters can activate the MainLoop update by calling view.notifyChange.
|
|
521
|
+
*
|
|
522
|
+
* @param {String} when - decide when the frameRequester should be called during
|
|
523
|
+
* the update cycle. Can be any of {@link MAIN_LOOP_EVENTS}.
|
|
524
|
+
* @param {FrameRequester} frameRequester - this function will be called at each
|
|
525
|
+
* MainLoop update with the time delta between last update, or 0 if the MainLoop
|
|
526
|
+
* has just been relaunched.
|
|
527
|
+
*/
|
|
528
|
+
addFrameRequester(when, frameRequester) {
|
|
529
|
+
if (typeof frameRequester !== 'function') {
|
|
530
|
+
throw new Error('frameRequester must be a function');
|
|
531
|
+
}
|
|
532
|
+
if (!this._frameRequesters[when]) {
|
|
533
|
+
this._frameRequesters[when] = [frameRequester];
|
|
534
|
+
} else {
|
|
535
|
+
this._frameRequesters[when].push(frameRequester);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Remove a frameRequester.
|
|
541
|
+
* The effective removal will happen either later; at worst it'll be at
|
|
542
|
+
* the beginning of the next frame.
|
|
543
|
+
*
|
|
544
|
+
* @param {String} when - attach point of this requester. Can be any of
|
|
545
|
+
* {@link MAIN_LOOP_EVENTS}.
|
|
546
|
+
* @param {FrameRequester} frameRequester
|
|
547
|
+
*/
|
|
548
|
+
removeFrameRequester(when, frameRequester) {
|
|
549
|
+
if (this._frameRequesters[when].includes(frameRequester)) {
|
|
550
|
+
this._delayedFrameRequesterRemoval.push({
|
|
551
|
+
when,
|
|
552
|
+
frameRequester
|
|
553
|
+
});
|
|
554
|
+
} else {
|
|
555
|
+
console.error('Invalid call to removeFrameRequester: frameRequester isn\'t registered');
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Removes all frame requesters.
|
|
561
|
+
*/
|
|
562
|
+
removeAllFrameRequesters() {
|
|
563
|
+
for (const when in this._frameRequesters) {
|
|
564
|
+
if (Object.prototype.hasOwnProperty.call(this._frameRequesters, when)) {
|
|
565
|
+
const frameRequesters = this._frameRequesters[when];
|
|
566
|
+
for (const frameRequester of frameRequesters) {
|
|
567
|
+
this.removeFrameRequester(when, frameRequester);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
this._executeFrameRequestersRemovals();
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Removes all viewer events.
|
|
576
|
+
*/
|
|
577
|
+
removeAllEvents() {
|
|
578
|
+
if (this._listeners === undefined) {
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
for (const type in this._listeners) {
|
|
582
|
+
if (Object.prototype.hasOwnProperty.call(this._listeners, type)) {
|
|
583
|
+
delete this._listeners[type];
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
this._listeners = undefined;
|
|
587
|
+
}
|
|
588
|
+
_executeFrameRequestersRemovals() {
|
|
589
|
+
for (const toDelete of this._delayedFrameRequesterRemoval) {
|
|
590
|
+
const index = this._frameRequesters[toDelete.when].indexOf(toDelete.frameRequester);
|
|
591
|
+
if (index >= 0) {
|
|
592
|
+
this._frameRequesters[toDelete.when].splice(index, 1);
|
|
593
|
+
} else {
|
|
594
|
+
console.warn('FrameReq has already been removed');
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
this._delayedFrameRequesterRemoval.length = 0;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Execute a frameRequester.
|
|
602
|
+
*
|
|
603
|
+
* @param {String} when - attach point of this (these) requester(s). Can be any
|
|
604
|
+
* of {@link MAIN_LOOP_EVENTS}.
|
|
605
|
+
* @param {Number} dt - delta between this update and the previous one
|
|
606
|
+
* @param {boolean} updateLoopRestarted
|
|
607
|
+
* @param {...*} args - optional arguments
|
|
608
|
+
*/
|
|
609
|
+
execFrameRequesters(when, dt, updateLoopRestarted) {
|
|
610
|
+
if (!this._frameRequesters[when]) {
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
if (this._delayedFrameRequesterRemoval.length > 0) {
|
|
614
|
+
this._executeFrameRequestersRemovals();
|
|
615
|
+
}
|
|
616
|
+
for (var _len = arguments.length, args = new Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
|
|
617
|
+
args[_key - 3] = arguments[_key];
|
|
618
|
+
}
|
|
619
|
+
for (const frameRequester of this._frameRequesters[when]) {
|
|
620
|
+
if (frameRequester.update) {
|
|
621
|
+
frameRequester.update(dt, updateLoopRestarted, args);
|
|
622
|
+
} else {
|
|
623
|
+
frameRequester(dt, updateLoopRestarted, args);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Extract view coordinates from a mouse-event / touch-event
|
|
630
|
+
* @param {event} event - event can be a MouseEvent or a TouchEvent
|
|
631
|
+
* @param {THREE.Vector2} target - the target to set the view coords in
|
|
632
|
+
* @param {number} [touchIdx=0] - finger index when using a TouchEvent
|
|
633
|
+
* @return {THREE.Vector2|undefined} - view coordinates (in pixels, 0-0 = top-left of the View).
|
|
634
|
+
* If the event is neither a `MouseEvent` nor a `TouchEvent`, the return is `undefined`.
|
|
635
|
+
*/
|
|
636
|
+
eventToViewCoords(event) {
|
|
637
|
+
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _eventCoords;
|
|
638
|
+
let touchIdx = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
|
|
639
|
+
const br = this.domElement.getBoundingClientRect();
|
|
640
|
+
if (event.touches && event.touches.length) {
|
|
641
|
+
return target.set(event.touches[touchIdx].clientX - br.x, event.touches[touchIdx].clientY - br.y);
|
|
642
|
+
} else if (event.offsetX !== undefined && event.offsetY !== undefined) {
|
|
643
|
+
const targetBoundingRect = event.target.getBoundingClientRect();
|
|
644
|
+
return target.set(targetBoundingRect.x + event.offsetX - br.x, targetBoundingRect.y + event.offsetY - br.y);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Extract normalized coordinates (NDC) from a mouse-event / touch-event
|
|
650
|
+
* @param {event} event - event can be a MouseEvent or a TouchEvent
|
|
651
|
+
* @param {number} touchIdx - finger index when using a TouchEvent (default: 0)
|
|
652
|
+
* @return {THREE.Vector2} - NDC coordinates (x and y are [-1, 1])
|
|
653
|
+
*/
|
|
654
|
+
eventToNormalizedCoords(event) {
|
|
655
|
+
let touchIdx = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
656
|
+
return this.viewToNormalizedCoords(this.eventToViewCoords(event, _eventCoords, touchIdx));
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Convert view coordinates to normalized coordinates (NDC)
|
|
661
|
+
* @param {THREE.Vector2} viewCoords (in pixels, 0-0 = top-left of the View)
|
|
662
|
+
* @param {THREE.Vector2} target
|
|
663
|
+
* @return {THREE.Vector2} - NDC coordinates (x and y are [-1, 1])
|
|
664
|
+
*/
|
|
665
|
+
viewToNormalizedCoords(viewCoords) {
|
|
666
|
+
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _eventCoords;
|
|
667
|
+
target.x = 2 * (viewCoords.x / this.camera.width) - 1;
|
|
668
|
+
target.y = -2 * (viewCoords.y / this.camera.height) + 1;
|
|
669
|
+
return target;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Convert NDC coordinates to view coordinates
|
|
674
|
+
* @param {THREE.Vector2} ndcCoords
|
|
675
|
+
* @return {THREE.Vector2} - view coordinates (in pixels, 0-0 = top-left of the View)
|
|
676
|
+
*/
|
|
677
|
+
normalizedToViewCoords(ndcCoords) {
|
|
678
|
+
_eventCoords.x = (ndcCoords.x + 1) * 0.5 * this.camera.width;
|
|
679
|
+
_eventCoords.y = (ndcCoords.y - 1) * -0.5 * this.camera.height;
|
|
680
|
+
return _eventCoords;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Searches for objects in {@link GeometryLayer} and specified
|
|
685
|
+
* `THREE.Object3D`, under the mouse or at a specified coordinates, in this
|
|
686
|
+
* view.
|
|
687
|
+
*
|
|
688
|
+
* @param {Object} mouseOrEvt - Mouse position in window coordinates (from
|
|
689
|
+
* the top left corner of the window) or `MouseEvent` or `TouchEvent`.
|
|
690
|
+
* @param {number} [radius=0] - The picking will happen in a circle centered
|
|
691
|
+
* on mouseOrEvt. This is the radius of this circle, in pixels.
|
|
692
|
+
* @param {GeometryLayer|string|Object3D|Array<GeometryLayer|string|Object3D>} [where] - Where to look for
|
|
693
|
+
* objects. It can be a single {@link GeometryLayer}, `THREE.Object3D`, ID of a layer or an array of one of these or
|
|
694
|
+
* of a mix of these. If no location is specified, it will query on all {@link GeometryLayer} present in this `View`.
|
|
695
|
+
*
|
|
696
|
+
* @return {Object[]} - An array of objects. Each element contains at least
|
|
697
|
+
* an object property which is the `THREE.Object3D` under the cursor. Then
|
|
698
|
+
* depending on the queried layer/source, there may be additionnal
|
|
699
|
+
* properties (coming from `THREE.Raycaster` for instance).
|
|
700
|
+
*
|
|
701
|
+
* @example
|
|
702
|
+
* view.pickObjectsAt({ x, y })
|
|
703
|
+
* view.pickObjectsAt({ x, y }, 1, 'wfsBuilding')
|
|
704
|
+
* view.pickObjectsAt({ x, y }, 3, 'wfsBuilding', myLayer)
|
|
705
|
+
*/
|
|
706
|
+
pickObjectsAt(mouseOrEvt) {
|
|
707
|
+
let radius = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
708
|
+
let where = arguments.length > 2 ? arguments[2] : undefined;
|
|
709
|
+
const sources = [];
|
|
710
|
+
if (!where || where.length === 0) {
|
|
711
|
+
where = this.getLayers(l => l.isGeometryLayer);
|
|
712
|
+
}
|
|
713
|
+
if (!Array.isArray(where)) {
|
|
714
|
+
where = [where];
|
|
715
|
+
}
|
|
716
|
+
where.forEach(l => {
|
|
717
|
+
if (typeof l === 'string') {
|
|
718
|
+
l = this.getLayerById(l);
|
|
719
|
+
}
|
|
720
|
+
if (l && (l.isGeometryLayer || l.isObject3D)) {
|
|
721
|
+
sources.push(l);
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
if (sources.length == 0) {
|
|
725
|
+
return [];
|
|
726
|
+
}
|
|
727
|
+
const results = [];
|
|
728
|
+
const mouse = mouseOrEvt instanceof Event ? this.eventToViewCoords(mouseOrEvt) : mouseOrEvt;
|
|
729
|
+
for (const source of sources) {
|
|
730
|
+
if (source.isAtmosphere) {
|
|
731
|
+
continue;
|
|
732
|
+
}
|
|
733
|
+
if (source.isGeometryLayer) {
|
|
734
|
+
if (!source.ready) {
|
|
735
|
+
console.warn('view.pickObjectAt : layer is not ready : ', source);
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
source.pickObjectsAt(this, mouse, radius, results);
|
|
739
|
+
} else {
|
|
740
|
+
Picking.pickObjectsAt(this, mouse, radius, source, results);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
return results;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Return the current zoom scale at the central point of the view. This
|
|
748
|
+
* function compute the scale of a map.
|
|
749
|
+
*
|
|
750
|
+
* @param {number} pitch - Screen pitch, in millimeters ; 0.28 by default
|
|
751
|
+
*
|
|
752
|
+
* @return {number} The zoom scale.
|
|
753
|
+
*/
|
|
754
|
+
getScale() {
|
|
755
|
+
let pitch = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0.28;
|
|
756
|
+
if (this.camera3D.isOrthographicCamera) {
|
|
757
|
+
return pitch * 1E-3 / this.getPixelsToMeters();
|
|
758
|
+
}
|
|
759
|
+
return this.getScaleFromDistance(pitch, this.getDistanceFromCamera());
|
|
760
|
+
}
|
|
761
|
+
getScaleFromDistance() {
|
|
762
|
+
let pitch = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0.28;
|
|
763
|
+
let distance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
|
|
764
|
+
pitch /= 1000;
|
|
765
|
+
const fov = THREE.MathUtils.degToRad(this.camera3D.fov);
|
|
766
|
+
const unit = this.camera.height / (2 * distance * Math.tan(fov * 0.5));
|
|
767
|
+
return pitch * unit;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Given a screen coordinates, get the distance between the projected
|
|
772
|
+
* coordinates and the camera associated to this view.
|
|
773
|
+
*
|
|
774
|
+
* @param {THREE.Vector2} [screenCoord] - The screen coordinate to get the
|
|
775
|
+
* distance at. By default this is the middle of the screen.
|
|
776
|
+
*
|
|
777
|
+
* @return {number} The distance in meters.
|
|
778
|
+
*/
|
|
779
|
+
getDistanceFromCamera(screenCoord) {
|
|
780
|
+
this.getPickingPositionFromDepth(screenCoord, positionVector);
|
|
781
|
+
return this.camera3D.position.distanceTo(positionVector);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Get, for a specific screen coordinate, the projected distance on the
|
|
786
|
+
* surface of the main layer of the view.
|
|
787
|
+
*
|
|
788
|
+
* @param {number} [pixels=1] - The size, in pixels, to get in meters.
|
|
789
|
+
* @param {THREE.Vector2} [screenCoord] - The screen coordinate to get the
|
|
790
|
+
* projected distance at. By default, this is the middle of the screen.
|
|
791
|
+
*
|
|
792
|
+
* @return {number} The projected distance in meters.
|
|
793
|
+
*/
|
|
794
|
+
getPixelsToMeters() {
|
|
795
|
+
let pixels = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
|
|
796
|
+
let screenCoord = arguments.length > 1 ? arguments[1] : undefined;
|
|
797
|
+
if (this.camera3D.isOrthographicCamera) {
|
|
798
|
+
screenMeters = (this.camera3D.right - this.camera3D.left) / this.camera3D.zoom;
|
|
799
|
+
return pixels * screenMeters / this.camera.width;
|
|
800
|
+
}
|
|
801
|
+
return this.getPixelsToMetersFromDistance(pixels, this.getDistanceFromCamera(screenCoord));
|
|
802
|
+
}
|
|
803
|
+
getPixelsToMetersFromDistance() {
|
|
804
|
+
let pixels = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
|
|
805
|
+
let distance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
|
|
806
|
+
return pixels * distance / this.camera._preSSE;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* Get, for a specific screen coordinate, the size in pixels of a projected
|
|
811
|
+
* distance on the surface of the main layer of the view.
|
|
812
|
+
*
|
|
813
|
+
* @param {number} [meters=1] - The size, in meters, to get in pixels.
|
|
814
|
+
* @param {THREE.Vector2} [screenCoord] - The screen coordinate to get the
|
|
815
|
+
* projected distance at. By default, this is the middle of the screen.
|
|
816
|
+
*
|
|
817
|
+
* @return {number} The projected distance in pixels.
|
|
818
|
+
*/
|
|
819
|
+
getMetersToPixels() {
|
|
820
|
+
let meters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
|
|
821
|
+
let screenCoord = arguments.length > 1 ? arguments[1] : undefined;
|
|
822
|
+
if (this.camera3D.isOrthographicCamera) {
|
|
823
|
+
screenMeters = (this.camera3D.right - this.camera3D.left) / this.camera3D.zoom;
|
|
824
|
+
return meters * this.camera.width / screenMeters;
|
|
825
|
+
}
|
|
826
|
+
return this.getMetersToPixelsFromDistance(meters, this.getDistanceFromCamera(screenCoord));
|
|
827
|
+
}
|
|
828
|
+
getMetersToPixelsFromDistance() {
|
|
829
|
+
let meters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
|
|
830
|
+
let distance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
|
|
831
|
+
return this.camera._preSSE * meters / distance;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Searches for {@link FeatureGeometry} in {@link ColorLayer}, under the mouse or at
|
|
836
|
+
* the specified coordinates, in this view. Combining them per layer and in a Feature
|
|
837
|
+
* like format.
|
|
838
|
+
*
|
|
839
|
+
* @param {Object} mouseOrEvt - Mouse position in window coordinates (from
|
|
840
|
+
* the top left corner of the window) or `MouseEvent` or `TouchEvent`.
|
|
841
|
+
* @param {number} [radius=3] - The picking will happen in a circle centered
|
|
842
|
+
* on mouseOrEvt. This is the radius of this circle, in pixels.
|
|
843
|
+
* @param {...ColorLayer|GeometryLayer|string} [where] - The layers to look
|
|
844
|
+
* into. If not specified, all {@link ColorLayer} and {@link GeometryLayer}
|
|
845
|
+
* layers of this view will be looked in.
|
|
846
|
+
*
|
|
847
|
+
* @return {Object} - An object, having one property per layer.
|
|
848
|
+
* For example, looking for features on layers `wfsBuilding` and `wfsRoads`
|
|
849
|
+
* will give an object like `{ wfsBuilding: [...], wfsRoads: [] }`.
|
|
850
|
+
* Each property is made of an array, that can be empty or filled with
|
|
851
|
+
* Feature like objects composed of:
|
|
852
|
+
* - the FeatureGeometry
|
|
853
|
+
* - the feature type
|
|
854
|
+
* - the style
|
|
855
|
+
* - the coordinate if the FeatureGeometry is a point
|
|
856
|
+
*
|
|
857
|
+
* @example
|
|
858
|
+
* view.pickFeaturesAt({ x, y });
|
|
859
|
+
* view.pickFeaturesAt({ x, y }, 1, 'wfsBuilding');
|
|
860
|
+
* view.pickFeaturesAt({ x, y }, 3, 'wfsBuilding', myLayer);
|
|
861
|
+
*/
|
|
862
|
+
pickFeaturesAt(mouseOrEvt) {
|
|
863
|
+
let radius = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3;
|
|
864
|
+
for (var _len2 = arguments.length, where = new Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
|
|
865
|
+
where[_key2 - 2] = arguments[_key2];
|
|
866
|
+
}
|
|
867
|
+
if (Array.isArray(where[0])) {
|
|
868
|
+
console.warn('Deprecated: the ...where argument of View#pickFeaturesAt should not be an array anymore, but a list: use the spread operator if needed.');
|
|
869
|
+
where = where[0];
|
|
870
|
+
}
|
|
871
|
+
const layers = [];
|
|
872
|
+
const result = {};
|
|
873
|
+
where = where.length == 0 ? this.getLayers(l => l.isColorLayer || l.isGeometryLayer) : where;
|
|
874
|
+
where.forEach(l => {
|
|
875
|
+
if (typeof l === 'string') {
|
|
876
|
+
l = this.getLayerById(l);
|
|
877
|
+
}
|
|
878
|
+
if (l && l.isLayer) {
|
|
879
|
+
result[l.id] = [];
|
|
880
|
+
if (l.isColorLayer) {
|
|
881
|
+
layers.push(l.id);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
// Get the mouse coordinates to the correct system
|
|
887
|
+
const mouse = mouseOrEvt instanceof Event ? this.eventToViewCoords(mouseOrEvt, _eventCoords) : mouseOrEvt;
|
|
888
|
+
const objects = this.pickObjectsAt(mouse, radius, ...where);
|
|
889
|
+
if (objects.length > 0) {
|
|
890
|
+
objects.forEach(o => result[o.layer.id].push(o));
|
|
891
|
+
}
|
|
892
|
+
if (layers.length == 0) {
|
|
893
|
+
return result;
|
|
894
|
+
}
|
|
895
|
+
this.getPickingPositionFromDepth(mouse, positionVector);
|
|
896
|
+
coordinates.crs = this.referenceCrs;
|
|
897
|
+
coordinates.setFromVector3(positionVector);
|
|
898
|
+
|
|
899
|
+
// Get the correct precision; the position variable will be set in this
|
|
900
|
+
// function.
|
|
901
|
+
let precision;
|
|
902
|
+
const precisions = {
|
|
903
|
+
M: this.getPixelsToMeters(radius, mouse),
|
|
904
|
+
D: 0.001 * radius
|
|
905
|
+
};
|
|
906
|
+
if (this.isPlanarView) {
|
|
907
|
+
precisions.D = precisions.M;
|
|
908
|
+
} else if (this.getPixelsToDegrees) {
|
|
909
|
+
precisions.D = this.getMetersToDegrees(precisions.M);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// Get the tile corresponding to where the cursor is
|
|
913
|
+
const tiles = Picking.pickTilesAt(this, mouse, radius, this.tileLayer);
|
|
914
|
+
for (const tile of tiles) {
|
|
915
|
+
if (!tile.object.material) {
|
|
916
|
+
continue;
|
|
917
|
+
}
|
|
918
|
+
for (const materialLayer of tile.object.material.getLayers(layers)) {
|
|
919
|
+
for (const texture of materialLayer.textures) {
|
|
920
|
+
if (!texture.features) {
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
precision = CRS.isMetricUnit(texture.features.crs) ? precisions.M : precisions.D;
|
|
924
|
+
const featuresUnderCoor = FeaturesUtils.filterFeaturesUnderCoordinate(coordinates, texture.features, precision);
|
|
925
|
+
featuresUnderCoor.forEach(feature => {
|
|
926
|
+
if (!result[materialLayer.id].find(f => f.geometry === feature.geometry)) {
|
|
927
|
+
result[materialLayer.id].push(feature);
|
|
928
|
+
}
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
return result;
|
|
934
|
+
}
|
|
935
|
+
readDepthBuffer(x, y, width, height, buffer) {
|
|
936
|
+
const g = this.mainLoop.gfxEngine;
|
|
937
|
+
const currentWireframe = this.tileLayer.wireframe;
|
|
938
|
+
const currentOpacity = this.tileLayer.opacity;
|
|
939
|
+
const currentVisibility = this.tileLayer.visible;
|
|
940
|
+
if (currentWireframe) {
|
|
941
|
+
this.tileLayer.wireframe = false;
|
|
942
|
+
}
|
|
943
|
+
if (currentOpacity < 1.0) {
|
|
944
|
+
this.tileLayer.opacity = 1.0;
|
|
945
|
+
}
|
|
946
|
+
if (!currentVisibility) {
|
|
947
|
+
this.tileLayer.visible = true;
|
|
948
|
+
}
|
|
949
|
+
const restore = this.tileLayer.level0Nodes.map(n => RenderMode.push(n, RenderMode.MODES.DEPTH));
|
|
950
|
+
buffer = g.renderViewToBuffer({
|
|
951
|
+
camera: this.camera,
|
|
952
|
+
scene: this.tileLayer.object3d
|
|
953
|
+
}, {
|
|
954
|
+
x,
|
|
955
|
+
y,
|
|
956
|
+
width,
|
|
957
|
+
height,
|
|
958
|
+
buffer
|
|
959
|
+
});
|
|
960
|
+
restore.forEach(r => r());
|
|
961
|
+
if (this.tileLayer.wireframe !== currentWireframe) {
|
|
962
|
+
this.tileLayer.wireframe = currentWireframe;
|
|
963
|
+
}
|
|
964
|
+
if (this.tileLayer.opacity !== currentOpacity) {
|
|
965
|
+
this.tileLayer.opacity = currentOpacity;
|
|
966
|
+
}
|
|
967
|
+
if (this.tileLayer.visible !== currentVisibility) {
|
|
968
|
+
this.tileLayer.visible = currentVisibility;
|
|
969
|
+
}
|
|
970
|
+
return buffer;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/**
|
|
974
|
+
* Returns the world position on the terrain (view's crs: referenceCrs) under view coordinates.
|
|
975
|
+
* This position is computed with depth buffer.
|
|
976
|
+
*
|
|
977
|
+
* @param {THREE.Vector2} mouse position in view coordinates (in pixel), if it's null so it's view's center.
|
|
978
|
+
* @param {THREE.Vector3} [target=THREE.Vector3()] target. the result will be copied into this Vector3. If not present a new one will be created.
|
|
979
|
+
* @return {THREE.Vector3} the world position on the terrain in view's crs: referenceCrs.
|
|
980
|
+
*/
|
|
981
|
+
|
|
982
|
+
getPickingPositionFromDepth(mouse) {
|
|
983
|
+
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Vector3();
|
|
984
|
+
if (!this.tileLayer || this.tileLayer.level0Nodes.length == 0 || !this.tileLayer.level0Nodes[0]) {
|
|
985
|
+
target = undefined;
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
const l = this.mainLoop;
|
|
989
|
+
const viewPaused = l.scheduler.commandsWaitingExecutionCount() == 0 && l.renderingState == RENDERING_PAUSED;
|
|
990
|
+
const g = l.gfxEngine;
|
|
991
|
+
const dim = g.getWindowSize();
|
|
992
|
+
mouse = mouse || dim.clone().multiplyScalar(0.5);
|
|
993
|
+
mouse.x = Math.floor(mouse.x);
|
|
994
|
+
mouse.y = Math.floor(mouse.y);
|
|
995
|
+
|
|
996
|
+
// Render/Read to buffer
|
|
997
|
+
let buffer;
|
|
998
|
+
if (viewPaused) {
|
|
999
|
+
if (this.#fullSizeDepthBuffer.needsUpdate) {
|
|
1000
|
+
this.readDepthBuffer(0, 0, dim.x, dim.y, this.#fullSizeDepthBuffer);
|
|
1001
|
+
this.#fullSizeDepthBuffer.needsUpdate = false;
|
|
1002
|
+
}
|
|
1003
|
+
const id = ((dim.y - mouse.y - 1) * dim.x + mouse.x) * 4;
|
|
1004
|
+
buffer = this.#fullSizeDepthBuffer.slice(id, id + 4);
|
|
1005
|
+
} else {
|
|
1006
|
+
buffer = this.readDepthBuffer(mouse.x, mouse.y, 1, 1, this.#pixelDepthBuffer);
|
|
1007
|
+
}
|
|
1008
|
+
screen.x = mouse.x / dim.x * 2 - 1;
|
|
1009
|
+
screen.y = -(mouse.y / dim.y) * 2 + 1;
|
|
1010
|
+
if (Capabilities.isLogDepthBufferSupported() && this.camera3D.type == 'PerspectiveCamera') {
|
|
1011
|
+
// TODO: solve this part with gl_FragCoord_Z and unproject
|
|
1012
|
+
// Origin
|
|
1013
|
+
ray.origin.copy(this.camera3D.position);
|
|
1014
|
+
|
|
1015
|
+
// Direction
|
|
1016
|
+
ray.direction.set(screen.x, screen.y, 0.5);
|
|
1017
|
+
// Unproject
|
|
1018
|
+
matrix.multiplyMatrices(this.camera3D.matrixWorld, matrix.copy(this.camera3D.projectionMatrix).invert());
|
|
1019
|
+
ray.direction.applyMatrix4(matrix);
|
|
1020
|
+
ray.direction.sub(ray.origin);
|
|
1021
|
+
direction.set(0, 0, 1.0);
|
|
1022
|
+
direction.applyMatrix4(matrix);
|
|
1023
|
+
direction.sub(ray.origin);
|
|
1024
|
+
const angle = direction.angleTo(ray.direction);
|
|
1025
|
+
const orthoZ = g.depthBufferRGBAValueToOrthoZ(buffer, this.camera3D);
|
|
1026
|
+
const length = orthoZ / Math.cos(angle);
|
|
1027
|
+
target.addVectors(this.camera3D.position, ray.direction.setLength(length));
|
|
1028
|
+
} else {
|
|
1029
|
+
const gl_FragCoord_Z = g.depthBufferRGBAValueToOrthoZ(buffer, this.camera3D);
|
|
1030
|
+
target.set(screen.x, screen.y, gl_FragCoord_Z);
|
|
1031
|
+
target.unproject(this.camera3D);
|
|
1032
|
+
}
|
|
1033
|
+
if (target.length() > 10000000) {
|
|
1034
|
+
return undefined;
|
|
1035
|
+
}
|
|
1036
|
+
return target;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* Returns the world {@link Coordinates} of the terrain at given view coordinates.
|
|
1041
|
+
*
|
|
1042
|
+
* @param {THREE.Vector2|event} [mouse] The view coordinates at which the world coordinates must be returned. This
|
|
1043
|
+
* parameter can also be set to a mouse event from which the view coordinates will be deducted. If not specified,
|
|
1044
|
+
* it will be defaulted to the view's center coordinates.
|
|
1045
|
+
* @param {Coordinates} [target] The result will be copied into this {@link Coordinates} in the coordinate reference
|
|
1046
|
+
* system of the given coordinate. If not specified, a new {@link Coordinates} instance will be created (in the
|
|
1047
|
+
* view referenceCrs).
|
|
1048
|
+
*
|
|
1049
|
+
* @returns {Coordinates} The world {@link Coordinates} of the terrain at the given view coordinates in the
|
|
1050
|
+
* coordinate reference system of the target or in the view referenceCrs if no target is specified.
|
|
1051
|
+
*/
|
|
1052
|
+
pickTerrainCoordinates(mouse) {
|
|
1053
|
+
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Coordinates(this.referenceCrs);
|
|
1054
|
+
if (mouse instanceof Event) {
|
|
1055
|
+
this.eventToViewCoords(mouse);
|
|
1056
|
+
} else if (mouse && mouse.x !== undefined && mouse.y !== undefined) {
|
|
1057
|
+
_eventCoords.copy(mouse);
|
|
1058
|
+
} else {
|
|
1059
|
+
_eventCoords.set(this.mainLoop.gfxEngine.width / 2, this.mainLoop.gfxEngine.height / 2);
|
|
1060
|
+
}
|
|
1061
|
+
this.getPickingPositionFromDepth(_eventCoords, positionVector);
|
|
1062
|
+
coordinates.crs = this.referenceCrs;
|
|
1063
|
+
coordinates.setFromVector3(positionVector);
|
|
1064
|
+
coordinates.as(target.crs, target);
|
|
1065
|
+
return target;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Returns the world {@link Coordinates} of the terrain at given view coordinates.
|
|
1070
|
+
*
|
|
1071
|
+
* @param {THREE.Vector2|event} [mouse] The view coordinates at which the world coordinates must be
|
|
1072
|
+
* returned. This parameter can also be set to a mouse event from
|
|
1073
|
+
* which the view coordinates will be deducted. If not specified, it
|
|
1074
|
+
* will be defaulted to the view's center coordinates.
|
|
1075
|
+
* @param {Coordinates} [target] The result will be copied into this {@link Coordinates}. If not
|
|
1076
|
+
* specified, a new {@link Coordinates} instance will be created.
|
|
1077
|
+
*
|
|
1078
|
+
* @returns {Coordinates} The world {@link Coordinates} of the terrain at the given view coordinates.
|
|
1079
|
+
*
|
|
1080
|
+
* @deprecated Use View#pickTerrainCoordinates instead.
|
|
1081
|
+
*/
|
|
1082
|
+
pickCoordinates(mouse) {
|
|
1083
|
+
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Coordinates(this.referenceCrs);
|
|
1084
|
+
console.warn('Deprecated, use View#pickTerrainCoordinates instead.');
|
|
1085
|
+
return this.pickTerrainCoordinates(mouse, target);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
/**
|
|
1089
|
+
* Resize the viewer.
|
|
1090
|
+
*
|
|
1091
|
+
* @param {number} [width=viewerDiv.clientWidth] - The width to resize the
|
|
1092
|
+
* viewer with. By default it is the `clientWidth` of the `viewerDiv`.
|
|
1093
|
+
* @param {number} [height=viewerDiv.clientHeight] - The height to resize
|
|
1094
|
+
* the viewer with. By default it is the `clientHeight` of the `viewerDiv`.
|
|
1095
|
+
*/
|
|
1096
|
+
resize(width, height) {
|
|
1097
|
+
if (width < 0 || height < 0) {
|
|
1098
|
+
console.warn(`Trying to resize the View with negative height (${height}) or width (${width}). Skipping resize.`);
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
if (width == undefined) {
|
|
1102
|
+
width = this.domElement.clientWidth;
|
|
1103
|
+
}
|
|
1104
|
+
if (height == undefined) {
|
|
1105
|
+
height = this.domElement.clientHeight;
|
|
1106
|
+
}
|
|
1107
|
+
this.#fullSizeDepthBuffer = new Uint8Array(4 * width * height);
|
|
1108
|
+
this.mainLoop.gfxEngine.onWindowResize(width, height);
|
|
1109
|
+
if (width !== 0 && height !== 0) {
|
|
1110
|
+
this.camera.resize(width, height);
|
|
1111
|
+
this.notifyChange(this.camera3D);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
export default View;
|