diff --git a/src/Core/View.js b/src/Core/View.js index 044bf6b205..3465ee34ff 100644 --- a/src/Core/View.js +++ b/src/Core/View.js @@ -32,6 +32,7 @@ export const VIEW_EVENTS = { INITIALIZED: 'initialized', COLOR_LAYERS_ORDER_CHANGED, CAMERA_MOVED: 'camera-moved', + DISPOSED: 'disposed', }; /** @@ -119,8 +120,11 @@ const viewers = []; // Size of the camera frustrum, in meters let screenMeters; +let id = 0; + /** - * @property {HTMLElement} domElement - Thhe domElement holding the canvas where the view is displayed + * @property {number} id - The id of the view. It's incremented at each new view instance, starting at 0. + * @property {HTMLElement} domElement - The domElement holding the canvas where the view is displayed * @property {String} referenceCrs - The coordinate reference system of the view * @property {MainLoop} mainLoop - itowns mainloop scheduling the operations * @property {THREE.Scene} scene - threejs scene of the view @@ -145,10 +149,10 @@ class View extends THREE.EventDispatcher { * var view = itowns.View('EPSG:4326', viewerDiv, { camera: { type: itowns.CAMERA_TYPE.ORTHOGRAPHIC } }); * var customControls = itowns.THREE.OrbitControls(view.camera3D, viewerDiv); * - * @param {string} crs - The default CRS of Three.js coordinates. Should be a cartesian CRS. + * @param {String} crs - The default CRS of Three.js coordinates. Should be a cartesian CRS. * @param {HTMLElement} viewerDiv - Where to instanciate the Three.js scene in the DOM * @param {Object} [options] - Optional properties. - * @param {object} [options.camera] - Options for the camera associated to the view. See {@link Camera} options. + * @param {Object} [options.camera] - Options for the camera associated to the view. See {@link Camera} options. * @param {MainLoop} [options.mainLoop] - {@link MainLoop} instance to use, otherwise a default one will be constructed * @param {WebGLRenderer|Object} [options.renderer] - {@link WebGLRenderer} instance to use, otherwise * a default one will be constructed. In this case, if options.renderer is an object, it will be used to @@ -169,6 +173,7 @@ class View extends THREE.EventDispatcher { super(); this.domElement = viewerDiv; + this.id = id++; this.referenceCrs = crs; @@ -303,8 +308,6 @@ class View extends THREE.EventDispatcher { } // remove alls frameRequester this.removeAllFrameRequesters(); - // remove alls events - this.removeAllEvents(); // remove all layers const layers = this.getLayers(l => !l.isTiledGeometryLayer && !l.isAtmosphere); for (const layer of layers) { @@ -321,6 +324,9 @@ class View extends THREE.EventDispatcher { viewers.splice(id, 1); // Remove remaining objects in the scene (e.g. helpers, debug, etc.) this.scene.traverse(ObjectRemovalHelper.cleanup); + this.dispatchEvent({ type: VIEW_EVENTS.DISPOSED }); + // remove alls events + this.removeAllEvents(); } /** diff --git a/src/Layer/OGC3DTilesLayer.js b/src/Layer/OGC3DTilesLayer.js index 1a75f9514a..2cb2057318 100644 --- a/src/Layer/OGC3DTilesLayer.js +++ b/src/Layer/OGC3DTilesLayer.js @@ -20,9 +20,15 @@ import PointsMaterial, { PNTS_SIZE_MODE, ClassificationScheme, } from 'Renderer/PointsMaterial'; +import { VIEW_EVENTS } from 'Core/View'; const _raycaster = new THREE.Raycaster(); +// Stores lruCache, downloadQueue and parseQueue for each id of view {@link View} +// every time a tileset has been added +// https://github.com/iTowns/itowns/issues/2426 +const viewers = {}; + // Internal instance of GLTFLoader, passed to 3d-tiles-renderer-js to support GLTF 1.0 and 2.0 // Temporary exported to be used in deprecated B3dmParser export const itownsGLTFLoader = new iGLTFLoader(); @@ -30,11 +36,6 @@ itownsGLTFLoader.register(() => new GLTFMeshFeaturesExtension()); itownsGLTFLoader.register(() => new GLTFStructuralMetadataExtension()); itownsGLTFLoader.register(() => new GLTFCesiumRTCExtension()); -// Instantiated by the first tileset. Used to share cache and download and parse queues between tilesets -let lruCache = null; -let downloadQueue = null; -let parseQueue = null; - export const OGC3DTILES_LAYER_EVENTS = { /** * Fired when a new root or child tile set is loaded @@ -152,9 +153,6 @@ class OGC3DTilesLayer extends GeometryLayer { this.tilesRenderer.manager.addHandler(/\.gltf$/, itownsGLTFLoader); - this._setupCacheAndQueues(); - this._setupEvents(); - this.object3d.add(this.tilesRenderer.group); // Add an initialization step that is resolved when the root tileset is loaded (see this._setup below), meaning @@ -190,25 +188,28 @@ class OGC3DTilesLayer extends GeometryLayer { this.pntsMaxAttenuatedSize = config.pntsMaxAttenuatedSize || 7; } + /** - * Sets the lruCache and download and parse queues so they are shared amongst all tilesets. + * Sets the lruCache and download and parse queues so they are shared amongst + * all tilesets from a same {@link View} view. + * @param {View} view - view associated to this layer. * @private */ - _setupCacheAndQueues() { - if (lruCache === null) { - lruCache = this.tilesRenderer.lruCache; - } else { - this.tilesRenderer.lruCache = lruCache; - } - if (downloadQueue === null) { - downloadQueue = this.tilesRenderer.downloadQueue; + _setupCacheAndQueues(view) { + const id = view.id; + if (viewers[id]) { + this.tilesRenderer.lruCache = viewers[id].lruCache; + this.tilesRenderer.downloadQueue = viewers[id].downloadQueue; + this.tilesRenderer.parseQueue = viewers[id].parseQueue; } else { - this.tilesRenderer.downloadQueue = downloadQueue; - } - if (parseQueue === null) { - parseQueue = this.tilesRenderer.parseQueue; - } else { - this.tilesRenderer.parseQueue = parseQueue; + viewers[id] = { + lruCache: this.tilesRenderer.lruCache, + downloadQueue: this.tilesRenderer.downloadQueue, + parseQueue: this.tilesRenderer.parseQueue, + }; + view.addEventListener(VIEW_EVENTS.DISPOSED, (evt) => { + delete viewers[evt.target.id]; + }); } } @@ -249,6 +250,12 @@ class OGC3DTilesLayer extends GeometryLayer { }); view.notifyChange(this); }); + + + this._setupCacheAndQueues(view); + this._setupEvents(); + + // Start loading tileset and tiles this.tilesRenderer.update(); }