UNPKG

@here/harp-mapview

Version:

Functionality needed to render a map.

413 lines 17.3 kB
"use strict"; /* * Copyright (C) 2020-2021 HERE Europe B.V. * Licensed under Apache 2.0, see full license in LICENSE * SPDX-License-Identifier: Apache-2.0 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.MapViewAtmosphere = exports.AtmosphereLightMode = void 0; const harp_geoutils_1 = require("@here/harp-geoutils"); const harp_materials_1 = require("@here/harp-materials"); const harp_utils_1 = require("@here/harp-utils"); const THREE = require("three"); const ClipPlanesEvaluator_1 = require("./ClipPlanesEvaluator"); /** * Atmosphere effect variants. */ var AtmosphereVariant; (function (AtmosphereVariant) { AtmosphereVariant[AtmosphereVariant["Ground"] = 1] = "Ground"; AtmosphereVariant[AtmosphereVariant["Sky"] = 2] = "Sky"; AtmosphereVariant[AtmosphereVariant["SkyAndGround"] = 3] = "SkyAndGround"; })(AtmosphereVariant || (AtmosphereVariant = {})); /** * Atmosphere shader variants. */ var AtmosphereShadingVariant; (function (AtmosphereShadingVariant) { AtmosphereShadingVariant[AtmosphereShadingVariant["ScatteringShader"] = 0] = "ScatteringShader"; AtmosphereShadingVariant[AtmosphereShadingVariant["SimpleColor"] = 1] = "SimpleColor"; AtmosphereShadingVariant[AtmosphereShadingVariant["Wireframe"] = 2] = "Wireframe"; })(AtmosphereShadingVariant || (AtmosphereShadingVariant = {})); /** * Lists light modes. */ var AtmosphereLightMode; (function (AtmosphereLightMode) { AtmosphereLightMode[AtmosphereLightMode["LightOverhead"] = 0] = "LightOverhead"; AtmosphereLightMode[AtmosphereLightMode["LightDynamic"] = 1] = "LightDynamic"; })(AtmosphereLightMode = exports.AtmosphereLightMode || (exports.AtmosphereLightMode = {})); /** * Maximum altitude that atmosphere reaches as the percent of the Earth radius. */ const SKY_ATMOSPHERE_ALTITUDE_FACTOR = 0.025; /** * Maximum altitude that ground atmosphere is visible as the percent of the Earth radius. */ const GROUND_ATMOSPHERE_ALTITUDE_FACTOR = 0.0001; /** * Utility cache for holding temporary values. */ const cache = { clipPlanes: { near: 0, far: 0 } }; /** * Class that provides {@link MapView}'s atmospheric scattering effect. */ class MapViewAtmosphere { /** * Creates and adds `Atmosphere` effects to the scene. * * @note Currently works only with globe projection. * * @param m_mapAnchors - The {@link MapAnchors} instance where the effect will be added. * @param m_sceneCamera - The camera used to render entire scene. * @param m_projection - The geo-projection used to transform geo coordinates to * cartesian space. * @param m_rendererCapabilities The capabilities of the WebGL renderer. * @param m_updateCallback - The optional callback to that should be called whenever atmosphere * configuration changes, may be used to inform related components (`MapView`) to redraw. * @param m_atmosphereVariant - The optional atmosphere configuration variant enum * [[AtmosphereVariant]], which denotes where the atmosphere scattering effect should be * applied, it may be ground or sky atmosphere only or most realistic for both, which is * chosen by default. * @param m_materialVariant - The optional material variant to be used, mainly for * testing and tweaking purposes. */ constructor(m_mapAnchors, m_sceneCamera, m_projection, m_rendererCapabilities, m_updateCallback, m_atmosphereVariant = AtmosphereVariant.SkyAndGround, m_materialVariant = AtmosphereShadingVariant.ScatteringShader) { this.m_mapAnchors = m_mapAnchors; this.m_sceneCamera = m_sceneCamera; this.m_projection = m_projection; this.m_rendererCapabilities = m_rendererCapabilities; this.m_updateCallback = m_updateCallback; this.m_atmosphereVariant = m_atmosphereVariant; this.m_materialVariant = m_materialVariant; this.m_enabled = true; this.m_clipPlanesEvaluator = new ClipPlanesEvaluator_1.TiltViewClipPlanesEvaluator(harp_geoutils_1.EarthConstants.EQUATORIAL_RADIUS * SKY_ATMOSPHERE_ALTITUDE_FACTOR, 0, 1.0, 0.05, 10000000.0); // TODO: Support for Theme definition should be added. //private m_cachedTheme: Theme = { styles: {} }; this.m_lightDirection = new THREE.Vector3(0.0, 1.0, 0.0); if (this.m_atmosphereVariant & AtmosphereVariant.Sky) { this.createSkyGeometry(); } if (this.m_atmosphereVariant & AtmosphereVariant.Ground) { this.createGroundGeometry(); } this.addToMapAnchors(this.m_mapAnchors); } /** * Check if map anchors have already atmosphere effect added. * * @param mapAnchors - MapAnchors to check. */ static isPresent(mapAnchors) { for (const mapAnchor of mapAnchors.children) { if (mapAnchor.name === MapViewAtmosphere.SkyAtmosphereUserName || mapAnchor.name === MapViewAtmosphere.GroundAtmosphereUserName) { return true; } } return false; } get skyMesh() { return this.m_skyMesh; } get groundMesh() { return this.m_groundMesh; } /** * Allows to enable/disable the atmosphere effect, regardless of the theme settings. * * Use this method to change the setup in runtime without defining corresponding theme setup. * * @param enable - A boolean that specifies whether the atmosphere should be enabled or * disabled. */ set enabled(enable) { // Check already disposed. if (this.disposed) { return; } if (this.m_enabled === enable) { return; } this.m_enabled = enable; const isAdded = MapViewAtmosphere.isPresent(this.m_mapAnchors); if (enable && !isAdded) { this.addToMapAnchors(this.m_mapAnchors); } else if (!enable && isAdded) { this.removeFromMapAnchors(this.m_mapAnchors); } } /** * Returns the current atmosphere status, enabled or disabled. */ get enabled() { return this.m_enabled; } set lightMode(lightMode) { if (this.m_materialVariant !== AtmosphereShadingVariant.ScatteringShader) { return; } const dynamicLight = lightMode === AtmosphereLightMode.LightDynamic; if (this.m_groundMaterial !== undefined) { const groundMat = this.m_groundMaterial; groundMat.setDynamicLighting(dynamicLight); } if (this.m_skyMaterial !== undefined) { const skyMat = this.m_skyMaterial; skyMat.setDynamicLighting(dynamicLight); } } /** * Disposes allocated resources. */ dispose() { var _a, _b, _c, _d; // Unlink from scene and mapview anchors if (this.enabled) { this.enabled = false; } (_a = this.m_skyMaterial) === null || _a === void 0 ? void 0 : _a.dispose(); (_b = this.m_groundMaterial) === null || _b === void 0 ? void 0 : _b.dispose(); (_c = this.m_skyGeometry) === null || _c === void 0 ? void 0 : _c.dispose(); (_d = this.m_groundGeometry) === null || _d === void 0 ? void 0 : _d.dispose(); // After disposal we may no longer enable effect. this.m_skyGeometry = undefined; this.m_groundGeometry = undefined; this.m_skyMaterial = undefined; this.m_groundMaterial = undefined; this.m_skyMesh = undefined; this.m_groundMesh = undefined; } /** * Sets the atmosphere depending on the * {@link @here/harp-datasource-protocol#Theme} instance provided. * * This function is called when a theme is loaded. Atmosphere is added only if the theme * contains a atmosphere definition with a: * - `color` property, used to set the atmosphere color. * * @param theme - A {@link @here/harp-datasource-protocol#Theme} instance. */ reset(theme) { //this.m_cachedTheme = theme; } get disposed() { return this.m_skyMesh === undefined && this.m_groundMesh === undefined; } /** * Handles atmosphere effect adding. */ addToMapAnchors(mapAnchors) { harp_utils_1.assert(!MapViewAtmosphere.isPresent(mapAnchors), "Atmosphere already added"); if (this.m_skyMesh !== undefined) { mapAnchors.add(createMapAnchor(this.m_skyMesh, Number.MIN_SAFE_INTEGER)); } if (this.m_groundMesh !== undefined) { mapAnchors.add(createMapAnchor(this.m_groundMesh, Number.MAX_SAFE_INTEGER)); } // Request an update once the anchor is added to {@link MapView}. if (this.m_updateCallback) { this.m_updateCallback(); } } /** * Handles atmosphere effect removal. */ removeFromMapAnchors(mapAnchors) { if (!MapViewAtmosphere.isPresent(mapAnchors)) { return; } let update = false; if (this.m_skyMesh !== undefined) { mapAnchors.remove(this.m_skyMesh); update = true; } if (this.m_groundMesh !== undefined) { mapAnchors.remove(this.m_groundMesh); update = true; } if (update && this.m_updateCallback) { this.m_updateCallback(); } } createSkyGeometry() { switch (this.m_projection.type) { case harp_geoutils_1.ProjectionType.Spherical: this.m_skyGeometry = new THREE.SphereGeometry(harp_geoutils_1.EarthConstants.EQUATORIAL_RADIUS * (1 + SKY_ATMOSPHERE_ALTITUDE_FACTOR), 256, 256); break; default: { this.m_skyGeometry = new THREE.PlaneGeometry(200, 200); break; } } this.m_skyGeometry.translate(0, 0, 0); if (this.m_materialVariant === AtmosphereShadingVariant.ScatteringShader) { this.m_skyMaterial = new harp_materials_1.SkyAtmosphereMaterial({ rendererCapabilities: this.m_rendererCapabilities }); } else if (this.m_materialVariant === AtmosphereShadingVariant.SimpleColor) { this.m_skyMaterial = new THREE.MeshBasicMaterial({ color: new THREE.Color(0xc4f8ed), opacity: 0.4, transparent: false, depthTest: true, depthWrite: false, side: THREE.BackSide, blending: THREE.NormalBlending, fog: false }); } else { this.m_skyMaterial = new THREE.MeshStandardMaterial({ color: 0x7fffff, depthTest: false, depthWrite: false, normalScale: new THREE.Vector2(-1, -1), side: THREE.BackSide, wireframe: true }); } this.m_skyMesh = new THREE.Mesh(this.m_skyGeometry, this.m_skyMaterial); // Assign custom name so sky object may be easily recognized withing the scene. this.m_skyMesh.name = MapViewAtmosphere.SkyAtmosphereUserName; this.setupSkyForRendering(); } createGroundGeometry() { switch (this.m_projection.type) { case harp_geoutils_1.ProjectionType.Spherical: this.m_groundGeometry = new THREE.SphereGeometry(harp_geoutils_1.EarthConstants.EQUATORIAL_RADIUS * (1 + GROUND_ATMOSPHERE_ALTITUDE_FACTOR), 256, 256); break; default: { this.m_groundGeometry = new THREE.PlaneGeometry(200, 200); break; } } this.m_groundGeometry.translate(0, 0, 0); if (this.m_materialVariant === AtmosphereShadingVariant.ScatteringShader) { this.m_groundMaterial = new harp_materials_1.GroundAtmosphereMaterial({ rendererCapabilities: this.m_rendererCapabilities }); } else if (this.m_materialVariant === AtmosphereShadingVariant.SimpleColor) { this.m_groundMaterial = new THREE.MeshBasicMaterial({ color: new THREE.Color(0x00c5ff), opacity: 0.4, transparent: true, depthTest: false, depthWrite: false, side: THREE.FrontSide, blending: THREE.NormalBlending, fog: false }); } else { this.m_groundMaterial = new THREE.MeshStandardMaterial({ color: 0x11899a, depthTest: true, depthWrite: false, side: THREE.FrontSide, wireframe: true }); } this.m_groundMesh = new THREE.Mesh(this.m_groundGeometry, this.m_groundMaterial); // Assign name so object may be recognized withing the scene. this.m_groundMesh.name = MapViewAtmosphere.GroundAtmosphereUserName; this.setupGroundForRendering(); } setupSkyForRendering() { if (this.m_skyMesh === undefined) { return; } // Depending on material variant we need to update uniforms or only // update camera near/far planes cause camera need to see further then // actual earth geometry. let onBeforeCallback; if (this.m_materialVariant !== AtmosphereShadingVariant.ScatteringShader) { // Setup only further clip planes before rendering. onBeforeCallback = (camera, _material) => { this.overrideClipPlanes(camera); }; } else { // Setup proper clip planes and update uniforms values. onBeforeCallback = (camera, material) => { this.overrideClipPlanes(camera); // Check material wasn't swapped. harp_utils_1.assert(material instanceof harp_materials_1.SkyAtmosphereMaterial); const mat = this.m_skyMaterial; mat.updateUniforms(mat, this.m_skyMesh, camera, this.m_lightDirection); }; } // Sky material should be already created with mesh. harp_utils_1.assert(this.m_skyMaterial !== undefined); this.m_skyMesh.onBeforeRender = (_renderer, _scene, camera, _geometry, material, _group) => { onBeforeCallback(camera, material); }; this.m_skyMesh.onAfterRender = (_renderer, _scene, camera, _geometry, _material, _group) => { this.revertClipPlanes(camera); }; } setupGroundForRendering() { if (this.m_groundMesh === undefined) { return; } if (this.m_materialVariant !== AtmosphereShadingVariant.ScatteringShader) { return; } // Ground material should be already created. harp_utils_1.assert(this.m_groundMaterial !== undefined); // Ground mesh does not need custom clip planes and uses the same camera setup as // real (data source based) geometry. this.m_groundMesh.onBeforeRender = (_renderer, _scene, camera, _geometry, material, _group) => { harp_utils_1.assert(material instanceof harp_materials_1.GroundAtmosphereMaterial); const mat = this.m_groundMaterial; mat.updateUniforms(mat, this.m_groundMesh, camera, this.m_lightDirection); }; } overrideClipPlanes(rteCamera) { // Store current clip planes used by global camera before modifying them. const sceneCam = this.m_sceneCamera; cache.clipPlanes.near = sceneCam.near; cache.clipPlanes.far = sceneCam.far; // Calculate view ranges using world camera. // NOTE: ElevationProvider is not passed to evaluator, leaves min/max altitudes unchanged. const viewRanges = this.m_clipPlanesEvaluator.evaluateClipPlanes(this.m_sceneCamera, this.m_projection); // Update relative to eye camera used internally in rendering. harp_utils_1.assert(rteCamera instanceof THREE.PerspectiveCamera); const c = rteCamera; c.near = viewRanges.near; // Small margin ensures that we never cull small triangles just below or at // horizon - possible due to frustum culling in-precisions. c.far = viewRanges.far + harp_geoutils_1.EarthConstants.EQUATORIAL_RADIUS * 0.1; c.updateProjectionMatrix(); } revertClipPlanes(rteCamera) { harp_utils_1.assert(rteCamera instanceof THREE.PerspectiveCamera); const c = rteCamera; // Restore scene camera clip planes. c.near = cache.clipPlanes.near; c.far = cache.clipPlanes.far; c.updateProjectionMatrix(); } } exports.MapViewAtmosphere = MapViewAtmosphere; /** * User data name attribute assigned to created mesh. */ MapViewAtmosphere.SkyAtmosphereUserName = "SkyAtmosphere"; /** * User data name attribute assigned to created mesh. */ MapViewAtmosphere.GroundAtmosphereUserName = "GroundAtmosphere"; function createMapAnchor(mesh, renderOrder) { const anchor = mesh; anchor.renderOrder = renderOrder; anchor.pickable = false; anchor.anchor = new THREE.Vector3(0, 0, 0); return anchor; } //# sourceMappingURL=MapViewAtmosphere.js.map