bruce-cesium 7.1.3 → 7.1.4
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/dist/bruce-cesium.es5.js +1000 -47
- package/dist/bruce-cesium.es5.js.map +1 -1
- package/dist/bruce-cesium.umd.js +970 -22
- package/dist/bruce-cesium.umd.js.map +1 -1
- package/dist/lib/bruce-cesium.js +2 -1
- package/dist/lib/bruce-cesium.js.map +1 -1
- package/dist/lib/rendering/displaced-surface-primitive.js +723 -0
- package/dist/lib/rendering/displaced-surface-primitive.js.map +1 -0
- package/dist/lib/rendering/entity-render-engine-polygon.js +193 -15
- package/dist/lib/rendering/entity-render-engine-polygon.js.map +1 -1
- package/dist/lib/rendering/entity-render-engine.js +1 -1
- package/dist/lib/rendering/entity-render-engine.js.map +1 -1
- package/dist/lib/rendering/texture-frame-series-animator.js +62 -5
- package/dist/lib/rendering/texture-frame-series-animator.js.map +1 -1
- package/dist/types/bruce-cesium.d.ts +2 -1
- package/dist/types/rendering/displaced-surface-primitive.d.ts +103 -0
- package/dist/types/rendering/entity-render-engine-polygon.d.ts +1 -1
- package/dist/types/rendering/texture-frame-series-animator.d.ts +25 -0
- package/package.json +2 -2
package/dist/bruce-cesium.umd.js
CHANGED
|
@@ -39639,6 +39639,8 @@
|
|
|
39639
39639
|
this.pendingIndex = -1;
|
|
39640
39640
|
this.displayedPixels = null;
|
|
39641
39641
|
this.fadeFromPixels = null;
|
|
39642
|
+
this.displayedValuePixels = null;
|
|
39643
|
+
this.fadeFromValuePixels = null;
|
|
39642
39644
|
this.fadeToIndex = -1;
|
|
39643
39645
|
this.fadeToDims = null;
|
|
39644
39646
|
this.fadeStartMs = null;
|
|
@@ -39647,6 +39649,8 @@
|
|
|
39647
39649
|
// forcing Cesium to re-upload the texture only when something changed.
|
|
39648
39650
|
this.pool = [document.createElement("canvas"), document.createElement("canvas")];
|
|
39649
39651
|
this.poolIdx = 0;
|
|
39652
|
+
this.valueDims = null;
|
|
39653
|
+
this.presentVersion = 0;
|
|
39650
39654
|
if (!options.entity.polygon) {
|
|
39651
39655
|
throw new Error("TextureFrameSeriesAnimator requires an entity with polygon graphics.");
|
|
39652
39656
|
}
|
|
@@ -39664,9 +39668,15 @@
|
|
|
39664
39668
|
this.frameDates = this.frames.map((f) => Cesium.JulianDate.fromIso8601(f.Timestamp));
|
|
39665
39669
|
this.frameCache = new Array(this.frames.length).fill(null);
|
|
39666
39670
|
this.frameDims = new Array(this.frames.length).fill(null);
|
|
39671
|
+
this.valueCache = new Array(this.frames.length).fill(null);
|
|
39672
|
+
this.produceValueCanvas = Boolean(options.produceValueCanvas);
|
|
39673
|
+
this.valueCanvas = this.produceValueCanvas ? document.createElement("canvas") : null;
|
|
39674
|
+
this.driveMaterial = options.driveMaterial !== false;
|
|
39667
39675
|
this.originalMaterial = options.entity.polygon.material;
|
|
39668
39676
|
this.imageProperty = new Cesium.CallbackProperty(() => this.pool[this.poolIdx], false);
|
|
39669
|
-
|
|
39677
|
+
if (this.driveMaterial) {
|
|
39678
|
+
options.entity.polygon.material = new Cesium.ImageMaterialProperty({ image: this.imageProperty, transparent: true });
|
|
39679
|
+
}
|
|
39670
39680
|
this.removeCrossfadeTick = this.viewer.scene.postUpdate.addEventListener(() => this.tickCrossfade());
|
|
39671
39681
|
this.removeOnTick = this.viewer.clock.onTick.addEventListener((clock) => this.onClockTick(clock.currentTime));
|
|
39672
39682
|
this.onClockTick(this.viewer.clock.currentTime);
|
|
@@ -39688,7 +39698,7 @@
|
|
|
39688
39698
|
this.removeCrossfadeTick();
|
|
39689
39699
|
this.removeCrossfadeTick = null;
|
|
39690
39700
|
}
|
|
39691
|
-
if (this.entity && this.entity.polygon) {
|
|
39701
|
+
if (this.driveMaterial && this.entity && this.entity.polygon) {
|
|
39692
39702
|
this.entity.polygon.material = this.originalMaterial;
|
|
39693
39703
|
}
|
|
39694
39704
|
}
|
|
@@ -39709,6 +39719,20 @@
|
|
|
39709
39719
|
GetImageProperty() {
|
|
39710
39720
|
return this.imageProperty;
|
|
39711
39721
|
}
|
|
39722
|
+
/**
|
|
39723
|
+
* The untinted canvas carrying the presented frame's value in RGB and its coverage in alpha,
|
|
39724
|
+
* or null unless the instance was constructed with produceValueCanvas.
|
|
39725
|
+
*/
|
|
39726
|
+
GetValueCanvas() {
|
|
39727
|
+
return this.valueDims ? this.valueCanvas : null;
|
|
39728
|
+
}
|
|
39729
|
+
/**
|
|
39730
|
+
* Increments every time a frame is presented, so a consumer holding the value canvas can tell
|
|
39731
|
+
* whether it needs re-uploading without diffing pixels or subscribing to anything.
|
|
39732
|
+
*/
|
|
39733
|
+
GetPresentVersion() {
|
|
39734
|
+
return this.presentVersion;
|
|
39735
|
+
}
|
|
39712
39736
|
onClockTick(currentTime) {
|
|
39713
39737
|
if (this.disposed) {
|
|
39714
39738
|
return;
|
|
@@ -39821,6 +39845,7 @@
|
|
|
39821
39845
|
}
|
|
39822
39846
|
this.frameCache[idx] = decoded.pixels;
|
|
39823
39847
|
this.frameDims[idx] = { width: decoded.width, height: decoded.height };
|
|
39848
|
+
this.valueCache[idx] = decoded.valuePixels || null;
|
|
39824
39849
|
if (idx === this.currentFrameIndex) {
|
|
39825
39850
|
this.beginCrossfadeTo(idx);
|
|
39826
39851
|
}
|
|
@@ -39844,11 +39869,17 @@
|
|
|
39844
39869
|
const ctx = canvas.getContext("2d");
|
|
39845
39870
|
ctx.drawImage(image, 0, 0);
|
|
39846
39871
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
39872
|
+
// Copied before tinting: the ramp is a lerp between two colours, so the value cannot be
|
|
39873
|
+
// recovered from the tinted pixels afterwards.
|
|
39874
|
+
const valuePixels = this.produceValueCanvas ? new Uint8ClampedArray(imageData.data) : undefined;
|
|
39847
39875
|
ApplyGrayscaleColorMask(imageData, this.lowColor, this.highColor);
|
|
39848
|
-
return { pixels: imageData.data, width: canvas.width, height: canvas.height };
|
|
39876
|
+
return { pixels: imageData.data, width: canvas.width, height: canvas.height, valuePixels };
|
|
39849
39877
|
}
|
|
39850
39878
|
beginCrossfadeTo(idx) {
|
|
39851
39879
|
const to = this.frameCache[idx];
|
|
39880
|
+
this.fadeFromValuePixels = this.displayedValuePixels === null && this.valueCache[idx]
|
|
39881
|
+
? new Uint8ClampedArray(this.valueCache[idx].length)
|
|
39882
|
+
: this.displayedValuePixels;
|
|
39852
39883
|
this.fadeFromPixels = this.displayedPixels === null ? new Uint8ClampedArray(to.length) : this.displayedPixels;
|
|
39853
39884
|
this.fadeToIndex = idx;
|
|
39854
39885
|
this.fadeToDims = this.frameDims[idx];
|
|
@@ -39867,13 +39898,28 @@
|
|
|
39867
39898
|
for (let i = 0; i < n; i++) {
|
|
39868
39899
|
blended[i] = from[i] + (to[i] - from[i]) * eased;
|
|
39869
39900
|
}
|
|
39870
|
-
|
|
39901
|
+
let blendedValue = null;
|
|
39902
|
+
const toValue = this.valueCache[this.fadeToIndex];
|
|
39903
|
+
if (toValue) {
|
|
39904
|
+
const fromValue = this.fadeFromValuePixels;
|
|
39905
|
+
blendedValue = new Uint8ClampedArray(toValue.length);
|
|
39906
|
+
if (fromValue && fromValue.length === toValue.length) {
|
|
39907
|
+
for (let i = 0; i < toValue.length; i++) {
|
|
39908
|
+
blendedValue[i] = fromValue[i] + (toValue[i] - fromValue[i]) * eased;
|
|
39909
|
+
}
|
|
39910
|
+
}
|
|
39911
|
+
else {
|
|
39912
|
+
blendedValue.set(toValue);
|
|
39913
|
+
}
|
|
39914
|
+
}
|
|
39915
|
+
this.presentPixels(blended, this.fadeToDims, blendedValue);
|
|
39871
39916
|
if (t >= 1) {
|
|
39872
39917
|
this.fadeStartMs = null;
|
|
39873
39918
|
this.fadeFromPixels = null;
|
|
39919
|
+
this.fadeFromValuePixels = null;
|
|
39874
39920
|
}
|
|
39875
39921
|
}
|
|
39876
|
-
presentPixels(pixels, dims) {
|
|
39922
|
+
presentPixels(pixels, dims, valuePixels) {
|
|
39877
39923
|
this.poolIdx = 1 - this.poolIdx;
|
|
39878
39924
|
const target = this.pool[this.poolIdx];
|
|
39879
39925
|
target.width = dims.width;
|
|
@@ -39883,6 +39929,17 @@
|
|
|
39883
39929
|
imgData.data.set(pixels);
|
|
39884
39930
|
ctx.putImageData(imgData, 0, 0);
|
|
39885
39931
|
this.displayedPixels = pixels;
|
|
39932
|
+
if (this.valueCanvas && valuePixels) {
|
|
39933
|
+
this.valueCanvas.width = dims.width;
|
|
39934
|
+
this.valueCanvas.height = dims.height;
|
|
39935
|
+
const valueCtx = this.valueCanvas.getContext("2d");
|
|
39936
|
+
const valueImgData = valueCtx.createImageData(dims.width, dims.height);
|
|
39937
|
+
valueImgData.data.set(valuePixels);
|
|
39938
|
+
valueCtx.putImageData(valueImgData, 0, 0);
|
|
39939
|
+
this.displayedValuePixels = valuePixels;
|
|
39940
|
+
this.valueDims = dims;
|
|
39941
|
+
}
|
|
39942
|
+
this.presentVersion++;
|
|
39886
39943
|
}
|
|
39887
39944
|
}
|
|
39888
39945
|
TextureFrameSeriesAnimator.Animator = Animator;
|
|
@@ -39894,10 +39951,726 @@
|
|
|
39894
39951
|
}
|
|
39895
39952
|
})(TextureFrameSeriesAnimator || (TextureFrameSeriesAnimator = {}));
|
|
39896
39953
|
|
|
39954
|
+
(function (DisplacedSurfacePrimitive) {
|
|
39955
|
+
// Grid densities a tile can draw at, coarsest first.
|
|
39956
|
+
const TIERS = [32, 64, 128, 192];
|
|
39957
|
+
// Tiles per side the extent is cut into.
|
|
39958
|
+
const TILES_PER_SIDE = 6;
|
|
39959
|
+
// Screen pixels one grid cell should cover. Lower means finer meshes sooner.
|
|
39960
|
+
const TARGET_CELL_PIXELS = 12;
|
|
39961
|
+
// Fraction of its distance to the surface the camera must move before densities are reconsidered.
|
|
39962
|
+
const MOVEMENT_THRESHOLD = 0.12;
|
|
39963
|
+
const RESELECT_INTERVAL_MS = 400;
|
|
39964
|
+
// Skirt depth as a fraction of the displaced range. The sheet is translucent, so an oversized
|
|
39965
|
+
// skirt blends through it and paints a seam down every tile boundary.
|
|
39966
|
+
const SKIRT_FRACTION = 0.015;
|
|
39967
|
+
// Alpha below which a texel counts as no-data and is not drawn at all.
|
|
39968
|
+
const COVERAGE_CUTOFF = 0.35;
|
|
39969
|
+
// With no exaggeration stated, the full value range is drawn as this fraction of the extent's shorter side.
|
|
39970
|
+
const AUTO_RELIEF_FRACTION = 0.02;
|
|
39971
|
+
// Terrain is sampled on this grid and interpolated between samples.
|
|
39972
|
+
// Per-texel sampling would be hundreds of thousands of lookups for a surface whose own data is far coarser than that.
|
|
39973
|
+
const GROUND_SAMPLES_PER_SIDE = 64;
|
|
39974
|
+
// Terrain detail level the samples are taken at.
|
|
39975
|
+
// Most-detailed would fetch every tile under a parcel that can be a hundred kilometres across.
|
|
39976
|
+
const GROUND_SAMPLE_LEVEL = 11;
|
|
39977
|
+
// Height window the packed ground texture covers, wide enough for any terrain on Earth.
|
|
39978
|
+
const GROUND_MIN_METRES = -1000;
|
|
39979
|
+
const GROUND_MAX_METRES = 9000;
|
|
39980
|
+
const DEFAULT_LOW_COLOR = { red: 255, green: 255, blue: 255, alpha: 0.12 };
|
|
39981
|
+
const DEFAULT_HIGH_COLOR = { red: 21, green: 96, blue: 196, alpha: 0.92 };
|
|
39982
|
+
const VERTEX_SHADER_GLSL300 = `
|
|
39983
|
+
in vec3 position3DHigh;
|
|
39984
|
+
in vec3 position3DLow;
|
|
39985
|
+
in vec3 normal;
|
|
39986
|
+
in vec2 st;
|
|
39987
|
+
in float skirt;
|
|
39988
|
+
|
|
39989
|
+
uniform sampler2D u_valueTexture;
|
|
39990
|
+
uniform sampler2D u_groundTexture;
|
|
39991
|
+
uniform float u_valueMin;
|
|
39992
|
+
uniform float u_valueRange;
|
|
39993
|
+
uniform float u_exaggeration;
|
|
39994
|
+
uniform float u_baseHeight;
|
|
39995
|
+
uniform float u_skirtDepth;
|
|
39996
|
+
uniform float u_groundWeight;
|
|
39997
|
+
uniform vec2 u_groundRange;
|
|
39998
|
+
uniform vec2 u_groundSize;
|
|
39999
|
+
|
|
40000
|
+
out vec2 v_st;
|
|
40001
|
+
out float v_value;
|
|
40002
|
+
out float v_coverage;
|
|
40003
|
+
|
|
40004
|
+
float decodeGround(vec2 uv) {
|
|
40005
|
+
// Channels arrive normalised to 0..1, so they go back to bytes before being reassembled.
|
|
40006
|
+
vec3 packed = texture(u_groundTexture, uv).rgb * 255.0;
|
|
40007
|
+
float normalised = dot(packed, vec3(65536.0, 256.0, 1.0)) / 16777215.0;
|
|
40008
|
+
return u_groundRange.x + normalised * (u_groundRange.y - u_groundRange.x);
|
|
40009
|
+
}
|
|
40010
|
+
|
|
40011
|
+
/*
|
|
40012
|
+
* Bilinear by hand, because the height is packed across three channels and hardware filtering would
|
|
40013
|
+
* blend the packing rather than the heights, turning a smooth slope into noise at every texel edge.
|
|
40014
|
+
*/
|
|
40015
|
+
float sampleGround(vec2 uv) {
|
|
40016
|
+
vec2 texel = 1.0 / u_groundSize;
|
|
40017
|
+
vec2 coord = uv * u_groundSize - 0.5;
|
|
40018
|
+
vec2 base = floor(coord);
|
|
40019
|
+
vec2 f = coord - base;
|
|
40020
|
+
vec2 origin = (base + 0.5) * texel;
|
|
40021
|
+
float h00 = decodeGround(origin);
|
|
40022
|
+
float h10 = decodeGround(origin + vec2(texel.x, 0.0));
|
|
40023
|
+
float h01 = decodeGround(origin + vec2(0.0, texel.y));
|
|
40024
|
+
float h11 = decodeGround(origin + texel);
|
|
40025
|
+
return mix(mix(h00, h10, f.x), mix(h01, h11, f.x), f.y);
|
|
40026
|
+
}
|
|
40027
|
+
|
|
40028
|
+
void main() {
|
|
40029
|
+
vec4 texel = texture(u_valueTexture, st);
|
|
40030
|
+
v_st = st;
|
|
40031
|
+
v_value = texel.r;
|
|
40032
|
+
v_coverage = texel.a;
|
|
40033
|
+
|
|
40034
|
+
float metres = u_valueMin + texel.r * u_valueRange;
|
|
40035
|
+
|
|
40036
|
+
// Value zero sits at the polygon's own altitude, plus the terrain underneath when the polygon
|
|
40037
|
+
// is following the ground rather than absolutely positioned.
|
|
40038
|
+
float ground = sampleGround(st) * u_groundWeight;
|
|
40039
|
+
float displacement = u_baseHeight + ground + metres * u_exaggeration * texel.a - skirt * u_skirtDepth;
|
|
40040
|
+
|
|
40041
|
+
// Added to the LOW half of the encoded position: a metre-scale offset added to the high half
|
|
40042
|
+
// would be lost to float32 rounding at an earth radius.
|
|
40043
|
+
vec4 p = czm_translateRelativeToEye(position3DHigh, position3DLow + normal * displacement);
|
|
40044
|
+
gl_Position = czm_modelViewProjectionRelativeToEye * p;
|
|
40045
|
+
}
|
|
40046
|
+
`;
|
|
40047
|
+
const FRAGMENT_SHADER_GLSL300 = `
|
|
40048
|
+
uniform sampler2D u_valueTexture;
|
|
40049
|
+
uniform vec4 u_lowColor;
|
|
40050
|
+
uniform vec4 u_highColor;
|
|
40051
|
+
uniform float u_coverageCutoff;
|
|
40052
|
+
uniform vec2 u_texelSize;
|
|
40053
|
+
uniform float u_valueRange;
|
|
40054
|
+
uniform float u_exaggeration;
|
|
40055
|
+
uniform float u_metresPerTexel;
|
|
40056
|
+
|
|
40057
|
+
in vec2 v_st;
|
|
40058
|
+
in float v_value;
|
|
40059
|
+
in float v_coverage;
|
|
40060
|
+
|
|
40061
|
+
void main() {
|
|
40062
|
+
if (v_coverage < u_coverageCutoff) {
|
|
40063
|
+
discard;
|
|
40064
|
+
}
|
|
40065
|
+
vec4 color = mix(u_lowColor, u_highColor, v_value);
|
|
40066
|
+
|
|
40067
|
+
// Relief shading from the value gradient, so the displacement reads at a distance without
|
|
40068
|
+
// recomputing vertex normals every time the texture changes.
|
|
40069
|
+
float left = texture(u_valueTexture, v_st - vec2(u_texelSize.x, 0.0)).r;
|
|
40070
|
+
float right = texture(u_valueTexture, v_st + vec2(u_texelSize.x, 0.0)).r;
|
|
40071
|
+
float down = texture(u_valueTexture, v_st - vec2(0.0, u_texelSize.y)).r;
|
|
40072
|
+
float up = texture(u_valueTexture, v_st + vec2(0.0, u_texelSize.y)).r;
|
|
40073
|
+
float scale = u_valueRange * u_exaggeration;
|
|
40074
|
+
vec3 n = normalize(vec3((left - right) * scale, (down - up) * scale, 2.0 * u_metresPerTexel));
|
|
40075
|
+
float lambert = clamp(dot(n, normalize(vec3(-0.5, -0.6, 0.62))), 0.0, 1.0);
|
|
40076
|
+
color.rgb *= 0.45 + 0.9 * lambert;
|
|
40077
|
+
|
|
40078
|
+
out_FragColor = vec4(color.rgb, color.a * v_coverage);
|
|
40079
|
+
}
|
|
40080
|
+
`;
|
|
40081
|
+
/*
|
|
40082
|
+
* Rewrites a GLSL 300 es source into the GLSL 100 dialect older Cesium builds author in.
|
|
40083
|
+
*/
|
|
40084
|
+
function toGlsl100(source, isFragment) {
|
|
40085
|
+
let result = source
|
|
40086
|
+
.replace(/\btexture\s*\(/g, "texture2D(")
|
|
40087
|
+
.replace(/^in\s+/gm, isFragment ? "varying " : "attribute ")
|
|
40088
|
+
.replace(/^out\s+/gm, "varying ");
|
|
40089
|
+
if (isFragment) {
|
|
40090
|
+
result = result.replace(/\bout_FragColor\b/g, "gl_FragColor");
|
|
40091
|
+
}
|
|
40092
|
+
return result;
|
|
40093
|
+
}
|
|
40094
|
+
let _glsl300Support = null;
|
|
40095
|
+
/*
|
|
40096
|
+
* Works out which GLSL dialect this host's Cesium authors shaders in.
|
|
40097
|
+
*/
|
|
40098
|
+
function usesGlsl300(context) {
|
|
40099
|
+
if (_glsl300Support !== null) {
|
|
40100
|
+
return _glsl300Support;
|
|
40101
|
+
}
|
|
40102
|
+
let modernizes = false;
|
|
40103
|
+
try {
|
|
40104
|
+
const probe = new Cesium.ShaderSource({
|
|
40105
|
+
sources: ["varying float v_probe;\nvoid main() { gl_Position = vec4(0.0); }"],
|
|
40106
|
+
includeBuiltIns: false
|
|
40107
|
+
});
|
|
40108
|
+
const combined = probe.createCombinedVertexShader(context);
|
|
40109
|
+
modernizes = combined.indexOf("varying") < 0;
|
|
40110
|
+
}
|
|
40111
|
+
catch (e) {
|
|
40112
|
+
// No probe means no evidence the host rewrites anything, so trust the context alone.
|
|
40113
|
+
modernizes = false;
|
|
40114
|
+
}
|
|
40115
|
+
_glsl300Support = Boolean(context.webgl2) && !modernizes;
|
|
40116
|
+
return _glsl300Support;
|
|
40117
|
+
}
|
|
40118
|
+
/*
|
|
40119
|
+
* Places grid node (fx, fy) on the ellipsoid, walking equal steps of east/north metres.
|
|
40120
|
+
* Cesium maps a polygon's material across a tangent-plane rectangle and the generated raster is
|
|
40121
|
+
* linear in a projected CRS, so stepping in degrees instead would skew the surface against the flat-textured polygon it replaces.
|
|
40122
|
+
*/
|
|
40123
|
+
function makePlacer(extent) {
|
|
40124
|
+
const ellipsoid = Cesium.Ellipsoid.WGS84;
|
|
40125
|
+
const centre = Cesium.Cartesian3.fromDegrees((extent.West + extent.East) / 2, (extent.South + extent.North) / 2, 0);
|
|
40126
|
+
const frame = Cesium.Transforms.eastNorthUpToFixedFrame(centre);
|
|
40127
|
+
const toLocal = Cesium.Matrix4.inverse(frame, new Cesium.Matrix4());
|
|
40128
|
+
const southWest = Cesium.Matrix4.multiplyByPoint(toLocal, Cesium.Cartesian3.fromDegrees(extent.West, extent.South, 0), new Cesium.Cartesian3());
|
|
40129
|
+
const northEast = Cesium.Matrix4.multiplyByPoint(toLocal, Cesium.Cartesian3.fromDegrees(extent.East, extent.North, 0), new Cesium.Cartesian3());
|
|
40130
|
+
return (fx, fy) => {
|
|
40131
|
+
const local = new Cesium.Cartesian3(Cesium.Math.lerp(southWest.x, northEast.x, fx), Cesium.Math.lerp(northEast.y, southWest.y, fy), 0);
|
|
40132
|
+
const world = Cesium.Matrix4.multiplyByPoint(frame, local, new Cesium.Cartesian3());
|
|
40133
|
+
return ellipsoid.scaleToGeodeticSurface(world, world);
|
|
40134
|
+
};
|
|
40135
|
+
}
|
|
40136
|
+
/*
|
|
40137
|
+
* Builds one tile's grid, plus a duplicated perimeter ring the shader pushes downwards to cover
|
|
40138
|
+
* the crack where a neighbouring tile at a different density interpolates the shared edge.
|
|
40139
|
+
*/
|
|
40140
|
+
function buildGeometry(extent, resolution, patch) {
|
|
40141
|
+
const cols = resolution;
|
|
40142
|
+
const rows = resolution;
|
|
40143
|
+
const gridCount = (cols + 1) * (rows + 1);
|
|
40144
|
+
const perimeterCount = 2 * (cols + 1) + 2 * (rows + 1) - 4;
|
|
40145
|
+
const count = gridCount + perimeterCount;
|
|
40146
|
+
const positions = new Float64Array(count * 3);
|
|
40147
|
+
const normals = new Float32Array(count * 3);
|
|
40148
|
+
const sts = new Float32Array(count * 2);
|
|
40149
|
+
const skirts = new Float32Array(count);
|
|
40150
|
+
const ellipsoid = Cesium.Ellipsoid.WGS84;
|
|
40151
|
+
const place = makePlacer(extent);
|
|
40152
|
+
let v = 0;
|
|
40153
|
+
for (let row = 0; row <= rows; row++) {
|
|
40154
|
+
// Texture row 0 is the north edge, so t runs opposite to northing.
|
|
40155
|
+
const fy = Cesium.Math.lerp(patch.v0, patch.v1, row / rows);
|
|
40156
|
+
for (let col = 0; col <= cols; col++) {
|
|
40157
|
+
const fx = Cesium.Math.lerp(patch.u0, patch.u1, col / cols);
|
|
40158
|
+
const position = place(fx, fy);
|
|
40159
|
+
positions[v * 3] = position.x;
|
|
40160
|
+
positions[v * 3 + 1] = position.y;
|
|
40161
|
+
positions[v * 3 + 2] = position.z;
|
|
40162
|
+
const up = ellipsoid.geodeticSurfaceNormal(position, new Cesium.Cartesian3());
|
|
40163
|
+
normals[v * 3] = up.x;
|
|
40164
|
+
normals[v * 3 + 1] = up.y;
|
|
40165
|
+
normals[v * 3 + 2] = up.z;
|
|
40166
|
+
sts[v * 2] = fx;
|
|
40167
|
+
sts[v * 2 + 1] = fy;
|
|
40168
|
+
v++;
|
|
40169
|
+
}
|
|
40170
|
+
}
|
|
40171
|
+
const indices = new Uint32Array(cols * rows * 6 + perimeterCount * 6);
|
|
40172
|
+
let i = 0;
|
|
40173
|
+
for (let row = 0; row < rows; row++) {
|
|
40174
|
+
for (let col = 0; col < cols; col++) {
|
|
40175
|
+
const topLeft = row * (cols + 1) + col;
|
|
40176
|
+
const topRight = topLeft + 1;
|
|
40177
|
+
const bottomLeft = topLeft + (cols + 1);
|
|
40178
|
+
const bottomRight = bottomLeft + 1;
|
|
40179
|
+
indices[i++] = topLeft;
|
|
40180
|
+
indices[i++] = bottomLeft;
|
|
40181
|
+
indices[i++] = topRight;
|
|
40182
|
+
indices[i++] = topRight;
|
|
40183
|
+
indices[i++] = bottomLeft;
|
|
40184
|
+
indices[i++] = bottomRight;
|
|
40185
|
+
}
|
|
40186
|
+
}
|
|
40187
|
+
// Perimeter walked as one closed loop, so consecutive entries are always neighbours.
|
|
40188
|
+
const loop = [];
|
|
40189
|
+
for (let col = 0; col <= cols; col++) {
|
|
40190
|
+
loop.push(col);
|
|
40191
|
+
}
|
|
40192
|
+
for (let row = 1; row <= rows; row++) {
|
|
40193
|
+
loop.push(row * (cols + 1) + cols);
|
|
40194
|
+
}
|
|
40195
|
+
for (let col = cols - 1; col >= 0; col--) {
|
|
40196
|
+
loop.push(rows * (cols + 1) + col);
|
|
40197
|
+
}
|
|
40198
|
+
for (let row = rows - 1; row >= 1; row--) {
|
|
40199
|
+
loop.push(row * (cols + 1));
|
|
40200
|
+
}
|
|
40201
|
+
const skirtStart = gridCount;
|
|
40202
|
+
loop.forEach((gridIndex, k) => {
|
|
40203
|
+
const target = skirtStart + k;
|
|
40204
|
+
positions[target * 3] = positions[gridIndex * 3];
|
|
40205
|
+
positions[target * 3 + 1] = positions[gridIndex * 3 + 1];
|
|
40206
|
+
positions[target * 3 + 2] = positions[gridIndex * 3 + 2];
|
|
40207
|
+
normals[target * 3] = normals[gridIndex * 3];
|
|
40208
|
+
normals[target * 3 + 1] = normals[gridIndex * 3 + 1];
|
|
40209
|
+
normals[target * 3 + 2] = normals[gridIndex * 3 + 2];
|
|
40210
|
+
sts[target * 2] = sts[gridIndex * 2];
|
|
40211
|
+
sts[target * 2 + 1] = sts[gridIndex * 2 + 1];
|
|
40212
|
+
skirts[target] = 1;
|
|
40213
|
+
});
|
|
40214
|
+
for (let k = 0; k < loop.length; k++) {
|
|
40215
|
+
const nextK = (k + 1) % loop.length;
|
|
40216
|
+
indices[i++] = loop[k];
|
|
40217
|
+
indices[i++] = skirtStart + k;
|
|
40218
|
+
indices[i++] = loop[nextK];
|
|
40219
|
+
indices[i++] = loop[nextK];
|
|
40220
|
+
indices[i++] = skirtStart + k;
|
|
40221
|
+
indices[i++] = skirtStart + nextK;
|
|
40222
|
+
}
|
|
40223
|
+
const geometry = new Cesium.Geometry({
|
|
40224
|
+
attributes: {
|
|
40225
|
+
position: new Cesium.GeometryAttribute({
|
|
40226
|
+
componentDatatype: Cesium.ComponentDatatype.DOUBLE,
|
|
40227
|
+
componentsPerAttribute: 3,
|
|
40228
|
+
values: positions
|
|
40229
|
+
}),
|
|
40230
|
+
normal: new Cesium.GeometryAttribute({
|
|
40231
|
+
componentDatatype: Cesium.ComponentDatatype.FLOAT,
|
|
40232
|
+
componentsPerAttribute: 3,
|
|
40233
|
+
values: normals
|
|
40234
|
+
}),
|
|
40235
|
+
st: new Cesium.GeometryAttribute({
|
|
40236
|
+
componentDatatype: Cesium.ComponentDatatype.FLOAT,
|
|
40237
|
+
componentsPerAttribute: 2,
|
|
40238
|
+
values: sts
|
|
40239
|
+
}),
|
|
40240
|
+
skirt: new Cesium.GeometryAttribute({
|
|
40241
|
+
componentDatatype: Cesium.ComponentDatatype.FLOAT,
|
|
40242
|
+
componentsPerAttribute: 1,
|
|
40243
|
+
values: skirts
|
|
40244
|
+
})
|
|
40245
|
+
},
|
|
40246
|
+
indices: indices,
|
|
40247
|
+
primitiveType: Cesium.PrimitiveType.TRIANGLES,
|
|
40248
|
+
boundingSphere: Cesium.BoundingSphere.fromVertices(Array.prototype.slice.call(positions))
|
|
40249
|
+
});
|
|
40250
|
+
// Splits position into the high/low pair Cesium's relative-to-eye shaders expect.
|
|
40251
|
+
Cesium.GeometryPipeline.encodeAttribute(geometry, "position", "position3DHigh", "position3DLow");
|
|
40252
|
+
return geometry;
|
|
40253
|
+
}
|
|
40254
|
+
class Surface {
|
|
40255
|
+
constructor(options, hasCoverage) {
|
|
40256
|
+
var _a, _b, _c, _d;
|
|
40257
|
+
this.show = true;
|
|
40258
|
+
this.valueSource = null;
|
|
40259
|
+
this.lastPresentVersion = -1;
|
|
40260
|
+
this.texture = null;
|
|
40261
|
+
this.textureDirty = true;
|
|
40262
|
+
this.shaderProgram = null;
|
|
40263
|
+
this.renderState = null;
|
|
40264
|
+
this.destroyed = false;
|
|
40265
|
+
this.lastEyePosition = null;
|
|
40266
|
+
this.lastSelectionMs = -Infinity;
|
|
40267
|
+
this.selectionDirty = true;
|
|
40268
|
+
this.groundTexture = null;
|
|
40269
|
+
this.groundPixels = null;
|
|
40270
|
+
this.groundRequested = false;
|
|
40271
|
+
this.followGround = Boolean(options.followGround);
|
|
40272
|
+
this.terrainProvider = options.terrainProvider || null;
|
|
40273
|
+
this.extent = options.extent;
|
|
40274
|
+
this.source = options.source || null;
|
|
40275
|
+
this.valueMin = (_a = options.valueMin) !== null && _a !== void 0 ? _a : 0;
|
|
40276
|
+
this.valueMax = (_b = options.valueMax) !== null && _b !== void 0 ? _b : 1;
|
|
40277
|
+
this.exaggeration = (_c = options.exaggeration) !== null && _c !== void 0 ? _c : autoExaggeration(this.extent, this.valueMin, this.valueMax);
|
|
40278
|
+
this.baseHeight = (_d = options.baseHeight) !== null && _d !== void 0 ? _d : 0;
|
|
40279
|
+
this.lowColor = toCesiumColor(options.lowColor || DEFAULT_LOW_COLOR);
|
|
40280
|
+
this.highColor = toCesiumColor(options.highColor || DEFAULT_HIGH_COLOR);
|
|
40281
|
+
this.tiles = buildTiles(hasCoverage);
|
|
40282
|
+
}
|
|
40283
|
+
GetFollowsGround() {
|
|
40284
|
+
return this.followGround;
|
|
40285
|
+
}
|
|
40286
|
+
GetBaseHeight() {
|
|
40287
|
+
return this.baseHeight;
|
|
40288
|
+
}
|
|
40289
|
+
GetExaggeration() {
|
|
40290
|
+
return this.exaggeration;
|
|
40291
|
+
}
|
|
40292
|
+
GetExtent() {
|
|
40293
|
+
return this.extent;
|
|
40294
|
+
}
|
|
40295
|
+
SetExaggeration(exaggeration) {
|
|
40296
|
+
this.exaggeration = exaggeration;
|
|
40297
|
+
}
|
|
40298
|
+
SetBaseHeight(baseHeight) {
|
|
40299
|
+
this.baseHeight = baseHeight;
|
|
40300
|
+
}
|
|
40301
|
+
/**
|
|
40302
|
+
* Feeds the surface from an animated frame series.
|
|
40303
|
+
* Height and colour advance with the clock without the caller having to push each frame in.
|
|
40304
|
+
*/
|
|
40305
|
+
BindAnimator(source) {
|
|
40306
|
+
this.valueSource = source;
|
|
40307
|
+
}
|
|
40308
|
+
/**
|
|
40309
|
+
* Tells the surface its source canvas has been redrawn and the texture needs re-uploading.
|
|
40310
|
+
*/
|
|
40311
|
+
MarkSourceDirty() {
|
|
40312
|
+
this.textureDirty = true;
|
|
40313
|
+
}
|
|
40314
|
+
update(frameState) {
|
|
40315
|
+
if (!this.show || this.destroyed || this.tiles.length === 0) {
|
|
40316
|
+
return;
|
|
40317
|
+
}
|
|
40318
|
+
const context = frameState.context;
|
|
40319
|
+
if (this.valueSource) {
|
|
40320
|
+
const canvas = this.valueSource.GetValueCanvas();
|
|
40321
|
+
if (!canvas) {
|
|
40322
|
+
// No frame decoded yet, so there is nothing to displace against.
|
|
40323
|
+
return;
|
|
40324
|
+
}
|
|
40325
|
+
const version = this.valueSource.GetPresentVersion();
|
|
40326
|
+
if (canvas !== this.source || version !== this.lastPresentVersion) {
|
|
40327
|
+
this.source = canvas;
|
|
40328
|
+
this.lastPresentVersion = version;
|
|
40329
|
+
this.textureDirty = true;
|
|
40330
|
+
}
|
|
40331
|
+
}
|
|
40332
|
+
if (!this.source || !this.source.width || !this.source.height) {
|
|
40333
|
+
return;
|
|
40334
|
+
}
|
|
40335
|
+
this.syncTexture(context);
|
|
40336
|
+
this.requestGround();
|
|
40337
|
+
this.syncGroundTexture(context);
|
|
40338
|
+
// A tile needs one mesh before it has a bounding sphere to measure against.
|
|
40339
|
+
for (const tile of this.tiles) {
|
|
40340
|
+
if (!tile.centre) {
|
|
40341
|
+
this.ensureMesh(context, tile, TIERS[0]);
|
|
40342
|
+
}
|
|
40343
|
+
}
|
|
40344
|
+
const nowMs = now();
|
|
40345
|
+
if (this.shouldReselect(frameState, nowMs)) {
|
|
40346
|
+
this.selectTiers(frameState);
|
|
40347
|
+
this.lastSelectionMs = nowMs;
|
|
40348
|
+
this.lastEyePosition = Cesium.Cartesian3.clone(frameState.camera.positionWC, this.lastEyePosition);
|
|
40349
|
+
this.selectionDirty = false;
|
|
40350
|
+
}
|
|
40351
|
+
for (const tile of this.tiles) {
|
|
40352
|
+
const mesh = this.ensureMesh(context, tile, tile.tier);
|
|
40353
|
+
frameState.commandList.push(this.commandFor(context, tile, mesh));
|
|
40354
|
+
}
|
|
40355
|
+
}
|
|
40356
|
+
isDestroyed() {
|
|
40357
|
+
return this.destroyed;
|
|
40358
|
+
}
|
|
40359
|
+
destroy() {
|
|
40360
|
+
this.destroyed = true;
|
|
40361
|
+
for (const tile of this.tiles) {
|
|
40362
|
+
tile.meshes.forEach((mesh) => mesh.vertexArray.destroy());
|
|
40363
|
+
tile.meshes.clear();
|
|
40364
|
+
tile.command = null;
|
|
40365
|
+
}
|
|
40366
|
+
if (this.shaderProgram) {
|
|
40367
|
+
this.shaderProgram.destroy();
|
|
40368
|
+
this.shaderProgram = null;
|
|
40369
|
+
}
|
|
40370
|
+
if (this.texture) {
|
|
40371
|
+
this.texture.destroy();
|
|
40372
|
+
this.texture = null;
|
|
40373
|
+
}
|
|
40374
|
+
if (this.groundTexture) {
|
|
40375
|
+
this.groundTexture.destroy();
|
|
40376
|
+
this.groundTexture = null;
|
|
40377
|
+
}
|
|
40378
|
+
return undefined;
|
|
40379
|
+
}
|
|
40380
|
+
/*
|
|
40381
|
+
* Samples terrain across the extent once and packs it into a texture the vertex shader reads.
|
|
40382
|
+
*
|
|
40383
|
+
* A texture rather than heights baked into the meshes, because the samples arrive
|
|
40384
|
+
* asynchronously and every cached tile mesh would otherwise have to be rebuilt when they land.
|
|
40385
|
+
*/
|
|
40386
|
+
requestGround() {
|
|
40387
|
+
if (this.groundRequested || !this.followGround) {
|
|
40388
|
+
return;
|
|
40389
|
+
}
|
|
40390
|
+
this.groundRequested = true;
|
|
40391
|
+
const sampler = Cesium.sampleTerrain;
|
|
40392
|
+
if (!this.terrainProvider || typeof sampler !== "function") {
|
|
40393
|
+
return;
|
|
40394
|
+
}
|
|
40395
|
+
const positions = [];
|
|
40396
|
+
for (let row = 0; row < GROUND_SAMPLES_PER_SIDE; row++) {
|
|
40397
|
+
const fy = row / (GROUND_SAMPLES_PER_SIDE - 1);
|
|
40398
|
+
const latitude = Cesium.Math.lerp(this.extent.North, this.extent.South, fy);
|
|
40399
|
+
for (let col = 0; col < GROUND_SAMPLES_PER_SIDE; col++) {
|
|
40400
|
+
const fx = col / (GROUND_SAMPLES_PER_SIDE - 1);
|
|
40401
|
+
const longitude = Cesium.Math.lerp(this.extent.West, this.extent.East, fx);
|
|
40402
|
+
positions.push(Cesium.Cartographic.fromDegrees(longitude, latitude));
|
|
40403
|
+
}
|
|
40404
|
+
}
|
|
40405
|
+
Promise.resolve(sampler(this.terrainProvider, GROUND_SAMPLE_LEVEL, positions))
|
|
40406
|
+
.then((sampled) => {
|
|
40407
|
+
if (this.destroyed || !sampled) {
|
|
40408
|
+
return;
|
|
40409
|
+
}
|
|
40410
|
+
const pixels = new Uint8Array(GROUND_SAMPLES_PER_SIDE * GROUND_SAMPLES_PER_SIDE * 4);
|
|
40411
|
+
const span = GROUND_MAX_METRES - GROUND_MIN_METRES;
|
|
40412
|
+
for (let i = 0; i < sampled.length; i++) {
|
|
40413
|
+
const height = sampled[i] && isFinite(sampled[i].height) ? sampled[i].height : 0;
|
|
40414
|
+
const clamped = Math.min(GROUND_MAX_METRES, Math.max(GROUND_MIN_METRES, height));
|
|
40415
|
+
const packed = Math.round(((clamped - GROUND_MIN_METRES) / span) * 16777215);
|
|
40416
|
+
pixels[i * 4] = (packed >> 16) & 255;
|
|
40417
|
+
pixels[i * 4 + 1] = (packed >> 8) & 255;
|
|
40418
|
+
pixels[i * 4 + 2] = packed & 255;
|
|
40419
|
+
pixels[i * 4 + 3] = 255;
|
|
40420
|
+
}
|
|
40421
|
+
this.groundPixels = pixels;
|
|
40422
|
+
})
|
|
40423
|
+
.catch(() => {
|
|
40424
|
+
// Terrain that cannot be sampled leaves the surface on the ellipsoid, which is
|
|
40425
|
+
// where a clamped polygon sat before this existed.
|
|
40426
|
+
});
|
|
40427
|
+
}
|
|
40428
|
+
/*
|
|
40429
|
+
* Uploads whatever ground samples exist, and a flat stand-in until they arrive, so the
|
|
40430
|
+
* shader always has a texture bound whether or not this surface follows the ground.
|
|
40431
|
+
*/
|
|
40432
|
+
syncGroundTexture(context) {
|
|
40433
|
+
if (this.groundTexture && (!this.groundPixels || this.groundTexture.width === GROUND_SAMPLES_PER_SIDE)) {
|
|
40434
|
+
return;
|
|
40435
|
+
}
|
|
40436
|
+
if (this.groundTexture) {
|
|
40437
|
+
this.groundTexture.destroy();
|
|
40438
|
+
}
|
|
40439
|
+
const zero = Math.round(((0 - GROUND_MIN_METRES) / (GROUND_MAX_METRES - GROUND_MIN_METRES)) * 16777215);
|
|
40440
|
+
const size = this.groundPixels ? GROUND_SAMPLES_PER_SIDE : 1;
|
|
40441
|
+
const arrayBufferView = this.groundPixels
|
|
40442
|
+
|| new Uint8Array([(zero >> 16) & 255, (zero >> 8) & 255, zero & 255, 255]);
|
|
40443
|
+
this.groundTexture = new Cesium.Texture({
|
|
40444
|
+
context,
|
|
40445
|
+
pixelFormat: Cesium.PixelFormat.RGBA,
|
|
40446
|
+
source: { width: size, height: size, arrayBufferView },
|
|
40447
|
+
flipY: false,
|
|
40448
|
+
sampler: new Cesium.Sampler({
|
|
40449
|
+
wrapS: Cesium.TextureWrap.CLAMP_TO_EDGE,
|
|
40450
|
+
wrapT: Cesium.TextureWrap.CLAMP_TO_EDGE,
|
|
40451
|
+
// Nearest, because the shader interpolates decoded heights itself.
|
|
40452
|
+
minificationFilter: Cesium.TextureMinificationFilter.NEAREST,
|
|
40453
|
+
magnificationFilter: Cesium.TextureMagnificationFilter.NEAREST
|
|
40454
|
+
})
|
|
40455
|
+
});
|
|
40456
|
+
}
|
|
40457
|
+
/*
|
|
40458
|
+
* Ground distance between neighbouring texels, so the shading normal is built from a real
|
|
40459
|
+
* slope rather than a made up one.
|
|
40460
|
+
*/
|
|
40461
|
+
metresPerTexel() {
|
|
40462
|
+
const southWest = Cesium.Cartesian3.fromDegrees(this.extent.West, this.extent.South, 0);
|
|
40463
|
+
const southEast = Cesium.Cartesian3.fromDegrees(this.extent.East, this.extent.South, 0);
|
|
40464
|
+
const width = Cesium.Cartesian3.distance(southWest, southEast);
|
|
40465
|
+
return Math.max(1, width / Math.max(1, this.source ? this.source.width : 1));
|
|
40466
|
+
}
|
|
40467
|
+
syncTexture(context) {
|
|
40468
|
+
const sizeChanged = this.texture
|
|
40469
|
+
&& (this.texture.width !== this.source.width || this.texture.height !== this.source.height);
|
|
40470
|
+
if (!this.texture || sizeChanged) {
|
|
40471
|
+
if (this.texture) {
|
|
40472
|
+
this.texture.destroy();
|
|
40473
|
+
}
|
|
40474
|
+
this.texture = new Cesium.Texture({
|
|
40475
|
+
context,
|
|
40476
|
+
source: this.source,
|
|
40477
|
+
// Cesium flips canvas rows on upload by default, which would put the archive's
|
|
40478
|
+
// row 0 (its north edge) at the south edge and mirror the whole surface.
|
|
40479
|
+
flipY: false,
|
|
40480
|
+
sampler: new Cesium.Sampler({
|
|
40481
|
+
wrapS: Cesium.TextureWrap.CLAMP_TO_EDGE,
|
|
40482
|
+
wrapT: Cesium.TextureWrap.CLAMP_TO_EDGE,
|
|
40483
|
+
minificationFilter: Cesium.TextureMinificationFilter.LINEAR,
|
|
40484
|
+
magnificationFilter: Cesium.TextureMagnificationFilter.LINEAR
|
|
40485
|
+
})
|
|
40486
|
+
});
|
|
40487
|
+
this.textureDirty = false;
|
|
40488
|
+
return;
|
|
40489
|
+
}
|
|
40490
|
+
if (this.textureDirty) {
|
|
40491
|
+
this.texture.copyFrom({ source: this.source });
|
|
40492
|
+
this.textureDirty = false;
|
|
40493
|
+
}
|
|
40494
|
+
}
|
|
40495
|
+
ensureMesh(context, tile, tier) {
|
|
40496
|
+
const cached = tile.meshes.get(tier);
|
|
40497
|
+
if (cached) {
|
|
40498
|
+
return cached;
|
|
40499
|
+
}
|
|
40500
|
+
const geometry = buildGeometry(this.extent, tier, tile.patch);
|
|
40501
|
+
const attributeLocations = Cesium.GeometryPipeline.createAttributeLocations(geometry);
|
|
40502
|
+
const mesh = {
|
|
40503
|
+
attributeLocations,
|
|
40504
|
+
vertexArray: Cesium.VertexArray.fromGeometry({
|
|
40505
|
+
context,
|
|
40506
|
+
geometry,
|
|
40507
|
+
attributeLocations,
|
|
40508
|
+
bufferUsage: Cesium.BufferUsage.STATIC_DRAW
|
|
40509
|
+
}),
|
|
40510
|
+
boundingSphere: geometry.boundingSphere
|
|
40511
|
+
};
|
|
40512
|
+
tile.meshes.set(tier, mesh);
|
|
40513
|
+
tile.centre = geometry.boundingSphere.center;
|
|
40514
|
+
tile.radius = geometry.boundingSphere.radius;
|
|
40515
|
+
return mesh;
|
|
40516
|
+
}
|
|
40517
|
+
/*
|
|
40518
|
+
* Picks each tile's density from how much screen it covers.
|
|
40519
|
+
*/
|
|
40520
|
+
selectTiers(frameState) {
|
|
40521
|
+
const camera = frameState.camera;
|
|
40522
|
+
const fovy = camera.frustum && camera.frustum.fovy ? camera.frustum.fovy : Cesium.Math.PI_OVER_THREE;
|
|
40523
|
+
const pixelsPerRadian = frameState.context.drawingBufferHeight / fovy;
|
|
40524
|
+
for (const tile of this.tiles) {
|
|
40525
|
+
if (!tile.centre) {
|
|
40526
|
+
tile.tier = TIERS[0];
|
|
40527
|
+
continue;
|
|
40528
|
+
}
|
|
40529
|
+
const distance = Math.max(1, Cesium.Cartesian3.distance(camera.positionWC, tile.centre) - tile.radius);
|
|
40530
|
+
const screenPixels = (2 * tile.radius / distance) * pixelsPerRadian;
|
|
40531
|
+
const wanted = screenPixels / TARGET_CELL_PIXELS;
|
|
40532
|
+
let tier = TIERS[0];
|
|
40533
|
+
for (const candidate of TIERS) {
|
|
40534
|
+
if (candidate <= wanted) {
|
|
40535
|
+
tier = candidate;
|
|
40536
|
+
}
|
|
40537
|
+
}
|
|
40538
|
+
tile.tier = tier;
|
|
40539
|
+
}
|
|
40540
|
+
}
|
|
40541
|
+
shouldReselect(frameState, nowMs) {
|
|
40542
|
+
if (this.selectionDirty) {
|
|
40543
|
+
return true;
|
|
40544
|
+
}
|
|
40545
|
+
if (nowMs - this.lastSelectionMs < RESELECT_INTERVAL_MS) {
|
|
40546
|
+
return false;
|
|
40547
|
+
}
|
|
40548
|
+
const eye = frameState.camera.positionWC;
|
|
40549
|
+
if (!this.lastEyePosition) {
|
|
40550
|
+
return true;
|
|
40551
|
+
}
|
|
40552
|
+
const moved = Cesium.Cartesian3.distance(eye, this.lastEyePosition);
|
|
40553
|
+
const anchor = this.tiles[0].centre;
|
|
40554
|
+
const reference = anchor ? Math.max(1, Cesium.Cartesian3.distance(eye, anchor)) : 1;
|
|
40555
|
+
return moved / reference > MOVEMENT_THRESHOLD;
|
|
40556
|
+
}
|
|
40557
|
+
commandFor(context, tile, mesh) {
|
|
40558
|
+
if (!this.shaderProgram) {
|
|
40559
|
+
const glsl300 = usesGlsl300(context);
|
|
40560
|
+
this.shaderProgram = Cesium.ShaderProgram.fromCache({
|
|
40561
|
+
context,
|
|
40562
|
+
vertexShaderSource: glsl300 ? VERTEX_SHADER_GLSL300 : toGlsl100(VERTEX_SHADER_GLSL300, false),
|
|
40563
|
+
fragmentShaderSource: glsl300 ? FRAGMENT_SHADER_GLSL300 : toGlsl100(FRAGMENT_SHADER_GLSL300, true),
|
|
40564
|
+
attributeLocations: mesh.attributeLocations
|
|
40565
|
+
});
|
|
40566
|
+
this.renderState = Cesium.RenderState.fromCache({
|
|
40567
|
+
depthTest: { enabled: true },
|
|
40568
|
+
// Translucent ramp: blend, and leave the depth buffer to the opaque world.
|
|
40569
|
+
depthMask: false,
|
|
40570
|
+
blending: Cesium.BlendingState.ALPHA_BLEND,
|
|
40571
|
+
cull: { enabled: false }
|
|
40572
|
+
});
|
|
40573
|
+
}
|
|
40574
|
+
if (!tile.command) {
|
|
40575
|
+
const self = this;
|
|
40576
|
+
tile.command = new Cesium.DrawCommand({
|
|
40577
|
+
shaderProgram: this.shaderProgram,
|
|
40578
|
+
renderState: this.renderState,
|
|
40579
|
+
pass: Cesium.Pass.TRANSLUCENT,
|
|
40580
|
+
modelMatrix: Cesium.Matrix4.IDENTITY,
|
|
40581
|
+
owner: this,
|
|
40582
|
+
uniformMap: {
|
|
40583
|
+
u_valueTexture: () => self.texture,
|
|
40584
|
+
u_groundTexture: () => self.groundTexture,
|
|
40585
|
+
u_groundWeight: () => (self.followGround && self.groundPixels ? 1 : 0),
|
|
40586
|
+
u_groundRange: () => new Cesium.Cartesian2(GROUND_MIN_METRES, GROUND_MAX_METRES),
|
|
40587
|
+
u_groundSize: () => new Cesium.Cartesian2(self.groundPixels ? GROUND_SAMPLES_PER_SIDE : 1, self.groundPixels ? GROUND_SAMPLES_PER_SIDE : 1),
|
|
40588
|
+
u_metresPerTexel: () => self.metresPerTexel(),
|
|
40589
|
+
u_valueMin: () => self.valueMin,
|
|
40590
|
+
u_valueRange: () => self.valueMax - self.valueMin,
|
|
40591
|
+
u_exaggeration: () => self.exaggeration,
|
|
40592
|
+
u_baseHeight: () => self.baseHeight,
|
|
40593
|
+
u_skirtDepth: () => Math.max(2, Math.abs(self.valueMax - self.valueMin) * self.exaggeration * SKIRT_FRACTION),
|
|
40594
|
+
u_coverageCutoff: () => COVERAGE_CUTOFF,
|
|
40595
|
+
u_lowColor: () => self.lowColor,
|
|
40596
|
+
u_highColor: () => self.highColor,
|
|
40597
|
+
u_texelSize: () => new Cesium.Cartesian2(1 / self.source.width, 1 / self.source.height)
|
|
40598
|
+
}
|
|
40599
|
+
});
|
|
40600
|
+
}
|
|
40601
|
+
// Bounding volume belongs to the mesh, so it follows whichever tier is drawing.
|
|
40602
|
+
tile.command.vertexArray = mesh.vertexArray;
|
|
40603
|
+
tile.command.boundingVolume = mesh.boundingSphere;
|
|
40604
|
+
return tile.command;
|
|
40605
|
+
}
|
|
40606
|
+
}
|
|
40607
|
+
DisplacedSurfacePrimitive.Surface = Surface;
|
|
40608
|
+
/*
|
|
40609
|
+
* Cuts the extent into tiles, dropping any the caller says carry no data.
|
|
40610
|
+
*/
|
|
40611
|
+
function buildTiles(hasCoverage) {
|
|
40612
|
+
const tiles = [];
|
|
40613
|
+
for (let row = 0; row < TILES_PER_SIDE; row++) {
|
|
40614
|
+
for (let col = 0; col < TILES_PER_SIDE; col++) {
|
|
40615
|
+
const patch = {
|
|
40616
|
+
u0: col / TILES_PER_SIDE,
|
|
40617
|
+
u1: (col + 1) / TILES_PER_SIDE,
|
|
40618
|
+
v0: row / TILES_PER_SIDE,
|
|
40619
|
+
v1: (row + 1) / TILES_PER_SIDE
|
|
40620
|
+
};
|
|
40621
|
+
if (hasCoverage && !hasCoverage(patch)) {
|
|
40622
|
+
continue;
|
|
40623
|
+
}
|
|
40624
|
+
tiles.push({
|
|
40625
|
+
patch,
|
|
40626
|
+
meshes: new Map(),
|
|
40627
|
+
tier: TIERS[0],
|
|
40628
|
+
command: null,
|
|
40629
|
+
centre: null,
|
|
40630
|
+
radius: 0
|
|
40631
|
+
});
|
|
40632
|
+
}
|
|
40633
|
+
}
|
|
40634
|
+
return tiles;
|
|
40635
|
+
}
|
|
40636
|
+
/**
|
|
40637
|
+
* Exaggeration that makes the full value range stand AUTO_RELIEF_FRACTION of the extent tall.
|
|
40638
|
+
*
|
|
40639
|
+
* Derived from the whole extent rather than per tile: scaling each tile to its own size would
|
|
40640
|
+
* make neighbours disagree along their shared edge and tear the surface apart.
|
|
40641
|
+
* @param extent
|
|
40642
|
+
* @param valueMin
|
|
40643
|
+
* @param valueMax
|
|
40644
|
+
*/
|
|
40645
|
+
function autoExaggeration(extent, valueMin, valueMax) {
|
|
40646
|
+
const range = Math.abs(valueMax - valueMin);
|
|
40647
|
+
if (!(range > 0)) {
|
|
40648
|
+
return 1;
|
|
40649
|
+
}
|
|
40650
|
+
const southWest = Cesium.Cartesian3.fromDegrees(extent.West, extent.South, 0);
|
|
40651
|
+
const southEast = Cesium.Cartesian3.fromDegrees(extent.East, extent.South, 0);
|
|
40652
|
+
const northWest = Cesium.Cartesian3.fromDegrees(extent.West, extent.North, 0);
|
|
40653
|
+
const shorterSide = Math.min(Cesium.Cartesian3.distance(southWest, southEast), Cesium.Cartesian3.distance(southWest, northWest));
|
|
40654
|
+
if (!(shorterSide > 0)) {
|
|
40655
|
+
return 1;
|
|
40656
|
+
}
|
|
40657
|
+
return (shorterSide * AUTO_RELIEF_FRACTION) / range;
|
|
40658
|
+
}
|
|
40659
|
+
DisplacedSurfacePrimitive.autoExaggeration = autoExaggeration;
|
|
40660
|
+
function toCesiumColor(color) {
|
|
40661
|
+
return new Cesium.Color(color.red / 255, color.green / 255, color.blue / 255, color.alpha);
|
|
40662
|
+
}
|
|
40663
|
+
function now() {
|
|
40664
|
+
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
40665
|
+
}
|
|
40666
|
+
})(exports.DisplacedSurfacePrimitive || (exports.DisplacedSurfacePrimitive = {}));
|
|
40667
|
+
|
|
39897
40668
|
const TEXTURE_FRAME_SERIES_ANIMATOR_KEY = "TextureFrameSeriesAnimator.Animator";
|
|
40669
|
+
const EXTRUSION_ANIMATOR_KEY = "TextureFrameSeriesAnimator.ExtrusionAnimator";
|
|
40670
|
+
const DISPLACED_SURFACE_KEY = "DisplacedSurfacePrimitive.Surface";
|
|
39898
40671
|
(function (EntityRenderEnginePolygon) {
|
|
39899
40672
|
async function Render(params) {
|
|
39900
|
-
var _a, _b, _c;
|
|
40673
|
+
var _a, _b, _c, _d;
|
|
39901
40674
|
const entity = params.entity;
|
|
39902
40675
|
const style = params.style;
|
|
39903
40676
|
const pRings = BModels.Entity.GetValue({
|
|
@@ -39930,14 +40703,36 @@
|
|
|
39930
40703
|
console.error(`Polygon.Render: failed to resolve textured fill for entity ${((_a = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _a === void 0 ? void 0 : _a.ID) || "<NONE>"}:`, e);
|
|
39931
40704
|
}
|
|
39932
40705
|
}
|
|
39933
|
-
const
|
|
39934
|
-
|
|
39935
|
-
|
|
39936
|
-
|
|
39937
|
-
|
|
39938
|
-
|
|
39939
|
-
|
|
39940
|
-
|
|
40706
|
+
const usesTextureExtrusion = (Boolean(style.useExtrusion) &&
|
|
40707
|
+
style.extrusionType === BModels.Style.EPolygonExtrusionType.Texture);
|
|
40708
|
+
let extrusionArchive = null;
|
|
40709
|
+
if (usesTextureExtrusion && !params.offline) {
|
|
40710
|
+
try {
|
|
40711
|
+
const resolved = await resolveTextureValue({
|
|
40712
|
+
api: params.api,
|
|
40713
|
+
textureValue: style.extrusionTexture || style.texture,
|
|
40714
|
+
style,
|
|
40715
|
+
entity,
|
|
40716
|
+
tags: params.tags
|
|
40717
|
+
});
|
|
40718
|
+
extrusionArchive = resolved.frameArchive || null;
|
|
40719
|
+
}
|
|
40720
|
+
catch (e) {
|
|
40721
|
+
console.error(`Polygon.Render: failed to resolve extrusion texture for entity ${((_b = entity === null || entity === void 0 ? void 0 : entity.Bruce) === null || _b === void 0 ? void 0 : _b.ID) || "<NONE>"}:`, e);
|
|
40722
|
+
}
|
|
40723
|
+
}
|
|
40724
|
+
const drawsDisplacedSurface = Boolean(extrusionArchive);
|
|
40725
|
+
const hasFill = (drawsDisplacedSurface ? true
|
|
40726
|
+
: fillType === BModels.Style.EPolygonFillType.Texture
|
|
40727
|
+
? Boolean(textureDataUri || frameArchive || cFillColor.alpha > 0)
|
|
40728
|
+
: cFillColor.alpha > 0);
|
|
40729
|
+
const fillMaterial = drawsDisplacedSurface
|
|
40730
|
+
? Cesium.Color.WHITE.withAlpha(0)
|
|
40731
|
+
: textureDataUri
|
|
40732
|
+
? new Cesium.ImageMaterialProperty({ image: textureDataUri, transparent: true })
|
|
40733
|
+
: frameArchive
|
|
40734
|
+
? Cesium.Color.WHITE.withAlpha(0)
|
|
40735
|
+
: cFillColor;
|
|
39941
40736
|
const lineColorTrace = style.lineColor ? BModels.Calculator.TraceGetColor(style.lineColor, entity, params.tags) : { value: null, effective: null };
|
|
39942
40737
|
const bLineColor = lineColorTrace.value;
|
|
39943
40738
|
const cLineColor = bLineColor ? ColorToCColor(bLineColor) : Cesium.Color.fromCssColorString("rgba(80, 80, 80, 0.8)");
|
|
@@ -39964,7 +40759,9 @@
|
|
|
39964
40759
|
const points = BModels.Geometry.ParsePoints(outerRing === null || outerRing === void 0 ? void 0 : outerRing.LinearRing);
|
|
39965
40760
|
let posses = points.map(x => Cesium.Cartesian3.fromDegrees(EnsureNumber(x.longitude), EnsureNumber(x.latitude), EnsureNumber(x.altitude)));
|
|
39966
40761
|
BModels.Cartes.CloseRing3(posses);
|
|
39967
|
-
const extrusion =
|
|
40762
|
+
const extrusion = drawsDisplacedSurface
|
|
40763
|
+
? { posses, value: undefined, exHeightRef: Cesium.HeightReference.NONE }
|
|
40764
|
+
: getPolygonExtrusion(entity, params.tags, outerRing, posses, heightRef, style);
|
|
39968
40765
|
posses = extrusion.posses;
|
|
39969
40766
|
posses = CullDuplicateCPosses(posses);
|
|
39970
40767
|
if (posses.length < 4) {
|
|
@@ -40096,7 +40893,16 @@
|
|
|
40096
40893
|
}
|
|
40097
40894
|
// Must run before any material is baked below: the Animator takes over polygon.material, so a bake
|
|
40098
40895
|
// has to know whether it is baking over a live animation or over a disposed one's restored material.
|
|
40099
|
-
const animator = syncTextureFrameArchive(cEntity, frameArchive, params.viewer, style.textureColorMask);
|
|
40896
|
+
const animator = syncTextureFrameArchive(cEntity, drawsDisplacedSurface ? null : frameArchive, params.viewer, style.textureColorMask);
|
|
40897
|
+
syncDisplacedSurface({
|
|
40898
|
+
cEntity,
|
|
40899
|
+
extrusionArchive,
|
|
40900
|
+
viewer: params.viewer,
|
|
40901
|
+
style,
|
|
40902
|
+
entity,
|
|
40903
|
+
heightRef,
|
|
40904
|
+
outerRingPosses: posses
|
|
40905
|
+
});
|
|
40100
40906
|
if (animator) {
|
|
40101
40907
|
exports.CesiumEntityStyler.SetDefaultTextureImage({
|
|
40102
40908
|
entity: cEntity,
|
|
@@ -40156,7 +40962,7 @@
|
|
|
40156
40962
|
}
|
|
40157
40963
|
}
|
|
40158
40964
|
let borderPosses = posses.map(x => x.clone ? x.clone() : { ...x });
|
|
40159
|
-
let cEntityBorder = (
|
|
40965
|
+
let cEntityBorder = (_d = (_c = params.rendered) === null || _c === void 0 ? void 0 : _c._siblingGraphics) === null || _d === void 0 ? void 0 : _d[0];
|
|
40160
40966
|
cEntity._siblingGraphics = [];
|
|
40161
40967
|
if (!cEntityBorder || ((!cEntityBorder.polyline && units == "px") ||
|
|
40162
40968
|
(!cEntityBorder.corridor && units == "m"))) {
|
|
@@ -40406,12 +41212,13 @@
|
|
|
40406
41212
|
/**
|
|
40407
41213
|
* Disposes a cEntity's TextureFrameSeriesAnimator.Animator (if any).
|
|
40408
41214
|
*/
|
|
40409
|
-
function DisposeTextureFrameSeriesAnimator(cEntity) {
|
|
41215
|
+
function DisposeTextureFrameSeriesAnimator(cEntity, viewer) {
|
|
40410
41216
|
const existing = cEntity === null || cEntity === void 0 ? void 0 : cEntity[TEXTURE_FRAME_SERIES_ANIMATOR_KEY];
|
|
40411
41217
|
if (existing && !existing.IsDisposed()) {
|
|
40412
41218
|
existing.Dispose();
|
|
40413
41219
|
cEntity[TEXTURE_FRAME_SERIES_ANIMATOR_KEY] = null;
|
|
40414
41220
|
}
|
|
41221
|
+
disposeDisplacedSurface(cEntity, viewer);
|
|
40415
41222
|
}
|
|
40416
41223
|
EntityRenderEnginePolygon.DisposeTextureFrameSeriesAnimator = DisposeTextureFrameSeriesAnimator;
|
|
40417
41224
|
})(exports.EntityRenderEnginePolygon || (exports.EntityRenderEnginePolygon = {}));
|
|
@@ -40525,6 +41332,140 @@
|
|
|
40525
41332
|
// Cache of tinted texture data URIs, keyed by resolved url + color mask, so repeated renders of the
|
|
40526
41333
|
// same polygon (or multiple polygons sharing a texture) don't refetch/reprocess the same image.
|
|
40527
41334
|
const _textureCache = new BModels.LRUCache(50);
|
|
41335
|
+
/**
|
|
41336
|
+
* Reconciles the displaced surface (and the animator feeding it) against this render's resolved
|
|
41337
|
+
* extrusion texture, creating, reusing or tearing down as the style demands.
|
|
41338
|
+
* @param params
|
|
41339
|
+
*/
|
|
41340
|
+
function syncDisplacedSurface(params) {
|
|
41341
|
+
var _a;
|
|
41342
|
+
const { cEntity, extrusionArchive, viewer, style, entity, heightRef, outerRingPosses } = params;
|
|
41343
|
+
const existingAnimator = cEntity[EXTRUSION_ANIMATOR_KEY];
|
|
41344
|
+
const existingSurface = cEntity[DISPLACED_SURFACE_KEY];
|
|
41345
|
+
// Nothing to draw and nothing left over, which is every polygon that does not use this feature.
|
|
41346
|
+
if (!extrusionArchive && !existingAnimator && !existingSurface) {
|
|
41347
|
+
return;
|
|
41348
|
+
}
|
|
41349
|
+
const reusable = extrusionArchive
|
|
41350
|
+
&& existingAnimator
|
|
41351
|
+
&& !existingAnimator.IsDisposed()
|
|
41352
|
+
&& existingAnimator.GetArchiveUrl() === extrusionArchive.url
|
|
41353
|
+
&& existingSurface
|
|
41354
|
+
&& !existingSurface.isDestroyed();
|
|
41355
|
+
const placement = surfacePlacement(entity, heightRef);
|
|
41356
|
+
// Whether the surface follows terrain is baked into its ground sampling, so a change there has
|
|
41357
|
+
// to rebuild rather than update in place.
|
|
41358
|
+
if (reusable && existingSurface.GetFollowsGround() === placement.followGround) {
|
|
41359
|
+
existingSurface.SetBaseHeight(placement.baseHeight);
|
|
41360
|
+
existingSurface.SetExaggeration((_a = style.extrusionExaggeration) !== null && _a !== void 0 ? _a : exports.DisplacedSurfacePrimitive.autoExaggeration(existingSurface.GetExtent(), extrusionArchive.metadata.ValueMin, extrusionArchive.metadata.ValueMax));
|
|
41361
|
+
return;
|
|
41362
|
+
}
|
|
41363
|
+
disposeDisplacedSurface(cEntity, viewer);
|
|
41364
|
+
if (!extrusionArchive) {
|
|
41365
|
+
return;
|
|
41366
|
+
}
|
|
41367
|
+
const extent = archiveExtent(extrusionArchive.metadata, outerRingPosses);
|
|
41368
|
+
if (!extent) {
|
|
41369
|
+
console.warn("Polygon.Render: extrusion texture has no usable extent, skipping displaced surface.");
|
|
41370
|
+
return;
|
|
41371
|
+
}
|
|
41372
|
+
// Drives the value canvas only: the surface paints the colour, so the polygon's own material is
|
|
41373
|
+
// left transparent rather than animated underneath it.
|
|
41374
|
+
const animator = new TextureFrameSeriesAnimator.Animator({
|
|
41375
|
+
viewer,
|
|
41376
|
+
entity: cEntity,
|
|
41377
|
+
archiveUrl: extrusionArchive.url,
|
|
41378
|
+
frames: extrusionArchive.metadata.Frames,
|
|
41379
|
+
textureColorMask: style.textureColorMask,
|
|
41380
|
+
driveMaterial: false,
|
|
41381
|
+
produceValueCanvas: true
|
|
41382
|
+
});
|
|
41383
|
+
cEntity[EXTRUSION_ANIMATOR_KEY] = animator;
|
|
41384
|
+
const mask = style.textureColorMask;
|
|
41385
|
+
const surface = new exports.DisplacedSurfacePrimitive.Surface({
|
|
41386
|
+
extent,
|
|
41387
|
+
source: null,
|
|
41388
|
+
baseHeight: placement.baseHeight,
|
|
41389
|
+
followGround: placement.followGround,
|
|
41390
|
+
terrainProvider: viewer && viewer.terrainProvider,
|
|
41391
|
+
valueMin: extrusionArchive.metadata.ValueMin,
|
|
41392
|
+
valueMax: extrusionArchive.metadata.ValueMax,
|
|
41393
|
+
exaggeration: style.extrusionExaggeration,
|
|
41394
|
+
lowColor: mask ? BModels.Color.ColorFromStr(mask.minColor) : null,
|
|
41395
|
+
highColor: mask ? BModels.Color.ColorFromStr(mask.maxColor) : null
|
|
41396
|
+
});
|
|
41397
|
+
surface.BindAnimator(animator);
|
|
41398
|
+
viewer.scene.primitives.add(surface);
|
|
41399
|
+
cEntity[DISPLACED_SURFACE_KEY] = surface;
|
|
41400
|
+
}
|
|
41401
|
+
/**
|
|
41402
|
+
* Turns the polygon's altitude option into the two things the displaced surface needs:
|
|
41403
|
+
* - how high value zero sits.
|
|
41404
|
+
* - whether that is measured from the terrain or from the ellipsoid.
|
|
41405
|
+
* @param entity
|
|
41406
|
+
* @param heightRef
|
|
41407
|
+
*/
|
|
41408
|
+
function surfacePlacement(entity, heightRef) {
|
|
41409
|
+
const rawAltitude = BModels.Entity.GetValue({
|
|
41410
|
+
entity,
|
|
41411
|
+
path: ["Bruce", "Location", "altitude"]
|
|
41412
|
+
});
|
|
41413
|
+
const altitude = rawAltitude ? EnsureNumber(rawAltitude) : 0;
|
|
41414
|
+
if (heightRef === Cesium.HeightReference.CLAMP_TO_GROUND) {
|
|
41415
|
+
return { baseHeight: 0, followGround: true };
|
|
41416
|
+
}
|
|
41417
|
+
if (heightRef === Cesium.HeightReference.RELATIVE_TO_GROUND) {
|
|
41418
|
+
return { baseHeight: altitude, followGround: true };
|
|
41419
|
+
}
|
|
41420
|
+
return { baseHeight: altitude, followGround: false };
|
|
41421
|
+
}
|
|
41422
|
+
/**
|
|
41423
|
+
* The archive's own extent when it recorded one, otherwise the polygon's own bounds so archives
|
|
41424
|
+
* generated before the extent was written still render somewhere sensible.
|
|
41425
|
+
* @param metadata
|
|
41426
|
+
* @param posses
|
|
41427
|
+
*/
|
|
41428
|
+
function archiveExtent(metadata, posses) {
|
|
41429
|
+
if (metadata.West != null && metadata.East != null && metadata.South != null && metadata.North != null) {
|
|
41430
|
+
return { West: metadata.West, East: metadata.East, South: metadata.South, North: metadata.North };
|
|
41431
|
+
}
|
|
41432
|
+
if (!posses || posses.length === 0) {
|
|
41433
|
+
return null;
|
|
41434
|
+
}
|
|
41435
|
+
const rectangle = Cesium.Rectangle.fromCartesianArray(posses);
|
|
41436
|
+
return {
|
|
41437
|
+
West: Cesium.Math.toDegrees(rectangle.west),
|
|
41438
|
+
East: Cesium.Math.toDegrees(rectangle.east),
|
|
41439
|
+
South: Cesium.Math.toDegrees(rectangle.south),
|
|
41440
|
+
North: Cesium.Math.toDegrees(rectangle.north)
|
|
41441
|
+
};
|
|
41442
|
+
}
|
|
41443
|
+
/**
|
|
41444
|
+
* Tears down the displaced surface and its feeding animator, if this entity has them.
|
|
41445
|
+
* @param cEntity
|
|
41446
|
+
* @param viewer
|
|
41447
|
+
*/
|
|
41448
|
+
function disposeDisplacedSurface(cEntity, viewer) {
|
|
41449
|
+
const surface = cEntity === null || cEntity === void 0 ? void 0 : cEntity[DISPLACED_SURFACE_KEY];
|
|
41450
|
+
if (surface) {
|
|
41451
|
+
const viewerAlive = viewer && (typeof viewer.isDestroyed !== "function" || !viewer.isDestroyed());
|
|
41452
|
+
if (viewerAlive && viewer.scene && viewer.scene.primitives) {
|
|
41453
|
+
// Removing from the collection destroys the primitive, releasing its meshes and texture.
|
|
41454
|
+
viewer.scene.primitives.remove(surface);
|
|
41455
|
+
}
|
|
41456
|
+
else if (!surface.isDestroyed()) {
|
|
41457
|
+
surface.destroy();
|
|
41458
|
+
}
|
|
41459
|
+
cEntity[DISPLACED_SURFACE_KEY] = null;
|
|
41460
|
+
}
|
|
41461
|
+
const animator = cEntity === null || cEntity === void 0 ? void 0 : cEntity[EXTRUSION_ANIMATOR_KEY];
|
|
41462
|
+
if (animator && !animator.IsDisposed()) {
|
|
41463
|
+
animator.Dispose();
|
|
41464
|
+
}
|
|
41465
|
+
if (cEntity) {
|
|
41466
|
+
cEntity[EXTRUSION_ANIMATOR_KEY] = null;
|
|
41467
|
+
}
|
|
41468
|
+
}
|
|
40528
41469
|
/**
|
|
40529
41470
|
* Reconciles cEntity's TextureFrameSeriesAnimator.Animator (if any) against this render's resolved texture.
|
|
40530
41471
|
* Called once cEntity is fully created/updated and definitely has a `polygon` graphics.
|
|
@@ -40615,8 +41556,15 @@
|
|
|
40615
41556
|
* @param params
|
|
40616
41557
|
*/
|
|
40617
41558
|
async function resolveTexturedFill(params) {
|
|
40618
|
-
|
|
40619
|
-
|
|
41559
|
+
return resolveTextureValue({ ...params, textureValue: params.style.texture });
|
|
41560
|
+
}
|
|
41561
|
+
/**
|
|
41562
|
+
* Resolves either shape a texture setting can take (a calculated file id/URL, or a search condition)
|
|
41563
|
+
* into a tinted data URI or, when the resolved file is a frame archive, the archive itself.
|
|
41564
|
+
* @param params
|
|
41565
|
+
*/
|
|
41566
|
+
async function resolveTextureValue(params) {
|
|
41567
|
+
const { api, style, entity, tags, textureValue } = params;
|
|
40620
41568
|
if (textureValue && !Array.isArray(textureValue)) {
|
|
40621
41569
|
return resolveTextureCondition({ api, condition: textureValue });
|
|
40622
41570
|
}
|
|
@@ -41393,7 +42341,7 @@
|
|
|
41393
42341
|
entity._dispose();
|
|
41394
42342
|
entity._dispose = null;
|
|
41395
42343
|
}
|
|
41396
|
-
exports.EntityRenderEnginePolygon.DisposeTextureFrameSeriesAnimator(entity);
|
|
42344
|
+
exports.EntityRenderEnginePolygon.DisposeTextureFrameSeriesAnimator(entity, viewer);
|
|
41397
42345
|
if (entity._parentEntity && !ignoreParent) {
|
|
41398
42346
|
doRemove({
|
|
41399
42347
|
viewer,
|
|
@@ -42141,7 +43089,7 @@
|
|
|
42141
43089
|
StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
|
|
42142
43090
|
})(exports.StyleUtils || (exports.StyleUtils = {}));
|
|
42143
43091
|
|
|
42144
|
-
const VERSION = "7.1.
|
|
43092
|
+
const VERSION = "7.1.4";
|
|
42145
43093
|
/**
|
|
42146
43094
|
* Updates the environment instance used by bruce-cesium to one specified.
|
|
42147
43095
|
* This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.
|