diff --git a/src/Core/Layer/Layer.js b/src/Core/Layer/Layer.js deleted file mode 100644 index f6ae6a233a..0000000000 --- a/src/Core/Layer/Layer.js +++ /dev/null @@ -1,182 +0,0 @@ -import { EventDispatcher } from 'three'; -import Picking from '../Picking'; - -/** - * Fires when layer sequence change (meaning when the order of the layer changes in the view) - * @event Layer#sequence-property-changed - * @property new {object} - * @property new.sequence {number} the new value of the layer sequence - * @property previous {object} - * @property previous.sequence {number} the previous value of the layer sequence - * @property target {Layer} dispatched on layer - * @property type {string} sequence-property-changed -*/ -/** - * Fires when layer opacity change - * @event Layer#opacity-property-changed - * @property new {object} - * @property new.opacity {object} the new value of the layer opacity - * @property previous {object} - * @property previous.opacity {object} the previous value of the layer opacity - * @property target {Layer} dispatched on layer - * @property type {string} opacity-property-changed -*/ -/** - * Fires when layer visibility change - * @event Layer#visible-property-changed - * @property new {object} - * @property new.visible {object} the new value of the layer visibility - * @property previous {object} - * @property previous.visible {object} the previous value of the layer visibility - * @property target {Layer} dispatched on layer - * @property type {string} visible-property-changed -*/ - -export const defineLayerProperty = function defineLayerProperty(layer, propertyName, defaultValue, onChange) { - const existing = Object.getOwnPropertyDescriptor(layer, propertyName); - if (!existing || !existing.set) { - var property = layer[propertyName] == undefined ? defaultValue : layer[propertyName]; - Object.defineProperty(layer, - propertyName, - { get: () => property, - set: (newValue) => { - if (property !== newValue) { - const event = { type: `${propertyName}-property-changed`, previous: {}, new: {} }; - event.previous[propertyName] = property; - event.new[propertyName] = newValue; - property = newValue; - if (onChange) { - onChange(layer, propertyName); - } - layer.dispatchEvent(event); - } - } }); - } -}; - -function GeometryLayer(id, object3d) { - if (!id) { - throw new Error('Missing id parameter (GeometryLayer must have a unique id defined)'); - } - if (!object3d || !object3d.isObject3D) { - throw new Error('Missing/Invalid object3d parameter (must be a three.js Object3D instance)'); - } - this._attachedLayers = []; - - if (object3d && object3d.type === 'Group' && object3d.name === '') { - object3d.name = id; - } - - Object.defineProperty(this, 'object3d', { - value: object3d, - writable: false, - }); - - Object.defineProperty(this, 'id', { - value: id, - writable: false, - }); - - // Setup default picking method - this.pickObjectsAt = (view, mouse, radius) => Picking.pickObjectsAt(view, mouse, radius, this.object3d); - - this.postUpdate = () => {}; -} - -GeometryLayer.prototype = Object.create(EventDispatcher.prototype); -GeometryLayer.prototype.constructor = GeometryLayer; - -GeometryLayer.prototype.attach = function attach(layer) { - if (!layer.update) { - throw new Error(`Missing 'update' function -> can't attach layer ${layer.id}`); - } - this._attachedLayers.push(layer); -}; - -GeometryLayer.prototype.detach = function detach(layer) { - const count = this._attachedLayers.length; - this._attachedLayers = this._attachedLayers.filter(attached => attached.id != layer.id); - return this._attachedLayers.length < count; -}; - -/** - * Don't use directly constructor to instance a new Layer - * use addLayer in {@link View} - * @example - * // add and create a new Layer - * const newLayer = view.addLayer({options}); - * - * // Change layer's visibilty - * const layerToChange = view.getLayers(layer => layer.id == 'idLayerToChange')[0]; - * layerToChange.visible = false; - * view.notifyChange(); // update viewer - * - * // Change layer's opacity - * const layerToChange = view.getLayers(layer => layer.id == 'idLayerToChange')[0]; - * layerToChange.opacity = 0.5; - * view.notifyChange(); // update viewer - * - * // Listen properties - * const layerToListen = view.getLayers(layer => layer.id == 'idLayerToListen')[0]; - * layerToListen.addEventListener('visible-property-changed', (event) => console.log(event)); - * layerToListen.addEventListener('opacity-property-changed', (event) => console.log(event)); - * @constructor - * @protected - * @param {String} id - */ -function Layer(id) { - Object.defineProperty(this, 'id', { - value: id, - writable: false, - }); -} - -Layer.prototype = Object.create(EventDispatcher.prototype); -Layer.prototype.constructor = Layer; - -const ImageryLayers = { - // move layer to new index - // After the modification : - // * the minimum sequence will always be 0 - // * the maximum sequence will always be layers.lenght - 1 - // the ordering of all layers (Except that specified) doesn't change - moveLayerToIndex: function moveLayerToIndex(layer, newIndex, imageryLayers) { - newIndex = Math.min(newIndex, imageryLayers.length - 1); - newIndex = Math.max(newIndex, 0); - const oldIndex = layer.sequence; - - for (const imagery of imageryLayers) { - if (imagery.id === layer.id) { - // change index of specified layer - imagery.sequence = newIndex; - } else if (imagery.sequence > oldIndex && imagery.sequence <= newIndex) { - // down all layers between the old index and new index (to compensate the deletion of the old index) - imagery.sequence--; - } else if (imagery.sequence >= newIndex && imagery.sequence < oldIndex) { - // up all layers between the new index and old index (to compensate the insertion of the new index) - imagery.sequence++; - } - } - }, - - moveLayerDown: function moveLayerDown(layer, imageryLayers) { - if (layer.sequence > 0) { - this.moveLayerToIndex(layer, layer.sequence - 1, imageryLayers); - } - }, - - moveLayerUp: function moveLayerUp(layer, imageryLayers) { - const m = imageryLayers.length - 1; - if (layer.sequence < m) { - this.moveLayerToIndex(layer, layer.sequence + 1, imageryLayers); - } - }, - - getColorLayersIdOrderedBySequence: function getColorLayersIdOrderedBySequence(imageryLayers) { - const copy = Array.from(imageryLayers); - copy.sort((a, b) => a.sequence - b.sequence); - return copy.map(l => l.id); - }, -}; - -export { GeometryLayer, Layer, ImageryLayers }; diff --git a/src/Core/MainLoop.js b/src/Core/MainLoop.js index bd7a1f7a3a..b722bed346 100644 --- a/src/Core/MainLoop.js +++ b/src/Core/MainLoop.js @@ -1,5 +1,6 @@ import { EventDispatcher } from 'three'; -import { GeometryLayer, Layer } from './Layer/Layer'; +import Layer from '../Layer/Layer'; +import GeometryLayer from '../Layer/GeometryLayer'; import Cache from '../Core/Scheduler/Cache'; export const RENDERING_PAUSED = 0; @@ -116,7 +117,7 @@ MainLoop.prototype._update = function _update(view, updateSources, dt) { const srcs = filterChangeSources(updateSources, geometryLayer); if (srcs.size > 0) { // `preUpdate` returns an array of elements to update - const elementsToUpdate = geometryLayer.preUpdate(context, geometryLayer, srcs); + const elementsToUpdate = geometryLayer.preUpdate(context, srcs); // `update` is called in `updateElements`. updateElements(context, geometryLayer, elementsToUpdate); // `postUpdate` is called when this geom layer update process is finished diff --git a/src/Core/Prefab/Globe/GlobeLayer.js b/src/Core/Prefab/Globe/GlobeLayer.js new file mode 100644 index 0000000000..900e8b8454 --- /dev/null +++ b/src/Core/Prefab/Globe/GlobeLayer.js @@ -0,0 +1,62 @@ +import * as THREE from 'three'; + +import TiledGeometryLayer from '../../../Layer/TiledGeometryLayer'; + +import { processTiledGeometryNode } from '../../../Process/TiledNodeProcessing'; +import { globeCulling, preGlobeUpdate, globeSubdivisionControl, globeSchemeTileWMTS, globeSchemeTile1 } from '../../../Process/GlobeTileProcessing'; +import BuilderEllipsoidTile from './BuilderEllipsoidTile'; +import SubdivisionControl from '../../../Process/SubdivisionControl'; +import Picking from '../../Picking'; + +class GlobeLayer extends TiledGeometryLayer { + /** + * A geometry layer to be used only with a {@link GlobeView}. + * + * @constructor + * + * @param {string} id + * @param {Object} options + * @param {THREE.Object3D} options.object3d + * @param {number} [options.maxSubdivisionLevel=18] + * @param {number} [options.sseSubdivisionThreshold=1] + * @param {number} [options.maxDeltaElevationLevel=4] + */ + constructor(id, options) { + super(id, options.object3d || new THREE.Group()); + + // Configure tiles + this.schemeTile = globeSchemeTileWMTS(globeSchemeTile1); + this.extent = this.schemeTile[0].clone(); + for (let i = 1; i < this.schemeTile.length; i++) { + this.extent.union(this.schemeTile[i]); + } + function subdivision(context, layer, node) { + if (SubdivisionControl.hasEnoughTexturesToSubdivide(context, layer, node)) { + return globeSubdivisionControl(2, + options.maxSubdivisionLevel || 18, + options.sseSubdivisionThreshold || 1.0, + options.maxDeltaElevationLevel || 4)(context, layer, node); + } + return false; + } + + this.update = processTiledGeometryNode(globeCulling(2), subdivision); + this.builder = new BuilderEllipsoidTile(); + // provide custom pick function + this.pickObjectsAt = (_view, mouse, radius = 5) => Picking.pickTilesAt(_view, mouse, radius, this); + } + + preUpdate(context, changeSources) { + SubdivisionControl.preUpdate(context, this); + + if (__DEBUG__) { + this._latestUpdateStartingLevel = 0; + } + + preGlobeUpdate(context, this); + + return super.preUpdate(context, changeSources); + } +} + +export default GlobeLayer; diff --git a/src/Core/Prefab/GlobeView.js b/src/Core/Prefab/GlobeView.js index 27aab463c8..f5961e1359 100644 --- a/src/Core/Prefab/GlobeView.js +++ b/src/Core/Prefab/GlobeView.js @@ -6,17 +6,11 @@ import { COLOR_LAYERS_ORDER_CHANGED } from '../../Renderer/ColorLayersOrdering'; import RendererConstant from '../../Renderer/RendererConstant'; import GlobeControls from '../../Renderer/ThreeExtended/GlobeControls'; -import { GeometryLayer } from '../Layer/Layer'; - +import GlobeLayer from './Globe/GlobeLayer'; import Atmosphere from './Globe/Atmosphere'; import CoordStars from '../Geographic/CoordStars'; import Coordinates, { C, ellipsoidSizes } from '../Geographic/Coordinates'; -import { processTiledGeometryNode } from '../../Process/TiledNodeProcessing'; -import { globeCulling, preGlobeUpdate, globeSubdivisionControl, globeSchemeTileWMTS, globeSchemeTile1 } from '../../Process/GlobeTileProcessing'; -import BuilderEllipsoidTile from './Globe/BuilderEllipsoidTile'; -import SubdivisionControl from '../../Process/SubdivisionControl'; -import Picking from '../Picking'; /** * Fires when the view is completely loaded. Controls and view's functions can be called then. @@ -71,92 +65,9 @@ export const GLOBE_VIEW_EVENTS = { COLOR_LAYERS_ORDER_CHANGED, }; -export function createGlobeLayer(id, options) { - // Configure tiles - const nodeInitFn = function nodeInitFn(layer, parent, node) { - node.material.setLightingOn(layer.lighting.enable); - node.material.uniforms.lightPosition.value = layer.lighting.position; - if (layer.noTextureColor) { - node.material.uniforms.noTextureColor.value.copy(layer.noTextureColor); - } - - if (__DEBUG__) { - node.material.uniforms.showOutline = { value: layer.showOutline || false }; - node.material.wireframe = layer.wireframe || false; - } - }; - - const wgs84TileLayer = new GeometryLayer(id, options.object3d || new THREE.Group()); - wgs84TileLayer.schemeTile = globeSchemeTileWMTS(globeSchemeTile1); - wgs84TileLayer.extent = wgs84TileLayer.schemeTile[0].clone(); - for (let i = 1; i < wgs84TileLayer.schemeTile.length; i++) { - wgs84TileLayer.extent.union(wgs84TileLayer.schemeTile[i]); - } - wgs84TileLayer.preUpdate = (context, layer, changeSources) => { - SubdivisionControl.preUpdate(context, layer); - - if (__DEBUG__) { - layer._latestUpdateStartingLevel = 0; - } - - preGlobeUpdate(context, layer); - - let commonAncestor; - for (const source of changeSources.values()) { - if (source.isCamera) { - // if the change is caused by a camera move, no need to bother - // to find common ancestor: we need to update the whole tree: - // some invisible tiles may now be visible - return layer.level0Nodes; - } - if (source.layer === layer) { - if (!commonAncestor) { - commonAncestor = source; - } else { - commonAncestor = source.findCommonAncestor(commonAncestor); - if (!commonAncestor) { - return layer.level0Nodes; - } - } - if (commonAncestor.material == null) { - commonAncestor = undefined; - } - } - } - if (commonAncestor) { - if (__DEBUG__) { - layer._latestUpdateStartingLevel = commonAncestor.level; - } - return [commonAncestor]; - } else { - return layer.level0Nodes; - } - }; - - function subdivision(context, layer, node) { - if (SubdivisionControl.hasEnoughTexturesToSubdivide(context, layer, node)) { - return globeSubdivisionControl(2, - options.maxSubdivisionLevel || 18, - options.sseSubdivisionThreshold || 1.0, - options.maxDeltaElevationLevel || 4)(context, layer, node); - } - return false; - } - - wgs84TileLayer.update = processTiledGeometryNode(globeCulling(2), subdivision); - wgs84TileLayer.builder = new BuilderEllipsoidTile(); - wgs84TileLayer.onTileCreated = nodeInitFn; - wgs84TileLayer.type = 'geometry'; - wgs84TileLayer.protocol = 'tile'; - wgs84TileLayer.visible = true; - wgs84TileLayer.lighting = { - enable: false, - position: { x: -0.5, y: 0.0, z: 1.0 }, - }; - // provide custom pick function - wgs84TileLayer.pickObjectsAt = (_view, mouse, radius = 5) => Picking.pickTilesAt(_view, mouse, radius, wgs84TileLayer); - - return wgs84TileLayer; +export function createGlobeLayer(id, extent, options) { + console.warn('createGlobeLayer is deprecated, use the GlobeLayer class instead.'); + return new GlobeLayer(id, extent, options); } /** @@ -190,7 +101,7 @@ function GlobeView(viewerDiv, coordCarto, options = {}) { this.camera.camera3D.updateProjectionMatrix(); this.camera.camera3D.updateMatrixWorld(true); - const wgs84TileLayer = createGlobeLayer('globe', options); + const wgs84TileLayer = new GlobeLayer('globe', options); const sun = new THREE.DirectionalLight(); sun.position.set(-0.5, 0, 1); diff --git a/src/Core/Prefab/Panorama/PanoramaLayer.js b/src/Core/Prefab/Panorama/PanoramaLayer.js new file mode 100644 index 0000000000..01e9e17da0 --- /dev/null +++ b/src/Core/Prefab/Panorama/PanoramaLayer.js @@ -0,0 +1,118 @@ +import * as THREE from 'three'; + +import TiledGeometryLayer from '../../../Layer/TiledGeometryLayer'; +import Extent from '../../Geographic/Extent'; +import { processTiledGeometryNode } from '../../../Process/TiledNodeProcessing'; +import { panoramaCulling, panoramaSubdivisionControl } from '../../../Process/PanoramaTileProcessing'; +import PanoramaTileBuilder from './PanoramaTileBuilder'; +import SubdivisionControl from '../../../Process/SubdivisionControl'; +import ProjectionType from './Constants'; +import Picking from '../../Picking'; + +class PanoramaLayer extends TiledGeometryLayer { + /** + * A geometry layer to be used only with a {@link PanoramaView}. + * + * @constructor + * + * @param {string} id + * @param {Coordinates} coordinates + * @param {string} type + * @param {Object} options + * @param {THREE.Object3D} options.object3d + * @param {number} options.ratio=1 + * @param {number} [options.maxSubdivisionLevel=10] + */ + constructor(id, coordinates, type, options) { + super(id, options.object3d || new THREE.Group()); + + coordinates.xyz(this.object3d.position); + this.object3d.quaternion.setFromUnitVectors( + new THREE.Vector3(0, 0, 1), coordinates.geodesicNormal); + this.object3d.updateMatrixWorld(true); + + // FIXME: add CRS = '0' support + this.extent = new Extent('EPSG:4326', { + west: -180, + east: 180, + north: 90, + south: -90, + }); + + if (type === ProjectionType.SPHERICAL) { + // equirectangular -> spherical geometry + this.schemeTile = [ + new Extent('EPSG:4326', { + west: -180, + east: 0, + north: 90, + south: -90, + }), new Extent('EPSG:4326', { + west: 0, + east: 180, + north: 90, + south: -90, + })]; + } else if (type === ProjectionType.CYLINDRICAL) { + // cylindrical geometry + this.schemeTile = [ + new Extent('EPSG:4326', { + west: -180, + east: -90, + north: 90, + south: -90, + }), new Extent('EPSG:4326', { + west: -90, + east: 0, + north: 90, + south: -90, + }), new Extent('EPSG:4326', { + west: 0, + east: 90, + north: 90, + south: -90, + }), new Extent('EPSG:4326', { + west: 90, + east: 180, + north: 90, + south: -90, + })]; + } else { + throw new Error(`Unsupported panorama projection type ${type}. + Only ProjectionType.SPHERICAL and ProjectionType.CYLINDRICAL are supported`); + } + this.disableSkirt = true; + + function subdivision(context, layer, node) { + if (SubdivisionControl.hasEnoughTexturesToSubdivide(context, layer, node)) { + return panoramaSubdivisionControl( + options.maxSubdivisionLevel || 10, new THREE.Vector2(512, 256))(context, layer, node); + } + return false; + } + + this.update = processTiledGeometryNode(panoramaCulling, subdivision); + this.builder = new PanoramaTileBuilder(type, options.ratio); + this.segments = 8; + this.quality = 0.5; + // provide custom pick function + this.pickObjectsAt = (_view, mouse, radius) => Picking.pickTilesAt(_view, mouse, radius, this); + } + + + preUpdate(context, changeSources) { + SubdivisionControl.preUpdate(context, this); + + if (__DEBUG__) { + this._latestUpdateStartingLevel = 0; + } + + if (changeSources.has(undefined) || changeSources.size == 0) { + return this.level0Nodes; + } + + return super.preUpdate(context, changeSources); + } +} + +export default PanoramaLayer; diff --git a/src/Core/Prefab/PanoramaView.js b/src/Core/Prefab/PanoramaView.js index 20b6ea7323..59286e1d9b 100644 --- a/src/Core/Prefab/PanoramaView.js +++ b/src/Core/Prefab/PanoramaView.js @@ -1,158 +1,11 @@ import * as THREE from 'three'; import View from '../View'; - -import { GeometryLayer } from '../Layer/Layer'; -import Extent from '../Geographic/Extent'; -import { processTiledGeometryNode } from '../../Process/TiledNodeProcessing'; -import { panoramaCulling, panoramaSubdivisionControl } from '../../Process/PanoramaTileProcessing'; -import PanoramaTileBuilder from './Panorama/PanoramaTileBuilder'; -import SubdivisionControl from '../../Process/SubdivisionControl'; -import ProjectionType from './Panorama/Constants'; -import Picking from '../Picking'; +import PanoramaLayer from './Panorama/PanoramaLayer'; export function createPanoramaLayer(id, coordinates, type, options = {}) { - const tileLayer = new GeometryLayer(id, options.object3d || new THREE.Group()); - - coordinates.xyz(tileLayer.object3d.position); - tileLayer.object3d.quaternion.setFromUnitVectors( - new THREE.Vector3(0, 0, 1), coordinates.geodesicNormal); - tileLayer.object3d.updateMatrixWorld(true); - - // FIXME: add CRS = '0' support - tileLayer.extent = new Extent('EPSG:4326', { - west: -180, - east: 180, - north: 90, - south: -90, - }); - - if (type === ProjectionType.SPHERICAL) { - // equirectangular -> spherical geometry - tileLayer.schemeTile = [ - new Extent('EPSG:4326', { - west: -180, - east: 0, - north: 90, - south: -90, - }), new Extent('EPSG:4326', { - west: 0, - east: 180, - north: 90, - south: -90, - })]; - } else if (type === ProjectionType.CYLINDRICAL) { - // cylindrical geometry - tileLayer.schemeTile = [ - new Extent('EPSG:4326', { - west: -180, - east: -90, - north: 90, - south: -90, - }), new Extent('EPSG:4326', { - west: -90, - east: 0, - north: 90, - south: -90, - }), new Extent('EPSG:4326', { - west: 0, - east: 90, - north: 90, - south: -90, - }), new Extent('EPSG:4326', { - west: 90, - east: 180, - north: 90, - south: -90, - })]; - } else { - throw new Error(`Unsupported panorama projection type ${type}. - Only ProjectionType.SPHERICAL and ProjectionType.CYLINDRICAL are supported`); - } - tileLayer.disableSkirt = true; - - // Configure tiles - const nodeInitFn = function nodeInitFn(layer, parent, node) { - if (layer.noTextureColor) { - node.material.uniforms.noTextureColor.value.copy(layer.noTextureColor); - } - node.material.depthWrite = false; - - if (__DEBUG__) { - node.material.uniforms.showOutline = { value: layer.showOutline || false }; - node.material.wireframe = layer.wireframe || false; - } - }; - - tileLayer.preUpdate = (context, layer, changeSources) => { - SubdivisionControl.preUpdate(context, layer); - - if (__DEBUG__) { - layer._latestUpdateStartingLevel = 0; - } - - if (changeSources.has(undefined) || changeSources.size == 0) { - return layer.level0Nodes; - } - - let commonAncestor; - for (const source of changeSources.values()) { - if (source.isCamera) { - // if the change is caused by a camera move, no need to bother - // to find common ancestor: we need to update the whole tree: - // some invisible tiles may now be visible - return layer.level0Nodes; - } - if (source.layer === layer.id) { - if (!commonAncestor) { - commonAncestor = source; - } else { - commonAncestor = source.findCommonAncestor(commonAncestor); - if (!commonAncestor) { - return layer.level0Nodes; - } - } - if (commonAncestor.material == null) { - commonAncestor = undefined; - } - } - } - if (commonAncestor) { - if (__DEBUG__) { - layer._latestUpdateStartingLevel = commonAncestor.level; - } - return [commonAncestor]; - } else { - return layer.level0Nodes; - } - }; - - - function subdivision(context, layer, node) { - if (SubdivisionControl.hasEnoughTexturesToSubdivide(context, layer, node)) { - return panoramaSubdivisionControl( - options.maxSubdivisionLevel || 10, new THREE.Vector2(512, 256))(context, layer, node); - } - return false; - } - - tileLayer.update = processTiledGeometryNode(panoramaCulling, subdivision); - tileLayer.builder = new PanoramaTileBuilder(type, options.ratio); - tileLayer.onTileCreated = nodeInitFn; - tileLayer.type = 'geometry'; - tileLayer.protocol = 'tile'; - tileLayer.visible = true; - tileLayer.segments = 8; - tileLayer.quality = 0.5; - tileLayer.lighting = { - enable: false, - position: { x: -0.5, y: 0.0, z: 1.0 }, - }; - // provide custom pick function - tileLayer.pickObjectsAt = (_view, mouse, radius) => Picking.pickTilesAt(_view, mouse, radius, tileLayer); - - - return tileLayer; + console.warn('createPanoramaLayer is deprecated, use the PanoramaLayer class instead.'); + return new PanoramaLayer(id, coordinates, type, options); } function PanoramaView(viewerDiv, coordinates, type, options = {}) { @@ -177,7 +30,7 @@ function PanoramaView(viewerDiv, coordinates, type, options = {}) { } camera.updateMatrixWorld(); - const tileLayer = createPanoramaLayer('panorama', coordinates, type, options); + const tileLayer = new PanoramaLayer('panorama', coordinates, type, options); View.prototype.addLayer.call(this, tileLayer); diff --git a/src/Core/Prefab/Planar/PlanarLayer.js b/src/Core/Prefab/Planar/PlanarLayer.js new file mode 100644 index 0000000000..7f412b4e79 --- /dev/null +++ b/src/Core/Prefab/Planar/PlanarLayer.js @@ -0,0 +1,62 @@ +import * as THREE from 'three'; + +import TiledGeometryLayer from '../../../Layer/TiledGeometryLayer'; + +import { processTiledGeometryNode } from '../../../Process/TiledNodeProcessing'; +import { planarCulling, planarSubdivisionControl, prePlanarUpdate } from '../../../Process/PlanarTileProcessing'; +import PlanarTileBuilder from './PlanarTileBuilder'; +import SubdivisionControl from '../../../Process/SubdivisionControl'; +import Picking from '../../Picking'; + +class PlanarLayer extends TiledGeometryLayer { + /** + * A geometry layer to be used only with a {@link PlanarView}. + * + * @constructor + * + * @param {string} id + * @param {Extent} extent - the extent to define the layer within + * @param {Object} options + * @param {THREE.Object3D} options.object3d + * @param {number} [options.maxSubdivisionLevel=5] + * @param {number} [options.maxDeltaElevationLevel=4] + */ + constructor(id, extent, options) { + super(id, options.object3d || new THREE.Group()); + + this.extent = extent; + this.schemeTile = [extent]; + + function subdivision(context, layer, node) { + if (SubdivisionControl.hasEnoughTexturesToSubdivide(context, layer, node)) { + return planarSubdivisionControl( + options.maxSubdivisionLevel || 5, + options.maxDeltaElevationLevel || 4)(context, layer, node); + } + return false; + } + + this.update = processTiledGeometryNode(planarCulling, subdivision); + this.builder = new PlanarTileBuilder(); + // provide custom pick function + this.pickObjectsAt = (_view, mouse, radius) => Picking.pickTilesAt(_view, mouse, radius, this); + } + + preUpdate(context, changeSources) { + SubdivisionControl.preUpdate(context, this); + + prePlanarUpdate(context, this); + + if (__DEBUG__) { + this._latestUpdateStartingLevel = 0; + } + + if (changeSources.has(undefined) || changeSources.size == 0) { + return this.level0Nodes; + } + + return super.preUpdate(context, changeSources); + } +} + +export default PlanarLayer; diff --git a/src/Core/Prefab/PlanarView.js b/src/Core/Prefab/PlanarView.js index 2b76f1446e..b153eba13d 100644 --- a/src/Core/Prefab/PlanarView.js +++ b/src/Core/Prefab/PlanarView.js @@ -4,102 +4,11 @@ import View from '../View'; import { RENDERING_PAUSED, MAIN_LOOP_EVENTS } from '../MainLoop'; import RendererConstant from '../../Renderer/RendererConstant'; -import { GeometryLayer } from '../Layer/Layer'; - -import { processTiledGeometryNode } from '../../Process/TiledNodeProcessing'; -import { planarCulling, planarSubdivisionControl, prePlanarUpdate } from '../../Process/PlanarTileProcessing'; -import PlanarTileBuilder from './Planar/PlanarTileBuilder'; -import SubdivisionControl from '../../Process/SubdivisionControl'; -import Picking from '../Picking'; +import PlanarLayer from './Planar/PlanarLayer'; export function createPlanarLayer(id, extent, options) { - const tileLayer = new GeometryLayer(id, options.object3d || new THREE.Group()); - tileLayer.extent = extent; - tileLayer.schemeTile = [extent]; - - // Configure tiles - const nodeInitFn = function nodeInitFn(layer, parent, node) { - node.material.setLightingOn(layer.lighting.enable); - node.material.uniforms.lightPosition.value = layer.lighting.position; - - if (layer.noTextureColor) { - node.material.uniforms.noTextureColor.value.copy(layer.noTextureColor); - } - - if (__DEBUG__) { - node.material.uniforms.showOutline = { value: layer.showOutline || false }; - node.material.wireframe = layer.wireframe || false; - } - }; - - tileLayer.preUpdate = (context, layer, changeSources) => { - SubdivisionControl.preUpdate(context, layer); - - prePlanarUpdate(context, layer); - - if (__DEBUG__) { - layer._latestUpdateStartingLevel = 0; - } - - if (changeSources.has(undefined) || changeSources.size == 0) { - return layer.level0Nodes; - } - - let commonAncestor; - for (const source of changeSources.values()) { - if (source.isCamera) { - // if the change is caused by a camera move, no need to bother - // to find common ancestor: we need to update the whole tree: - // some invisible tiles may now be visible - return layer.level0Nodes; - } - if (source.layer === layer) { - if (!commonAncestor) { - commonAncestor = source; - } else { - commonAncestor = source.findCommonAncestor(commonAncestor); - if (!commonAncestor) { - return layer.level0Nodes; - } - } - if (commonAncestor.material == null) { - commonAncestor = undefined; - } - } - } - if (commonAncestor) { - if (__DEBUG__) { - layer._latestUpdateStartingLevel = commonAncestor.level; - } - return [commonAncestor]; - } else { - return layer.level0Nodes; - } - }; - - - function subdivision(context, layer, node) { - if (SubdivisionControl.hasEnoughTexturesToSubdivide(context, layer, node)) { - return planarSubdivisionControl(options.maxSubdivisionLevel || 5, - options.maxDeltaElevationLevel || 4)(context, layer, node); - } - return false; - } - - tileLayer.update = processTiledGeometryNode(planarCulling, subdivision); - tileLayer.builder = new PlanarTileBuilder(); - tileLayer.onTileCreated = nodeInitFn; - tileLayer.type = 'geometry'; - tileLayer.protocol = 'tile'; - tileLayer.visible = true; - tileLayer.lighting = { - enable: false, - position: { x: -0.5, y: 0.0, z: 1.0 }, - }; - // provide custom pick function - tileLayer.pickObjectsAt = (_view, mouse, radius) => Picking.pickTilesAt(_view, mouse, radius, tileLayer); - - return tileLayer; + console.warn('createPlanarLayer is deprecated, use the PlanarLayer class instead.'); + return new PlanarLayer(id, extent, options); } function PlanarView(viewerDiv, extent, options = {}) { @@ -122,7 +31,7 @@ function PlanarView(viewerDiv, extent, options = {}) { this.camera.camera3D.updateProjectionMatrix(); this.camera.camera3D.updateMatrixWorld(true); - const tileLayer = createPlanarLayer('planar', extent, options); + const tileLayer = new PlanarLayer('planar', extent, options); this.addLayer(tileLayer); diff --git a/src/Core/View.js b/src/Core/View.js index 6de58a5a3a..2bf9c14af5 100644 --- a/src/Core/View.js +++ b/src/Core/View.js @@ -1,10 +1,15 @@ /* global window */ -import { Scene, EventDispatcher, Vector2, Object3D } from 'three'; +import * as THREE from 'three'; import Camera from '../Renderer/Camera'; import MainLoop, { MAIN_LOOP_EVENTS, RENDERING_PAUSED } from './MainLoop'; import c3DEngine from '../Renderer/c3DEngine'; -import { STRATEGY_MIN_NETWORK_TRAFFIC } from './Layer/LayerUpdateStrategy'; -import { GeometryLayer, Layer, defineLayerProperty } from './Layer/Layer'; + +import { STRATEGY_MIN_NETWORK_TRAFFIC } from '../Layer/LayerUpdateStrategy'; +import Layer from '../Layer/Layer'; +import ColorLayer from '../Layer/ColorLayer'; +import ElevationLayer from '../Layer/ElevationLayer'; +import GeometryLayer from '../Layer/GeometryLayer'; + import Scheduler from './Scheduler/Scheduler'; import Picking from './Picking'; import { updateLayeredMaterialNodeImagery, updateLayeredMaterialNodeElevation } from '../Process/LayeredMaterialNodeProcessing'; @@ -59,7 +64,7 @@ function View(crs, viewerDiv, options = {}) { this.mainLoop = options.mainLoop || new MainLoop(new Scheduler(), engine); - this.scene = options.scene3D || new Scene(); + this.scene = options.scene3D || new THREE.Scene(); if (!options.scene3D) { this.scene.autoUpdate = false; } @@ -76,7 +81,7 @@ function View(crs, viewerDiv, options = {}) { window.addEventListener('resize', () => { // If the user gave us a container (