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 Request from "../Core/Request.js";
import RequestScheduler from "../Core/RequestScheduler.js";
import RequestState from "../Core/RequestState.js";
import RequestType from "../Core/RequestType.js";
import RuntimeError from "../Core/RuntimeError.js";
import Cesium3DContentGroup from "./Cesium3DContentGroup.js";
import Cesium3DTileContentType from "./Cesium3DTileContentType.js";
import Cesium3DTileContentFactory from "./Cesium3DTileContentFactory.js";
import findContentMetadata from "./findContentMetadata.js";
import findGroupMetadata from "./findGroupMetadata.js";
import preprocess3DTileContent from "./preprocess3DTileContent.js";
/**
* A collection of contents for tiles that have multiple contents, either via the tile JSON (3D Tiles 1.1) or the 3DTILES_multiple_contents
extension.
*
* Implements the {@link Cesium3DTileContent} interface. *
* * @see {@link https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_multiple_contents|3DTILES_multiple_contents extension} * * @alias Multiple3DTileContent * @constructor * * @param {Cesium3DTileset} tileset The tileset this content belongs to * @param {Cesium3DTile} tile The content this content belongs to * @param {Resource} tilesetResource The resource that points to the tileset. This will be used to derive each inner content's resource. * @param {object} contentsJson Either the tile JSON containing the contents array (3D Tiles 1.1), or3DTILES_multiple_contents
extension JSON
*
* @private
* @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
*/
function Multiple3DTileContent(tileset, tile, tilesetResource, contentsJson) {
this._tileset = tileset;
this._tile = tile;
this._tilesetResource = tilesetResource;
this._contents = [];
this._contentsCreated = false;
// An older version of 3DTILES_multiple_contents used "content" instead of "contents"
const contentHeaders = defined(contentsJson.contents)
? contentsJson.contents
: contentsJson.content;
this._innerContentHeaders = contentHeaders;
this._requestsInFlight = 0;
// How many times cancelPendingRequests() has been called. This is
// used to help short-circuit computations after a tile was canceled.
this._cancelCount = 0;
const contentCount = this._innerContentHeaders.length;
this._arrayFetchPromises = new Array(contentCount);
this._requests = new Array(contentCount);
this._ready = false;
this._resolveContent = undefined;
this._readyPromise = new Promise((resolve) => {
this._resolveContent = resolve;
});
this._innerContentResources = new Array(contentCount);
this._serverKeys = new Array(contentCount);
for (let i = 0; i < contentCount; i++) {
const contentResource = tilesetResource.getDerivedResource({
url: contentHeaders[i].uri,
});
const serverKey = RequestScheduler.getServerKey(
contentResource.getUrlComponent()
);
this._innerContentResources[i] = contentResource;
this._serverKeys[i] = serverKey;
}
}
Object.defineProperties(Multiple3DTileContent.prototype, {
/**
* Part of the {@link Cesium3DTileContent} interface. Multiple3DTileContent
checks if any of the inner contents have dirty featurePropertiesDirty.
* @memberof Multiple3DTileContent.prototype
*
* @type {boolean}
*
* @private
*/
featurePropertiesDirty: {
get: function () {
const contents = this._contents;
const length = contents.length;
for (let i = 0; i < length; ++i) {
if (contents[i].featurePropertiesDirty) {
return true;
}
}
return false;
},
set: function (value) {
const contents = this._contents;
const length = contents.length;
for (let i = 0; i < length; ++i) {
contents[i].featurePropertiesDirty = value;
}
},
},
/**
* Part of the {@link Cesium3DTileContent} interface. Multiple3DTileContent
* always returns 0
. Instead call featuresLength
for a specific inner content.
*
* @memberof Multiple3DTileContent.prototype
*
* @type {number}
* @readonly
*
* @private
*/
featuresLength: {
get: function () {
return 0;
},
},
/**
* Part of the {@link Cesium3DTileContent} interface. Multiple3DTileContent
* always returns 0
. Instead, call pointsLength
for a specific inner content.
*
* @memberof Multiple3DTileContent.prototype
*
* @type {number}
* @readonly
*
* @private
*/
pointsLength: {
get: function () {
return 0;
},
},
/**
* Part of the {@link Cesium3DTileContent} interface. Multiple3DTileContent
* always returns 0
. Instead call trianglesLength
for a specific inner content.
*
* @memberof Multiple3DTileContent.prototype
*
* @type {number}
* @readonly
*
* @private
*/
trianglesLength: {
get: function () {
return 0;
},
},
/**
* Part of the {@link Cesium3DTileContent} interface. Multiple3DTileContent
* always returns 0
. Instead call geometryByteLength
for a specific inner content.
*
* @memberof Multiple3DTileContent.prototype
*
* @type {number}
* @readonly
*
* @private
*/
geometryByteLength: {
get: function () {
return 0;
},
},
/**
* Part of the {@link Cesium3DTileContent} interface. Multiple3DTileContent
* always returns 0
. Instead call texturesByteLength
for a specific inner content.
*
* @memberof Multiple3DTileContent.prototype
*
* @type {number}
* @readonly
*
* @private
*/
texturesByteLength: {
get: function () {
return 0;
},
},
/**
* Part of the {@link Cesium3DTileContent} interface. Multiple3DTileContent
* always returns 0
. Instead call batchTableByteLength
for a specific inner content.
*
* @memberof Multiple3DTileContent.prototype
*
* @type {number}
* @readonly
*
* @private
*/
batchTableByteLength: {
get: function () {
return 0;
},
},
innerContents: {
get: function () {
return this._contents;
},
},
/**
* Returns true when the tile's content is ready to render; otherwise false
*
* @memberof Multiple3DTileContent.prototype
*
* @type {boolean}
* @readonly
* @private
*/
ready: {
get: function () {
if (!this._contentsCreated) {
return false;
}
return this._ready;
},
},
/**
* Gets the promise that will be resolved when the tile's content is ready to render.
*
* @memberof Multiple3DTileContent.prototype
*
* @type {PromiseMultiple3DTileContent
does not
* have a single URL, so this returns undefined.
* @memberof Multiple3DTileContent.prototype
*
* @type {string}
* @readonly
* @private
*/
url: {
get: function () {
return undefined;
},
},
/**
* Part of the {@link Cesium3DTileContent} interface. Multiple3DTileContent
* always returns undefined
. Instead call metadata
for a specific inner content.
* @memberof Multiple3DTileContent.prototype
* @private
*/
metadata: {
get: function () {
return undefined;
},
set: function () {
//>>includeStart('debug', pragmas.debug);
throw new DeveloperError("Multiple3DTileContent cannot have metadata");
//>>includeEnd('debug');
},
},
/**
* Part of the {@link Cesium3DTileContent} interface. Multiple3DTileContent
* always returns undefined
. Instead call batchTable
for a specific inner content.
* @memberof Multiple3DTileContent.prototype
* @private
*/
batchTable: {
get: function () {
return undefined;
},
},
/**
* Part of the {@link Cesium3DTileContent} interface. Multiple3DTileContent
* always returns undefined
. Instead call group
for a specific inner content.
* @memberof Multiple3DTileContent.prototype
* @private
*/
group: {
get: function () {
return undefined;
},
set: function () {
//>>includeStart('debug', pragmas.debug);
throw new DeveloperError(
"Multiple3DTileContent cannot have group metadata"
);
//>>includeEnd('debug');
},
},
/**
* Get an array of the inner content URLs, regardless of whether they've
* been fetched or not. This is intended for use with
* {@link Cesium3DTileset#debugShowUrl}.
* @memberof Multiple3DTileContent.prototype
*
* @type {string[]}
* @readonly
* @private
*/
innerContentUrls: {
get: function () {
return this._innerContentHeaders.map(function (contentHeader) {
return contentHeader.uri;
});
},
},
});
function updatePendingRequests(multipleContents, deltaRequestCount) {
multipleContents._requestsInFlight += deltaRequestCount;
multipleContents.tileset.statistics.numberOfPendingRequests += deltaRequestCount;
}
function cancelPendingRequests(multipleContents, originalContentState) {
multipleContents._cancelCount++;
// reset the tile's content state to try again later.
multipleContents._tile._contentState = originalContentState;
const statistics = multipleContents.tileset.statistics;
statistics.numberOfPendingRequests -= multipleContents._requestsInFlight;
statistics.numberOfAttemptedRequests += multipleContents._requestsInFlight;
multipleContents._requestsInFlight = 0;
// Discard the request promises.
const contentCount = multipleContents._innerContentHeaders.length;
multipleContents._arrayFetchPromises = new Array(contentCount);
}
/**
* Request the inner contents of this Multiple3DTileContent
. This must be called once a frame until
* {@link Multiple3DTileContent#contentsFetchedPromise} is defined. This promise
* becomes available as soon as all requests are scheduled.
* * This method also updates the tile statistics' pending request count if the * requests are successfully scheduled. *
* * @return {PromiseMultiple3DTileContent
* always returns false
. Instead call hasProperty
for a specific inner content
* @private
*/
Multiple3DTileContent.prototype.hasProperty = function (batchId, name) {
return false;
};
/**
* Part of the {@link Cesium3DTileContent} interface. Multiple3DTileContent
* always returns undefined
. Instead call getFeature
for a specific inner content
* @private
*/
Multiple3DTileContent.prototype.getFeature = function (batchId) {
return undefined;
};
Multiple3DTileContent.prototype.applyDebugSettings = function (enabled, color) {
const contents = this._contents;
const length = contents.length;
for (let i = 0; i < length; ++i) {
contents[i].applyDebugSettings(enabled, color);
}
};
Multiple3DTileContent.prototype.applyStyle = function (style) {
const contents = this._contents;
const length = contents.length;
for (let i = 0; i < length; ++i) {
contents[i].applyStyle(style);
}
};
Multiple3DTileContent.prototype.update = function (tileset, frameState) {
const contents = this._contents;
const length = contents.length;
let ready = true;
for (let i = 0; i < length; ++i) {
contents[i].update(tileset, frameState);
ready = ready && contents[i].ready;
}
if (!this._ready && ready) {
this._ready = true;
this._resolveContent(this);
}
};
Multiple3DTileContent.prototype.isDestroyed = function () {
return false;
};
Multiple3DTileContent.prototype.destroy = function () {
const contents = this._contents;
const length = contents.length;
for (let i = 0; i < length; ++i) {
contents[i].destroy();
}
return destroyObject(this);
};
export default Multiple3DTileContent;