@geoblocks/cesium-post-process 0.0.0-stage → 0.1.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/LICENSE +29 -0
- package/README.md +235 -2
- package/analog-video.js +138 -0
- package/blur.js +39 -0
- package/cesium-post-process.js +23 -0
- package/cloud-cover.js +49 -0
- package/cloud-map.js +122 -0
- package/cloud-noise.js +229 -0
- package/clouds.js +684 -0
- package/color-isolation.js +69 -0
- package/depth-test.js +37 -0
- package/digital-video.js +108 -0
- package/drone-display.js +170 -0
- package/effect.js +64 -0
- package/focus.js +25 -0
- package/frame-clock.js +61 -0
- package/height.js +23 -0
- package/infrared.js +40 -0
- package/jello.js +146 -0
- package/lens-distortion.js +68 -0
- package/lightning.js +321 -0
- package/motion-blur.js +117 -0
- package/package.json +55 -4
- package/precipitation.js +493 -0
- package/shaders/Alpenglow.js +2 -0
- package/shaders/AnalogVideo.js +2 -0
- package/shaders/Beam.js +2 -0
- package/shaders/CloudsBlur.js +2 -0
- package/shaders/CloudsComposite.js +2 -0
- package/shaders/CloudsDensity.js +2 -0
- package/shaders/CloudsDepth.js +2 -0
- package/shaders/CloudsMarch.js +2 -0
- package/shaders/CloudsResolve.js +2 -0
- package/shaders/CloudsShadow.js +2 -0
- package/shaders/CloudsShadowMap.js +2 -0
- package/shaders/CloudsSlab.js +2 -0
- package/shaders/ColorIsolation.js +2 -0
- package/shaders/DepthEdges.js +2 -0
- package/shaders/DigitalVideo.js +2 -0
- package/shaders/DroneDisplay.js +2 -0
- package/shaders/EyeFromDepth.js +2 -0
- package/shaders/Filmic.js +2 -0
- package/shaders/Flashlight.js +2 -0
- package/shaders/Fog.js +2 -0
- package/shaders/Frame.js +2 -0
- package/shaders/Hash.js +2 -0
- package/shaders/Height.js +2 -0
- package/shaders/Hsl.js +2 -0
- package/shaders/Infrared.js +2 -0
- package/shaders/Jello.js +2 -0
- package/shaders/LensDistortion.js +2 -0
- package/shaders/Lightning.js +2 -0
- package/shaders/MotionBlur.js +2 -0
- package/shaders/MotionBlurVelocity.js +2 -0
- package/shaders/Noise.js +2 -0
- package/shaders/Normal.js +2 -0
- package/shaders/Phase.js +2 -0
- package/shaders/Precipitation.js +2 -0
- package/shaders/PrecipitationShaftBlur.js +2 -0
- package/shaders/PrecipitationShafts.js +2 -0
- package/shaders/PrecipitationWind.js +2 -0
- package/shaders/RainHaze.js +2 -0
- package/shaders/Shockwave.js +2 -0
- package/shaders/SkyTable.js +2 -0
- package/shaders/SnowLine.js +2 -0
- package/shaders/SpeedLines.js +2 -0
- package/shaders/Spotlight.js +2 -0
- package/shaders/SpotlightBeam.js +2 -0
- package/shaders/SpotlightBeamBlur.js +2 -0
- package/shaders/SpotlightCone.js +2 -0
- package/shaders/Super8.js +2 -0
- package/shaders/Technicolor.js +2 -0
- package/shaders/TiltShift.js +2 -0
- package/shaders/Toy.js +2 -0
- package/shaders/Trails.js +2 -0
- package/shaders/ValleyFog.js +2 -0
- package/shaders/WeatherMap.js +2 -0
- package/shaders/WhiteModel.js +2 -0
- package/snow-line.js +108 -0
- package/speed-lines.js +71 -0
- package/spotlight.js +193 -0
- package/strikes.js +30 -0
- package/super8.js +164 -0
- package/technicolor.js +56 -0
- package/tilt-shift.js +124 -0
- package/toy.js +192 -0
- package/valley-fog.js +105 -0
- package/weather-map.js +290 -0
- package/white-model.js +105 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {PostProcessStage} from '@cesium/engine';
|
|
2
|
+
import Effect from './effect.js';
|
|
3
|
+
import ColorIsolationShader from './shaders/ColorIsolation.js';
|
|
4
|
+
import Hsl from './shaders/Hsl.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Color isolation: one hue keeps its color, the rest of the scene turns gray.
|
|
8
|
+
*/
|
|
9
|
+
export default class ColorIsolation extends Effect {
|
|
10
|
+
/**
|
|
11
|
+
* @param {import('@cesium/engine').CesiumWidget} viewer
|
|
12
|
+
* @param {{hue?: number, range?: number, strength?: number}} [options]
|
|
13
|
+
*/
|
|
14
|
+
constructor(viewer, options = {}) {
|
|
15
|
+
super(viewer);
|
|
16
|
+
this.hue_ = options.hue ?? 0;
|
|
17
|
+
this.range_ = options.range ?? 60;
|
|
18
|
+
this.strength_ = options.strength ?? 1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** @override */
|
|
22
|
+
createStage_() {
|
|
23
|
+
return new PostProcessStage({
|
|
24
|
+
fragmentShader: Hsl + ColorIsolationShader,
|
|
25
|
+
uniforms: {
|
|
26
|
+
hue: () => this.hue_ / 360,
|
|
27
|
+
range: () => this.range_ / 360,
|
|
28
|
+
strength: () => this.strength_,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Hue kept in color, in degrees: 0 red, 60 yellow, 120 green, 180 cyan,
|
|
35
|
+
* 240 blue, 300 magenta.
|
|
36
|
+
*/
|
|
37
|
+
get hue() {
|
|
38
|
+
return this.hue_;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
set hue(value) {
|
|
42
|
+
this.hue_ = value;
|
|
43
|
+
this.viewer.scene.requestRender();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Half width of the hues kept, in degrees.
|
|
48
|
+
*/
|
|
49
|
+
get range() {
|
|
50
|
+
return this.range_;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
set range(value) {
|
|
54
|
+
this.range_ = value;
|
|
55
|
+
this.viewer.scene.requestRender();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* How gray the rest turns, 0 to 1.
|
|
60
|
+
*/
|
|
61
|
+
get strength() {
|
|
62
|
+
return this.strength_;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
set strength(value) {
|
|
66
|
+
this.strength_ = value;
|
|
67
|
+
this.viewer.scene.requestRender();
|
|
68
|
+
}
|
|
69
|
+
}
|
package/depth-test.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// The post-process stages only get the depth of the terrain when it is depth tested. Several
|
|
2
|
+
// effects may need it at once: the first one turns depth testing on, the last one restores it.
|
|
3
|
+
|
|
4
|
+
/** @type {WeakMap<import('@cesium/engine').Globe, {count: number, previous: boolean}>} */
|
|
5
|
+
const users = new WeakMap();
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
9
|
+
*/
|
|
10
|
+
export function acquireTerrainDepth(scene) {
|
|
11
|
+
const globe = scene.globe;
|
|
12
|
+
if (!globe) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const user = users.get(globe);
|
|
16
|
+
if (user) {
|
|
17
|
+
user.count++;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
users.set(globe, {count: 1, previous: globe.depthTestAgainstTerrain});
|
|
21
|
+
globe.depthTestAgainstTerrain = true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
26
|
+
*/
|
|
27
|
+
export function releaseTerrainDepth(scene) {
|
|
28
|
+
const globe = scene.globe;
|
|
29
|
+
const user = globe && users.get(globe);
|
|
30
|
+
if (!globe || !user) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (--user.count === 0) {
|
|
34
|
+
globe.depthTestAgainstTerrain = user.previous;
|
|
35
|
+
users.delete(globe);
|
|
36
|
+
}
|
|
37
|
+
}
|
package/digital-video.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {PostProcessStage} from '@cesium/engine';
|
|
2
|
+
import Effect from './effect.js';
|
|
3
|
+
import {acquireFrames, releaseFrames} from './frame-clock.js';
|
|
4
|
+
import DigitalVideoShader from './shaders/DigitalVideo.js';
|
|
5
|
+
import Hash from './shaders/Hash.js';
|
|
6
|
+
|
|
7
|
+
// digital FPV video runs at this rate
|
|
8
|
+
const FRAME_RATE = 30;
|
|
9
|
+
// at full breakup, a breakup starts on average every this many seconds
|
|
10
|
+
const MIN_INTERVAL = 2;
|
|
11
|
+
// length of a breakup, in seconds, and of its black screen
|
|
12
|
+
const DURATION = [0.4, 1.5];
|
|
13
|
+
const BLACKOUT = 0.25;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A digital FPV video feed: clean, then at times broken by a weak signal
|
|
17
|
+
* into macroblocks, displaced blocks and strips and split colors, sometimes
|
|
18
|
+
* with a short black screen. While active the scene renders at 30 frames per
|
|
19
|
+
* second, also in requestRenderMode.
|
|
20
|
+
*/
|
|
21
|
+
export default class DigitalVideo extends Effect {
|
|
22
|
+
/**
|
|
23
|
+
* @param {import('@cesium/engine').CesiumWidget} viewer
|
|
24
|
+
* @param {{breakup?: number}} [options]
|
|
25
|
+
*/
|
|
26
|
+
constructor(viewer, options = {}) {
|
|
27
|
+
super(viewer);
|
|
28
|
+
this.breakup_ = options.breakup ?? 0.5;
|
|
29
|
+
// the current breakup: start and length in seconds, and whether it goes black
|
|
30
|
+
/** @type {{start: number, duration: number, blackout: boolean} | undefined} */
|
|
31
|
+
this.episode_ = undefined;
|
|
32
|
+
// when the breakups were last updated, in seconds
|
|
33
|
+
this.updated_ = 0;
|
|
34
|
+
// the uniforms, updated once per frame
|
|
35
|
+
this.severity_ = 0;
|
|
36
|
+
this.blackout_ = 0;
|
|
37
|
+
this.onPreRender_ = () => this.update_();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** @override */
|
|
41
|
+
createStage_() {
|
|
42
|
+
return new PostProcessStage({
|
|
43
|
+
fragmentShader: Hash + DigitalVideoShader,
|
|
44
|
+
uniforms: {
|
|
45
|
+
severity: () => this.severity_,
|
|
46
|
+
blackout: () => this.blackout_,
|
|
47
|
+
frame: () => Math.floor((performance.now() / 1000) * FRAME_RATE) % 100000,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @override
|
|
54
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
55
|
+
*/
|
|
56
|
+
activated_(scene) {
|
|
57
|
+
this.updated_ = performance.now() / 1000;
|
|
58
|
+
scene.preRender.addEventListener(this.onPreRender_);
|
|
59
|
+
acquireFrames(scene, FRAME_RATE);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @override
|
|
64
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
65
|
+
*/
|
|
66
|
+
deactivating_(scene) {
|
|
67
|
+
releaseFrames(scene, FRAME_RATE);
|
|
68
|
+
scene.preRender.removeEventListener(this.onPreRender_);
|
|
69
|
+
this.episode_ = undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// before each render: end the finished breakup, start one with the chance of the time since the
|
|
73
|
+
// last update, so that breakups start at the same rate whatever the frame rate (at most a second,
|
|
74
|
+
// as a hidden page does not render), then how broken the picture is now: rising and falling over
|
|
75
|
+
// the breakup, black around its middle
|
|
76
|
+
update_() {
|
|
77
|
+
const now = performance.now() / 1000;
|
|
78
|
+
const elapsed = Math.min(now - this.updated_, 1);
|
|
79
|
+
this.updated_ = now;
|
|
80
|
+
if (this.episode_ && now > this.episode_.start + this.episode_.duration) {
|
|
81
|
+
this.episode_ = undefined;
|
|
82
|
+
}
|
|
83
|
+
if (!this.episode_ && Math.random() < 1 - Math.exp((-this.breakup_ / MIN_INTERVAL) * elapsed)) {
|
|
84
|
+
const duration = DURATION[0] + Math.random() * (DURATION[1] - DURATION[0]);
|
|
85
|
+
this.episode_ = {start: now, duration, blackout: Math.random() < 0.5 * this.breakup_};
|
|
86
|
+
}
|
|
87
|
+
const episode = this.episode_;
|
|
88
|
+
if (!episode) {
|
|
89
|
+
this.severity_ = 0;
|
|
90
|
+
this.blackout_ = 0;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const t = (now - episode.start) / episode.duration;
|
|
94
|
+
this.severity_ = Math.max(0, Math.sin(Math.PI * Math.min(t, 1))) ** 0.7 * (0.4 + 0.6 * this.breakup_);
|
|
95
|
+
this.blackout_ = episode.blackout && Math.abs(t - 0.5) * episode.duration < BLACKOUT / 2 ? 1 : 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* How often and how badly the signal breaks up, 0 to 1.
|
|
100
|
+
*/
|
|
101
|
+
get breakup() {
|
|
102
|
+
return this.breakup_;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
set breakup(value) {
|
|
106
|
+
this.breakup_ = value;
|
|
107
|
+
}
|
|
108
|
+
}
|
package/drone-display.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import {Cartesian3, Math as CesiumMath} from '@cesium/core';
|
|
2
|
+
import {PostProcessStage} from '@cesium/engine';
|
|
3
|
+
import Effect from './effect.js';
|
|
4
|
+
import DroneDisplayShader from './shaders/DroneDisplay.js';
|
|
5
|
+
|
|
6
|
+
// a 3S battery, full, empty, the seconds it takes to drain between them at the cruise throttle,
|
|
7
|
+
// and how far its voltage sags at full throttle
|
|
8
|
+
const FULL_VOLTS = 12.6;
|
|
9
|
+
const EMPTY_VOLTS = 10.5;
|
|
10
|
+
const DRAIN_SECONDS = 2400;
|
|
11
|
+
const SAG_VOLTS = 0.4;
|
|
12
|
+
// the throttle in percent at cruise, and more of it per m/s of climb
|
|
13
|
+
const CRUISE_THROTTLE = 70;
|
|
14
|
+
const CLIMB_THROTTLE = 5;
|
|
15
|
+
// seconds for the speeds to get two thirds of the way to the camera's; after a pause longer than
|
|
16
|
+
// the gap, as in requestRenderMode, the motion is not measured across it
|
|
17
|
+
const SMOOTHING = 0.5;
|
|
18
|
+
const GAP = 0.5;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A fixed-wing plane's OSD as ArduPilot draws it on an HD video system, in the
|
|
22
|
+
* blocky white glyphs of an analog chip with some in color: an artificial
|
|
23
|
+
* horizon of dots following the camera's pitch and roll as ArduPilot's does,
|
|
24
|
+
* the flight mode, the heading, the altitude since the display turned on, the
|
|
25
|
+
* vertical speed and the air speed of the camera, the throttle following its
|
|
26
|
+
* climb, the battery voltage draining with the throttle, and a made-up link
|
|
27
|
+
* quality; and in failsafe while the control link is lost, the RTL mode and the
|
|
28
|
+
* FAILSAFE warning.
|
|
29
|
+
*/
|
|
30
|
+
export default class DroneDisplay extends Effect {
|
|
31
|
+
/**
|
|
32
|
+
* @param {import('@cesium/engine').CesiumWidget} viewer
|
|
33
|
+
* @param {{opacity?: number}} [options]
|
|
34
|
+
*/
|
|
35
|
+
constructor(viewer, options = {}) {
|
|
36
|
+
super(viewer);
|
|
37
|
+
this.opacity_ = options.opacity ?? 1;
|
|
38
|
+
this.rxLoss_ = false;
|
|
39
|
+
// when the display turned on, and the camera's height then
|
|
40
|
+
this.startTime_ = 0;
|
|
41
|
+
this.startHeight_ = 0;
|
|
42
|
+
// the camera's speed and vertical speed in m/s, smoothed, from its position and height at the
|
|
43
|
+
// previous frame
|
|
44
|
+
this.speed_ = 0;
|
|
45
|
+
this.verticalSpeed_ = 0;
|
|
46
|
+
this.previousPosition_ = new Cartesian3();
|
|
47
|
+
this.previousHeight_ = 0;
|
|
48
|
+
this.previousTime_ = 0;
|
|
49
|
+
// the share of the battery drained
|
|
50
|
+
this.drained_ = 0;
|
|
51
|
+
this.onPostRender_ = () => this.measure_(false);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @override
|
|
56
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
57
|
+
*/
|
|
58
|
+
createStage_(scene) {
|
|
59
|
+
return new PostProcessStage({
|
|
60
|
+
fragmentShader: DroneDisplayShader,
|
|
61
|
+
uniforms: {
|
|
62
|
+
opacity: () => this.opacity_,
|
|
63
|
+
pitch: () => CesiumMath.toDegrees(scene.camera.pitch),
|
|
64
|
+
roll: () => CesiumMath.toDegrees(CesiumMath.negativePiToPi(scene.camera.roll)),
|
|
65
|
+
heading: () => CesiumMath.toDegrees(scene.camera.heading),
|
|
66
|
+
altitude: () => scene.camera.positionCartographic.height - this.startHeight_,
|
|
67
|
+
verticalSpeed: () => this.verticalSpeed_,
|
|
68
|
+
speed: () => this.speed_ * 3.6,
|
|
69
|
+
voltage: () => this.voltage_(),
|
|
70
|
+
linkQuality: () => this.linkQuality_(),
|
|
71
|
+
throttle: () => this.throttle_(),
|
|
72
|
+
failsafe: () => (this.rxLoss_ ? 1 : 0),
|
|
73
|
+
// twice per second, as ArduPilot's blinking
|
|
74
|
+
blink: () => (Math.floor(performance.now() / 250) % 2 === 0 ? 1 : 0),
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @override
|
|
81
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
82
|
+
*/
|
|
83
|
+
activated_(scene) {
|
|
84
|
+
this.startTime_ = performance.now() / 1000;
|
|
85
|
+
this.startHeight_ = scene.camera.positionCartographic.height;
|
|
86
|
+
this.speed_ = 0;
|
|
87
|
+
this.verticalSpeed_ = 0;
|
|
88
|
+
this.drained_ = 0;
|
|
89
|
+
this.measure_(true);
|
|
90
|
+
scene.postRender.addEventListener(this.onPostRender_);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @override
|
|
95
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
96
|
+
*/
|
|
97
|
+
deactivating_(scene) {
|
|
98
|
+
scene.postRender.removeEventListener(this.onPostRender_);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Eases the speeds toward the camera's motion since the previous frame, and
|
|
103
|
+
* drains the battery at the throttle.
|
|
104
|
+
* @param {boolean} first whether there is no previous frame
|
|
105
|
+
*/
|
|
106
|
+
measure_(first) {
|
|
107
|
+
const camera = this.viewer.scene.camera;
|
|
108
|
+
const time = performance.now() / 1000;
|
|
109
|
+
const height = camera.positionCartographic.height;
|
|
110
|
+
const dt = time - this.previousTime_;
|
|
111
|
+
if (!first && dt > 0 && dt < GAP) {
|
|
112
|
+
const ease = 1 - Math.exp(-dt / SMOOTHING);
|
|
113
|
+
this.speed_ += (Cartesian3.distance(camera.positionWC, this.previousPosition_) / dt - this.speed_) * ease;
|
|
114
|
+
this.verticalSpeed_ += ((height - this.previousHeight_) / dt - this.verticalSpeed_) * ease;
|
|
115
|
+
}
|
|
116
|
+
if (!first) {
|
|
117
|
+
this.drained_ += (dt / DRAIN_SECONDS) * (this.throttle_() / CRUISE_THROTTLE);
|
|
118
|
+
}
|
|
119
|
+
Cartesian3.clone(camera.positionWC, this.previousPosition_);
|
|
120
|
+
this.previousHeight_ = height;
|
|
121
|
+
this.previousTime_ = time;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// the battery drains over the flight, and sags under the throttle's load
|
|
125
|
+
voltage_() {
|
|
126
|
+
const drained = (FULL_VOLTS - EMPTY_VOLTS) * Math.min(this.drained_, 1);
|
|
127
|
+
return FULL_VOLTS - drained - (SAG_VOLTS * this.throttle_()) / 100;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// the link hovers in the nineties, with a deep dip once in a while
|
|
131
|
+
linkQuality_() {
|
|
132
|
+
const seconds = performance.now() / 1000 - this.startTime_;
|
|
133
|
+
const wander = 4 * Math.abs(Math.sin(seconds * 0.9) * Math.sin(seconds * 0.37));
|
|
134
|
+
const dip = Math.sin(seconds * 0.11) > 0.97 ? 35 : 0;
|
|
135
|
+
return Math.round(99 - wander - dip);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// the throttle holds the cruise, more of it climbing and less diving, wandering a little
|
|
139
|
+
throttle_() {
|
|
140
|
+
const seconds = performance.now() / 1000 - this.startTime_;
|
|
141
|
+
const wander = 2 * Math.sin(seconds * 0.3) * Math.sin(seconds * 0.13 + 1);
|
|
142
|
+
return Math.round(Math.min(Math.max(CRUISE_THROTTLE + CLIMB_THROTTLE * this.verticalSpeed_ + wander, 0), 100));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Opacity of the display, 0 to 1.
|
|
147
|
+
*/
|
|
148
|
+
get opacity() {
|
|
149
|
+
return this.opacity_;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
set opacity(value) {
|
|
153
|
+
this.opacity_ = value;
|
|
154
|
+
this.viewer.scene.requestRender();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Whether the control link is lost: the mode turns to RTL, the link bar to
|
|
159
|
+
* red, and the FAILSAFE warning blinks below the middle. It blinks only while
|
|
160
|
+
* the scene renders.
|
|
161
|
+
*/
|
|
162
|
+
get rxLoss() {
|
|
163
|
+
return this.rxLoss_;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
set rxLoss(value) {
|
|
167
|
+
this.rxLoss_ = value;
|
|
168
|
+
this.viewer.scene.requestRender();
|
|
169
|
+
}
|
|
170
|
+
}
|
package/effect.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A screen-space effect: a post-process stage on a scene while active.
|
|
3
|
+
* Subclasses create their stage in `createStage_` and take their side effects
|
|
4
|
+
* in `activated_` and `deactivating_`.
|
|
5
|
+
*/
|
|
6
|
+
export default class Effect {
|
|
7
|
+
/**
|
|
8
|
+
* @param {import('@cesium/engine').CesiumWidget} viewer
|
|
9
|
+
*/
|
|
10
|
+
constructor(viewer) {
|
|
11
|
+
this.viewer = viewer;
|
|
12
|
+
/** @type {import('@cesium/engine').PostProcessStage | import('@cesium/engine').PostProcessStageComposite | undefined} */
|
|
13
|
+
this.stage_ = undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get active() {
|
|
17
|
+
return this.stage_ !== undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
set active(active) {
|
|
21
|
+
const scene = this.viewer.scene;
|
|
22
|
+
if (active) {
|
|
23
|
+
if (this.stage_) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
this.stage_ = this.createStage_(scene);
|
|
27
|
+
scene.postProcessStages.add(this.stage_);
|
|
28
|
+
this.activated_(scene);
|
|
29
|
+
} else {
|
|
30
|
+
if (!this.stage_) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
this.deactivating_(scene);
|
|
34
|
+
// removing a stage also destroys it
|
|
35
|
+
scene.postProcessStages.remove(this.stage_);
|
|
36
|
+
this.stage_ = undefined;
|
|
37
|
+
}
|
|
38
|
+
scene.requestRender();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {import('@cesium/engine').Scene} _scene
|
|
43
|
+
* @return {import('@cesium/engine').PostProcessStage | import('@cesium/engine').PostProcessStageComposite}
|
|
44
|
+
*/
|
|
45
|
+
createStage_(_scene) {
|
|
46
|
+
throw new Error(`${this.constructor.name} does not implement createStage_`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Called once the stage is added to the scene.
|
|
51
|
+
* @param {import('@cesium/engine').Scene} _scene
|
|
52
|
+
*/
|
|
53
|
+
activated_(_scene) {}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Called before the stage is removed from the scene.
|
|
57
|
+
* @param {import('@cesium/engine').Scene} _scene
|
|
58
|
+
*/
|
|
59
|
+
deactivating_(_scene) {}
|
|
60
|
+
|
|
61
|
+
destroy() {
|
|
62
|
+
this.active = false;
|
|
63
|
+
}
|
|
64
|
+
}
|
package/focus.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {Cartesian3, Cartesian4, Matrix4} from '@cesium/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What an effect focuses on: a position, a function called every frame for a moving target, or
|
|
5
|
+
* undefined for what is in the middle of the screen.
|
|
6
|
+
* @typedef {Cartesian3 | (() => Cartesian3 | undefined) | undefined} Focus
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const eyeScratch = new Cartesian3();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The focus in eye coordinates, computed in double precision; w is 0 for the middle of the
|
|
13
|
+
* screen, which the shaders read from the depth texture.
|
|
14
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
15
|
+
* @param {Focus} focus
|
|
16
|
+
* @param {Cartesian4} result
|
|
17
|
+
*/
|
|
18
|
+
export function eyeFocus(scene, focus, result) {
|
|
19
|
+
const position = typeof focus === 'function' ? focus() : focus;
|
|
20
|
+
if (!position) {
|
|
21
|
+
return Cartesian4.fromElements(0, 0, 0, 0, result);
|
|
22
|
+
}
|
|
23
|
+
const eye = Matrix4.multiplyByPoint(scene.camera.viewMatrix, position, eyeScratch);
|
|
24
|
+
return Cartesian4.fromElements(eye.x, eye.y, eye.z, 1, result);
|
|
25
|
+
}
|
package/frame-clock.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Animated effects need the scene to render at their frame rate, also in requestRenderMode. With a
|
|
2
|
+
// timer each, their render requests would not line up and would add up: one timer per scene
|
|
3
|
+
// requests renders at the highest rate the active effects need.
|
|
4
|
+
|
|
5
|
+
/** @type {WeakMap<import('@cesium/engine').Scene, {rates: number[], interval: ReturnType<typeof setInterval> | undefined}>} */
|
|
6
|
+
const clocks = new WeakMap();
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
10
|
+
* @param {{rates: number[], interval: ReturnType<typeof setInterval> | undefined}} clock
|
|
11
|
+
*/
|
|
12
|
+
function restart(scene, clock) {
|
|
13
|
+
clearInterval(clock.interval);
|
|
14
|
+
clock.interval = clock.rates.length > 0 ? setInterval(() => tick(scene, clock), 1000 / Math.max(...clock.rates)) : undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A render request; once the scene is destroyed, with effects left active, the clock stops: a
|
|
19
|
+
* destroyed scene throws on every call.
|
|
20
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
21
|
+
* @param {{rates: number[], interval: ReturnType<typeof setInterval> | undefined}} clock
|
|
22
|
+
*/
|
|
23
|
+
function tick(scene, clock) {
|
|
24
|
+
if (scene.isDestroyed()) {
|
|
25
|
+
clearInterval(clock.interval);
|
|
26
|
+
clocks.delete(scene);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
scene.requestRender();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
34
|
+
* @param {number} rate frames per second
|
|
35
|
+
*/
|
|
36
|
+
export function acquireFrames(scene, rate) {
|
|
37
|
+
let clock = clocks.get(scene);
|
|
38
|
+
if (!clock) {
|
|
39
|
+
clock = {rates: [], interval: undefined};
|
|
40
|
+
clocks.set(scene, clock);
|
|
41
|
+
}
|
|
42
|
+
clock.rates.push(rate);
|
|
43
|
+
restart(scene, clock);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
48
|
+
* @param {number} rate the rate given to acquireFrames
|
|
49
|
+
*/
|
|
50
|
+
export function releaseFrames(scene, rate) {
|
|
51
|
+
const clock = clocks.get(scene);
|
|
52
|
+
const index = clock ? clock.rates.indexOf(rate) : -1;
|
|
53
|
+
if (!clock || index < 0) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
clock.rates.splice(index, 1);
|
|
57
|
+
restart(scene, clock);
|
|
58
|
+
if (clock.rates.length === 0) {
|
|
59
|
+
clocks.delete(scene);
|
|
60
|
+
}
|
|
61
|
+
}
|
package/height.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {Cartesian3, Matrix4} from '@cesium/core';
|
|
2
|
+
|
|
3
|
+
const upScratch = new Cartesian3();
|
|
4
|
+
const surfaceScratch = new Cartesian3();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The uniforms of Height.glsl: the camera's height, the local up direction in
|
|
8
|
+
* eye coordinates and the Earth's radius below the camera, in double precision.
|
|
9
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
10
|
+
*/
|
|
11
|
+
export function heightUniforms(scene) {
|
|
12
|
+
return {
|
|
13
|
+
cameraHeight: () => scene.camera.positionCartographic.height,
|
|
14
|
+
up: () => {
|
|
15
|
+
const up = scene.ellipsoid.geodeticSurfaceNormal(scene.camera.positionWC, upScratch);
|
|
16
|
+
return Matrix4.multiplyByPointAsVector(scene.camera.viewMatrix, up, up);
|
|
17
|
+
},
|
|
18
|
+
radius: () => {
|
|
19
|
+
const surface = scene.ellipsoid.scaleToGeodeticSurface(scene.camera.positionWC, surfaceScratch);
|
|
20
|
+
return surface ? Cartesian3.magnitude(surface) : scene.ellipsoid.maximumRadius;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
package/infrared.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {PostProcessStage} from '@cesium/engine';
|
|
2
|
+
import Effect from './effect.js';
|
|
3
|
+
import Hsl from './shaders/Hsl.js';
|
|
4
|
+
import InfraredShader from './shaders/Infrared.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Black and white infrared film: bright foliage, dark sky and water.
|
|
8
|
+
*/
|
|
9
|
+
export default class Infrared extends Effect {
|
|
10
|
+
/**
|
|
11
|
+
* @param {import('@cesium/engine').CesiumWidget} viewer
|
|
12
|
+
* @param {{strength?: number}} [options]
|
|
13
|
+
*/
|
|
14
|
+
constructor(viewer, options = {}) {
|
|
15
|
+
super(viewer);
|
|
16
|
+
this.strength_ = options.strength ?? 1;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** @override */
|
|
20
|
+
createStage_() {
|
|
21
|
+
return new PostProcessStage({
|
|
22
|
+
fragmentShader: Hsl + InfraredShader,
|
|
23
|
+
uniforms: {
|
|
24
|
+
strength: () => this.strength_,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* How far from the original colors, 0 to 1.
|
|
31
|
+
*/
|
|
32
|
+
get strength() {
|
|
33
|
+
return this.strength_;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
set strength(value) {
|
|
37
|
+
this.strength_ = value;
|
|
38
|
+
this.viewer.scene.requestRender();
|
|
39
|
+
}
|
|
40
|
+
}
|