123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047 |
- import ApproximateTerrainHeights from "../Core/ApproximateTerrainHeights.js";
- import BoundingSphere from "../Core/BoundingSphere.js";
- import Cartesian3 from "../Core/Cartesian3.js";
- import Cartographic from "../Core/Cartographic.js";
- import Check from "../Core/Check.js";
- import defaultValue from "../Core/defaultValue.js";
- import defined from "../Core/defined.js";
- import deprecationWarning from "../Core/deprecationWarning.js";
- import destroyObject from "../Core/destroyObject.js";
- import DeveloperError from "../Core/DeveloperError.js";
- import GeometryInstance from "../Core/GeometryInstance.js";
- import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
- import Rectangle from "../Core/Rectangle.js";
- import TerrainExaggeration from "../Core/TerrainExaggeration.js";
- import ClassificationPrimitive from "./ClassificationPrimitive.js";
- import ClassificationType from "./ClassificationType.js";
- import PerInstanceColorAppearance from "./PerInstanceColorAppearance.js";
- import SceneMode from "./SceneMode.js";
- import ShadowVolumeAppearance from "./ShadowVolumeAppearance.js";
- const GroundPrimitiveUniformMap = {
- u_globeMinimumAltitude: function () {
- return 55000.0;
- },
- };
- /**
- * A ground primitive represents geometry draped over terrain or 3D Tiles in the {@link Scene}.
- * <p>
- * A primitive combines geometry instances with an {@link Appearance} that describes the full shading, including
- * {@link Material} and {@link RenderState}. Roughly, the geometry instance defines the structure and placement,
- * and the appearance defines the visual characteristics. Decoupling geometry and appearance allows us to mix
- * and match most of them and add a new geometry or appearance independently of each other.
- * </p>
- * <p>
- * Support for the WEBGL_depth_texture extension is required to use GeometryInstances with different PerInstanceColors
- * or materials besides PerInstanceColorAppearance.
- * </p>
- * <p>
- * Textured GroundPrimitives were designed for notional patterns and are not meant for precisely mapping
- * textures to terrain - for that use case, use {@link SingleTileImageryProvider}.
- * </p>
- * <p>
- * For correct rendering, this feature requires the EXT_frag_depth WebGL extension. For hardware that do not support this extension, there
- * will be rendering artifacts for some viewing angles.
- * </p>
- * <p>
- * Valid geometries are {@link CircleGeometry}, {@link CorridorGeometry}, {@link EllipseGeometry}, {@link PolygonGeometry}, and {@link RectangleGeometry}.
- * </p>
- *
- * @alias GroundPrimitive
- * @constructor
- *
- * @param {object} [options] Object with the following properties:
- * @param {Array|GeometryInstance} [options.geometryInstances] The geometry instances to render.
- * @param {Appearance} [options.appearance] The appearance used to render the primitive. Defaults to a flat PerInstanceColorAppearance when GeometryInstances have a color attribute.
- * @param {boolean} [options.show=true] Determines if this primitive will be shown.
- * @param {boolean} [options.vertexCacheOptimize=false] When <code>true</code>, geometry vertices are optimized for the pre and post-vertex-shader caches.
- * @param {boolean} [options.interleave=false] When <code>true</code>, geometry vertex attributes are interleaved, which can slightly improve rendering performance but increases load time.
- * @param {boolean} [options.compressVertices=true] When <code>true</code>, the geometry vertices are compressed, which will save memory.
- * @param {boolean} [options.releaseGeometryInstances=true] When <code>true</code>, the primitive does not keep a reference to the input <code>geometryInstances</code> to save memory.
- * @param {boolean} [options.allowPicking=true] When <code>true</code>, each geometry instance will only be pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved.
- * @param {boolean} [options.asynchronous=true] Determines if the primitive will be created asynchronously or block until ready. If false initializeTerrainHeights() must be called first.
- * @param {ClassificationType} [options.classificationType=ClassificationType.BOTH] Determines whether terrain, 3D Tiles or both will be classified.
- * @param {boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown.
- * @param {boolean} [options.debugShowShadowVolume=false] For debugging only. Determines if the shadow volume for each geometry in the primitive is drawn. Must be <code>true</code> on
- * creation for the volumes to be created before the geometry is released or options.releaseGeometryInstance must be <code>false</code>.
- *
- * @example
- * // Example 1: Create primitive with a single instance
- * const rectangleInstance = new Cesium.GeometryInstance({
- * geometry : new Cesium.RectangleGeometry({
- * rectangle : Cesium.Rectangle.fromDegrees(-140.0, 30.0, -100.0, 40.0)
- * }),
- * id : 'rectangle',
- * attributes : {
- * color : new Cesium.ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 0.5)
- * }
- * });
- * scene.primitives.add(new Cesium.GroundPrimitive({
- * geometryInstances : rectangleInstance
- * }));
- *
- * // Example 2: Batch instances
- * const color = new Cesium.ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 0.5); // Both instances must have the same color.
- * const rectangleInstance = new Cesium.GeometryInstance({
- * geometry : new Cesium.RectangleGeometry({
- * rectangle : Cesium.Rectangle.fromDegrees(-140.0, 30.0, -100.0, 40.0)
- * }),
- * id : 'rectangle',
- * attributes : {
- * color : color
- * }
- * });
- * const ellipseInstance = new Cesium.GeometryInstance({
- * geometry : new Cesium.EllipseGeometry({
- * center : Cesium.Cartesian3.fromDegrees(-105.0, 40.0),
- * semiMinorAxis : 300000.0,
- * semiMajorAxis : 400000.0
- * }),
- * id : 'ellipse',
- * attributes : {
- * color : color
- * }
- * });
- * scene.primitives.add(new Cesium.GroundPrimitive({
- * geometryInstances : [rectangleInstance, ellipseInstance]
- * }));
- *
- * @see Primitive
- * @see ClassificationPrimitive
- * @see GeometryInstance
- * @see Appearance
- */
- function GroundPrimitive(options) {
- options = defaultValue(options, defaultValue.EMPTY_OBJECT);
- let appearance = options.appearance;
- const geometryInstances = options.geometryInstances;
- if (!defined(appearance) && defined(geometryInstances)) {
- const geometryInstancesArray = Array.isArray(geometryInstances)
- ? geometryInstances
- : [geometryInstances];
- const geometryInstanceCount = geometryInstancesArray.length;
- for (let i = 0; i < geometryInstanceCount; i++) {
- const attributes = geometryInstancesArray[i].attributes;
- if (defined(attributes) && defined(attributes.color)) {
- appearance = new PerInstanceColorAppearance({
- flat: true,
- });
- break;
- }
- }
- }
- /**
- * The {@link Appearance} used to shade this primitive. Each geometry
- * instance is shaded with the same appearance. Some appearances, like
- * {@link PerInstanceColorAppearance} allow giving each instance unique
- * properties.
- *
- * @type Appearance
- *
- * @default undefined
- */
- this.appearance = appearance;
- /**
- * The geometry instances rendered with this primitive. This may
- * be <code>undefined</code> if <code>options.releaseGeometryInstances</code>
- * is <code>true</code> when the primitive is constructed.
- * <p>
- * Changing this property after the primitive is rendered has no effect.
- * </p>
- *
- * @readonly
- * @type {Array|GeometryInstance}
- *
- * @default undefined
- */
- this.geometryInstances = options.geometryInstances;
- /**
- * Determines if the primitive will be shown. This affects all geometry
- * instances in the primitive.
- *
- * @type {boolean}
- *
- * @default true
- */
- this.show = defaultValue(options.show, true);
- /**
- * Determines whether terrain, 3D Tiles or both will be classified.
- *
- * @type {ClassificationType}
- *
- * @default ClassificationType.BOTH
- */
- this.classificationType = defaultValue(
- options.classificationType,
- ClassificationType.BOTH
- );
- /**
- * This property is for debugging only; it is not for production use nor is it optimized.
- * <p>
- * Draws the bounding sphere for each draw command in the primitive.
- * </p>
- *
- * @type {boolean}
- *
- * @default false
- */
- this.debugShowBoundingVolume = defaultValue(
- options.debugShowBoundingVolume,
- false
- );
- /**
- * This property is for debugging only; it is not for production use nor is it optimized.
- * <p>
- * Draws the shadow volume for each geometry in the primitive.
- * </p>
- *
- * @type {boolean}
- *
- * @default false
- */
- this.debugShowShadowVolume = defaultValue(
- options.debugShowShadowVolume,
- false
- );
- this._boundingVolumes = [];
- this._boundingVolumes2D = [];
- this._ready = false;
- const groundPrimitive = this;
- // This is here for backwards compatibility. This promise wrapper can be removed once readyPromise is removed.
- this._readyPromise = new Promise((resolve, reject) => {
- groundPrimitive._completeLoad = () => {
- if (this._ready) {
- return;
- }
- this._ready = true;
- if (this.releaseGeometryInstances) {
- this.geometryInstances = undefined;
- }
- const error = this._error;
- if (!defined(error)) {
- resolve(this);
- } else {
- reject(error);
- }
- };
- });
- this._primitive = undefined;
- this._maxHeight = undefined;
- this._minHeight = undefined;
- this._maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight;
- this._minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight;
- this._boundingSpheresKeys = [];
- this._boundingSpheres = [];
- this._useFragmentCulling = false;
- // Used when inserting in an OrderedPrimitiveCollection
- this._zIndex = undefined;
- const that = this;
- this._classificationPrimitiveOptions = {
- geometryInstances: undefined,
- appearance: undefined,
- vertexCacheOptimize: defaultValue(options.vertexCacheOptimize, false),
- interleave: defaultValue(options.interleave, false),
- releaseGeometryInstances: defaultValue(
- options.releaseGeometryInstances,
- true
- ),
- allowPicking: defaultValue(options.allowPicking, true),
- asynchronous: defaultValue(options.asynchronous, true),
- compressVertices: defaultValue(options.compressVertices, true),
- _createBoundingVolumeFunction: undefined,
- _updateAndQueueCommandsFunction: undefined,
- _pickPrimitive: that,
- _extruded: true,
- _uniformMap: GroundPrimitiveUniformMap,
- };
- }
- Object.defineProperties(GroundPrimitive.prototype, {
- /**
- * When <code>true</code>, geometry vertices are optimized for the pre and post-vertex-shader caches.
- *
- * @memberof GroundPrimitive.prototype
- *
- * @type {boolean}
- * @readonly
- *
- * @default true
- */
- vertexCacheOptimize: {
- get: function () {
- return this._classificationPrimitiveOptions.vertexCacheOptimize;
- },
- },
- /**
- * Determines if geometry vertex attributes are interleaved, which can slightly improve rendering performance.
- *
- * @memberof GroundPrimitive.prototype
- *
- * @type {boolean}
- * @readonly
- *
- * @default false
- */
- interleave: {
- get: function () {
- return this._classificationPrimitiveOptions.interleave;
- },
- },
- /**
- * When <code>true</code>, the primitive does not keep a reference to the input <code>geometryInstances</code> to save memory.
- *
- * @memberof GroundPrimitive.prototype
- *
- * @type {boolean}
- * @readonly
- *
- * @default true
- */
- releaseGeometryInstances: {
- get: function () {
- return this._classificationPrimitiveOptions.releaseGeometryInstances;
- },
- },
- /**
- * When <code>true</code>, each geometry instance will only be pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved.
- *
- * @memberof GroundPrimitive.prototype
- *
- * @type {boolean}
- * @readonly
- *
- * @default true
- */
- allowPicking: {
- get: function () {
- return this._classificationPrimitiveOptions.allowPicking;
- },
- },
- /**
- * Determines if the geometry instances will be created and batched on a web worker.
- *
- * @memberof GroundPrimitive.prototype
- *
- * @type {boolean}
- * @readonly
- *
- * @default true
- */
- asynchronous: {
- get: function () {
- return this._classificationPrimitiveOptions.asynchronous;
- },
- },
- /**
- * When <code>true</code>, geometry vertices are compressed, which will save memory.
- *
- * @memberof GroundPrimitive.prototype
- *
- * @type {boolean}
- * @readonly
- *
- * @default true
- */
- compressVertices: {
- get: function () {
- return this._classificationPrimitiveOptions.compressVertices;
- },
- },
- /**
- * Determines if the primitive is complete and ready to render. If this property is
- * true, the primitive will be rendered the next time that {@link GroundPrimitive#update}
- * is called.
- *
- * @memberof GroundPrimitive.prototype
- *
- * @type {boolean}
- * @readonly
- */
- ready: {
- get: function () {
- return this._ready;
- },
- },
- /**
- * Gets a promise that resolves when the primitive is ready to render.
- * @memberof GroundPrimitive.prototype
- * @type {Promise<GroundPrimitive>}
- * @readonly
- * @deprecated
- */
- readyPromise: {
- get: function () {
- deprecationWarning(
- "GroundPrimitive.readyPromise",
- "GroundPrimitive.readyPromise was deprecated in CesiumJS 1.104. It will be removed in 1.107. Wait for GroundPrimitive.ready to return true instead."
- );
- return this._readyPromise;
- },
- },
- });
- /**
- * Determines if GroundPrimitive rendering is supported.
- *
- * @function
- * @param {Scene} scene The scene.
- * @returns {boolean} <code>true</code> if GroundPrimitives are supported; otherwise, returns <code>false</code>
- */
- GroundPrimitive.isSupported = ClassificationPrimitive.isSupported;
- function getComputeMaximumHeightFunction(primitive) {
- return function (granularity, ellipsoid) {
- const r = ellipsoid.maximumRadius;
- const delta = r / Math.cos(granularity * 0.5) - r;
- return primitive._maxHeight + delta;
- };
- }
- function getComputeMinimumHeightFunction(primitive) {
- return function (granularity, ellipsoid) {
- return primitive._minHeight;
- };
- }
- const scratchBVCartesianHigh = new Cartesian3();
- const scratchBVCartesianLow = new Cartesian3();
- const scratchBVCartesian = new Cartesian3();
- const scratchBVCartographic = new Cartographic();
- const scratchBVRectangle = new Rectangle();
- function getRectangle(frameState, geometry) {
- const ellipsoid = frameState.mapProjection.ellipsoid;
- if (
- !defined(geometry.attributes) ||
- !defined(geometry.attributes.position3DHigh)
- ) {
- if (defined(geometry.rectangle)) {
- return geometry.rectangle;
- }
- return undefined;
- }
- const highPositions = geometry.attributes.position3DHigh.values;
- const lowPositions = geometry.attributes.position3DLow.values;
- const length = highPositions.length;
- let minLat = Number.POSITIVE_INFINITY;
- let minLon = Number.POSITIVE_INFINITY;
- let maxLat = Number.NEGATIVE_INFINITY;
- let maxLon = Number.NEGATIVE_INFINITY;
- for (let i = 0; i < length; i += 3) {
- const highPosition = Cartesian3.unpack(
- highPositions,
- i,
- scratchBVCartesianHigh
- );
- const lowPosition = Cartesian3.unpack(
- lowPositions,
- i,
- scratchBVCartesianLow
- );
- const position = Cartesian3.add(
- highPosition,
- lowPosition,
- scratchBVCartesian
- );
- const cartographic = ellipsoid.cartesianToCartographic(
- position,
- scratchBVCartographic
- );
- const latitude = cartographic.latitude;
- const longitude = cartographic.longitude;
- minLat = Math.min(minLat, latitude);
- minLon = Math.min(minLon, longitude);
- maxLat = Math.max(maxLat, latitude);
- maxLon = Math.max(maxLon, longitude);
- }
- const rectangle = scratchBVRectangle;
- rectangle.north = maxLat;
- rectangle.south = minLat;
- rectangle.east = maxLon;
- rectangle.west = minLon;
- return rectangle;
- }
- function setMinMaxTerrainHeights(primitive, rectangle, ellipsoid) {
- const result = ApproximateTerrainHeights.getMinimumMaximumHeights(
- rectangle,
- ellipsoid
- );
- primitive._minTerrainHeight = result.minimumTerrainHeight;
- primitive._maxTerrainHeight = result.maximumTerrainHeight;
- }
- function createBoundingVolume(groundPrimitive, frameState, geometry) {
- const ellipsoid = frameState.mapProjection.ellipsoid;
- const rectangle = getRectangle(frameState, geometry);
- const obb = OrientedBoundingBox.fromRectangle(
- rectangle,
- groundPrimitive._minHeight,
- groundPrimitive._maxHeight,
- ellipsoid
- );
- groundPrimitive._boundingVolumes.push(obb);
- if (!frameState.scene3DOnly) {
- const projection = frameState.mapProjection;
- const boundingVolume = BoundingSphere.fromRectangleWithHeights2D(
- rectangle,
- projection,
- groundPrimitive._maxHeight,
- groundPrimitive._minHeight
- );
- Cartesian3.fromElements(
- boundingVolume.center.z,
- boundingVolume.center.x,
- boundingVolume.center.y,
- boundingVolume.center
- );
- groundPrimitive._boundingVolumes2D.push(boundingVolume);
- }
- }
- function boundingVolumeIndex(commandIndex, length) {
- return Math.floor((commandIndex % length) / 2);
- }
- function updateAndQueueRenderCommand(
- groundPrimitive,
- command,
- frameState,
- modelMatrix,
- cull,
- boundingVolume,
- debugShowBoundingVolume
- ) {
- // Use derived appearance command for 2D if needed
- const classificationPrimitive = groundPrimitive._primitive;
- if (
- frameState.mode !== SceneMode.SCENE3D &&
- command.shaderProgram === classificationPrimitive._spColor &&
- classificationPrimitive._needs2DShader
- ) {
- command = command.derivedCommands.appearance2D;
- }
- command.owner = groundPrimitive;
- command.modelMatrix = modelMatrix;
- command.boundingVolume = boundingVolume;
- command.cull = cull;
- command.debugShowBoundingVolume = debugShowBoundingVolume;
- frameState.commandList.push(command);
- }
- function updateAndQueuePickCommand(
- groundPrimitive,
- command,
- frameState,
- modelMatrix,
- cull,
- boundingVolume
- ) {
- // Use derived pick command for 2D if needed
- const classificationPrimitive = groundPrimitive._primitive;
- if (
- frameState.mode !== SceneMode.SCENE3D &&
- command.shaderProgram === classificationPrimitive._spPick &&
- classificationPrimitive._needs2DShader
- ) {
- command = command.derivedCommands.pick2D;
- }
- command.owner = groundPrimitive;
- command.modelMatrix = modelMatrix;
- command.boundingVolume = boundingVolume;
- command.cull = cull;
- frameState.commandList.push(command);
- }
- function updateAndQueueCommands(
- groundPrimitive,
- frameState,
- colorCommands,
- pickCommands,
- modelMatrix,
- cull,
- debugShowBoundingVolume,
- twoPasses
- ) {
- let boundingVolumes;
- if (frameState.mode === SceneMode.SCENE3D) {
- boundingVolumes = groundPrimitive._boundingVolumes;
- } else {
- boundingVolumes = groundPrimitive._boundingVolumes2D;
- }
- const classificationType = groundPrimitive.classificationType;
- const queueTerrainCommands =
- classificationType !== ClassificationType.CESIUM_3D_TILE;
- const queue3DTilesCommands =
- classificationType !== ClassificationType.TERRAIN;
- const passes = frameState.passes;
- const classificationPrimitive = groundPrimitive._primitive;
- let i;
- let boundingVolume;
- let command;
- if (passes.render) {
- const colorLength = colorCommands.length;
- for (i = 0; i < colorLength; ++i) {
- boundingVolume = boundingVolumes[boundingVolumeIndex(i, colorLength)];
- if (queueTerrainCommands) {
- command = colorCommands[i];
- updateAndQueueRenderCommand(
- groundPrimitive,
- command,
- frameState,
- modelMatrix,
- cull,
- boundingVolume,
- debugShowBoundingVolume
- );
- }
- if (queue3DTilesCommands) {
- command = colorCommands[i].derivedCommands.tileset;
- updateAndQueueRenderCommand(
- groundPrimitive,
- command,
- frameState,
- modelMatrix,
- cull,
- boundingVolume,
- debugShowBoundingVolume
- );
- }
- }
- if (frameState.invertClassification) {
- const ignoreShowCommands = classificationPrimitive._commandsIgnoreShow;
- const ignoreShowCommandsLength = ignoreShowCommands.length;
- for (i = 0; i < ignoreShowCommandsLength; ++i) {
- boundingVolume = boundingVolumes[i];
- command = ignoreShowCommands[i];
- updateAndQueueRenderCommand(
- groundPrimitive,
- command,
- frameState,
- modelMatrix,
- cull,
- boundingVolume,
- debugShowBoundingVolume
- );
- }
- }
- }
- if (passes.pick) {
- const pickLength = pickCommands.length;
- let pickOffsets;
- if (!groundPrimitive._useFragmentCulling) {
- // Must be using pick offsets
- pickOffsets = classificationPrimitive._primitive._pickOffsets;
- }
- for (i = 0; i < pickLength; ++i) {
- boundingVolume = boundingVolumes[boundingVolumeIndex(i, pickLength)];
- if (!groundPrimitive._useFragmentCulling) {
- const pickOffset = pickOffsets[boundingVolumeIndex(i, pickLength)];
- boundingVolume = boundingVolumes[pickOffset.index];
- }
- if (queueTerrainCommands) {
- command = pickCommands[i];
- updateAndQueuePickCommand(
- groundPrimitive,
- command,
- frameState,
- modelMatrix,
- cull,
- boundingVolume
- );
- }
- if (queue3DTilesCommands) {
- command = pickCommands[i].derivedCommands.tileset;
- updateAndQueuePickCommand(
- groundPrimitive,
- command,
- frameState,
- modelMatrix,
- cull,
- boundingVolume
- );
- }
- }
- }
- }
- /**
- * Initializes the minimum and maximum terrain heights. This only needs to be called if you are creating the
- * GroundPrimitive synchronously.
- *
- * @returns {Promise<void>} A promise that will resolve once the terrain heights have been loaded.
- *
- */
- GroundPrimitive.initializeTerrainHeights = function () {
- return ApproximateTerrainHeights.initialize();
- };
- /**
- * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
- * get the draw commands needed to render this primitive.
- * <p>
- * Do not call this function directly. This is documented just to
- * list the exceptions that may be propagated when the scene is rendered:
- * </p>
- *
- * @exception {DeveloperError} For synchronous GroundPrimitive, you must call GroundPrimitive.initializeTerrainHeights() and wait for the returned promise to resolve.
- * @exception {DeveloperError} All instance geometries must have the same primitiveType.
- * @exception {DeveloperError} Appearance and material have a uniform with the same name.
- */
- GroundPrimitive.prototype.update = function (frameState) {
- if (!defined(this._primitive) && !defined(this.geometryInstances)) {
- return;
- }
- if (!ApproximateTerrainHeights.initialized) {
- //>>includeStart('debug', pragmas.debug);
- if (!this.asynchronous) {
- throw new DeveloperError(
- "For synchronous GroundPrimitives, you must call GroundPrimitive.initializeTerrainHeights() and wait for the returned promise to resolve."
- );
- }
- //>>includeEnd('debug');
- GroundPrimitive.initializeTerrainHeights();
- return;
- }
- const that = this;
- const primitiveOptions = this._classificationPrimitiveOptions;
- if (!defined(this._primitive)) {
- const ellipsoid = frameState.mapProjection.ellipsoid;
- let instance;
- let geometry;
- let instanceType;
- const instances = Array.isArray(this.geometryInstances)
- ? this.geometryInstances
- : [this.geometryInstances];
- const length = instances.length;
- const groundInstances = new Array(length);
- let i;
- let rectangle;
- for (i = 0; i < length; ++i) {
- instance = instances[i];
- geometry = instance.geometry;
- const instanceRectangle = getRectangle(frameState, geometry);
- if (!defined(rectangle)) {
- rectangle = Rectangle.clone(instanceRectangle);
- } else if (defined(instanceRectangle)) {
- Rectangle.union(rectangle, instanceRectangle, rectangle);
- }
- const id = instance.id;
- if (defined(id) && defined(instanceRectangle)) {
- const boundingSphere = ApproximateTerrainHeights.getBoundingSphere(
- instanceRectangle,
- ellipsoid
- );
- this._boundingSpheresKeys.push(id);
- this._boundingSpheres.push(boundingSphere);
- }
- instanceType = geometry.constructor;
- if (!defined(instanceType) || !defined(instanceType.createShadowVolume)) {
- //>>includeStart('debug', pragmas.debug);
- throw new DeveloperError(
- "Not all of the geometry instances have GroundPrimitive support."
- );
- //>>includeEnd('debug');
- }
- }
- // Now compute the min/max heights for the primitive
- setMinMaxTerrainHeights(this, rectangle, ellipsoid);
- const exaggeration = frameState.terrainExaggeration;
- const exaggerationRelativeHeight =
- frameState.terrainExaggerationRelativeHeight;
- this._minHeight = TerrainExaggeration.getHeight(
- this._minTerrainHeight,
- exaggeration,
- exaggerationRelativeHeight
- );
- this._maxHeight = TerrainExaggeration.getHeight(
- this._maxTerrainHeight,
- exaggeration,
- exaggerationRelativeHeight
- );
- const useFragmentCulling = GroundPrimitive._supportsMaterials(
- frameState.context
- );
- this._useFragmentCulling = useFragmentCulling;
- if (useFragmentCulling) {
- // Determine whether to add spherical or planar extent attributes for computing texture coordinates.
- // This depends on the size of the GeometryInstances.
- let attributes;
- let usePlanarExtents = true;
- for (i = 0; i < length; ++i) {
- instance = instances[i];
- geometry = instance.geometry;
- rectangle = getRectangle(frameState, geometry);
- if (ShadowVolumeAppearance.shouldUseSphericalCoordinates(rectangle)) {
- usePlanarExtents = false;
- break;
- }
- }
- for (i = 0; i < length; ++i) {
- instance = instances[i];
- geometry = instance.geometry;
- instanceType = geometry.constructor;
- const boundingRectangle = getRectangle(frameState, geometry);
- const textureCoordinateRotationPoints =
- geometry.textureCoordinateRotationPoints;
- if (usePlanarExtents) {
- attributes = ShadowVolumeAppearance.getPlanarTextureCoordinateAttributes(
- boundingRectangle,
- textureCoordinateRotationPoints,
- ellipsoid,
- frameState.mapProjection,
- this._maxHeight
- );
- } else {
- attributes = ShadowVolumeAppearance.getSphericalExtentGeometryInstanceAttributes(
- boundingRectangle,
- textureCoordinateRotationPoints,
- ellipsoid,
- frameState.mapProjection
- );
- }
- const instanceAttributes = instance.attributes;
- for (const attributeKey in instanceAttributes) {
- if (instanceAttributes.hasOwnProperty(attributeKey)) {
- attributes[attributeKey] = instanceAttributes[attributeKey];
- }
- }
- groundInstances[i] = new GeometryInstance({
- geometry: instanceType.createShadowVolume(
- geometry,
- getComputeMinimumHeightFunction(this),
- getComputeMaximumHeightFunction(this)
- ),
- attributes: attributes,
- id: instance.id,
- });
- }
- } else {
- // ClassificationPrimitive will check if the colors are all the same if it detects lack of fragment culling attributes
- for (i = 0; i < length; ++i) {
- instance = instances[i];
- geometry = instance.geometry;
- instanceType = geometry.constructor;
- groundInstances[i] = new GeometryInstance({
- geometry: instanceType.createShadowVolume(
- geometry,
- getComputeMinimumHeightFunction(this),
- getComputeMaximumHeightFunction(this)
- ),
- attributes: instance.attributes,
- id: instance.id,
- });
- }
- }
- primitiveOptions.geometryInstances = groundInstances;
- primitiveOptions.appearance = this.appearance;
- primitiveOptions._createBoundingVolumeFunction = function (
- frameState,
- geometry
- ) {
- createBoundingVolume(that, frameState, geometry);
- };
- primitiveOptions._updateAndQueueCommandsFunction = function (
- primitive,
- frameState,
- colorCommands,
- pickCommands,
- modelMatrix,
- cull,
- debugShowBoundingVolume,
- twoPasses
- ) {
- updateAndQueueCommands(
- that,
- frameState,
- colorCommands,
- pickCommands,
- modelMatrix,
- cull,
- debugShowBoundingVolume,
- twoPasses
- );
- };
- this._primitive = new ClassificationPrimitive(primitiveOptions);
- }
- this._primitive.appearance = this.appearance;
- this._primitive.show = this.show;
- this._primitive.debugShowShadowVolume = this.debugShowShadowVolume;
- this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume;
- this._primitive.update(frameState);
- frameState.afterRender.push(() => {
- if (!this._ready && defined(this._primitive) && this._primitive.ready) {
- this._completeLoad();
- }
- });
- };
- /**
- * @private
- */
- GroundPrimitive.prototype.getBoundingSphere = function (id) {
- const index = this._boundingSpheresKeys.indexOf(id);
- if (index !== -1) {
- return this._boundingSpheres[index];
- }
- return undefined;
- };
- /**
- * Returns the modifiable per-instance attributes for a {@link GeometryInstance}.
- *
- * @param {*} id The id of the {@link GeometryInstance}.
- * @returns {object} The typed array in the attribute's format or undefined if the is no instance with id.
- *
- * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes.
- *
- * @example
- * const attributes = primitive.getGeometryInstanceAttributes('an id');
- * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA);
- * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true);
- */
- GroundPrimitive.prototype.getGeometryInstanceAttributes = function (id) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(this._primitive)) {
- throw new DeveloperError(
- "must call update before calling getGeometryInstanceAttributes"
- );
- }
- //>>includeEnd('debug');
- return this._primitive.getGeometryInstanceAttributes(id);
- };
- /**
- * Returns true if this object was destroyed; otherwise, false.
- * <p>
- * If this object was destroyed, it should not be used; calling any function other than
- * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
- * </p>
- *
- * @returns {boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
- *
- * @see GroundPrimitive#destroy
- */
- GroundPrimitive.prototype.isDestroyed = function () {
- return false;
- };
- /**
- * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
- * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
- * <p>
- * Once an object is destroyed, it should not be used; calling any function other than
- * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
- * assign the return value (<code>undefined</code>) to the object as done in the example.
- * </p>
- *
- * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
- *
- * @example
- * e = e && e.destroy();
- *
- * @see GroundPrimitive#isDestroyed
- */
- GroundPrimitive.prototype.destroy = function () {
- this._primitive = this._primitive && this._primitive.destroy();
- return destroyObject(this);
- };
- /**
- * Exposed for testing.
- *
- * @param {Context} context Rendering context
- * @returns {boolean} Whether or not the current context supports materials on GroundPrimitives.
- * @private
- */
- GroundPrimitive._supportsMaterials = function (context) {
- return context.depthTexture;
- };
- /**
- * Checks if the given Scene supports materials on GroundPrimitives.
- * Materials on GroundPrimitives require support for the WEBGL_depth_texture extension.
- *
- * @param {Scene} scene The current scene.
- * @returns {boolean} Whether or not the current scene supports materials on GroundPrimitives.
- */
- GroundPrimitive.supportsMaterials = function (scene) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.object("scene", scene);
- //>>includeEnd('debug');
- return GroundPrimitive._supportsMaterials(scene.frameState.context);
- };
- export default GroundPrimitive;
|