import { Theme } from "@here/harp-datasource-protocol" ; import { EarthConstants , Projection , ProjectionType } from "@here/harp-geoutils" ; import { GroundAtmosphereMaterial , SkyAtmosphereMaterial } from "@here/harp-materials" ; import { assert } from "@here/harp-utils" ; import * as THREE from "three" ; import { TiltViewClipPlanesEvaluator } from "./ClipPlanesEvaluator" ; import { MapAnchor , MapAnchors } from "./MapAnchors" ; /**
 * Atmosphere effect variants.
 */ enum AtmosphereVariant { Ground = 0x1 , Sky = 0x2 , SkyAndGround = 0x3 } /**
 * Atmosphere shader variants.
 */ enum AtmosphereShadingVariant { ScatteringShader , SimpleColor , Wireframe } /**
 * Lists light modes.
 */ export enum AtmosphereLightMode { LightOverhead = 0 , LightDynamic = 1 } /**
 * 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  MapView 's atmospheric scattering effect.
  export class MapViewAtmosphere { /**
     * User data name attribute assigned to created mesh.
     */ static SkyAtmosphereUserName : string = "SkyAtmosphere" ; /**
     * User data name attribute assigned to created mesh.
     */ static GroundAtmosphereUserName : string = "GroundAtmosphere" ; param mapAnchors static isPresent ( mapAnchors ) { for ( const mapAnchor of mapAnchors . children ) { if ( mapAnchor . name === MapViewAtmosphere . SkyAtmosphereUserName || mapAnchor . name === MapViewAtmosphere . GroundAtmosphereUserName ) { return true ; } } return false ; } private m_enabled : boolean = true ; private m_skyGeometry ? : THREE . BufferGeometry ; private m_skyMaterial ? : THREE . Material ; private m_skyMesh ? : THREE . Mesh ; private m_groundGeometry ? : THREE . BufferGeometry ; private m_groundMaterial ? : THREE . Material ; private m_groundMesh ? : THREE . Mesh ; private readonly m_clipPlanesEvaluator = new TiltViewClipPlanesEvaluator ( EarthConstants . EQUATORIAL_RADIUS * SKY_ATMOSPHERE_ALTITUDE_FACTOR , 0 , 1.0 , 0.05 , 10000000.0 ) ; private readonly m_lightDirection = new THREE . Vector3 ( 0.0 , 1.0 , 0.0 ) ; note param m_mapAnchors - The  MapAnchors instance where the effect will be added.
     *  param m_sceneCamera param m_projection param m_rendererCapabilities param m_updateCallback param m_atmosphereVariant param m_materialVariant constructor ( private readonly m_mapAnchors , private readonly m_sceneCamera , private readonly m_projection , private readonly m_rendererCapabilities , private readonly m_updateCallback ? , private readonly m_atmosphereVariant = AtmosphereVariant . SkyAndGround , private readonly m_materialVariant = AtmosphereShadingVariant . ScatteringShader ) { if ( this . m_atmosphereVariant & AtmosphereVariant . Sky ) { this . createSkyGeometry ( ) ; } if ( this . m_atmosphereVariant & AtmosphereVariant . Ground ) { this . createGroundGeometry ( ) ; } this . addToMapAnchors ( this . m_mapAnchors ) ; } get skyMesh ( ) : THREE . Mesh | undefined { return this . m_skyMesh ; } get groundMesh ( ) : THREE . Mesh | undefined { return this . m_groundMesh ; } param enable set enabled ( enable ) { 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 ( ) : boolean { 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 as GroundAtmosphereMaterial ; groundMat . setDynamicLighting ( dynamicLight ) ; } if ( this . m_skyMaterial !== undefined ) { const skyMat = this . m_skyMaterial as SkyAtmosphereMaterial ; skyMat . setDynamicLighting ( dynamicLight ) ; } } /**
     * Disposes allocated resources.
     */ dispose ( ) { if ( this . enabled ) { this . enabled = false ; } this . m_skyMaterial ?. dispose ( ) ; this . m_groundMaterial ?. dispose ( ) ; this . m_skyGeometry ?. dispose ( ) ; this . m_groundGeometry ?. dispose ( ) ; 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 ) { } private get disposed ( ) { return this . m_skyMesh === undefined && this . m_groundMesh === undefined ; } /**
     * Handles atmosphere effect adding.
     */ private addToMapAnchors ( mapAnchors ) { 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 ) ) ; } if ( this . m_updateCallback ) { this . m_updateCallback ( ) ; } } /**
     * Handles atmosphere effect removal.
     */ private 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 ( ) ; } } private createSkyGeometry ( ) { switch ( this . m_projection . type ) { case ProjectionType . Spherical : this . m_skyGeometry = new THREE . SphereGeometry ( 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 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 ) ; this . m_skyMesh . name = MapViewAtmosphere . SkyAtmosphereUserName ; this . setupSkyForRendering ( ) ; } private createGroundGeometry ( ) { switch ( this . m_projection . type ) { case ProjectionType . Spherical : this . m_groundGeometry = new THREE . SphereGeometry ( 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 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 ) ; this . m_groundMesh . name = MapViewAtmosphere . GroundAtmosphereUserName ; this . setupGroundForRendering ( ) ; } private setupSkyForRendering ( ) { if ( this . m_skyMesh === undefined ) { return ; } let onBeforeCallback ; if ( this . m_materialVariant !== AtmosphereShadingVariant . ScatteringShader ) { onBeforeCallback = ( camera , _material ) => { this . overrideClipPlanes ( camera ) ; } ; } else { onBeforeCallback = ( camera , material ) => { this . overrideClipPlanes ( camera ) ; assert ( material instanceof SkyAtmosphereMaterial ) ; const mat = this . m_skyMaterial as SkyAtmosphereMaterial ; mat . updateUniforms ( mat , this . m_skyMesh ! , camera , this . m_lightDirection ) ; } ; } 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 ) ; } ; } private setupGroundForRendering ( ) { if ( this . m_groundMesh === undefined ) { return ; } if ( this . m_materialVariant !== AtmosphereShadingVariant . ScatteringShader ) { return ; } assert ( this . m_groundMaterial !== undefined ) ; this . m_groundMesh . onBeforeRender = ( _renderer , _scene , camera , _geometry , material , _group ) => { assert ( material instanceof GroundAtmosphereMaterial ) ; const mat = this . m_groundMaterial as GroundAtmosphereMaterial ; mat . updateUniforms ( mat , this . m_groundMesh ! , camera , this . m_lightDirection ) ; } ; } private overrideClipPlanes ( rteCamera ) { const sceneCam = this . m_sceneCamera as THREE . PerspectiveCamera ; cache . clipPlanes . near = sceneCam . near ; cache . clipPlanes . far = sceneCam . far ; const viewRanges = this . m_clipPlanesEvaluator . evaluateClipPlanes ( this . m_sceneCamera , this . m_projection ) ; assert ( rteCamera instanceof THREE . PerspectiveCamera ) ; const c = rteCamera as THREE . PerspectiveCamera ; c . near = viewRanges . near ; c . far = viewRanges . far + EarthConstants . EQUATORIAL_RADIUS * 0.1 ; c . updateProjectionMatrix ( ) ; } private revertClipPlanes ( rteCamera ) { assert ( rteCamera instanceof THREE . PerspectiveCamera ) ; const c = rteCamera as THREE . PerspectiveCamera ; c . near = cache . clipPlanes . near ; c . far = cache . clipPlanes . far ; c . updateProjectionMatrix ( ) ; } } function createMapAnchor ( mesh , renderOrder ) { const anchor = mesh as MapAnchor < THREE . Mesh > ; anchor . renderOrder = renderOrder ; anchor . pickable = false ; anchor . anchor = new THREE . Vector3 ( 0 , 0 , 0 ) ; return anchor ; }