bruce-cesium 7.1.3 → 7.1.5
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 +1164 -50
- package/dist/bruce-cesium.es5.js.map +1 -1
- package/dist/bruce-cesium.umd.js +1134 -25
- 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 +734 -0
- package/dist/lib/rendering/displaced-surface-primitive.js.map +1 -0
- package/dist/lib/rendering/entity-render-engine-polygon.js +211 -17
- 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 +197 -6
- 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 +108 -0
- package/dist/types/rendering/entity-render-engine-polygon.d.ts +1 -1
- package/dist/types/rendering/texture-frame-series-animator.d.ts +53 -0
- package/package.json +2 -2
package/dist/bruce-cesium.es5.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Cesium from 'cesium';
|
|
2
|
-
import { Cartographic, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, Math as Math$1,
|
|
3
|
-
import { Cartes, Entity as Entity$1, ENVIRONMENT, Api, Calculator, ClientFile, EntityTag, EntityType, LRUCache, ObjectUtils, Style, EntityLod, MenuItem, ProjectView, ProjectViewBookmark, Tileset, ZoomControl, Bounds, Color as Color$1,
|
|
2
|
+
import { Cartographic, ColorMaterialProperty, Entity, Color, ConstantProperty, CallbackProperty, Primitive, Cesium3DTileFeature, DistanceDisplayCondition, ShaderSource, Cartesian3, Math as Math$1, Ellipsoid, Transforms, Matrix4, Geometry, GeometryAttribute, ComponentDatatype, PrimitiveType, BoundingSphere, GeometryPipeline, sampleTerrain, Texture, PixelFormat, Sampler, TextureWrap, TextureMinificationFilter, TextureMagnificationFilter, VertexArray, BufferUsage, ShaderProgram, RenderState, BlendingState, DrawCommand, Pass, Cartesian2, HeightReference, HorizontalOrigin, VerticalOrigin, ClassificationType, ConstantPositionProperty, ArcType, CornerType, ShadowMode, ColorBlendMode, HeadingPitchRoll, Model, JulianDate, Quaternion, ImageMaterialProperty, PolygonHierarchy, PolylineGraphics, Rectangle, SceneTransforms, NearFarScalar, Matrix3, KmlDataSource, GeoJsonDataSource, SceneMode, Ion, IonResource, Cesium3DTileStyle, HeadingPitchRange, Cesium3DTileColorBlendMode, OrthographicFrustum, EasingFunction, EllipsoidTerrainProvider, IonImageryProvider, createWorldImagery, createWorldImageryAsync, BingMapsImageryProvider, BingMapsStyle, MapboxImageryProvider, MapboxStyleImageryProvider, ArcGisMapServerImageryProvider, OpenStreetMapImageryProvider, UrlTemplateImageryProvider, GridImageryProvider, GeographicTilingScheme, ImageryLayer, TileMapServiceImageryProvider, CesiumTerrainProvider, Cesium3DTileset, Intersect, ModelGraphics, PolygonGraphics, CorridorGraphics, PointGraphics, BillboardGraphics, EllipseGraphics, PolylineDashMaterialProperty, EllipsoidGeodesic, sampleTerrainMostDetailed, defined, CustomDataSource, PolygonPipeline, CesiumInspector, ClockRange, GeometryInstance, ScreenSpaceEventHandler, ScreenSpaceEventType, Fullscreen, CzmlDataSource } from 'cesium';
|
|
3
|
+
import { Cartes, Entity as Entity$1, ENVIRONMENT, Api, Calculator, ClientFile, EntityTag, EntityType, LRUCache, ObjectUtils, Style, Carto, Geometry as Geometry$1, EntityLod, MenuItem, ProjectView, ProjectViewBookmark, Tileset, ZoomControl, Bounds, Color as Color$1, BruceEvent, EntityCoords, DataLab, DelayQueue, EntityHistoricData, BruceApi, AccountConcept, RecordChangeFeed, EntityRelation, ProgramKey, EntitySource, Camera, ProjectViewTile, ProjectViewLegacyTile, Account, AccountLimits, Session, UrlUtils, ProjectViewBookmarkGroup, EntityAttachment, EntityAttachmentType, EntityAttribute, MathUtils, EntityRelationType } from 'bruce-models';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Ensures a number is returned from a given value.
|
|
@@ -3563,11 +3563,11 @@ var SimplifyGeometry;
|
|
|
3563
3563
|
else if (geometry.Polygon) {
|
|
3564
3564
|
for (let i = 0; i < geometry.Polygon.length; i++) {
|
|
3565
3565
|
const polygon = geometry.Polygon[i];
|
|
3566
|
-
count += Geometry.ParsePoints(polygon.LinearRing).length;
|
|
3566
|
+
count += Geometry$1.ParsePoints(polygon.LinearRing).length;
|
|
3567
3567
|
}
|
|
3568
3568
|
}
|
|
3569
3569
|
else if (geometry.LineString) {
|
|
3570
|
-
count += Geometry.ParsePoints(geometry.LineString).length;
|
|
3570
|
+
count += Geometry$1.ParsePoints(geometry.LineString).length;
|
|
3571
3571
|
}
|
|
3572
3572
|
else if (geometry.Point) {
|
|
3573
3573
|
count += 1;
|
|
@@ -3588,7 +3588,7 @@ var SimplifyGeometry;
|
|
|
3588
3588
|
return geometry;
|
|
3589
3589
|
}
|
|
3590
3590
|
// Convert to geojson so that we can interact with turf.
|
|
3591
|
-
const gFeature = Geometry.ToGeoJsonFeature({
|
|
3591
|
+
const gFeature = Geometry$1.ToGeoJsonFeature({
|
|
3592
3592
|
geometry: geometry
|
|
3593
3593
|
});
|
|
3594
3594
|
// Unkink the geometry.
|
|
@@ -3607,7 +3607,7 @@ var SimplifyGeometry;
|
|
|
3607
3607
|
});
|
|
3608
3608
|
}
|
|
3609
3609
|
// Converting back to Bruce geometry.
|
|
3610
|
-
geometry = (_a = Geometry.FromGeoJson({
|
|
3610
|
+
geometry = (_a = Geometry$1.FromGeoJson({
|
|
3611
3611
|
geoJson: gFeature
|
|
3612
3612
|
})) === null || _a === void 0 ? void 0 : _a.geometry;
|
|
3613
3613
|
return geometry;
|
|
@@ -11288,7 +11288,7 @@ var EntitiesRenderManager;
|
|
|
11288
11288
|
};
|
|
11289
11289
|
for (let i = 0; i < entities.length; i++) {
|
|
11290
11290
|
const entity = entities[i];
|
|
11291
|
-
const feature = Geometry.ToGeoJsonFeature({
|
|
11291
|
+
const feature = Geometry$1.ToGeoJsonFeature({
|
|
11292
11292
|
geometry: entity.Bruce.VectorGeometry,
|
|
11293
11293
|
noAltitude: polygonsClamped && lineWidthPx <= 0,
|
|
11294
11294
|
altitude: lineWidthPx > 0 && polygonsClamped ? 1 : null,
|
|
@@ -12324,7 +12324,7 @@ var EntitiesLoadedRenderManager;
|
|
|
12324
12324
|
};
|
|
12325
12325
|
for (let i = 0; i < entities.length; i++) {
|
|
12326
12326
|
const entity = entities[i];
|
|
12327
|
-
const feature = Geometry.ToGeoJsonFeature({
|
|
12327
|
+
const feature = Geometry$1.ToGeoJsonFeature({
|
|
12328
12328
|
geometry: entity.Bruce.VectorGeometry,
|
|
12329
12329
|
noAltitude: polygonsClamped && lineWidthPx <= 0,
|
|
12330
12330
|
altitude: lineWidthPx > 0 && polygonsClamped ? 1 : null,
|
|
@@ -17048,7 +17048,7 @@ function createBoundsPolygonGeometry(bounds) {
|
|
|
17048
17048
|
return {
|
|
17049
17049
|
Polygon: [
|
|
17050
17050
|
{
|
|
17051
|
-
Facing: Geometry.EPolygonRingType.Boundaries,
|
|
17051
|
+
Facing: Geometry$1.EPolygonRingType.Boundaries,
|
|
17052
17052
|
LinearRing: ring
|
|
17053
17053
|
}
|
|
17054
17054
|
]
|
|
@@ -35816,7 +35816,7 @@ var EntityUtils;
|
|
|
35816
35816
|
// "0.0,0.0" seems to be generated on missing geometry in our re-index.
|
|
35817
35817
|
// No location -> turns into 0,0 -> turns into 0.0,0.0 point geometry.
|
|
35818
35818
|
if (pointStr && typeof pointStr == "string" && pointStr != "0.0,0.0") {
|
|
35819
|
-
const points = Geometry.ParsePoints(pointStr);
|
|
35819
|
+
const points = Geometry$1.ParsePoints(pointStr);
|
|
35820
35820
|
const point = points.length > 0 ? points[0] : null;
|
|
35821
35821
|
if (point && Carto.ValidateCarto(point)) {
|
|
35822
35822
|
const pos3d = Cartesian3.fromDegrees(EnsureNumber(point.longitude), EnsureNumber(point.latitude), EnsureNumber(point.altitude));
|
|
@@ -35825,7 +35825,7 @@ var EntityUtils;
|
|
|
35825
35825
|
}
|
|
35826
35826
|
const lineStr = geometry.LineString;
|
|
35827
35827
|
if (lineStr && typeof lineStr == "string") {
|
|
35828
|
-
const points = Geometry.ParsePoints(lineStr);
|
|
35828
|
+
const points = Geometry$1.ParsePoints(lineStr);
|
|
35829
35829
|
if (points.length > 0) {
|
|
35830
35830
|
const pPosses = points.map(x => Cartesian3.fromDegrees(EnsureNumber(x.longitude), EnsureNumber(x.latitude), EnsureNumber(x.altitude)));
|
|
35831
35831
|
await ensureHeightRefs(pPosses, sample);
|
|
@@ -35834,9 +35834,9 @@ var EntityUtils;
|
|
|
35834
35834
|
}
|
|
35835
35835
|
const pRings = geometry.Polygon;
|
|
35836
35836
|
if (pRings && typeof pRings == "object") {
|
|
35837
|
-
const boundary = pRings.find(x => x.Facing == Geometry.EPolygonRingType.Boundaries);
|
|
35837
|
+
const boundary = pRings.find(x => x.Facing == Geometry$1.EPolygonRingType.Boundaries);
|
|
35838
35838
|
if (boundary === null || boundary === void 0 ? void 0 : boundary.LinearRing) {
|
|
35839
|
-
const points = Geometry.ParsePoints(boundary.LinearRing);
|
|
35839
|
+
const points = Geometry$1.ParsePoints(boundary.LinearRing);
|
|
35840
35840
|
const pPosses = points.map(x => Cartesian3.fromDegrees(EnsureNumber(x.longitude), EnsureNumber(x.latitude), EnsureNumber(x.altitude)));
|
|
35841
35841
|
await ensureHeightRefs(pPosses, sample);
|
|
35842
35842
|
posses = posses.concat(pPosses);
|
|
@@ -36331,7 +36331,7 @@ var EntityUtils;
|
|
|
36331
36331
|
if (geometry && typeof geometry == "object") {
|
|
36332
36332
|
const pointStr = geometry.Point;
|
|
36333
36333
|
if (pointStr && typeof pointStr == "string") {
|
|
36334
|
-
const points = Geometry.ParsePoints(pointStr);
|
|
36334
|
+
const points = Geometry$1.ParsePoints(pointStr);
|
|
36335
36335
|
const point = points.length > 0 ? points[0] : null;
|
|
36336
36336
|
if (point && Carto.ValidateCarto(point)) {
|
|
36337
36337
|
return Cartesian3.fromDegrees(EnsureNumber(point.longitude), EnsureNumber(point.latitude), EnsureNumber(point.altitude));
|
|
@@ -36339,7 +36339,7 @@ var EntityUtils;
|
|
|
36339
36339
|
}
|
|
36340
36340
|
const lineStr = geometry.LineString;
|
|
36341
36341
|
if (lineStr && typeof lineStr == "string") {
|
|
36342
|
-
const points = Geometry.ParsePoints(lineStr);
|
|
36342
|
+
const points = Geometry$1.ParsePoints(lineStr);
|
|
36343
36343
|
if (points.length > 0) {
|
|
36344
36344
|
const posses = points.map(x => Cartesian3.fromDegrees(EnsureNumber(x.longitude), EnsureNumber(x.latitude), EnsureNumber(x.altitude)));
|
|
36345
36345
|
const { totalLength: length } = MeasureUtils.MeasurePolyline({
|
|
@@ -36362,9 +36362,9 @@ var EntityUtils;
|
|
|
36362
36362
|
}
|
|
36363
36363
|
const pRings = geometry.Polygon;
|
|
36364
36364
|
if (pRings && typeof pRings == "object") {
|
|
36365
|
-
const boundary = pRings.find(x => x.Facing == Geometry.EPolygonRingType.Boundaries);
|
|
36365
|
+
const boundary = pRings.find(x => x.Facing == Geometry$1.EPolygonRingType.Boundaries);
|
|
36366
36366
|
if (boundary === null || boundary === void 0 ? void 0 : boundary.LinearRing) {
|
|
36367
|
-
const points = Geometry.ParsePoints(boundary.LinearRing);
|
|
36367
|
+
const points = Geometry$1.ParsePoints(boundary.LinearRing);
|
|
36368
36368
|
const point = Carto.GetCenter(points);
|
|
36369
36369
|
if (point && Carto.ValidateCarto(point)) {
|
|
36370
36370
|
return Cartesian3.fromDegrees(EnsureNumber(point.longitude), EnsureNumber(point.latitude), EnsureNumber(point.altitude));
|
|
@@ -37098,7 +37098,7 @@ function calculateDistance(point, entity) {
|
|
|
37098
37098
|
if (geometry.Polygon) {
|
|
37099
37099
|
const polygonRings = geometry.Polygon;
|
|
37100
37100
|
const polygon = typeof polygonRings == "string" ? String(polygonRings) : polygonRings.length ? polygonRings[0].LinearRing : "";
|
|
37101
|
-
let points = polygon ? Geometry.ParsePoints(polygon) : [];
|
|
37101
|
+
let points = polygon ? Geometry$1.ParsePoints(polygon) : [];
|
|
37102
37102
|
if (points.length > 2) {
|
|
37103
37103
|
const distance = distanceToPolygon(point, points);
|
|
37104
37104
|
if (!isNaN(distance)) {
|
|
@@ -37108,7 +37108,7 @@ function calculateDistance(point, entity) {
|
|
|
37108
37108
|
}
|
|
37109
37109
|
}
|
|
37110
37110
|
if (!passedGeometry && geometry.LineString) {
|
|
37111
|
-
const points = Geometry.ParsePoints(geometry.LineString);
|
|
37111
|
+
const points = Geometry$1.ParsePoints(geometry.LineString);
|
|
37112
37112
|
if (points.length > 1) {
|
|
37113
37113
|
const pointsLen = points.length;
|
|
37114
37114
|
for (let i = 0; i < pointsLen; i++) {
|
|
@@ -39252,7 +39252,7 @@ var EntityRenderEnginePolyline;
|
|
|
39252
39252
|
entity: entity,
|
|
39253
39253
|
path: ["Bruce", "VectorGeometry", "LineString"]
|
|
39254
39254
|
});
|
|
39255
|
-
const points = lineString && typeof lineString == "string" ? Geometry.ParsePoints(lineString) : [];
|
|
39255
|
+
const points = lineString && typeof lineString == "string" ? Geometry$1.ParsePoints(lineString) : [];
|
|
39256
39256
|
if (!points || points.length < 2) {
|
|
39257
39257
|
return null;
|
|
39258
39258
|
}
|
|
@@ -39734,15 +39734,68 @@ var TextureFrameSeriesAnimator;
|
|
|
39734
39734
|
return Boolean(data && data.generation && Array.isArray(data.generation.Frames) && data.generation.Frames.length > 0);
|
|
39735
39735
|
}
|
|
39736
39736
|
TextureFrameSeriesAnimator.IsFrameArchiveMetadata = IsFrameArchiveMetadata;
|
|
39737
|
+
// How far covered values bleed outward into uncovered texels before the GPU samples them.
|
|
39738
|
+
const VALUE_DILATE_PASSES = 2;
|
|
39739
|
+
/*
|
|
39740
|
+
* Bleeds covered values outward into uncovered texels, leaving alpha untouched.
|
|
39741
|
+
*
|
|
39742
|
+
* The GPU samples the value texture with linear filtering, so an uncovered texel's RGB still gets
|
|
39743
|
+
* averaged into the vertices next to it even though its alpha is zero. Land sits at one end of the
|
|
39744
|
+
* ramp, so without this the coastline grows a row of spikes exactly where the data stops. Masking the
|
|
39745
|
+
* baseline creates more of these edges, which is what makes this necessary rather than cosmetic.
|
|
39746
|
+
*/
|
|
39747
|
+
function dilateValues(value, width, height, passes) {
|
|
39748
|
+
const texels = width * height;
|
|
39749
|
+
const filled = new Uint8Array(texels);
|
|
39750
|
+
for (let p = 0; p < texels; p++) {
|
|
39751
|
+
filled[p] = value[p * 4 + 3] !== 0 ? 1 : 0;
|
|
39752
|
+
}
|
|
39753
|
+
for (let pass = 0; pass < passes; pass++) {
|
|
39754
|
+
const source = value.slice();
|
|
39755
|
+
const wasFilled = filled.slice();
|
|
39756
|
+
for (let y = 0; y < height; y++) {
|
|
39757
|
+
for (let x = 0; x < width; x++) {
|
|
39758
|
+
const p = y * width + x;
|
|
39759
|
+
if (wasFilled[p]) {
|
|
39760
|
+
continue;
|
|
39761
|
+
}
|
|
39762
|
+
let sum = 0;
|
|
39763
|
+
let hits = 0;
|
|
39764
|
+
for (let dy = -1; dy <= 1; dy++) {
|
|
39765
|
+
for (let dx = -1; dx <= 1; dx++) {
|
|
39766
|
+
const nx = x + dx;
|
|
39767
|
+
const ny = y + dy;
|
|
39768
|
+
if (nx < 0 || ny < 0 || nx >= width || ny >= height || !wasFilled[ny * width + nx]) {
|
|
39769
|
+
continue;
|
|
39770
|
+
}
|
|
39771
|
+
sum += source[(ny * width + nx) * 4];
|
|
39772
|
+
hits++;
|
|
39773
|
+
}
|
|
39774
|
+
}
|
|
39775
|
+
if (hits > 0) {
|
|
39776
|
+
const v = Math.round(sum / hits);
|
|
39777
|
+
value[p * 4] = v;
|
|
39778
|
+
value[p * 4 + 1] = v;
|
|
39779
|
+
value[p * 4 + 2] = v;
|
|
39780
|
+
filled[p] = 1;
|
|
39781
|
+
}
|
|
39782
|
+
}
|
|
39783
|
+
}
|
|
39784
|
+
}
|
|
39785
|
+
}
|
|
39737
39786
|
const DEFAULT_CROSSFADE_MS = 900;
|
|
39738
39787
|
const DEFAULT_LOW_COLOR = { red: 255, green: 255, blue: 255, alpha: 0.12 };
|
|
39739
39788
|
const DEFAULT_HIGH_COLOR = { red: 21, green: 96, blue: 196, alpha: 0.92 };
|
|
39740
39789
|
class Animator {
|
|
39741
39790
|
constructor(options) {
|
|
39742
|
-
var _a;
|
|
39791
|
+
var _a, _b;
|
|
39743
39792
|
this.removeOnTick = null;
|
|
39744
39793
|
this.removeCrossfadeTick = null;
|
|
39745
39794
|
this.disposed = false;
|
|
39795
|
+
// One alpha byte per texel, 1 meaning the texel is baseline. Fetched once, the mesh does not move.
|
|
39796
|
+
this.baselineFlags = null;
|
|
39797
|
+
this.baselineDims = null;
|
|
39798
|
+
this.baselineLoad = null;
|
|
39746
39799
|
this.currentFrameIndex = -1;
|
|
39747
39800
|
// Single-in-flight fetch queue: only one Range GET outstanding at a time, and the NEXT one
|
|
39748
39801
|
// is chosen right before sending, this is what makes reprioritizing mid-scrub take effect
|
|
@@ -39751,6 +39804,8 @@ var TextureFrameSeriesAnimator;
|
|
|
39751
39804
|
this.pendingIndex = -1;
|
|
39752
39805
|
this.displayedPixels = null;
|
|
39753
39806
|
this.fadeFromPixels = null;
|
|
39807
|
+
this.displayedValuePixels = null;
|
|
39808
|
+
this.fadeFromValuePixels = null;
|
|
39754
39809
|
this.fadeToIndex = -1;
|
|
39755
39810
|
this.fadeToDims = null;
|
|
39756
39811
|
this.fadeStartMs = null;
|
|
@@ -39759,6 +39814,8 @@ var TextureFrameSeriesAnimator;
|
|
|
39759
39814
|
// forcing Cesium to re-upload the texture only when something changed.
|
|
39760
39815
|
this.pool = [document.createElement("canvas"), document.createElement("canvas")];
|
|
39761
39816
|
this.poolIdx = 0;
|
|
39817
|
+
this.valueDims = null;
|
|
39818
|
+
this.presentVersion = 0;
|
|
39762
39819
|
if (!options.entity.polygon) {
|
|
39763
39820
|
throw new Error("TextureFrameSeriesAnimator requires an entity with polygon graphics.");
|
|
39764
39821
|
}
|
|
@@ -39776,9 +39833,17 @@ var TextureFrameSeriesAnimator;
|
|
|
39776
39833
|
this.frameDates = this.frames.map((f) => JulianDate.fromIso8601(f.Timestamp));
|
|
39777
39834
|
this.frameCache = new Array(this.frames.length).fill(null);
|
|
39778
39835
|
this.frameDims = new Array(this.frames.length).fill(null);
|
|
39836
|
+
this.valueCache = new Array(this.frames.length).fill(null);
|
|
39837
|
+
this.produceValueCanvas = Boolean(options.produceValueCanvas);
|
|
39838
|
+
this.baselineMaskEntry = options.baselineMask || null;
|
|
39839
|
+
this.maskBaseline = (_b = options.maskBaseline) !== null && _b !== void 0 ? _b : Boolean(options.baselineMask);
|
|
39840
|
+
this.valueCanvas = this.produceValueCanvas ? document.createElement("canvas") : null;
|
|
39841
|
+
this.driveMaterial = options.driveMaterial !== false;
|
|
39779
39842
|
this.originalMaterial = options.entity.polygon.material;
|
|
39780
39843
|
this.imageProperty = new CallbackProperty(() => this.pool[this.poolIdx], false);
|
|
39781
|
-
|
|
39844
|
+
if (this.driveMaterial) {
|
|
39845
|
+
options.entity.polygon.material = new ImageMaterialProperty({ image: this.imageProperty, transparent: true });
|
|
39846
|
+
}
|
|
39782
39847
|
this.removeCrossfadeTick = this.viewer.scene.postUpdate.addEventListener(() => this.tickCrossfade());
|
|
39783
39848
|
this.removeOnTick = this.viewer.clock.onTick.addEventListener((clock) => this.onClockTick(clock.currentTime));
|
|
39784
39849
|
this.onClockTick(this.viewer.clock.currentTime);
|
|
@@ -39800,7 +39865,7 @@ var TextureFrameSeriesAnimator;
|
|
|
39800
39865
|
this.removeCrossfadeTick();
|
|
39801
39866
|
this.removeCrossfadeTick = null;
|
|
39802
39867
|
}
|
|
39803
|
-
if (this.entity && this.entity.polygon) {
|
|
39868
|
+
if (this.driveMaterial && this.entity && this.entity.polygon) {
|
|
39804
39869
|
this.entity.polygon.material = this.originalMaterial;
|
|
39805
39870
|
}
|
|
39806
39871
|
}
|
|
@@ -39821,6 +39886,20 @@ var TextureFrameSeriesAnimator;
|
|
|
39821
39886
|
GetImageProperty() {
|
|
39822
39887
|
return this.imageProperty;
|
|
39823
39888
|
}
|
|
39889
|
+
/**
|
|
39890
|
+
* The untinted canvas carrying the presented frame's value in RGB and its coverage in alpha,
|
|
39891
|
+
* or null unless the instance was constructed with produceValueCanvas.
|
|
39892
|
+
*/
|
|
39893
|
+
GetValueCanvas() {
|
|
39894
|
+
return this.valueDims ? this.valueCanvas : null;
|
|
39895
|
+
}
|
|
39896
|
+
/**
|
|
39897
|
+
* Increments every time a frame is presented, so a consumer holding the value canvas can tell
|
|
39898
|
+
* whether it needs re-uploading without diffing pixels or subscribing to anything.
|
|
39899
|
+
*/
|
|
39900
|
+
GetPresentVersion() {
|
|
39901
|
+
return this.presentVersion;
|
|
39902
|
+
}
|
|
39824
39903
|
onClockTick(currentTime) {
|
|
39825
39904
|
if (this.disposed) {
|
|
39826
39905
|
return;
|
|
@@ -39927,12 +40006,19 @@ var TextureFrameSeriesAnimator;
|
|
|
39927
40006
|
if (this.disposed) {
|
|
39928
40007
|
return;
|
|
39929
40008
|
}
|
|
40009
|
+
// Awaited before decoding rather than applied later, so no frame is ever cached unmasked and
|
|
40010
|
+
// then corrected once the mask lands.
|
|
40011
|
+
await this.ensureBaselineMask();
|
|
40012
|
+
if (this.disposed) {
|
|
40013
|
+
return;
|
|
40014
|
+
}
|
|
39930
40015
|
const decoded = await this.decodeAndTint(buffer);
|
|
39931
40016
|
if (this.disposed) {
|
|
39932
40017
|
return;
|
|
39933
40018
|
}
|
|
39934
40019
|
this.frameCache[idx] = decoded.pixels;
|
|
39935
40020
|
this.frameDims[idx] = { width: decoded.width, height: decoded.height };
|
|
40021
|
+
this.valueCache[idx] = decoded.valuePixels || null;
|
|
39936
40022
|
if (idx === this.currentFrameIndex) {
|
|
39937
40023
|
this.beginCrossfadeTo(idx);
|
|
39938
40024
|
}
|
|
@@ -39956,11 +40042,90 @@ var TextureFrameSeriesAnimator;
|
|
|
39956
40042
|
const ctx = canvas.getContext("2d");
|
|
39957
40043
|
ctx.drawImage(image, 0, 0);
|
|
39958
40044
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
40045
|
+
// Copied before tinting: the ramp is a lerp between two colours, so the value cannot be
|
|
40046
|
+
// recovered from the tinted pixels afterwards.
|
|
40047
|
+
const valuePixels = this.produceValueCanvas ? new Uint8ClampedArray(imageData.data) : undefined;
|
|
39959
40048
|
ApplyGrayscaleColorMask(imageData, this.lowColor, this.highColor);
|
|
39960
|
-
|
|
40049
|
+
this.applyBaselineMask(imageData.data, valuePixels, canvas.width, canvas.height);
|
|
40050
|
+
if (valuePixels) {
|
|
40051
|
+
dilateValues(valuePixels, canvas.width, canvas.height, VALUE_DILATE_PASSES);
|
|
40052
|
+
}
|
|
40053
|
+
return { pixels: imageData.data, width: canvas.width, height: canvas.height, valuePixels };
|
|
40054
|
+
}
|
|
40055
|
+
/**
|
|
40056
|
+
* Fetches and decodes the archive's static baseline mask, at most once.
|
|
40057
|
+
*/
|
|
40058
|
+
ensureBaselineMask() {
|
|
40059
|
+
if (!this.maskBaseline || !this.baselineMaskEntry) {
|
|
40060
|
+
return Promise.resolve();
|
|
40061
|
+
}
|
|
40062
|
+
if (this.baselineLoad) {
|
|
40063
|
+
return this.baselineLoad;
|
|
40064
|
+
}
|
|
40065
|
+
const entry = this.baselineMaskEntry;
|
|
40066
|
+
const start = entry.ByteOffset;
|
|
40067
|
+
const end = entry.ByteOffset + entry.ByteLength - 1;
|
|
40068
|
+
this.baselineLoad = fetch(this.archiveUrl, { headers: { Range: `bytes=${start}-${end}` } })
|
|
40069
|
+
.then((response) => response.arrayBuffer())
|
|
40070
|
+
.then(async (buffer) => {
|
|
40071
|
+
if (this.disposed) {
|
|
40072
|
+
return;
|
|
40073
|
+
}
|
|
40074
|
+
const blob = new Blob([buffer], { type: "image/png" });
|
|
40075
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
40076
|
+
let image;
|
|
40077
|
+
try {
|
|
40078
|
+
image = await loadImage(objectUrl);
|
|
40079
|
+
}
|
|
40080
|
+
finally {
|
|
40081
|
+
URL.revokeObjectURL(objectUrl);
|
|
40082
|
+
}
|
|
40083
|
+
const canvas = document.createElement("canvas");
|
|
40084
|
+
canvas.width = image.width;
|
|
40085
|
+
canvas.height = image.height;
|
|
40086
|
+
const ctx = canvas.getContext("2d");
|
|
40087
|
+
ctx.drawImage(image, 0, 0);
|
|
40088
|
+
const pixels = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
|
|
40089
|
+
const flags = new Uint8Array(canvas.width * canvas.height);
|
|
40090
|
+
for (let p = 0; p < flags.length; p++) {
|
|
40091
|
+
flags[p] = pixels[p * 4 + 3] >= 128 ? 1 : 0;
|
|
40092
|
+
}
|
|
40093
|
+
this.baselineFlags = flags;
|
|
40094
|
+
this.baselineDims = { width: canvas.width, height: canvas.height };
|
|
40095
|
+
})
|
|
40096
|
+
.catch((e) => {
|
|
40097
|
+
// A missing or unreadable mask must not stop the animation; it just means nothing is hidden.
|
|
40098
|
+
console.warn("TextureFrameSeriesAnimator: could not load the baseline mask, drawing it instead.", e);
|
|
40099
|
+
});
|
|
40100
|
+
return this.baselineLoad;
|
|
40101
|
+
}
|
|
40102
|
+
/**
|
|
40103
|
+
* Clears alpha on baseline texels, which is what makes hiding the dry/land sentinel a render choice.
|
|
40104
|
+
*/
|
|
40105
|
+
applyBaselineMask(pixels, valuePixels, width, height) {
|
|
40106
|
+
const flags = this.baselineFlags;
|
|
40107
|
+
if (!flags || !this.baselineDims) {
|
|
40108
|
+
return;
|
|
40109
|
+
}
|
|
40110
|
+
// A mask from a different raster size cannot be indexed against this frame, and guessing a
|
|
40111
|
+
// resampling would silently hide the wrong ground.
|
|
40112
|
+
if (this.baselineDims.width !== width || this.baselineDims.height !== height) {
|
|
40113
|
+
return;
|
|
40114
|
+
}
|
|
40115
|
+
for (let p = 0; p < flags.length; p++) {
|
|
40116
|
+
if (flags[p]) {
|
|
40117
|
+
pixels[p * 4 + 3] = 0;
|
|
40118
|
+
if (valuePixels) {
|
|
40119
|
+
valuePixels[p * 4 + 3] = 0;
|
|
40120
|
+
}
|
|
40121
|
+
}
|
|
40122
|
+
}
|
|
39961
40123
|
}
|
|
39962
40124
|
beginCrossfadeTo(idx) {
|
|
39963
40125
|
const to = this.frameCache[idx];
|
|
40126
|
+
this.fadeFromValuePixels = this.displayedValuePixels === null && this.valueCache[idx]
|
|
40127
|
+
? new Uint8ClampedArray(this.valueCache[idx].length)
|
|
40128
|
+
: this.displayedValuePixels;
|
|
39964
40129
|
this.fadeFromPixels = this.displayedPixels === null ? new Uint8ClampedArray(to.length) : this.displayedPixels;
|
|
39965
40130
|
this.fadeToIndex = idx;
|
|
39966
40131
|
this.fadeToDims = this.frameDims[idx];
|
|
@@ -39979,13 +40144,28 @@ var TextureFrameSeriesAnimator;
|
|
|
39979
40144
|
for (let i = 0; i < n; i++) {
|
|
39980
40145
|
blended[i] = from[i] + (to[i] - from[i]) * eased;
|
|
39981
40146
|
}
|
|
39982
|
-
|
|
40147
|
+
let blendedValue = null;
|
|
40148
|
+
const toValue = this.valueCache[this.fadeToIndex];
|
|
40149
|
+
if (toValue) {
|
|
40150
|
+
const fromValue = this.fadeFromValuePixels;
|
|
40151
|
+
blendedValue = new Uint8ClampedArray(toValue.length);
|
|
40152
|
+
if (fromValue && fromValue.length === toValue.length) {
|
|
40153
|
+
for (let i = 0; i < toValue.length; i++) {
|
|
40154
|
+
blendedValue[i] = fromValue[i] + (toValue[i] - fromValue[i]) * eased;
|
|
40155
|
+
}
|
|
40156
|
+
}
|
|
40157
|
+
else {
|
|
40158
|
+
blendedValue.set(toValue);
|
|
40159
|
+
}
|
|
40160
|
+
}
|
|
40161
|
+
this.presentPixels(blended, this.fadeToDims, blendedValue);
|
|
39983
40162
|
if (t >= 1) {
|
|
39984
40163
|
this.fadeStartMs = null;
|
|
39985
40164
|
this.fadeFromPixels = null;
|
|
40165
|
+
this.fadeFromValuePixels = null;
|
|
39986
40166
|
}
|
|
39987
40167
|
}
|
|
39988
|
-
presentPixels(pixels, dims) {
|
|
40168
|
+
presentPixels(pixels, dims, valuePixels) {
|
|
39989
40169
|
this.poolIdx = 1 - this.poolIdx;
|
|
39990
40170
|
const target = this.pool[this.poolIdx];
|
|
39991
40171
|
target.width = dims.width;
|
|
@@ -39995,6 +40175,17 @@ var TextureFrameSeriesAnimator;
|
|
|
39995
40175
|
imgData.data.set(pixels);
|
|
39996
40176
|
ctx.putImageData(imgData, 0, 0);
|
|
39997
40177
|
this.displayedPixels = pixels;
|
|
40178
|
+
if (this.valueCanvas && valuePixels) {
|
|
40179
|
+
this.valueCanvas.width = dims.width;
|
|
40180
|
+
this.valueCanvas.height = dims.height;
|
|
40181
|
+
const valueCtx = this.valueCanvas.getContext("2d");
|
|
40182
|
+
const valueImgData = valueCtx.createImageData(dims.width, dims.height);
|
|
40183
|
+
valueImgData.data.set(valuePixels);
|
|
40184
|
+
valueCtx.putImageData(valueImgData, 0, 0);
|
|
40185
|
+
this.displayedValuePixels = valuePixels;
|
|
40186
|
+
this.valueDims = dims;
|
|
40187
|
+
}
|
|
40188
|
+
this.presentVersion++;
|
|
39998
40189
|
}
|
|
39999
40190
|
}
|
|
40000
40191
|
TextureFrameSeriesAnimator.Animator = Animator;
|
|
@@ -40006,11 +40197,743 @@ var TextureFrameSeriesAnimator;
|
|
|
40006
40197
|
}
|
|
40007
40198
|
})(TextureFrameSeriesAnimator || (TextureFrameSeriesAnimator = {}));
|
|
40008
40199
|
|
|
40200
|
+
/**
|
|
40201
|
+
* Renders a value texture as a displaced, coloured surface.
|
|
40202
|
+
* The source canvas is expected as RGBA: value in R, coverage in A.
|
|
40203
|
+
*/
|
|
40204
|
+
var DisplacedSurfacePrimitive;
|
|
40205
|
+
(function (DisplacedSurfacePrimitive) {
|
|
40206
|
+
// Grid densities a tile can draw at, coarsest first.
|
|
40207
|
+
const TIERS = [32, 64, 128, 192];
|
|
40208
|
+
// Tiles per side the extent is cut into.
|
|
40209
|
+
const TILES_PER_SIDE = 6;
|
|
40210
|
+
// Screen pixels one grid cell should cover. Lower means finer meshes sooner.
|
|
40211
|
+
const TARGET_CELL_PIXELS = 12;
|
|
40212
|
+
// Fraction of its distance to the surface the camera must move before densities are reconsidered.
|
|
40213
|
+
const MOVEMENT_THRESHOLD = 0.12;
|
|
40214
|
+
const RESELECT_INTERVAL_MS = 400;
|
|
40215
|
+
// Skirt depth as a fraction of the displaced range. The sheet is translucent, so an oversized
|
|
40216
|
+
// skirt blends through it and paints a seam down every tile boundary.
|
|
40217
|
+
const SKIRT_FRACTION = 0.015;
|
|
40218
|
+
// Alpha below which a texel counts as no-data and is not drawn at all.
|
|
40219
|
+
const COVERAGE_CUTOFF = 0.35;
|
|
40220
|
+
// With no exaggeration stated, the full value range is drawn as this fraction of the extent's shorter side.
|
|
40221
|
+
const AUTO_RELIEF_FRACTION = 0.02;
|
|
40222
|
+
// Terrain is sampled on this grid and interpolated between samples.
|
|
40223
|
+
// Per-texel sampling would be hundreds of thousands of lookups for a surface whose own data is far coarser than that.
|
|
40224
|
+
const GROUND_SAMPLES_PER_SIDE = 64;
|
|
40225
|
+
// Terrain detail level the samples are taken at.
|
|
40226
|
+
// Most-detailed would fetch every tile under a parcel that can be a hundred kilometres across.
|
|
40227
|
+
const GROUND_SAMPLE_LEVEL = 11;
|
|
40228
|
+
// Height window the packed ground texture covers, wide enough for any terrain on Earth.
|
|
40229
|
+
const GROUND_MIN_METRES = -1000;
|
|
40230
|
+
const GROUND_MAX_METRES = 9000;
|
|
40231
|
+
const DEFAULT_LOW_COLOR = { red: 255, green: 255, blue: 255, alpha: 0.12 };
|
|
40232
|
+
const DEFAULT_HIGH_COLOR = { red: 21, green: 96, blue: 196, alpha: 0.92 };
|
|
40233
|
+
const VERTEX_SHADER_GLSL300 = `
|
|
40234
|
+
in vec3 position3DHigh;
|
|
40235
|
+
in vec3 position3DLow;
|
|
40236
|
+
in vec3 normal;
|
|
40237
|
+
in vec2 st;
|
|
40238
|
+
in float skirt;
|
|
40239
|
+
|
|
40240
|
+
uniform sampler2D u_valueTexture;
|
|
40241
|
+
uniform sampler2D u_groundTexture;
|
|
40242
|
+
uniform float u_valueMin;
|
|
40243
|
+
uniform float u_valueRange;
|
|
40244
|
+
uniform float u_exaggeration;
|
|
40245
|
+
uniform float u_baseHeight;
|
|
40246
|
+
uniform float u_skirtDepth;
|
|
40247
|
+
uniform float u_groundWeight;
|
|
40248
|
+
uniform vec2 u_groundRange;
|
|
40249
|
+
uniform vec2 u_groundSize;
|
|
40250
|
+
|
|
40251
|
+
out vec2 v_st;
|
|
40252
|
+
out float v_value;
|
|
40253
|
+
out float v_coverage;
|
|
40254
|
+
|
|
40255
|
+
float decodeGround(vec2 uv) {
|
|
40256
|
+
// Channels arrive normalised to 0..1, so they go back to bytes before being reassembled.
|
|
40257
|
+
vec3 packed = texture(u_groundTexture, uv).rgb * 255.0;
|
|
40258
|
+
float normalised = dot(packed, vec3(65536.0, 256.0, 1.0)) / 16777215.0;
|
|
40259
|
+
return u_groundRange.x + normalised * (u_groundRange.y - u_groundRange.x);
|
|
40260
|
+
}
|
|
40261
|
+
|
|
40262
|
+
/*
|
|
40263
|
+
* Bilinear by hand, because the height is packed across three channels and hardware filtering would
|
|
40264
|
+
* blend the packing rather than the heights, turning a smooth slope into noise at every texel edge.
|
|
40265
|
+
*/
|
|
40266
|
+
float sampleGround(vec2 uv) {
|
|
40267
|
+
vec2 texel = 1.0 / u_groundSize;
|
|
40268
|
+
vec2 coord = uv * u_groundSize - 0.5;
|
|
40269
|
+
vec2 base = floor(coord);
|
|
40270
|
+
vec2 f = coord - base;
|
|
40271
|
+
vec2 origin = (base + 0.5) * texel;
|
|
40272
|
+
float h00 = decodeGround(origin);
|
|
40273
|
+
float h10 = decodeGround(origin + vec2(texel.x, 0.0));
|
|
40274
|
+
float h01 = decodeGround(origin + vec2(0.0, texel.y));
|
|
40275
|
+
float h11 = decodeGround(origin + texel);
|
|
40276
|
+
return mix(mix(h00, h10, f.x), mix(h01, h11, f.x), f.y);
|
|
40277
|
+
}
|
|
40278
|
+
|
|
40279
|
+
void main() {
|
|
40280
|
+
vec4 texel = texture(u_valueTexture, st);
|
|
40281
|
+
v_st = st;
|
|
40282
|
+
v_value = texel.r;
|
|
40283
|
+
v_coverage = texel.a;
|
|
40284
|
+
|
|
40285
|
+
float metres = u_valueMin + texel.r * u_valueRange;
|
|
40286
|
+
|
|
40287
|
+
// Value zero sits at the polygon's own altitude, plus the terrain underneath when the polygon
|
|
40288
|
+
// is following the ground rather than absolutely positioned.
|
|
40289
|
+
float ground = sampleGround(st) * u_groundWeight;
|
|
40290
|
+
float displacement = u_baseHeight + ground + metres * u_exaggeration * texel.a - skirt * u_skirtDepth;
|
|
40291
|
+
|
|
40292
|
+
// Added to the LOW half of the encoded position: a metre-scale offset added to the high half
|
|
40293
|
+
// would be lost to float32 rounding at an earth radius.
|
|
40294
|
+
vec4 p = czm_translateRelativeToEye(position3DHigh, position3DLow + normal * displacement);
|
|
40295
|
+
gl_Position = czm_modelViewProjectionRelativeToEye * p;
|
|
40296
|
+
}
|
|
40297
|
+
`;
|
|
40298
|
+
const FRAGMENT_SHADER_GLSL300 = `
|
|
40299
|
+
uniform sampler2D u_valueTexture;
|
|
40300
|
+
uniform vec4 u_lowColor;
|
|
40301
|
+
uniform vec4 u_highColor;
|
|
40302
|
+
uniform float u_coverageCutoff;
|
|
40303
|
+
uniform vec2 u_texelSize;
|
|
40304
|
+
uniform float u_valueRange;
|
|
40305
|
+
uniform float u_exaggeration;
|
|
40306
|
+
uniform float u_metresPerTexel;
|
|
40307
|
+
|
|
40308
|
+
in vec2 v_st;
|
|
40309
|
+
in float v_value;
|
|
40310
|
+
in float v_coverage;
|
|
40311
|
+
|
|
40312
|
+
void main() {
|
|
40313
|
+
if (v_coverage < u_coverageCutoff) {
|
|
40314
|
+
discard;
|
|
40315
|
+
}
|
|
40316
|
+
vec4 color = mix(u_lowColor, u_highColor, v_value);
|
|
40317
|
+
|
|
40318
|
+
// Relief shading from the value gradient, so the displacement reads at a distance without
|
|
40319
|
+
// recomputing vertex normals every time the texture changes.
|
|
40320
|
+
float left = texture(u_valueTexture, v_st - vec2(u_texelSize.x, 0.0)).r;
|
|
40321
|
+
float right = texture(u_valueTexture, v_st + vec2(u_texelSize.x, 0.0)).r;
|
|
40322
|
+
float down = texture(u_valueTexture, v_st - vec2(0.0, u_texelSize.y)).r;
|
|
40323
|
+
float up = texture(u_valueTexture, v_st + vec2(0.0, u_texelSize.y)).r;
|
|
40324
|
+
float scale = u_valueRange * u_exaggeration;
|
|
40325
|
+
vec3 n = normalize(vec3((left - right) * scale, (down - up) * scale, 2.0 * u_metresPerTexel));
|
|
40326
|
+
float lambert = clamp(dot(n, normalize(vec3(-0.5, -0.6, 0.62))), 0.0, 1.0);
|
|
40327
|
+
color.rgb *= 0.45 + 0.9 * lambert;
|
|
40328
|
+
|
|
40329
|
+
out_FragColor = vec4(color.rgb, color.a * v_coverage);
|
|
40330
|
+
}
|
|
40331
|
+
`;
|
|
40332
|
+
/*
|
|
40333
|
+
* Rewrites a GLSL 300 es source into the GLSL 100 dialect older Cesium builds author in.
|
|
40334
|
+
*/
|
|
40335
|
+
function toGlsl100(source, isFragment) {
|
|
40336
|
+
let result = source
|
|
40337
|
+
.replace(/\btexture\s*\(/g, "texture2D(")
|
|
40338
|
+
.replace(/^in\s+/gm, isFragment ? "varying " : "attribute ")
|
|
40339
|
+
.replace(/^out\s+/gm, "varying ");
|
|
40340
|
+
if (isFragment) {
|
|
40341
|
+
result = result.replace(/\bout_FragColor\b/g, "gl_FragColor");
|
|
40342
|
+
}
|
|
40343
|
+
return result;
|
|
40344
|
+
}
|
|
40345
|
+
let _glsl300Support = null;
|
|
40346
|
+
/*
|
|
40347
|
+
* Works out which GLSL dialect this host's Cesium authors shaders in.
|
|
40348
|
+
*/
|
|
40349
|
+
function usesGlsl300(context) {
|
|
40350
|
+
if (_glsl300Support !== null) {
|
|
40351
|
+
return _glsl300Support;
|
|
40352
|
+
}
|
|
40353
|
+
let modernizes = false;
|
|
40354
|
+
try {
|
|
40355
|
+
const probe = new ShaderSource({
|
|
40356
|
+
sources: ["varying float v_probe;\nvoid main() { gl_Position = vec4(0.0); }"],
|
|
40357
|
+
includeBuiltIns: false
|
|
40358
|
+
});
|
|
40359
|
+
const combined = probe.createCombinedVertexShader(context);
|
|
40360
|
+
modernizes = combined.indexOf("varying") < 0;
|
|
40361
|
+
}
|
|
40362
|
+
catch (e) {
|
|
40363
|
+
// No probe means no evidence the host rewrites anything, so trust the context alone.
|
|
40364
|
+
modernizes = false;
|
|
40365
|
+
}
|
|
40366
|
+
_glsl300Support = Boolean(context.webgl2) && !modernizes;
|
|
40367
|
+
return _glsl300Support;
|
|
40368
|
+
}
|
|
40369
|
+
/*
|
|
40370
|
+
* Places grid node (fx, fy) on the ellipsoid, stepping the way the raster was sampled.
|
|
40371
|
+
* A raster sampled at lon/lat has to be walked in degrees; one sampled linearly across a projected CRS has to be walked in tangent-plane metres.
|
|
40372
|
+
*/
|
|
40373
|
+
function makePlacer(extent, pixelPlacement) {
|
|
40374
|
+
if (pixelPlacement === "lonlat") {
|
|
40375
|
+
return (fx, fy) => Cartesian3.fromDegrees(Math$1.lerp(extent.West, extent.East, fx), Math$1.lerp(extent.North, extent.South, fy), 0);
|
|
40376
|
+
}
|
|
40377
|
+
const ellipsoid = Ellipsoid.WGS84;
|
|
40378
|
+
const centre = Cartesian3.fromDegrees((extent.West + extent.East) / 2, (extent.South + extent.North) / 2, 0);
|
|
40379
|
+
const frame = Transforms.eastNorthUpToFixedFrame(centre);
|
|
40380
|
+
const toLocal = Matrix4.inverse(frame, new Matrix4());
|
|
40381
|
+
const southWest = Matrix4.multiplyByPoint(toLocal, Cartesian3.fromDegrees(extent.West, extent.South, 0), new Cartesian3());
|
|
40382
|
+
const northEast = Matrix4.multiplyByPoint(toLocal, Cartesian3.fromDegrees(extent.East, extent.North, 0), new Cartesian3());
|
|
40383
|
+
return (fx, fy) => {
|
|
40384
|
+
const local = new Cartesian3(Math$1.lerp(southWest.x, northEast.x, fx), Math$1.lerp(northEast.y, southWest.y, fy), 0);
|
|
40385
|
+
const world = Matrix4.multiplyByPoint(frame, local, new Cartesian3());
|
|
40386
|
+
return ellipsoid.scaleToGeodeticSurface(world, world);
|
|
40387
|
+
};
|
|
40388
|
+
}
|
|
40389
|
+
/*
|
|
40390
|
+
* Builds one tile's grid, optionally plus a duplicated perimeter ring the shader pushes downwards to
|
|
40391
|
+
* cover the crack where a neighbouring tile at a different density interpolates the shared edge.
|
|
40392
|
+
*/
|
|
40393
|
+
function buildGeometry(extent, resolution, patch, pixelPlacement, withSkirt = false) {
|
|
40394
|
+
const cols = resolution;
|
|
40395
|
+
const rows = resolution;
|
|
40396
|
+
const gridCount = (cols + 1) * (rows + 1);
|
|
40397
|
+
const perimeterCount = withSkirt ? 2 * (cols + 1) + 2 * (rows + 1) - 4 : 0;
|
|
40398
|
+
const count = gridCount + perimeterCount;
|
|
40399
|
+
const positions = new Float64Array(count * 3);
|
|
40400
|
+
const normals = new Float32Array(count * 3);
|
|
40401
|
+
const sts = new Float32Array(count * 2);
|
|
40402
|
+
const skirts = new Float32Array(count);
|
|
40403
|
+
const ellipsoid = Ellipsoid.WGS84;
|
|
40404
|
+
const place = makePlacer(extent, pixelPlacement);
|
|
40405
|
+
let v = 0;
|
|
40406
|
+
for (let row = 0; row <= rows; row++) {
|
|
40407
|
+
// Texture row 0 is the north edge, so t runs opposite to northing.
|
|
40408
|
+
const fy = Math$1.lerp(patch.v0, patch.v1, row / rows);
|
|
40409
|
+
for (let col = 0; col <= cols; col++) {
|
|
40410
|
+
const fx = Math$1.lerp(patch.u0, patch.u1, col / cols);
|
|
40411
|
+
const position = place(fx, fy);
|
|
40412
|
+
positions[v * 3] = position.x;
|
|
40413
|
+
positions[v * 3 + 1] = position.y;
|
|
40414
|
+
positions[v * 3 + 2] = position.z;
|
|
40415
|
+
const up = ellipsoid.geodeticSurfaceNormal(position, new Cartesian3());
|
|
40416
|
+
normals[v * 3] = up.x;
|
|
40417
|
+
normals[v * 3 + 1] = up.y;
|
|
40418
|
+
normals[v * 3 + 2] = up.z;
|
|
40419
|
+
sts[v * 2] = fx;
|
|
40420
|
+
sts[v * 2 + 1] = fy;
|
|
40421
|
+
v++;
|
|
40422
|
+
}
|
|
40423
|
+
}
|
|
40424
|
+
const indices = new Uint32Array(cols * rows * 6 + perimeterCount * 6);
|
|
40425
|
+
let i = 0;
|
|
40426
|
+
for (let row = 0; row < rows; row++) {
|
|
40427
|
+
for (let col = 0; col < cols; col++) {
|
|
40428
|
+
const topLeft = row * (cols + 1) + col;
|
|
40429
|
+
const topRight = topLeft + 1;
|
|
40430
|
+
const bottomLeft = topLeft + (cols + 1);
|
|
40431
|
+
const bottomRight = bottomLeft + 1;
|
|
40432
|
+
indices[i++] = topLeft;
|
|
40433
|
+
indices[i++] = bottomLeft;
|
|
40434
|
+
indices[i++] = topRight;
|
|
40435
|
+
indices[i++] = topRight;
|
|
40436
|
+
indices[i++] = bottomLeft;
|
|
40437
|
+
indices[i++] = bottomRight;
|
|
40438
|
+
}
|
|
40439
|
+
}
|
|
40440
|
+
// Perimeter walked as one closed loop, so consecutive entries are always neighbours.
|
|
40441
|
+
const loop = [];
|
|
40442
|
+
for (let col = 0; col <= cols; col++) {
|
|
40443
|
+
loop.push(col);
|
|
40444
|
+
}
|
|
40445
|
+
for (let row = 1; row <= rows; row++) {
|
|
40446
|
+
loop.push(row * (cols + 1) + cols);
|
|
40447
|
+
}
|
|
40448
|
+
for (let col = cols - 1; col >= 0; col--) {
|
|
40449
|
+
loop.push(rows * (cols + 1) + col);
|
|
40450
|
+
}
|
|
40451
|
+
for (let row = rows - 1; row >= 1; row--) {
|
|
40452
|
+
loop.push(row * (cols + 1));
|
|
40453
|
+
}
|
|
40454
|
+
const skirtStart = gridCount;
|
|
40455
|
+
if (withSkirt) {
|
|
40456
|
+
loop.forEach((gridIndex, k) => {
|
|
40457
|
+
const target = skirtStart + k;
|
|
40458
|
+
positions[target * 3] = positions[gridIndex * 3];
|
|
40459
|
+
positions[target * 3 + 1] = positions[gridIndex * 3 + 1];
|
|
40460
|
+
positions[target * 3 + 2] = positions[gridIndex * 3 + 2];
|
|
40461
|
+
normals[target * 3] = normals[gridIndex * 3];
|
|
40462
|
+
normals[target * 3 + 1] = normals[gridIndex * 3 + 1];
|
|
40463
|
+
normals[target * 3 + 2] = normals[gridIndex * 3 + 2];
|
|
40464
|
+
sts[target * 2] = sts[gridIndex * 2];
|
|
40465
|
+
sts[target * 2 + 1] = sts[gridIndex * 2 + 1];
|
|
40466
|
+
skirts[target] = 1;
|
|
40467
|
+
});
|
|
40468
|
+
for (let k = 0; k < loop.length; k++) {
|
|
40469
|
+
const nextK = (k + 1) % loop.length;
|
|
40470
|
+
indices[i++] = loop[k];
|
|
40471
|
+
indices[i++] = skirtStart + k;
|
|
40472
|
+
indices[i++] = loop[nextK];
|
|
40473
|
+
indices[i++] = loop[nextK];
|
|
40474
|
+
indices[i++] = skirtStart + k;
|
|
40475
|
+
indices[i++] = skirtStart + nextK;
|
|
40476
|
+
}
|
|
40477
|
+
}
|
|
40478
|
+
const geometry = new Geometry({
|
|
40479
|
+
attributes: {
|
|
40480
|
+
position: new GeometryAttribute({
|
|
40481
|
+
componentDatatype: ComponentDatatype.DOUBLE,
|
|
40482
|
+
componentsPerAttribute: 3,
|
|
40483
|
+
values: positions
|
|
40484
|
+
}),
|
|
40485
|
+
normal: new GeometryAttribute({
|
|
40486
|
+
componentDatatype: ComponentDatatype.FLOAT,
|
|
40487
|
+
componentsPerAttribute: 3,
|
|
40488
|
+
values: normals
|
|
40489
|
+
}),
|
|
40490
|
+
st: new GeometryAttribute({
|
|
40491
|
+
componentDatatype: ComponentDatatype.FLOAT,
|
|
40492
|
+
componentsPerAttribute: 2,
|
|
40493
|
+
values: sts
|
|
40494
|
+
}),
|
|
40495
|
+
skirt: new GeometryAttribute({
|
|
40496
|
+
componentDatatype: ComponentDatatype.FLOAT,
|
|
40497
|
+
componentsPerAttribute: 1,
|
|
40498
|
+
values: skirts
|
|
40499
|
+
})
|
|
40500
|
+
},
|
|
40501
|
+
indices: indices,
|
|
40502
|
+
primitiveType: PrimitiveType.TRIANGLES,
|
|
40503
|
+
boundingSphere: BoundingSphere.fromVertices(Array.prototype.slice.call(positions))
|
|
40504
|
+
});
|
|
40505
|
+
// Splits position into the high/low pair Cesium's relative-to-eye shaders expect.
|
|
40506
|
+
GeometryPipeline.encodeAttribute(geometry, "position", "position3DHigh", "position3DLow");
|
|
40507
|
+
return geometry;
|
|
40508
|
+
}
|
|
40509
|
+
class Surface {
|
|
40510
|
+
constructor(options, hasCoverage) {
|
|
40511
|
+
var _a, _b, _c, _d;
|
|
40512
|
+
this.show = true;
|
|
40513
|
+
this.valueSource = null;
|
|
40514
|
+
this.lastPresentVersion = -1;
|
|
40515
|
+
this.texture = null;
|
|
40516
|
+
this.textureDirty = true;
|
|
40517
|
+
this.shaderProgram = null;
|
|
40518
|
+
this.renderState = null;
|
|
40519
|
+
this.destroyed = false;
|
|
40520
|
+
this.lastEyePosition = null;
|
|
40521
|
+
this.lastSelectionMs = -Infinity;
|
|
40522
|
+
this.selectionDirty = true;
|
|
40523
|
+
this.groundTexture = null;
|
|
40524
|
+
this.groundPixels = null;
|
|
40525
|
+
this.groundRequested = false;
|
|
40526
|
+
this.followGround = Boolean(options.followGround);
|
|
40527
|
+
this.terrainProvider = options.terrainProvider || null;
|
|
40528
|
+
this.extent = options.extent;
|
|
40529
|
+
this.source = options.source || null;
|
|
40530
|
+
this.valueMin = (_a = options.valueMin) !== null && _a !== void 0 ? _a : 0;
|
|
40531
|
+
this.valueMax = (_b = options.valueMax) !== null && _b !== void 0 ? _b : 1;
|
|
40532
|
+
this.exaggeration = (_c = options.exaggeration) !== null && _c !== void 0 ? _c : autoExaggeration(this.extent, this.valueMin, this.valueMax);
|
|
40533
|
+
this.baseHeight = (_d = options.baseHeight) !== null && _d !== void 0 ? _d : 0;
|
|
40534
|
+
this.pixelPlacement = options.pixelPlacement;
|
|
40535
|
+
this.tileSkirts = Boolean(options.tileSkirts);
|
|
40536
|
+
this.lowColor = toCesiumColor(options.lowColor || DEFAULT_LOW_COLOR);
|
|
40537
|
+
this.highColor = toCesiumColor(options.highColor || DEFAULT_HIGH_COLOR);
|
|
40538
|
+
this.tiles = buildTiles(hasCoverage);
|
|
40539
|
+
}
|
|
40540
|
+
GetFollowsGround() {
|
|
40541
|
+
return this.followGround;
|
|
40542
|
+
}
|
|
40543
|
+
GetPixelPlacement() {
|
|
40544
|
+
return this.pixelPlacement;
|
|
40545
|
+
}
|
|
40546
|
+
GetBaseHeight() {
|
|
40547
|
+
return this.baseHeight;
|
|
40548
|
+
}
|
|
40549
|
+
GetExaggeration() {
|
|
40550
|
+
return this.exaggeration;
|
|
40551
|
+
}
|
|
40552
|
+
GetExtent() {
|
|
40553
|
+
return this.extent;
|
|
40554
|
+
}
|
|
40555
|
+
SetExaggeration(exaggeration) {
|
|
40556
|
+
this.exaggeration = exaggeration;
|
|
40557
|
+
}
|
|
40558
|
+
SetBaseHeight(baseHeight) {
|
|
40559
|
+
this.baseHeight = baseHeight;
|
|
40560
|
+
}
|
|
40561
|
+
/**
|
|
40562
|
+
* Feeds the surface from an animated frame series.
|
|
40563
|
+
* Height and colour advance with the clock without the caller having to push each frame in.
|
|
40564
|
+
*/
|
|
40565
|
+
BindAnimator(source) {
|
|
40566
|
+
this.valueSource = source;
|
|
40567
|
+
}
|
|
40568
|
+
/**
|
|
40569
|
+
* Tells the surface its source canvas has been redrawn and the texture needs re-uploading.
|
|
40570
|
+
*/
|
|
40571
|
+
MarkSourceDirty() {
|
|
40572
|
+
this.textureDirty = true;
|
|
40573
|
+
}
|
|
40574
|
+
update(frameState) {
|
|
40575
|
+
if (!this.show || this.destroyed || this.tiles.length === 0) {
|
|
40576
|
+
return;
|
|
40577
|
+
}
|
|
40578
|
+
const context = frameState.context;
|
|
40579
|
+
if (this.valueSource) {
|
|
40580
|
+
const canvas = this.valueSource.GetValueCanvas();
|
|
40581
|
+
if (!canvas) {
|
|
40582
|
+
// No frame decoded yet, so there is nothing to displace against.
|
|
40583
|
+
return;
|
|
40584
|
+
}
|
|
40585
|
+
const version = this.valueSource.GetPresentVersion();
|
|
40586
|
+
if (canvas !== this.source || version !== this.lastPresentVersion) {
|
|
40587
|
+
this.source = canvas;
|
|
40588
|
+
this.lastPresentVersion = version;
|
|
40589
|
+
this.textureDirty = true;
|
|
40590
|
+
}
|
|
40591
|
+
}
|
|
40592
|
+
if (!this.source || !this.source.width || !this.source.height) {
|
|
40593
|
+
return;
|
|
40594
|
+
}
|
|
40595
|
+
this.syncTexture(context);
|
|
40596
|
+
this.requestGround();
|
|
40597
|
+
this.syncGroundTexture(context);
|
|
40598
|
+
// A tile needs one mesh before it has a bounding sphere to measure against.
|
|
40599
|
+
for (const tile of this.tiles) {
|
|
40600
|
+
if (!tile.centre) {
|
|
40601
|
+
this.ensureMesh(context, tile, TIERS[0]);
|
|
40602
|
+
}
|
|
40603
|
+
}
|
|
40604
|
+
const nowMs = now();
|
|
40605
|
+
if (this.shouldReselect(frameState, nowMs)) {
|
|
40606
|
+
this.selectTiers(frameState);
|
|
40607
|
+
this.lastSelectionMs = nowMs;
|
|
40608
|
+
this.lastEyePosition = Cartesian3.clone(frameState.camera.positionWC, this.lastEyePosition);
|
|
40609
|
+
this.selectionDirty = false;
|
|
40610
|
+
}
|
|
40611
|
+
for (const tile of this.tiles) {
|
|
40612
|
+
const mesh = this.ensureMesh(context, tile, tile.tier);
|
|
40613
|
+
frameState.commandList.push(this.commandFor(context, tile, mesh));
|
|
40614
|
+
}
|
|
40615
|
+
}
|
|
40616
|
+
isDestroyed() {
|
|
40617
|
+
return this.destroyed;
|
|
40618
|
+
}
|
|
40619
|
+
destroy() {
|
|
40620
|
+
this.destroyed = true;
|
|
40621
|
+
for (const tile of this.tiles) {
|
|
40622
|
+
tile.meshes.forEach((mesh) => mesh.vertexArray.destroy());
|
|
40623
|
+
tile.meshes.clear();
|
|
40624
|
+
tile.command = null;
|
|
40625
|
+
}
|
|
40626
|
+
if (this.shaderProgram) {
|
|
40627
|
+
this.shaderProgram.destroy();
|
|
40628
|
+
this.shaderProgram = null;
|
|
40629
|
+
}
|
|
40630
|
+
if (this.texture) {
|
|
40631
|
+
this.texture.destroy();
|
|
40632
|
+
this.texture = null;
|
|
40633
|
+
}
|
|
40634
|
+
if (this.groundTexture) {
|
|
40635
|
+
this.groundTexture.destroy();
|
|
40636
|
+
this.groundTexture = null;
|
|
40637
|
+
}
|
|
40638
|
+
return undefined;
|
|
40639
|
+
}
|
|
40640
|
+
/*
|
|
40641
|
+
* Samples terrain across the extent once and packs it into a texture the vertex shader reads.
|
|
40642
|
+
*
|
|
40643
|
+
* A texture rather than heights baked into the meshes, because the samples arrive
|
|
40644
|
+
* asynchronously and every cached tile mesh would otherwise have to be rebuilt when they land.
|
|
40645
|
+
*/
|
|
40646
|
+
requestGround() {
|
|
40647
|
+
if (this.groundRequested || !this.followGround) {
|
|
40648
|
+
return;
|
|
40649
|
+
}
|
|
40650
|
+
this.groundRequested = true;
|
|
40651
|
+
const sampler = sampleTerrain;
|
|
40652
|
+
if (!this.terrainProvider || typeof sampler !== "function") {
|
|
40653
|
+
return;
|
|
40654
|
+
}
|
|
40655
|
+
const positions = [];
|
|
40656
|
+
for (let row = 0; row < GROUND_SAMPLES_PER_SIDE; row++) {
|
|
40657
|
+
const fy = row / (GROUND_SAMPLES_PER_SIDE - 1);
|
|
40658
|
+
const latitude = Math$1.lerp(this.extent.North, this.extent.South, fy);
|
|
40659
|
+
for (let col = 0; col < GROUND_SAMPLES_PER_SIDE; col++) {
|
|
40660
|
+
const fx = col / (GROUND_SAMPLES_PER_SIDE - 1);
|
|
40661
|
+
const longitude = Math$1.lerp(this.extent.West, this.extent.East, fx);
|
|
40662
|
+
positions.push(Cartographic.fromDegrees(longitude, latitude));
|
|
40663
|
+
}
|
|
40664
|
+
}
|
|
40665
|
+
Promise.resolve(sampler(this.terrainProvider, GROUND_SAMPLE_LEVEL, positions))
|
|
40666
|
+
.then((sampled) => {
|
|
40667
|
+
if (this.destroyed || !sampled) {
|
|
40668
|
+
return;
|
|
40669
|
+
}
|
|
40670
|
+
const pixels = new Uint8Array(GROUND_SAMPLES_PER_SIDE * GROUND_SAMPLES_PER_SIDE * 4);
|
|
40671
|
+
const span = GROUND_MAX_METRES - GROUND_MIN_METRES;
|
|
40672
|
+
for (let i = 0; i < sampled.length; i++) {
|
|
40673
|
+
const height = sampled[i] && isFinite(sampled[i].height) ? sampled[i].height : 0;
|
|
40674
|
+
const clamped = Math.min(GROUND_MAX_METRES, Math.max(GROUND_MIN_METRES, height));
|
|
40675
|
+
const packed = Math.round(((clamped - GROUND_MIN_METRES) / span) * 16777215);
|
|
40676
|
+
pixels[i * 4] = (packed >> 16) & 255;
|
|
40677
|
+
pixels[i * 4 + 1] = (packed >> 8) & 255;
|
|
40678
|
+
pixels[i * 4 + 2] = packed & 255;
|
|
40679
|
+
pixels[i * 4 + 3] = 255;
|
|
40680
|
+
}
|
|
40681
|
+
this.groundPixels = pixels;
|
|
40682
|
+
})
|
|
40683
|
+
.catch(() => {
|
|
40684
|
+
// Terrain that cannot be sampled leaves the surface on the ellipsoid, which is
|
|
40685
|
+
// where a clamped polygon sat before this existed.
|
|
40686
|
+
});
|
|
40687
|
+
}
|
|
40688
|
+
/*
|
|
40689
|
+
* Uploads whatever ground samples exist, and a flat stand-in until they arrive, so the
|
|
40690
|
+
* shader always has a texture bound whether or not this surface follows the ground.
|
|
40691
|
+
*/
|
|
40692
|
+
syncGroundTexture(context) {
|
|
40693
|
+
if (this.groundTexture && (!this.groundPixels || this.groundTexture.width === GROUND_SAMPLES_PER_SIDE)) {
|
|
40694
|
+
return;
|
|
40695
|
+
}
|
|
40696
|
+
if (this.groundTexture) {
|
|
40697
|
+
this.groundTexture.destroy();
|
|
40698
|
+
}
|
|
40699
|
+
const zero = Math.round(((0 - GROUND_MIN_METRES) / (GROUND_MAX_METRES - GROUND_MIN_METRES)) * 16777215);
|
|
40700
|
+
const size = this.groundPixels ? GROUND_SAMPLES_PER_SIDE : 1;
|
|
40701
|
+
const arrayBufferView = this.groundPixels
|
|
40702
|
+
|| new Uint8Array([(zero >> 16) & 255, (zero >> 8) & 255, zero & 255, 255]);
|
|
40703
|
+
this.groundTexture = new Texture({
|
|
40704
|
+
context,
|
|
40705
|
+
pixelFormat: PixelFormat.RGBA,
|
|
40706
|
+
source: { width: size, height: size, arrayBufferView },
|
|
40707
|
+
flipY: false,
|
|
40708
|
+
sampler: new Sampler({
|
|
40709
|
+
wrapS: TextureWrap.CLAMP_TO_EDGE,
|
|
40710
|
+
wrapT: TextureWrap.CLAMP_TO_EDGE,
|
|
40711
|
+
// Nearest, because the shader interpolates decoded heights itself.
|
|
40712
|
+
minificationFilter: TextureMinificationFilter.NEAREST,
|
|
40713
|
+
magnificationFilter: TextureMagnificationFilter.NEAREST
|
|
40714
|
+
})
|
|
40715
|
+
});
|
|
40716
|
+
}
|
|
40717
|
+
/*
|
|
40718
|
+
* Ground distance between neighbouring texels, so the shading normal is built from a real
|
|
40719
|
+
* slope rather than a made up one.
|
|
40720
|
+
*/
|
|
40721
|
+
metresPerTexel() {
|
|
40722
|
+
const southWest = Cartesian3.fromDegrees(this.extent.West, this.extent.South, 0);
|
|
40723
|
+
const southEast = Cartesian3.fromDegrees(this.extent.East, this.extent.South, 0);
|
|
40724
|
+
const width = Cartesian3.distance(southWest, southEast);
|
|
40725
|
+
return Math.max(1, width / Math.max(1, this.source ? this.source.width : 1));
|
|
40726
|
+
}
|
|
40727
|
+
syncTexture(context) {
|
|
40728
|
+
const sizeChanged = this.texture
|
|
40729
|
+
&& (this.texture.width !== this.source.width || this.texture.height !== this.source.height);
|
|
40730
|
+
if (!this.texture || sizeChanged) {
|
|
40731
|
+
if (this.texture) {
|
|
40732
|
+
this.texture.destroy();
|
|
40733
|
+
}
|
|
40734
|
+
this.texture = new Texture({
|
|
40735
|
+
context,
|
|
40736
|
+
source: this.source,
|
|
40737
|
+
// Cesium flips canvas rows on upload by default, which would put the archive's
|
|
40738
|
+
// row 0 (its north edge) at the south edge and mirror the whole surface.
|
|
40739
|
+
flipY: false,
|
|
40740
|
+
sampler: new Sampler({
|
|
40741
|
+
wrapS: TextureWrap.CLAMP_TO_EDGE,
|
|
40742
|
+
wrapT: TextureWrap.CLAMP_TO_EDGE,
|
|
40743
|
+
minificationFilter: TextureMinificationFilter.LINEAR,
|
|
40744
|
+
magnificationFilter: TextureMagnificationFilter.LINEAR
|
|
40745
|
+
})
|
|
40746
|
+
});
|
|
40747
|
+
this.textureDirty = false;
|
|
40748
|
+
return;
|
|
40749
|
+
}
|
|
40750
|
+
if (this.textureDirty) {
|
|
40751
|
+
this.texture.copyFrom({ source: this.source });
|
|
40752
|
+
this.textureDirty = false;
|
|
40753
|
+
}
|
|
40754
|
+
}
|
|
40755
|
+
ensureMesh(context, tile, tier) {
|
|
40756
|
+
const cached = tile.meshes.get(tier);
|
|
40757
|
+
if (cached) {
|
|
40758
|
+
return cached;
|
|
40759
|
+
}
|
|
40760
|
+
const geometry = buildGeometry(this.extent, tier, tile.patch, this.pixelPlacement, this.tileSkirts);
|
|
40761
|
+
const attributeLocations = GeometryPipeline.createAttributeLocations(geometry);
|
|
40762
|
+
const mesh = {
|
|
40763
|
+
attributeLocations,
|
|
40764
|
+
vertexArray: VertexArray.fromGeometry({
|
|
40765
|
+
context,
|
|
40766
|
+
geometry,
|
|
40767
|
+
attributeLocations,
|
|
40768
|
+
bufferUsage: BufferUsage.STATIC_DRAW
|
|
40769
|
+
}),
|
|
40770
|
+
boundingSphere: geometry.boundingSphere
|
|
40771
|
+
};
|
|
40772
|
+
tile.meshes.set(tier, mesh);
|
|
40773
|
+
tile.centre = geometry.boundingSphere.center;
|
|
40774
|
+
tile.radius = geometry.boundingSphere.radius;
|
|
40775
|
+
return mesh;
|
|
40776
|
+
}
|
|
40777
|
+
/*
|
|
40778
|
+
* Picks each tile's density from how much screen it covers.
|
|
40779
|
+
*/
|
|
40780
|
+
selectTiers(frameState) {
|
|
40781
|
+
const camera = frameState.camera;
|
|
40782
|
+
const fovy = camera.frustum && camera.frustum.fovy ? camera.frustum.fovy : Math$1.PI_OVER_THREE;
|
|
40783
|
+
const pixelsPerRadian = frameState.context.drawingBufferHeight / fovy;
|
|
40784
|
+
for (const tile of this.tiles) {
|
|
40785
|
+
if (!tile.centre) {
|
|
40786
|
+
tile.tier = TIERS[0];
|
|
40787
|
+
continue;
|
|
40788
|
+
}
|
|
40789
|
+
const distance = Math.max(1, Cartesian3.distance(camera.positionWC, tile.centre) - tile.radius);
|
|
40790
|
+
const screenPixels = (2 * tile.radius / distance) * pixelsPerRadian;
|
|
40791
|
+
const wanted = screenPixels / TARGET_CELL_PIXELS;
|
|
40792
|
+
let tier = TIERS[0];
|
|
40793
|
+
for (const candidate of TIERS) {
|
|
40794
|
+
if (candidate <= wanted) {
|
|
40795
|
+
tier = candidate;
|
|
40796
|
+
}
|
|
40797
|
+
}
|
|
40798
|
+
tile.tier = tier;
|
|
40799
|
+
}
|
|
40800
|
+
}
|
|
40801
|
+
shouldReselect(frameState, nowMs) {
|
|
40802
|
+
if (this.selectionDirty) {
|
|
40803
|
+
return true;
|
|
40804
|
+
}
|
|
40805
|
+
if (nowMs - this.lastSelectionMs < RESELECT_INTERVAL_MS) {
|
|
40806
|
+
return false;
|
|
40807
|
+
}
|
|
40808
|
+
const eye = frameState.camera.positionWC;
|
|
40809
|
+
if (!this.lastEyePosition) {
|
|
40810
|
+
return true;
|
|
40811
|
+
}
|
|
40812
|
+
const moved = Cartesian3.distance(eye, this.lastEyePosition);
|
|
40813
|
+
const anchor = this.tiles[0].centre;
|
|
40814
|
+
const reference = anchor ? Math.max(1, Cartesian3.distance(eye, anchor)) : 1;
|
|
40815
|
+
return moved / reference > MOVEMENT_THRESHOLD;
|
|
40816
|
+
}
|
|
40817
|
+
commandFor(context, tile, mesh) {
|
|
40818
|
+
if (!this.shaderProgram) {
|
|
40819
|
+
const glsl300 = usesGlsl300(context);
|
|
40820
|
+
this.shaderProgram = ShaderProgram.fromCache({
|
|
40821
|
+
context,
|
|
40822
|
+
vertexShaderSource: glsl300 ? VERTEX_SHADER_GLSL300 : toGlsl100(VERTEX_SHADER_GLSL300, false),
|
|
40823
|
+
fragmentShaderSource: glsl300 ? FRAGMENT_SHADER_GLSL300 : toGlsl100(FRAGMENT_SHADER_GLSL300, true),
|
|
40824
|
+
attributeLocations: mesh.attributeLocations
|
|
40825
|
+
});
|
|
40826
|
+
this.renderState = RenderState.fromCache({
|
|
40827
|
+
depthTest: { enabled: true },
|
|
40828
|
+
// Translucent ramp: blend, and leave the depth buffer to the opaque world.
|
|
40829
|
+
depthMask: false,
|
|
40830
|
+
blending: BlendingState.ALPHA_BLEND,
|
|
40831
|
+
cull: { enabled: false }
|
|
40832
|
+
});
|
|
40833
|
+
}
|
|
40834
|
+
if (!tile.command) {
|
|
40835
|
+
const self = this;
|
|
40836
|
+
tile.command = new DrawCommand({
|
|
40837
|
+
shaderProgram: this.shaderProgram,
|
|
40838
|
+
renderState: this.renderState,
|
|
40839
|
+
pass: Pass.TRANSLUCENT,
|
|
40840
|
+
modelMatrix: Matrix4.IDENTITY,
|
|
40841
|
+
owner: this,
|
|
40842
|
+
uniformMap: {
|
|
40843
|
+
u_valueTexture: () => self.texture,
|
|
40844
|
+
u_groundTexture: () => self.groundTexture,
|
|
40845
|
+
u_groundWeight: () => (self.followGround && self.groundPixels ? 1 : 0),
|
|
40846
|
+
u_groundRange: () => new Cartesian2(GROUND_MIN_METRES, GROUND_MAX_METRES),
|
|
40847
|
+
u_groundSize: () => new Cartesian2(self.groundPixels ? GROUND_SAMPLES_PER_SIDE : 1, self.groundPixels ? GROUND_SAMPLES_PER_SIDE : 1),
|
|
40848
|
+
u_metresPerTexel: () => self.metresPerTexel(),
|
|
40849
|
+
u_valueMin: () => self.valueMin,
|
|
40850
|
+
u_valueRange: () => self.valueMax - self.valueMin,
|
|
40851
|
+
u_exaggeration: () => self.exaggeration,
|
|
40852
|
+
u_baseHeight: () => self.baseHeight,
|
|
40853
|
+
u_skirtDepth: () => self.tileSkirts
|
|
40854
|
+
? Math.max(2, Math.abs(self.valueMax - self.valueMin) * self.exaggeration * SKIRT_FRACTION)
|
|
40855
|
+
: 0,
|
|
40856
|
+
u_coverageCutoff: () => COVERAGE_CUTOFF,
|
|
40857
|
+
u_lowColor: () => self.lowColor,
|
|
40858
|
+
u_highColor: () => self.highColor,
|
|
40859
|
+
u_texelSize: () => new Cartesian2(1 / self.source.width, 1 / self.source.height)
|
|
40860
|
+
}
|
|
40861
|
+
});
|
|
40862
|
+
}
|
|
40863
|
+
// Bounding volume belongs to the mesh, so it follows whichever tier is drawing.
|
|
40864
|
+
tile.command.vertexArray = mesh.vertexArray;
|
|
40865
|
+
tile.command.boundingVolume = mesh.boundingSphere;
|
|
40866
|
+
return tile.command;
|
|
40867
|
+
}
|
|
40868
|
+
}
|
|
40869
|
+
DisplacedSurfacePrimitive.Surface = Surface;
|
|
40870
|
+
/*
|
|
40871
|
+
* Cuts the extent into tiles, dropping any the caller says carry no data.
|
|
40872
|
+
*/
|
|
40873
|
+
function buildTiles(hasCoverage) {
|
|
40874
|
+
const tiles = [];
|
|
40875
|
+
for (let row = 0; row < TILES_PER_SIDE; row++) {
|
|
40876
|
+
for (let col = 0; col < TILES_PER_SIDE; col++) {
|
|
40877
|
+
const patch = {
|
|
40878
|
+
u0: col / TILES_PER_SIDE,
|
|
40879
|
+
u1: (col + 1) / TILES_PER_SIDE,
|
|
40880
|
+
v0: row / TILES_PER_SIDE,
|
|
40881
|
+
v1: (row + 1) / TILES_PER_SIDE
|
|
40882
|
+
};
|
|
40883
|
+
if (hasCoverage && !hasCoverage(patch)) {
|
|
40884
|
+
continue;
|
|
40885
|
+
}
|
|
40886
|
+
tiles.push({
|
|
40887
|
+
patch,
|
|
40888
|
+
meshes: new Map(),
|
|
40889
|
+
tier: TIERS[0],
|
|
40890
|
+
command: null,
|
|
40891
|
+
centre: null,
|
|
40892
|
+
radius: 0
|
|
40893
|
+
});
|
|
40894
|
+
}
|
|
40895
|
+
}
|
|
40896
|
+
return tiles;
|
|
40897
|
+
}
|
|
40898
|
+
/**
|
|
40899
|
+
* Exaggeration that makes the full value range stand AUTO_RELIEF_FRACTION of the extent tall.
|
|
40900
|
+
*
|
|
40901
|
+
* Derived from the whole extent rather than per tile: scaling each tile to its own size would
|
|
40902
|
+
* make neighbours disagree along their shared edge and tear the surface apart.
|
|
40903
|
+
* @param extent
|
|
40904
|
+
* @param valueMin
|
|
40905
|
+
* @param valueMax
|
|
40906
|
+
*/
|
|
40907
|
+
function autoExaggeration(extent, valueMin, valueMax) {
|
|
40908
|
+
const range = Math.abs(valueMax - valueMin);
|
|
40909
|
+
if (!(range > 0)) {
|
|
40910
|
+
return 1;
|
|
40911
|
+
}
|
|
40912
|
+
const southWest = Cartesian3.fromDegrees(extent.West, extent.South, 0);
|
|
40913
|
+
const southEast = Cartesian3.fromDegrees(extent.East, extent.South, 0);
|
|
40914
|
+
const northWest = Cartesian3.fromDegrees(extent.West, extent.North, 0);
|
|
40915
|
+
const shorterSide = Math.min(Cartesian3.distance(southWest, southEast), Cartesian3.distance(southWest, northWest));
|
|
40916
|
+
if (!(shorterSide > 0)) {
|
|
40917
|
+
return 1;
|
|
40918
|
+
}
|
|
40919
|
+
return (shorterSide * AUTO_RELIEF_FRACTION) / range;
|
|
40920
|
+
}
|
|
40921
|
+
DisplacedSurfacePrimitive.autoExaggeration = autoExaggeration;
|
|
40922
|
+
function toCesiumColor(color) {
|
|
40923
|
+
return new Color(color.red / 255, color.green / 255, color.blue / 255, color.alpha);
|
|
40924
|
+
}
|
|
40925
|
+
function now() {
|
|
40926
|
+
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
40927
|
+
}
|
|
40928
|
+
})(DisplacedSurfacePrimitive || (DisplacedSurfacePrimitive = {}));
|
|
40929
|
+
|
|
40009
40930
|
const TEXTURE_FRAME_SERIES_ANIMATOR_KEY = "TextureFrameSeriesAnimator.Animator";
|
|
40931
|
+
const EXTRUSION_ANIMATOR_KEY = "TextureFrameSeriesAnimator.ExtrusionAnimator";
|
|
40932
|
+
const DISPLACED_SURFACE_KEY = "DisplacedSurfacePrimitive.Surface";
|
|
40010
40933
|
var EntityRenderEnginePolygon;
|
|
40011
40934
|
(function (EntityRenderEnginePolygon) {
|
|
40012
40935
|
async function Render(params) {
|
|
40013
|
-
var _a, _b, _c;
|
|
40936
|
+
var _a, _b, _c, _d;
|
|
40014
40937
|
const entity = params.entity;
|
|
40015
40938
|
const style = params.style;
|
|
40016
40939
|
const pRings = Entity$1.GetValue({
|
|
@@ -40043,14 +40966,38 @@ var EntityRenderEnginePolygon;
|
|
|
40043
40966
|
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);
|
|
40044
40967
|
}
|
|
40045
40968
|
}
|
|
40046
|
-
const
|
|
40047
|
-
|
|
40048
|
-
|
|
40049
|
-
|
|
40050
|
-
|
|
40051
|
-
|
|
40052
|
-
|
|
40053
|
-
|
|
40969
|
+
const usesTextureExtrusion = (Boolean(style.useExtrusion) &&
|
|
40970
|
+
style.extrusionType === Style.EPolygonExtrusionType.Texture);
|
|
40971
|
+
let extrusionArchive = null;
|
|
40972
|
+
if (usesTextureExtrusion && !params.offline) {
|
|
40973
|
+
try {
|
|
40974
|
+
const resolved = await resolveTextureValue({
|
|
40975
|
+
api: params.api,
|
|
40976
|
+
textureValue: style.extrusionTexture || style.texture,
|
|
40977
|
+
style,
|
|
40978
|
+
entity,
|
|
40979
|
+
tags: params.tags
|
|
40980
|
+
});
|
|
40981
|
+
extrusionArchive = resolved.frameArchive || null;
|
|
40982
|
+
}
|
|
40983
|
+
catch (e) {
|
|
40984
|
+
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);
|
|
40985
|
+
}
|
|
40986
|
+
}
|
|
40987
|
+
const drawsDisplacedSurface = Boolean(extrusionArchive);
|
|
40988
|
+
// The displaced surface IS the entity's fill, drawn as its own primitive at the value's height.
|
|
40989
|
+
// Leaving the flat polygon filled as well shows a second copy of the parcel on the ground under it.
|
|
40990
|
+
const hasFill = (drawsDisplacedSurface ? true
|
|
40991
|
+
: fillType === Style.EPolygonFillType.Texture
|
|
40992
|
+
? Boolean(textureDataUri || frameArchive || cFillColor.alpha > 0)
|
|
40993
|
+
: cFillColor.alpha > 0);
|
|
40994
|
+
const fillMaterial = drawsDisplacedSurface
|
|
40995
|
+
? Color.WHITE.withAlpha(0)
|
|
40996
|
+
: textureDataUri
|
|
40997
|
+
? new ImageMaterialProperty({ image: textureDataUri, transparent: true })
|
|
40998
|
+
: frameArchive
|
|
40999
|
+
? Color.WHITE.withAlpha(0)
|
|
41000
|
+
: cFillColor;
|
|
40054
41001
|
const lineColorTrace = style.lineColor ? Calculator.TraceGetColor(style.lineColor, entity, params.tags) : { value: null, effective: null };
|
|
40055
41002
|
const bLineColor = lineColorTrace.value;
|
|
40056
41003
|
const cLineColor = bLineColor ? ColorToCColor(bLineColor) : Color.fromCssColorString("rgba(80, 80, 80, 0.8)");
|
|
@@ -40073,19 +41020,21 @@ var EntityRenderEnginePolygon;
|
|
|
40073
41020
|
return null;
|
|
40074
41021
|
}
|
|
40075
41022
|
let heightRef = getHeightRef$3(style);
|
|
40076
|
-
const outerRing = pRings.find(x => x.Facing == Geometry.EPolygonRingType.Boundaries);
|
|
40077
|
-
const points = Geometry.ParsePoints(outerRing === null || outerRing === void 0 ? void 0 : outerRing.LinearRing);
|
|
41023
|
+
const outerRing = pRings.find(x => x.Facing == Geometry$1.EPolygonRingType.Boundaries);
|
|
41024
|
+
const points = Geometry$1.ParsePoints(outerRing === null || outerRing === void 0 ? void 0 : outerRing.LinearRing);
|
|
40078
41025
|
let posses = points.map(x => Cartesian3.fromDegrees(EnsureNumber(x.longitude), EnsureNumber(x.latitude), EnsureNumber(x.altitude)));
|
|
40079
41026
|
Cartes.CloseRing3(posses);
|
|
40080
|
-
const extrusion =
|
|
41027
|
+
const extrusion = drawsDisplacedSurface
|
|
41028
|
+
? { posses, value: undefined, exHeightRef: HeightReference.NONE }
|
|
41029
|
+
: getPolygonExtrusion(entity, params.tags, outerRing, posses, heightRef, style);
|
|
40081
41030
|
posses = extrusion.posses;
|
|
40082
41031
|
posses = CullDuplicateCPosses(posses);
|
|
40083
41032
|
if (posses.length < 4) {
|
|
40084
41033
|
return null;
|
|
40085
41034
|
}
|
|
40086
|
-
const holeRings = pRings.filter(x => x.Facing == Geometry.EPolygonRingType.Hole);
|
|
41035
|
+
const holeRings = pRings.filter(x => x.Facing == Geometry$1.EPolygonRingType.Hole);
|
|
40087
41036
|
const holePosses = holeRings.map((x) => {
|
|
40088
|
-
const points = Geometry.ParsePoints(x.LinearRing);
|
|
41037
|
+
const points = Geometry$1.ParsePoints(x.LinearRing);
|
|
40089
41038
|
let holePosses = points.map(x => Cartesian3.fromDegrees(EnsureNumber(x.longitude), EnsureNumber(x.latitude), EnsureNumber(x.altitude)));
|
|
40090
41039
|
holePosses = CullDuplicateCPosses(holePosses);
|
|
40091
41040
|
Cartes.CloseRing3(holePosses);
|
|
@@ -40126,6 +41075,7 @@ var EntityRenderEnginePolygon;
|
|
|
40126
41075
|
polygon: {
|
|
40127
41076
|
hierarchy: new PolygonHierarchy(posses, holePosses.map(x => new PolygonHierarchy(x))),
|
|
40128
41077
|
material: fillMaterial,
|
|
41078
|
+
fill: !drawsDisplacedSurface,
|
|
40129
41079
|
extrudedHeight: extrusion.value,
|
|
40130
41080
|
extrudedHeightReference: extrusion.exHeightRef,
|
|
40131
41081
|
shadows: ShadowMode.ENABLED,
|
|
@@ -40156,6 +41106,12 @@ var EntityRenderEnginePolygon;
|
|
|
40156
41106
|
if (!CompareCPolygonHierarchies(oldHierarchy, newHierarchy)) {
|
|
40157
41107
|
cEntity.polygon.hierarchy = new ConstantProperty(newHierarchy);
|
|
40158
41108
|
}
|
|
41109
|
+
// Toggling extrusion styling on an already-rendered entity flips this,
|
|
41110
|
+
// so it has to be reconciled rather than set only at creation.
|
|
41111
|
+
const oldFill = GetCValue(params.viewer, cEntity.polygon.fill);
|
|
41112
|
+
if (oldFill !== !drawsDisplacedSurface) {
|
|
41113
|
+
cEntity.polygon.fill = new ConstantProperty(!drawsDisplacedSurface);
|
|
41114
|
+
}
|
|
40159
41115
|
const oldExtrusion = GetCValue(params.viewer, cEntity.polygon.extrudedHeight);
|
|
40160
41116
|
if (oldExtrusion != extrusion.value) {
|
|
40161
41117
|
cEntity.polygon.extrudedHeight = new ConstantProperty(extrusion.value);
|
|
@@ -40209,7 +41165,16 @@ var EntityRenderEnginePolygon;
|
|
|
40209
41165
|
}
|
|
40210
41166
|
// Must run before any material is baked below: the Animator takes over polygon.material, so a bake
|
|
40211
41167
|
// has to know whether it is baking over a live animation or over a disposed one's restored material.
|
|
40212
|
-
const animator = syncTextureFrameArchive(cEntity, frameArchive, params.viewer, style.textureColorMask);
|
|
41168
|
+
const animator = syncTextureFrameArchive(cEntity, drawsDisplacedSurface ? null : frameArchive, params.viewer, style.textureColorMask, style.maskTextureBaseline);
|
|
41169
|
+
syncDisplacedSurface({
|
|
41170
|
+
cEntity,
|
|
41171
|
+
extrusionArchive,
|
|
41172
|
+
viewer: params.viewer,
|
|
41173
|
+
style,
|
|
41174
|
+
entity,
|
|
41175
|
+
heightRef,
|
|
41176
|
+
outerRingPosses: posses
|
|
41177
|
+
});
|
|
40213
41178
|
if (animator) {
|
|
40214
41179
|
CesiumEntityStyler.SetDefaultTextureImage({
|
|
40215
41180
|
entity: cEntity,
|
|
@@ -40269,7 +41234,7 @@ var EntityRenderEnginePolygon;
|
|
|
40269
41234
|
}
|
|
40270
41235
|
}
|
|
40271
41236
|
let borderPosses = posses.map(x => x.clone ? x.clone() : { ...x });
|
|
40272
|
-
let cEntityBorder = (
|
|
41237
|
+
let cEntityBorder = (_d = (_c = params.rendered) === null || _c === void 0 ? void 0 : _c._siblingGraphics) === null || _d === void 0 ? void 0 : _d[0];
|
|
40273
41238
|
cEntity._siblingGraphics = [];
|
|
40274
41239
|
if (!cEntityBorder || ((!cEntityBorder.polyline && units == "px") ||
|
|
40275
41240
|
(!cEntityBorder.corridor && units == "m"))) {
|
|
@@ -40519,12 +41484,13 @@ var EntityRenderEnginePolygon;
|
|
|
40519
41484
|
/**
|
|
40520
41485
|
* Disposes a cEntity's TextureFrameSeriesAnimator.Animator (if any).
|
|
40521
41486
|
*/
|
|
40522
|
-
function DisposeTextureFrameSeriesAnimator(cEntity) {
|
|
41487
|
+
function DisposeTextureFrameSeriesAnimator(cEntity, viewer) {
|
|
40523
41488
|
const existing = cEntity === null || cEntity === void 0 ? void 0 : cEntity[TEXTURE_FRAME_SERIES_ANIMATOR_KEY];
|
|
40524
41489
|
if (existing && !existing.IsDisposed()) {
|
|
40525
41490
|
existing.Dispose();
|
|
40526
41491
|
cEntity[TEXTURE_FRAME_SERIES_ANIMATOR_KEY] = null;
|
|
40527
41492
|
}
|
|
41493
|
+
disposeDisplacedSurface(cEntity, viewer);
|
|
40528
41494
|
}
|
|
40529
41495
|
EntityRenderEnginePolygon.DisposeTextureFrameSeriesAnimator = DisposeTextureFrameSeriesAnimator;
|
|
40530
41496
|
})(EntityRenderEnginePolygon || (EntityRenderEnginePolygon = {}));
|
|
@@ -40638,6 +41604,145 @@ async function getStyle$4(api, entity, styleId) {
|
|
|
40638
41604
|
// Cache of tinted texture data URIs, keyed by resolved url + color mask, so repeated renders of the
|
|
40639
41605
|
// same polygon (or multiple polygons sharing a texture) don't refetch/reprocess the same image.
|
|
40640
41606
|
const _textureCache = new LRUCache(50);
|
|
41607
|
+
/**
|
|
41608
|
+
* Reconciles the displaced surface (and the animator feeding it) against this render's resolved
|
|
41609
|
+
* extrusion texture, creating, reusing or tearing down as the style demands.
|
|
41610
|
+
* @param params
|
|
41611
|
+
*/
|
|
41612
|
+
function syncDisplacedSurface(params) {
|
|
41613
|
+
var _a;
|
|
41614
|
+
const { cEntity, extrusionArchive, viewer, style, entity, heightRef, outerRingPosses } = params;
|
|
41615
|
+
const existingAnimator = cEntity[EXTRUSION_ANIMATOR_KEY];
|
|
41616
|
+
const existingSurface = cEntity[DISPLACED_SURFACE_KEY];
|
|
41617
|
+
// Nothing to draw and nothing left over, which is every polygon that does not use this feature.
|
|
41618
|
+
if (!extrusionArchive && !existingAnimator && !existingSurface) {
|
|
41619
|
+
return;
|
|
41620
|
+
}
|
|
41621
|
+
const reusable = extrusionArchive
|
|
41622
|
+
&& existingAnimator
|
|
41623
|
+
&& !existingAnimator.IsDisposed()
|
|
41624
|
+
&& existingAnimator.GetArchiveUrl() === extrusionArchive.url
|
|
41625
|
+
&& existingSurface
|
|
41626
|
+
&& !existingSurface.isDestroyed();
|
|
41627
|
+
const placement = surfacePlacement(entity, heightRef);
|
|
41628
|
+
// Whether the surface follows terrain is baked into its ground sampling, so a change there has
|
|
41629
|
+
// to rebuild rather than update in place.
|
|
41630
|
+
if (reusable && existingSurface.GetFollowsGround() === placement.followGround) {
|
|
41631
|
+
existingSurface.SetBaseHeight(placement.baseHeight);
|
|
41632
|
+
existingSurface.SetExaggeration((_a = style.extrusionExaggeration) !== null && _a !== void 0 ? _a : DisplacedSurfacePrimitive.autoExaggeration(existingSurface.GetExtent(), extrusionArchive.metadata.ValueMin, extrusionArchive.metadata.ValueMax));
|
|
41633
|
+
return;
|
|
41634
|
+
}
|
|
41635
|
+
disposeDisplacedSurface(cEntity, viewer);
|
|
41636
|
+
if (!extrusionArchive) {
|
|
41637
|
+
return;
|
|
41638
|
+
}
|
|
41639
|
+
const extent = archiveExtent(extrusionArchive.metadata, outerRingPosses);
|
|
41640
|
+
if (!extent) {
|
|
41641
|
+
console.warn("Polygon.Render: extrusion texture has no usable extent, skipping displaced surface.");
|
|
41642
|
+
return;
|
|
41643
|
+
}
|
|
41644
|
+
// Drives the value canvas only: the surface paints the colour, so the polygon's own material is
|
|
41645
|
+
// left transparent rather than animated underneath it.
|
|
41646
|
+
const animator = new TextureFrameSeriesAnimator.Animator({
|
|
41647
|
+
viewer,
|
|
41648
|
+
entity: cEntity,
|
|
41649
|
+
archiveUrl: extrusionArchive.url,
|
|
41650
|
+
frames: extrusionArchive.metadata.Frames,
|
|
41651
|
+
textureColorMask: style.textureColorMask,
|
|
41652
|
+
driveMaterial: false,
|
|
41653
|
+
produceValueCanvas: true,
|
|
41654
|
+
baselineMask: extrusionArchive.metadata.BaselineMask,
|
|
41655
|
+
// Off-type for the same reason as the fill path: the pinned bruce-models predates the field.
|
|
41656
|
+
maskBaseline: style.maskTextureBaseline
|
|
41657
|
+
});
|
|
41658
|
+
cEntity[EXTRUSION_ANIMATOR_KEY] = animator;
|
|
41659
|
+
const mask = style.textureColorMask;
|
|
41660
|
+
const surface = new DisplacedSurfacePrimitive.Surface({
|
|
41661
|
+
extent,
|
|
41662
|
+
// The grid has to be walked the way the raster was sampled, or the surface skews across the extent.
|
|
41663
|
+
pixelPlacement: extrusionArchive.metadata.PixelPlacement,
|
|
41664
|
+
source: null,
|
|
41665
|
+
baseHeight: placement.baseHeight,
|
|
41666
|
+
followGround: placement.followGround,
|
|
41667
|
+
terrainProvider: viewer && viewer.terrainProvider,
|
|
41668
|
+
valueMin: extrusionArchive.metadata.ValueMin,
|
|
41669
|
+
valueMax: extrusionArchive.metadata.ValueMax,
|
|
41670
|
+
exaggeration: style.extrusionExaggeration,
|
|
41671
|
+
lowColor: mask ? Color$1.ColorFromStr(mask.minColor) : null,
|
|
41672
|
+
highColor: mask ? Color$1.ColorFromStr(mask.maxColor) : null
|
|
41673
|
+
});
|
|
41674
|
+
surface.BindAnimator(animator);
|
|
41675
|
+
viewer.scene.primitives.add(surface);
|
|
41676
|
+
cEntity[DISPLACED_SURFACE_KEY] = surface;
|
|
41677
|
+
}
|
|
41678
|
+
/**
|
|
41679
|
+
* Turns the polygon's altitude option into the two things the displaced surface needs:
|
|
41680
|
+
* - how high value zero sits.
|
|
41681
|
+
* - whether that is measured from the terrain or from the ellipsoid.
|
|
41682
|
+
* @param entity
|
|
41683
|
+
* @param heightRef
|
|
41684
|
+
*/
|
|
41685
|
+
function surfacePlacement(entity, heightRef) {
|
|
41686
|
+
const rawAltitude = Entity$1.GetValue({
|
|
41687
|
+
entity,
|
|
41688
|
+
path: ["Bruce", "Location", "altitude"]
|
|
41689
|
+
});
|
|
41690
|
+
const altitude = rawAltitude ? EnsureNumber(rawAltitude) : 0;
|
|
41691
|
+
if (heightRef === HeightReference.CLAMP_TO_GROUND) {
|
|
41692
|
+
return { baseHeight: 0, followGround: true };
|
|
41693
|
+
}
|
|
41694
|
+
if (heightRef === HeightReference.RELATIVE_TO_GROUND) {
|
|
41695
|
+
return { baseHeight: altitude, followGround: true };
|
|
41696
|
+
}
|
|
41697
|
+
return { baseHeight: altitude, followGround: false };
|
|
41698
|
+
}
|
|
41699
|
+
/**
|
|
41700
|
+
* The archive's own extent when it recorded one, otherwise the polygon's own bounds so archives
|
|
41701
|
+
* generated before the extent was written still render somewhere sensible.
|
|
41702
|
+
* @param metadata
|
|
41703
|
+
* @param posses
|
|
41704
|
+
*/
|
|
41705
|
+
function archiveExtent(metadata, posses) {
|
|
41706
|
+
if (metadata.West != null && metadata.East != null && metadata.South != null && metadata.North != null) {
|
|
41707
|
+
return { West: metadata.West, East: metadata.East, South: metadata.South, North: metadata.North };
|
|
41708
|
+
}
|
|
41709
|
+
if (!posses || posses.length === 0) {
|
|
41710
|
+
return null;
|
|
41711
|
+
}
|
|
41712
|
+
const rectangle = Rectangle.fromCartesianArray(posses);
|
|
41713
|
+
return {
|
|
41714
|
+
West: Math$1.toDegrees(rectangle.west),
|
|
41715
|
+
East: Math$1.toDegrees(rectangle.east),
|
|
41716
|
+
South: Math$1.toDegrees(rectangle.south),
|
|
41717
|
+
North: Math$1.toDegrees(rectangle.north)
|
|
41718
|
+
};
|
|
41719
|
+
}
|
|
41720
|
+
/**
|
|
41721
|
+
* Tears down the displaced surface and its feeding animator, if this entity has them.
|
|
41722
|
+
* @param cEntity
|
|
41723
|
+
* @param viewer
|
|
41724
|
+
*/
|
|
41725
|
+
function disposeDisplacedSurface(cEntity, viewer) {
|
|
41726
|
+
const surface = cEntity === null || cEntity === void 0 ? void 0 : cEntity[DISPLACED_SURFACE_KEY];
|
|
41727
|
+
if (surface) {
|
|
41728
|
+
const viewerAlive = viewer && (typeof viewer.isDestroyed !== "function" || !viewer.isDestroyed());
|
|
41729
|
+
if (viewerAlive && viewer.scene && viewer.scene.primitives) {
|
|
41730
|
+
// Removing from the collection destroys the primitive, releasing its meshes and texture.
|
|
41731
|
+
viewer.scene.primitives.remove(surface);
|
|
41732
|
+
}
|
|
41733
|
+
else if (!surface.isDestroyed()) {
|
|
41734
|
+
surface.destroy();
|
|
41735
|
+
}
|
|
41736
|
+
cEntity[DISPLACED_SURFACE_KEY] = null;
|
|
41737
|
+
}
|
|
41738
|
+
const animator = cEntity === null || cEntity === void 0 ? void 0 : cEntity[EXTRUSION_ANIMATOR_KEY];
|
|
41739
|
+
if (animator && !animator.IsDisposed()) {
|
|
41740
|
+
animator.Dispose();
|
|
41741
|
+
}
|
|
41742
|
+
if (cEntity) {
|
|
41743
|
+
cEntity[EXTRUSION_ANIMATOR_KEY] = null;
|
|
41744
|
+
}
|
|
41745
|
+
}
|
|
40641
41746
|
/**
|
|
40642
41747
|
* Reconciles cEntity's TextureFrameSeriesAnimator.Animator (if any) against this render's resolved texture.
|
|
40643
41748
|
* Called once cEntity is fully created/updated and definitely has a `polygon` graphics.
|
|
@@ -40647,7 +41752,7 @@ const _textureCache = new LRUCache(50);
|
|
|
40647
41752
|
* @param viewer
|
|
40648
41753
|
* @param textureColorMask
|
|
40649
41754
|
*/
|
|
40650
|
-
function syncTextureFrameArchive(cEntity, frameArchive, viewer, textureColorMask) {
|
|
41755
|
+
function syncTextureFrameArchive(cEntity, frameArchive, viewer, textureColorMask, maskTextureBaseline) {
|
|
40651
41756
|
const existing = cEntity[TEXTURE_FRAME_SERIES_ANIMATOR_KEY];
|
|
40652
41757
|
if (frameArchive && existing && !existing.IsDisposed() && existing.GetArchiveUrl() === frameArchive.url) {
|
|
40653
41758
|
return existing;
|
|
@@ -40664,7 +41769,9 @@ function syncTextureFrameArchive(cEntity, frameArchive, viewer, textureColorMask
|
|
|
40664
41769
|
entity: cEntity,
|
|
40665
41770
|
archiveUrl: frameArchive.url,
|
|
40666
41771
|
frames: frameArchive.metadata.Frames,
|
|
40667
|
-
textureColorMask
|
|
41772
|
+
textureColorMask,
|
|
41773
|
+
baselineMask: frameArchive.metadata.BaselineMask,
|
|
41774
|
+
maskBaseline: maskTextureBaseline
|
|
40668
41775
|
});
|
|
40669
41776
|
cEntity[TEXTURE_FRAME_SERIES_ANIMATOR_KEY] = animator;
|
|
40670
41777
|
return animator;
|
|
@@ -40728,8 +41835,15 @@ function extractClientFileIdFromTextureValue(raw) {
|
|
|
40728
41835
|
* @param params
|
|
40729
41836
|
*/
|
|
40730
41837
|
async function resolveTexturedFill(params) {
|
|
40731
|
-
|
|
40732
|
-
|
|
41838
|
+
return resolveTextureValue({ ...params, textureValue: params.style.texture });
|
|
41839
|
+
}
|
|
41840
|
+
/**
|
|
41841
|
+
* Resolves either shape a texture setting can take (a calculated file id/URL, or a search condition)
|
|
41842
|
+
* into a tinted data URI or, when the resolved file is a frame archive, the archive itself.
|
|
41843
|
+
* @param params
|
|
41844
|
+
*/
|
|
41845
|
+
async function resolveTextureValue(params) {
|
|
41846
|
+
const { api, style, entity, tags, textureValue } = params;
|
|
40733
41847
|
if (textureValue && !Array.isArray(textureValue)) {
|
|
40734
41848
|
return resolveTextureCondition({ api, condition: textureValue });
|
|
40735
41849
|
}
|
|
@@ -41518,7 +42632,7 @@ var EntityRenderEngine;
|
|
|
41518
42632
|
entity._dispose();
|
|
41519
42633
|
entity._dispose = null;
|
|
41520
42634
|
}
|
|
41521
|
-
EntityRenderEnginePolygon.DisposeTextureFrameSeriesAnimator(entity);
|
|
42635
|
+
EntityRenderEnginePolygon.DisposeTextureFrameSeriesAnimator(entity, viewer);
|
|
41522
42636
|
if (entity._parentEntity && !ignoreParent) {
|
|
41523
42637
|
doRemove({
|
|
41524
42638
|
viewer,
|
|
@@ -42269,7 +43383,7 @@ var StyleUtils;
|
|
|
42269
43383
|
StyleUtils.ApplyTypeStyle = ApplyTypeStyle;
|
|
42270
43384
|
})(StyleUtils || (StyleUtils = {}));
|
|
42271
43385
|
|
|
42272
|
-
const VERSION = "7.1.
|
|
43386
|
+
const VERSION = "7.1.5";
|
|
42273
43387
|
/**
|
|
42274
43388
|
* Updates the environment instance used by bruce-cesium to one specified.
|
|
42275
43389
|
* This can be used to ensure that the instance a parent is referencing is shared between bruce-cesium, bruce-models, and the parent app.
|
|
@@ -42286,5 +43400,5 @@ const getENVIRONMENT = () => {
|
|
|
42286
43400
|
return ENVIRONMENT;
|
|
42287
43401
|
};
|
|
42288
43402
|
|
|
42289
|
-
export { VERSION, setENVIRONMENT, getENVIRONMENT, CesiumAnimatedInOut, CesiumAnimatedProperty, isStyleChanged, isOutlineChanged$1 as isOutlineChanged, StyleEffective, EntityRenderEngine, EntityRenderEngineModel3d, EntityRenderEnginePoint, EntityRenderEnginePolygon, EntityRenderEnginePolyline, MenuItemCreator, MenuItemManager, CesiumParabola, EntityLabel, LiveCursor, SharedGetters, DataSourceStaticKmlManager, DataLabRenderManager, EntitiesIdsRenderManager, EntitiesLoadedRenderManager, EntitiesRenderManager, EntityRenderManager, AssemblyRenderManager, GoogleSearchRenderManager, RelationsRenderManager, RenderManager, TilesetArbRenderManager, TilesetCadRenderManager, TilesetEntitiesRenderManager, TilesetGooglePhotosRenderManager, TilesetOsmRenderManager, TilesetPointcloudRenderManager, TileRenderEngine, createTileset, TilesetRenderEngine, ViewRenderEngine, VisualRegisterCuller, VisualsRegister, XGridsRenderEngine, CesiumEntityStyler, DrawingUtils, EntityUtils, MeasureUtils, StyleUtils, CESIUM_INSPECTOR_KEY, CESIUM_TIMELINE_KEY, CESIUM_TIMELINE_LIVE_KEY, CESIUM_TIMELINE_LIVE_PADDING_KEY, CESIUM_TIMELINE_INTERVAL_KEY, CESIUM_MODEL_SPACE_KEY, DEFAULT_LIVE_PADDING_SECONDS, ViewUtils, CesiumViewMonitor, ViewGroundArea, ViewerEventTracker, ViewerUtils, Draw3dPolygon, Draw3dPolyline, MeasureCreator, Walkthrough, WidgetControlViewBar, WidgetControlViewBarSearch, WidgetLeftPanelTab, WidgetLeftPanelTabBookmarks, WidgetLeftPanelTabDashboard, EMenuItemState, WidgetLeftPanelTabMenuItems, Widget, VIEWER_BOOKMARKS_WIDGET_KEY, WidgetBookmarks, WidgetBranding, WidgetCursorBar, WidgetEmbeddedInfoView, WidgetInfoView, VIEWER_LEFT_PANEL_WIDGET_KEY, VIEWER_LEFT_PANEL_CSS_VAR_LEFT, WidgetLeftPanel, WidgetNavCompass$$1 as WidgetNavCompass, VIEWER_VIEW_BAR_WIDGET_KEY, WidgetViewBar };
|
|
43403
|
+
export { VERSION, setENVIRONMENT, getENVIRONMENT, CesiumAnimatedInOut, DisplacedSurfacePrimitive, CesiumAnimatedProperty, isStyleChanged, isOutlineChanged$1 as isOutlineChanged, StyleEffective, EntityRenderEngine, EntityRenderEngineModel3d, EntityRenderEnginePoint, EntityRenderEnginePolygon, EntityRenderEnginePolyline, MenuItemCreator, MenuItemManager, CesiumParabola, EntityLabel, LiveCursor, SharedGetters, DataSourceStaticKmlManager, DataLabRenderManager, EntitiesIdsRenderManager, EntitiesLoadedRenderManager, EntitiesRenderManager, EntityRenderManager, AssemblyRenderManager, GoogleSearchRenderManager, RelationsRenderManager, RenderManager, TilesetArbRenderManager, TilesetCadRenderManager, TilesetEntitiesRenderManager, TilesetGooglePhotosRenderManager, TilesetOsmRenderManager, TilesetPointcloudRenderManager, TileRenderEngine, createTileset, TilesetRenderEngine, ViewRenderEngine, VisualRegisterCuller, VisualsRegister, XGridsRenderEngine, CesiumEntityStyler, DrawingUtils, EntityUtils, MeasureUtils, StyleUtils, CESIUM_INSPECTOR_KEY, CESIUM_TIMELINE_KEY, CESIUM_TIMELINE_LIVE_KEY, CESIUM_TIMELINE_LIVE_PADDING_KEY, CESIUM_TIMELINE_INTERVAL_KEY, CESIUM_MODEL_SPACE_KEY, DEFAULT_LIVE_PADDING_SECONDS, ViewUtils, CesiumViewMonitor, ViewGroundArea, ViewerEventTracker, ViewerUtils, Draw3dPolygon, Draw3dPolyline, MeasureCreator, Walkthrough, WidgetControlViewBar, WidgetControlViewBarSearch, WidgetLeftPanelTab, WidgetLeftPanelTabBookmarks, WidgetLeftPanelTabDashboard, EMenuItemState, WidgetLeftPanelTabMenuItems, Widget, VIEWER_BOOKMARKS_WIDGET_KEY, WidgetBookmarks, WidgetBranding, WidgetCursorBar, WidgetEmbeddedInfoView, WidgetInfoView, VIEWER_LEFT_PANEL_WIDGET_KEY, VIEWER_LEFT_PANEL_CSS_VAR_LEFT, WidgetLeftPanel, WidgetNavCompass$$1 as WidgetNavCompass, VIEWER_VIEW_BAR_WIDGET_KEY, WidgetViewBar };
|
|
42290
43404
|
//# sourceMappingURL=bruce-cesium.es5.js.map
|