mirror of https://github.com/CesiumGS/cesium.git
Compare commits
16 Commits
b4ee10c6df
...
bdcd200d47
| Author | SHA1 | Date |
|---|---|---|
|
|
bdcd200d47 | |
|
|
41a0f18ba9 | |
|
|
8bd8ae7f63 | |
|
|
1a8764265f | |
|
|
48bbf6fd4f | |
|
|
59b9f79c4a | |
|
|
6a07d54781 | |
|
|
020749de32 | |
|
|
f4e49d7b64 | |
|
|
e2024b33bb | |
|
|
165d426689 | |
|
|
0daebfe832 | |
|
|
9bd92a616f | |
|
|
df75727e02 | |
|
|
f79f19f6fc | |
|
|
9311afbe3d |
|
|
@ -1,11 +1,12 @@
|
|||
# Change Log
|
||||
|
||||
## 1.136
|
||||
## 1.136 - 2025-12-01
|
||||
|
||||
### @cesium/engine
|
||||
|
||||
#### Fixes :wrench:
|
||||
|
||||
- Fixed depth testing bug with billboards and labels clipping through models [#13012](https://github.com/CesiumGS/cesium/issues/13012)
|
||||
- Billboards using `imageSubRegion` now render as expected. [#12585](https://github.com/CesiumGS/cesium/issues/12585)
|
||||
- Improved scaling of SVGs in billboards [#13020](https://github.com/CesiumGS/cesium/issues/13020)
|
||||
- Fixed unexpected outline artifacts around billboards [#13038](https://github.com/CesiumGS/cesium/issues/13038)
|
||||
|
|
|
|||
|
|
@ -790,7 +790,17 @@ Resource.prototype.appendForwardSlash = function () {
|
|||
* @returns {Promise<ArrayBuffer>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if <code>request.throttle</code> is true and the request does not have high enough priority.
|
||||
*
|
||||
* @example
|
||||
* // load a single URL asynchronously
|
||||
* // load a single URL asynchronously.
|
||||
* // Note that fetchArrayBuffer may return 'undefined', and this will cause
|
||||
* // an error here. There is no way to know when it will return 'undefined'
|
||||
* // or an actual promise. If it returns 'undefined', it is necessary to
|
||||
* // call it again, until it returns the actual promise. But it may not be
|
||||
* // called again after it returned a promise, because then there will be
|
||||
* // multiple promises. Also note that the returned promise may be
|
||||
* // rejected and receive 'undefined' as the error, so it's impossible to
|
||||
* // know WHY it was rejected. So you can either ignore that, or just try
|
||||
* // it again, hoping that it will not be rejected next time.
|
||||
* // If you are reading this: GOOD LUCK!
|
||||
* resource.fetchArrayBuffer().then(function(arrayBuffer) {
|
||||
* // use the data
|
||||
* }).catch(function(error) {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import SDFSettings from "./SDFSettings.js";
|
|||
import TextureAtlas from "../Renderer/TextureAtlas.js";
|
||||
import VerticalOrigin from "./VerticalOrigin.js";
|
||||
import Ellipsoid from "../Core/Ellipsoid.js";
|
||||
import WebGLConstants from "../Core/WebGLConstants.js";
|
||||
|
||||
const SHOW_INDEX = Billboard.SHOW_INDEX;
|
||||
const POSITION_INDEX = Billboard.POSITION_INDEX;
|
||||
|
|
@ -2039,7 +2040,8 @@ BillboardCollection.prototype.update = function (frameState) {
|
|||
) {
|
||||
this._rsOpaque = RenderState.fromCache({
|
||||
depthTest: {
|
||||
enabled: false,
|
||||
enabled: true,
|
||||
func: WebGLConstants.LESS,
|
||||
},
|
||||
depthMask: true,
|
||||
});
|
||||
|
|
@ -2059,7 +2061,10 @@ BillboardCollection.prototype.update = function (frameState) {
|
|||
) {
|
||||
this._rsTranslucent = RenderState.fromCache({
|
||||
depthTest: {
|
||||
enabled: false,
|
||||
enabled: true,
|
||||
func: useTranslucentDepthMask
|
||||
? WebGLConstants.LEQUAL
|
||||
: WebGLConstants.LESS,
|
||||
},
|
||||
depthMask: useTranslucentDepthMask,
|
||||
blending: BlendingState.ALPHA_BLEND,
|
||||
|
|
|
|||
|
|
@ -21,16 +21,12 @@ import RequestState from "../Core/RequestState.js";
|
|||
import RequestType from "../Core/RequestType.js";
|
||||
import Resource from "../Core/Resource.js";
|
||||
import RuntimeError from "../Core/RuntimeError.js";
|
||||
import Cesium3DContentGroup from "./Cesium3DContentGroup.js";
|
||||
import Cesium3DTileContentFactory from "./Cesium3DTileContentFactory.js";
|
||||
import Cesium3DTileContentState from "./Cesium3DTileContentState.js";
|
||||
import Cesium3DTileContentType from "./Cesium3DTileContentType.js";
|
||||
import Cesium3DTileOptimizationHint from "./Cesium3DTileOptimizationHint.js";
|
||||
import Cesium3DTilePass from "./Cesium3DTilePass.js";
|
||||
import Cesium3DTileRefine from "./Cesium3DTileRefine.js";
|
||||
import Empty3DTileContent from "./Empty3DTileContent.js";
|
||||
import findContentMetadata from "./findContentMetadata.js";
|
||||
import findGroupMetadata from "./findGroupMetadata.js";
|
||||
import findTileMetadata from "./findTileMetadata.js";
|
||||
import hasExtension from "./hasExtension.js";
|
||||
import Multiple3DTileContent from "./Multiple3DTileContent.js";
|
||||
|
|
@ -43,6 +39,8 @@ import TileBoundingSphere from "./TileBoundingSphere.js";
|
|||
import TileOrientedBoundingBox from "./TileOrientedBoundingBox.js";
|
||||
import Pass from "../Renderer/Pass.js";
|
||||
import VerticalExaggeration from "../Core/VerticalExaggeration.js";
|
||||
import finishContent from "./finishContent.js";
|
||||
import Dynamic3DTileContent from "./Dynamic3DTileContent.js";
|
||||
|
||||
/**
|
||||
* A tile in a {@link Cesium3DTileset}. When a tile is first created, its content is not loaded;
|
||||
|
|
@ -62,19 +60,6 @@ function Cesium3DTile(tileset, baseResource, header, parent) {
|
|||
this._tileset = tileset;
|
||||
this._header = header;
|
||||
|
||||
const hasContentsArray = defined(header.contents);
|
||||
const hasMultipleContents =
|
||||
(hasContentsArray && header.contents.length > 1) ||
|
||||
hasExtension(header, "3DTILES_multiple_contents");
|
||||
|
||||
// In the 1.0 schema, content is stored in tile.content instead of tile.contents
|
||||
const contentHeader =
|
||||
hasContentsArray && !hasMultipleContents
|
||||
? header.contents[0]
|
||||
: header.content;
|
||||
|
||||
this._contentHeader = contentHeader;
|
||||
|
||||
/**
|
||||
* The local transform of this tile.
|
||||
* @type {Matrix4}
|
||||
|
|
@ -131,22 +116,6 @@ function Cesium3DTile(tileset, baseResource, header, parent) {
|
|||
);
|
||||
this._boundingVolume2D = undefined;
|
||||
|
||||
let contentBoundingVolume;
|
||||
|
||||
if (defined(contentHeader) && defined(contentHeader.boundingVolume)) {
|
||||
// Non-leaf tiles may have a content bounding-volume, which is a tight-fit bounding volume
|
||||
// around only the features in the tile. This box is useful for culling for rendering,
|
||||
// but not for culling for traversing the tree since it does not guarantee spatial coherence, i.e.,
|
||||
// since it only bounds features in the tile, not the entire tile, children may be
|
||||
// outside of this box.
|
||||
contentBoundingVolume = this.createBoundingVolume(
|
||||
contentHeader.boundingVolume,
|
||||
computedTransform,
|
||||
);
|
||||
}
|
||||
this._contentBoundingVolume = contentBoundingVolume;
|
||||
this._contentBoundingVolume2D = undefined;
|
||||
|
||||
let viewerRequestVolume;
|
||||
if (defined(header.viewerRequestVolume)) {
|
||||
viewerRequestVolume = this.createBoundingVolume(
|
||||
|
|
@ -178,27 +147,6 @@ function Cesium3DTile(tileset, baseResource, header, parent) {
|
|||
|
||||
this.updateGeometricErrorScale();
|
||||
|
||||
let refine;
|
||||
if (defined(header.refine)) {
|
||||
if (header.refine === "replace" || header.refine === "add") {
|
||||
Cesium3DTile._deprecationWarning(
|
||||
"lowercase-refine",
|
||||
`This tile uses a lowercase refine "${
|
||||
header.refine
|
||||
}". Instead use "${header.refine.toUpperCase()}".`,
|
||||
);
|
||||
}
|
||||
refine =
|
||||
header.refine.toUpperCase() === "REPLACE"
|
||||
? Cesium3DTileRefine.REPLACE
|
||||
: Cesium3DTileRefine.ADD;
|
||||
} else if (defined(parent)) {
|
||||
// Inherit from parent tile if omitted.
|
||||
refine = parent.refine;
|
||||
} else {
|
||||
refine = Cesium3DTileRefine.REPLACE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the type of refinement that is used when traversing this tile for rendering.
|
||||
*
|
||||
|
|
@ -206,7 +154,7 @@ function Cesium3DTile(tileset, baseResource, header, parent) {
|
|||
* @readonly
|
||||
* @private
|
||||
*/
|
||||
this.refine = refine;
|
||||
this.refine = determineRefine(header.refine, parent);
|
||||
|
||||
/**
|
||||
* Gets the tile's children.
|
||||
|
|
@ -229,58 +177,6 @@ function Cesium3DTile(tileset, baseResource, header, parent) {
|
|||
*/
|
||||
this.parent = parent;
|
||||
|
||||
let content;
|
||||
let hasEmptyContent = false;
|
||||
let contentState;
|
||||
let contentResource;
|
||||
let serverKey;
|
||||
|
||||
baseResource = Resource.createIfNeeded(baseResource);
|
||||
|
||||
if (hasMultipleContents) {
|
||||
contentState = Cesium3DTileContentState.UNLOADED;
|
||||
// Each content may have its own URI, but they all need to be resolved
|
||||
// relative to the tileset, so the base resource is used.
|
||||
contentResource = baseResource.clone();
|
||||
} else if (defined(contentHeader)) {
|
||||
let contentHeaderUri = contentHeader.uri;
|
||||
if (defined(contentHeader.url)) {
|
||||
Cesium3DTile._deprecationWarning(
|
||||
"contentUrl",
|
||||
'This tileset JSON uses the "content.url" property which has been deprecated. Use "content.uri" instead.',
|
||||
);
|
||||
contentHeaderUri = contentHeader.url;
|
||||
}
|
||||
if (contentHeaderUri === "") {
|
||||
Cesium3DTile._deprecationWarning(
|
||||
"contentUriEmpty",
|
||||
"content.uri property is an empty string, which creates a circular dependency, making this tileset invalid. Omit the content property instead",
|
||||
);
|
||||
content = new Empty3DTileContent(tileset, this);
|
||||
hasEmptyContent = true;
|
||||
contentState = Cesium3DTileContentState.READY;
|
||||
} else {
|
||||
contentState = Cesium3DTileContentState.UNLOADED;
|
||||
contentResource = baseResource.getDerivedResource({
|
||||
url: contentHeaderUri,
|
||||
});
|
||||
serverKey = RequestScheduler.getServerKey(
|
||||
contentResource.getUrlComponent(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
content = new Empty3DTileContent(tileset, this);
|
||||
hasEmptyContent = true;
|
||||
contentState = Cesium3DTileContentState.READY;
|
||||
}
|
||||
|
||||
this._content = content;
|
||||
this._contentResource = contentResource;
|
||||
this._contentState = contentState;
|
||||
this._expiredContent = undefined;
|
||||
|
||||
this._serverKey = serverKey;
|
||||
|
||||
/**
|
||||
* When <code>true</code>, the tile has no content.
|
||||
*
|
||||
|
|
@ -289,7 +185,7 @@ function Cesium3DTile(tileset, baseResource, header, parent) {
|
|||
*
|
||||
* @private
|
||||
*/
|
||||
this.hasEmptyContent = hasEmptyContent;
|
||||
this.hasEmptyContent = false;
|
||||
|
||||
/**
|
||||
* When <code>true</code>, the tile's content points to an external tileset.
|
||||
|
|
@ -334,7 +230,7 @@ function Cesium3DTile(tileset, baseResource, header, parent) {
|
|||
*
|
||||
* @private
|
||||
*/
|
||||
this.hasRenderableContent = !hasEmptyContent;
|
||||
this.hasRenderableContent = false;
|
||||
|
||||
/**
|
||||
* When <code>true</code>, the tile contains content metadata from implicit tiling. This flag is set
|
||||
|
|
@ -362,7 +258,17 @@ function Cesium3DTile(tileset, baseResource, header, parent) {
|
|||
*
|
||||
* @private
|
||||
*/
|
||||
this.hasMultipleContents = hasMultipleContents;
|
||||
this.hasMultipleContents = false;
|
||||
|
||||
// Initialize the content-related properties
|
||||
this._contentBoundingVolume = undefined;
|
||||
this._contentBoundingVolume2D = undefined;
|
||||
this._content = undefined;
|
||||
this._contentResource = undefined;
|
||||
this._contentState = undefined;
|
||||
this._expiredContent = undefined;
|
||||
this._serverKey = undefined;
|
||||
initializeContent(this, baseResource, header);
|
||||
|
||||
/**
|
||||
* The node in the tileset's LRU cache, used to determine when to unload a tile's content.
|
||||
|
|
@ -483,7 +389,7 @@ function Cesium3DTile(tileset, baseResource, header, parent) {
|
|||
*/
|
||||
this.implicitSubtree = undefined;
|
||||
|
||||
// Members that are updated every frame for tree traversal and rendering optimizations:
|
||||
// Members that are updated every frame for tree traversal and rendering optimizations.
|
||||
this._distanceToCamera = 0.0;
|
||||
this._centerZDepth = 0.0;
|
||||
this._screenSpaceError = 0.0;
|
||||
|
|
@ -535,6 +441,161 @@ function Cesium3DTile(tileset, baseResource, header, parent) {
|
|||
this._request = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the content-related properties of the given tile.
|
||||
*
|
||||
* This assumes that the <code>_tileset</code> and the
|
||||
* <code>computedTransform</code> of the given tile have
|
||||
* already been set.
|
||||
*
|
||||
* It will initialize the following properties of the tile, based
|
||||
* on the given resource and header information:
|
||||
*
|
||||
* - _contentHeader
|
||||
* - _contentBoundingVolume
|
||||
* - _contentBoundingVolume2D
|
||||
* - _content
|
||||
* - _contentResource
|
||||
* - _contentState
|
||||
* - _expiredContent
|
||||
* - _serverKey
|
||||
* - hasEmptyContent
|
||||
* - hasRenderableContent
|
||||
* - hasMultipleContents
|
||||
*
|
||||
* The exact meaning of these properties has to be derived from
|
||||
* the code. This function was just introduced as first cleanup.
|
||||
*
|
||||
* @param {Cesium3DTile} tile The tile
|
||||
* @param {Resource} baseResource The base resource for the tileset
|
||||
* @param {object} header The JSON header for the tile
|
||||
*/
|
||||
function initializeContent(tile, baseResource, header) {
|
||||
const hasContentsArray = defined(header.contents);
|
||||
const hasMultipleContents =
|
||||
(hasContentsArray && header.contents.length > 1) ||
|
||||
hasExtension(header, "3DTILES_multiple_contents");
|
||||
|
||||
// In the 1.0 schema, content is stored in tile.content instead of tile.contents
|
||||
const contentHeader =
|
||||
hasContentsArray && !hasMultipleContents
|
||||
? header.contents[0]
|
||||
: header.content;
|
||||
|
||||
let contentBoundingVolume;
|
||||
|
||||
if (defined(contentHeader) && defined(contentHeader.boundingVolume)) {
|
||||
// Non-leaf tiles may have a content bounding-volume, which is a tight-fit bounding volume
|
||||
// around only the features in the tile. This box is useful for culling for rendering,
|
||||
// but not for culling for traversing the tree since it does not guarantee spatial coherence, i.e.,
|
||||
// since it only bounds features in the tile, not the entire tile, children may be
|
||||
// outside of this box.
|
||||
contentBoundingVolume = tile.createBoundingVolume(
|
||||
contentHeader.boundingVolume,
|
||||
tile.computedTransform,
|
||||
);
|
||||
}
|
||||
|
||||
let content;
|
||||
let contentState;
|
||||
let contentResource;
|
||||
let serverKey;
|
||||
let hasEmptyContent = false;
|
||||
|
||||
baseResource = Resource.createIfNeeded(baseResource);
|
||||
|
||||
if (hasMultipleContents) {
|
||||
contentState = Cesium3DTileContentState.UNLOADED;
|
||||
// Each content may have its own URI, but they all need to be resolved
|
||||
// relative to the tileset, so the base resource is used.
|
||||
contentResource = baseResource.clone();
|
||||
} else if (defined(contentHeader)) {
|
||||
let contentHeaderUri = contentHeader.uri;
|
||||
if (defined(contentHeader.url)) {
|
||||
Cesium3DTile._deprecationWarning(
|
||||
"contentUrl",
|
||||
'This tileset JSON uses the "content.url" property which has been deprecated. Use "content.uri" instead.',
|
||||
);
|
||||
contentHeaderUri = contentHeader.url;
|
||||
}
|
||||
if (contentHeaderUri === "") {
|
||||
Cesium3DTile._deprecationWarning(
|
||||
"contentUriEmpty",
|
||||
"content.uri property is an empty string, which creates a circular dependency, making this tileset invalid. Omit the content property instead",
|
||||
);
|
||||
content = new Empty3DTileContent(tile._tileset, tile);
|
||||
hasEmptyContent = true;
|
||||
contentState = Cesium3DTileContentState.READY;
|
||||
} else {
|
||||
contentState = Cesium3DTileContentState.UNLOADED;
|
||||
contentResource = baseResource.getDerivedResource({
|
||||
url: contentHeaderUri,
|
||||
});
|
||||
serverKey = RequestScheduler.getServerKey(
|
||||
contentResource.getUrlComponent(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
content = new Empty3DTileContent(tile._tileset, tile);
|
||||
hasEmptyContent = true;
|
||||
contentState = Cesium3DTileContentState.READY;
|
||||
}
|
||||
|
||||
tile._contentHeader = contentHeader;
|
||||
tile._contentBoundingVolume = contentBoundingVolume;
|
||||
tile._contentBoundingVolume2D = undefined;
|
||||
tile._content = content;
|
||||
tile._contentResource = contentResource;
|
||||
tile._contentState = contentState;
|
||||
tile._expiredContent = undefined;
|
||||
tile._serverKey = serverKey;
|
||||
tile.hasEmptyContent = hasEmptyContent;
|
||||
tile.hasRenderableContent = !hasEmptyContent;
|
||||
tile.hasMultipleContents = hasMultipleContents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value for the 'refine' property of a tile.
|
||||
*
|
||||
* If the given value from the header is one of the known, deprecated
|
||||
* lowercase values ("add" or "remove"), then a deprecation warning
|
||||
* will be printed, and the corresponding constant will be returned.
|
||||
*
|
||||
* If the value is <code>undefined</code>, and the parent is not
|
||||
* <code>undefined</code>, then the value from the parent will
|
||||
* be inherited and returned.
|
||||
*
|
||||
* Otherwise, <code>REPLACE</code> is returned as the default.
|
||||
*
|
||||
* @param {string|undefined} headerRefine The refine value from the JSON
|
||||
* @param {Cesium3DTile|undefined} parent The parent tile
|
||||
* @returns {number} The <code>Cesium3DTileRefine</code> value
|
||||
*/
|
||||
function determineRefine(headerRefine, parent) {
|
||||
// Note: This will not create a warning for strings like "RePlAcE",
|
||||
// but still handle them by uppercasing them.
|
||||
if (defined(headerRefine)) {
|
||||
if (headerRefine === "replace" || headerRefine === "add") {
|
||||
Cesium3DTile._deprecationWarning(
|
||||
"lowercase-refine",
|
||||
`This tile uses a lowercase refine "${
|
||||
headerRefine
|
||||
}". Instead use "${headerRefine.toUpperCase()}".`,
|
||||
);
|
||||
}
|
||||
const refine =
|
||||
headerRefine.toUpperCase() === "REPLACE"
|
||||
? Cesium3DTileRefine.REPLACE
|
||||
: Cesium3DTileRefine.ADD;
|
||||
return refine;
|
||||
}
|
||||
if (defined(parent)) {
|
||||
// Inherit from parent tile if omitted.
|
||||
return parent.refine;
|
||||
}
|
||||
return Cesium3DTileRefine.REPLACE;
|
||||
}
|
||||
|
||||
// This can be overridden for testing purposes
|
||||
Cesium3DTile._deprecationWarning = deprecationWarning;
|
||||
|
||||
|
|
@ -1128,11 +1189,9 @@ Cesium3DTile.prototype.requestContent = function () {
|
|||
if (this.hasEmptyContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.hasMultipleContents) {
|
||||
return requestMultipleContents(this);
|
||||
}
|
||||
|
||||
return requestSingleContent(this);
|
||||
};
|
||||
|
||||
|
|
@ -1350,52 +1409,12 @@ async function makeContent(tile, arrayBuffer) {
|
|||
tile.hasRenderableContent = false;
|
||||
}
|
||||
|
||||
let content;
|
||||
const contentFactory = Cesium3DTileContentFactory[preprocessed.contentType];
|
||||
if (tile.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (defined(preprocessed.binaryPayload)) {
|
||||
content = await Promise.resolve(
|
||||
contentFactory(
|
||||
tileset,
|
||||
tile,
|
||||
tile._contentResource,
|
||||
preprocessed.binaryPayload.buffer,
|
||||
0,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// JSON formats
|
||||
content = await Promise.resolve(
|
||||
contentFactory(
|
||||
tileset,
|
||||
tile,
|
||||
tile._contentResource,
|
||||
preprocessed.jsonPayload,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const resource = tile._contentResource;
|
||||
const contentHeader = tile._contentHeader;
|
||||
|
||||
if (tile.hasImplicitContentMetadata) {
|
||||
const subtree = tile.implicitSubtree;
|
||||
const coordinates = tile.implicitCoordinates;
|
||||
content.metadata = subtree.getContentMetadataView(coordinates, 0);
|
||||
} else if (!tile.hasImplicitContent) {
|
||||
content.metadata = findContentMetadata(tileset, contentHeader);
|
||||
}
|
||||
|
||||
const groupMetadata = findGroupMetadata(tileset, contentHeader);
|
||||
if (defined(groupMetadata)) {
|
||||
content.group = new Cesium3DContentGroup({
|
||||
metadata: groupMetadata,
|
||||
});
|
||||
}
|
||||
|
||||
return content;
|
||||
return finishContent(tile, resource, preprocessed, contentHeader, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1405,8 +1424,13 @@ async function makeContent(tile, arrayBuffer) {
|
|||
* @private
|
||||
*/
|
||||
Cesium3DTile.prototype.cancelRequests = function () {
|
||||
// XXX_DYNAMIC: This actually happens sometimes, but only when the tile is
|
||||
// in the "LOADING" state. Now... what do do with dynamic tiles?
|
||||
console.log("Cesium3DTile.cancelRequests is called");
|
||||
if (this.hasMultipleContents) {
|
||||
this._content.cancelRequests();
|
||||
} else if (this._content instanceof Dynamic3DTileContent) {
|
||||
this._content.cancelRequests();
|
||||
} else {
|
||||
this._request.cancel();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -302,17 +302,17 @@ Cesium3DTileContent.prototype.getFeature = function (batchId) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Called when {@link Cesium3DTileset#debugColorizeTiles} changes.
|
||||
* <p>
|
||||
* This is used to implement the <code>Cesium3DTileContent</code> interface, but is
|
||||
* not part of the public Cesium API.
|
||||
* </p>
|
||||
*
|
||||
* @param {boolean} enabled Whether to enable or disable debug settings.
|
||||
* @returns {Cesium3DTileFeature} The corresponding {@link Cesium3DTileFeature} object.
|
||||
|
||||
* @private
|
||||
*/
|
||||
* Called when {@link Cesium3DTileset#debugColorizeTiles} changes.
|
||||
* <p>
|
||||
* This is used to implement the <code>Cesium3DTileContent</code> interface, but is
|
||||
* not part of the public Cesium API.
|
||||
* </p>
|
||||
*
|
||||
* @param {boolean} enabled Whether to enable or disable debug settings.
|
||||
* @param {Color|undefined} color The color to apply
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
Cesium3DTileContent.prototype.applyDebugSettings = function (enabled, color) {
|
||||
DeveloperError.throwInstantiationError();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import Tileset3DTileContent from "./Tileset3DTileContent.js";
|
|||
import Vector3DTileContent from "./Vector3DTileContent.js";
|
||||
import GaussianSplat3DTileContent from "./GaussianSplat3DTileContent.js";
|
||||
import RuntimeError from "../Core/RuntimeError.js";
|
||||
import Dynamic3DTileContent from "./Dynamic3DTileContent.js";
|
||||
|
||||
/**
|
||||
* Maps a tile's magic field in its header to a new content object for the tile's payload.
|
||||
|
|
@ -54,6 +55,9 @@ const Cesium3DTileContentFactory = {
|
|||
externalTileset: function (tileset, tile, resource, json) {
|
||||
return Tileset3DTileContent.fromJson(tileset, tile, resource, json);
|
||||
},
|
||||
dynamicContents: function (tileset, tile, resource, json) {
|
||||
return Dynamic3DTileContent.fromJson(tileset, tile, resource, json);
|
||||
},
|
||||
geom: function (tileset, tile, resource, arrayBuffer, byteOffset) {
|
||||
return new Geometry3DTileContent(
|
||||
tileset,
|
||||
|
|
|
|||
|
|
@ -113,6 +113,16 @@ const Cesium3DTileContentType = {
|
|||
* @private
|
||||
*/
|
||||
EXTERNAL_TILESET: "externalTileset",
|
||||
/**
|
||||
* The content is a dynamic content, which contains an array of
|
||||
* content objects with 'keys' that identify which content is
|
||||
* active at a certain point in time.
|
||||
*
|
||||
* @type {string}
|
||||
* @constant
|
||||
* @private
|
||||
*/
|
||||
DYNAMIC_CONTENTS: "dynamicContents",
|
||||
/**
|
||||
* Multiple contents are handled separately from the other content types
|
||||
* due to differences in request scheduling.
|
||||
|
|
|
|||
|
|
@ -216,10 +216,82 @@ function Cesium3DTileset(options) {
|
|||
this._modelUpAxis = undefined;
|
||||
this._modelForwardAxis = undefined;
|
||||
this._cache = new Cesium3DTilesetCache();
|
||||
this._processingQueue = [];
|
||||
this._selectedTiles = [];
|
||||
|
||||
this._emptyTiles = [];
|
||||
|
||||
/**
|
||||
* The tiles that are 'selected' by the traversal.
|
||||
*
|
||||
* During the 'Cesium3DTileset.update' call, the tile traversal is
|
||||
* executed. This includes the execution of the 'selectTiles'
|
||||
* function of the traversal (which exists in different forms,
|
||||
* depending on the traversal - but it's not really an interface,
|
||||
* just different functions).
|
||||
*
|
||||
* The 'selectTiles' function will first clear this list of
|
||||
* selected tiles, and then fill it with the tiles that are
|
||||
* 'selected'.
|
||||
*
|
||||
* (This usually/roughly means that they are in the view frustum
|
||||
* and have the right level of detail, but the details may vary)
|
||||
*
|
||||
* Some of these tiles may also be moved into the '_requestedTiles'
|
||||
* as part of the traversal.
|
||||
*/
|
||||
this._selectedTiles = [];
|
||||
|
||||
/**
|
||||
* Tiles that are 'requested' according to the traversal.
|
||||
*
|
||||
* This is usually a subset of the '_selectedTiles': The list
|
||||
* of requested tiles is cleared at the beginning of the traversal,
|
||||
* and then some tiles that are 'selected' will also be added to
|
||||
* these 'requested' tiles.
|
||||
*
|
||||
* There is no clear definition of what a 'requested' tile is.
|
||||
* It roughly means that ~"their content has to be loaded".
|
||||
* The tiles are added to this list, usually in a function
|
||||
* called 'loadTile', which is literally saying that the tile
|
||||
* is added to this list "if appropriate".
|
||||
*
|
||||
* The important point is that AFTER the traversal, the
|
||||
* contents of these tiles will be loaded, meaning that
|
||||
* 'Cesium3DTile.requestContent' will be called for them,
|
||||
* and they will be added to the '_requestedTilesInFlight'.
|
||||
*
|
||||
* (Once the content is loaded, the tiles will be added to
|
||||
* the '_processingQueue');
|
||||
*/
|
||||
this._requestedTiles = [];
|
||||
|
||||
/**
|
||||
* The tiles for which a content request is currently "in flight".
|
||||
*
|
||||
* This list is filled with tiles from the '_requestedTiles'
|
||||
* in each frame. Tiles are removed from this list after each
|
||||
* frame (when 'cancelOutOfViewRequests' is called), if their
|
||||
* '_contentState' is no longer 'LOADING'.
|
||||
*
|
||||
* So a tile being in this list roughly means that its content
|
||||
* is currently being loaded.
|
||||
*/
|
||||
this._requestedTilesInFlight = [];
|
||||
|
||||
/**
|
||||
* The tiles that are currently being processed.
|
||||
*
|
||||
* These are the tiles that have been 'selected' and 'requested'
|
||||
* and whose content was eventually obtained. Before the next
|
||||
* rendering pass, these tiles will be "processed", meaning that
|
||||
* their 'Cesium3DTile.process' method will be called.
|
||||
*
|
||||
* This mainly means that the 'Cesium3DTileContent.update' function
|
||||
* of their content is called, loading data and creating WebGL
|
||||
* resources and doing other random stuff, which eventually leads
|
||||
* to the tile moving from the 'PROCESSING' state into the 'READY' state.
|
||||
*/
|
||||
this._processingQueue = [];
|
||||
|
||||
this._selectedTilesToStyle = [];
|
||||
this._loadTimestamp = undefined;
|
||||
this._timeSinceLoad = 0.0;
|
||||
|
|
@ -276,8 +348,6 @@ function Cesium3DTileset(options) {
|
|||
this._statisticsPerPass[i] = new Cesium3DTilesetStatistics();
|
||||
}
|
||||
|
||||
this._requestedTilesInFlight = [];
|
||||
|
||||
this._maximumPriority = {
|
||||
foveatedFactor: -Number.MAX_VALUE,
|
||||
depth: -Number.MAX_VALUE,
|
||||
|
|
@ -1077,6 +1147,17 @@ function Cesium3DTileset(options) {
|
|||
instanceFeatureIdLabel = `instanceFeatureId_${instanceFeatureIdLabel}`;
|
||||
}
|
||||
this._instanceFeatureIdLabel = instanceFeatureIdLabel;
|
||||
|
||||
/**
|
||||
* The function that determines which inner contents of a dynamic
|
||||
* contents object are currently active.
|
||||
*
|
||||
* See the setter of this property for details.
|
||||
*
|
||||
* @type {Function|undefined}
|
||||
* @private
|
||||
*/
|
||||
this._dynamicContentPropertyProvider = undefined;
|
||||
}
|
||||
|
||||
Object.defineProperties(Cesium3DTileset.prototype, {
|
||||
|
|
@ -2103,6 +2184,34 @@ Object.defineProperties(Cesium3DTileset.prototype, {
|
|||
this._instanceFeatureIdLabel = value;
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* The function that provides the properties based on which inner
|
||||
* contents of a dynamic content should be active.
|
||||
*
|
||||
* This is a function that returns a JSON plain object. This object corresponds
|
||||
* to one 'key' of a dynamic content definition. It will cause the content
|
||||
* with this key to be the currently active content, namely, when the
|
||||
* "update" function of that content is called.
|
||||
*
|
||||
* @memberof Cesium3DTileset.prototype
|
||||
* @readonly
|
||||
* @type {Function|undefined}
|
||||
* @private
|
||||
*/
|
||||
dynamicContentPropertyProvider: {
|
||||
get: function () {
|
||||
return this._dynamicContentPropertyProvider;
|
||||
},
|
||||
set: function (value) {
|
||||
if (defined(value) && !defined(this._dynamicContentsDimensions)) {
|
||||
console.log(
|
||||
"This tileset does not contain the 3DTILES_dynamic extension. The given function will not have an effect.",
|
||||
);
|
||||
}
|
||||
this._dynamicContentPropertyProvider = value;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -2261,6 +2370,18 @@ Cesium3DTileset.fromUrl = async function (url, options) {
|
|||
tileset._initialClippingPlanesOriginMatrix,
|
||||
);
|
||||
|
||||
// Extract the information about the "dimensions" of the dynamic contents,
|
||||
// if present.
|
||||
// XXX_DYNAMIC This should probably not be done here, but ...
|
||||
// maybe in the constructor or so...? The lifecycle, though...
|
||||
const hasDynamicContents = hasExtension(tilesetJson, "3DTILES_dynamic");
|
||||
if (hasDynamicContents) {
|
||||
const dynamicContentsExtension = tilesetJson.extensions["3DTILES_dynamic"];
|
||||
tileset._dynamicContentsDimensions = dynamicContentsExtension.dimensions;
|
||||
} else {
|
||||
tileset._dynamicContentsDimensions = undefined;
|
||||
}
|
||||
|
||||
return tileset;
|
||||
};
|
||||
|
||||
|
|
@ -2362,6 +2483,54 @@ Cesium3DTileset.prototype.loadTileset = function (
|
|||
return rootTile;
|
||||
};
|
||||
|
||||
/**
|
||||
* XXX_DYNAMIC A draft for a convenience function for the dynamic content
|
||||
* properties provider. Whether or not this should be offered depends on
|
||||
* how much we want to specialize all this for single ISO8601 date strings.
|
||||
* We could even omit the "timeDimensionName" if this was a fixed, specified
|
||||
* string like "isoTimeStamp" or so.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* Set the function that determines which dynamic content is currently active,
|
||||
* based on the ISO8601 string representation of the current time of the given
|
||||
* clock.
|
||||
*
|
||||
* @param {string} timeDimensionName The name of the property that will
|
||||
* contain the ISO8601 date string of the current time of the clock
|
||||
* @param {Clock} clock The clock that provides the current time
|
||||
*/
|
||||
Cesium3DTileset.prototype.setDefaultTimeDynamicContentPropertyProvider =
|
||||
function (timeDimensionName, clock) {
|
||||
//>>includeStart('debug', pragmas.debug);
|
||||
Check.typeOf.string("timeDimensionName", timeDimensionName);
|
||||
Check.typeOf.object("clock", clock);
|
||||
//>>includeEnd('debug');
|
||||
|
||||
const dimensions = this._dynamicContentsDimensions;
|
||||
if (defined(dimensions)) {
|
||||
const dimensionNames = dimensions.map((d) => d.name);
|
||||
if (!dimensionNames.includes(timeDimensionName)) {
|
||||
console.log(
|
||||
`The time dimension name ${timeDimensionName} is not a valid dimension name. Valid dimension names are`,
|
||||
dimensionNames,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const dynamicContentPropertyProvider = () => {
|
||||
const currentTime = clock.currentTime;
|
||||
if (!defined(currentTime)) {
|
||||
return undefined;
|
||||
}
|
||||
const currentTimeString = JulianDate.toIso8601(currentTime);
|
||||
return {
|
||||
[timeDimensionName]: currentTimeString,
|
||||
};
|
||||
};
|
||||
this.dynamicContentPropertyProvider = dynamicContentPropertyProvider;
|
||||
};
|
||||
|
||||
/**
|
||||
* Make a {@link Cesium3DTile} for a specific tile. If the tile's header has implicit
|
||||
* tiling (3D Tiles 1.1) or uses the <code>3DTILES_implicit_tiling</code> extension,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -6,12 +6,9 @@ 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 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";
|
||||
import finishContent from "./finishContent.js";
|
||||
|
||||
/**
|
||||
* A collection of contents for tiles that have multiple contents, either via the tile JSON (3D Tiles 1.1) or the <code>3DTILES_multiple_contents</code> extension.
|
||||
|
|
@ -35,7 +32,11 @@ import preprocess3DTileContent from "./preprocess3DTileContent.js";
|
|||
function Multiple3DTileContent(tileset, tile, tilesetResource, contentsJson) {
|
||||
this._tileset = tileset;
|
||||
this._tile = tile;
|
||||
this._tilesetResource = tilesetResource;
|
||||
|
||||
// XXX_DYNAMIC Was unused... ?!
|
||||
// This could be avoided by writing a COMMENT!!!
|
||||
//this._tilesetResource = tilesetResource;
|
||||
|
||||
this._contents = [];
|
||||
this._contentsCreated = false;
|
||||
|
||||
|
|
@ -532,8 +533,8 @@ async function createInnerContent(multipleContents, arrayBuffer, index) {
|
|||
try {
|
||||
const preprocessed = preprocess3DTileContent(arrayBuffer);
|
||||
|
||||
const tileset = multipleContents._tileset;
|
||||
const resource = multipleContents._innerContentResources[index];
|
||||
const contentHeader = multipleContents._innerContentHeaders[index];
|
||||
const tile = multipleContents._tile;
|
||||
|
||||
if (preprocessed.contentType === Cesium3DTileContentType.EXTERNAL_TILESET) {
|
||||
|
|
@ -546,42 +547,7 @@ async function createInnerContent(multipleContents, arrayBuffer, index) {
|
|||
preprocessed.contentType === Cesium3DTileContentType.GEOMETRY ||
|
||||
preprocessed.contentType === Cesium3DTileContentType.VECTOR;
|
||||
|
||||
let content;
|
||||
const contentFactory = Cesium3DTileContentFactory[preprocessed.contentType];
|
||||
if (defined(preprocessed.binaryPayload)) {
|
||||
content = await Promise.resolve(
|
||||
contentFactory(
|
||||
tileset,
|
||||
tile,
|
||||
resource,
|
||||
preprocessed.binaryPayload.buffer,
|
||||
0,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// JSON formats
|
||||
content = await Promise.resolve(
|
||||
contentFactory(tileset, tile, resource, preprocessed.jsonPayload),
|
||||
);
|
||||
}
|
||||
|
||||
const contentHeader = multipleContents._innerContentHeaders[index];
|
||||
|
||||
if (tile.hasImplicitContentMetadata) {
|
||||
const subtree = tile.implicitSubtree;
|
||||
const coordinates = tile.implicitCoordinates;
|
||||
content.metadata = subtree.getContentMetadataView(coordinates, index);
|
||||
} else if (!tile.hasImplicitContent) {
|
||||
content.metadata = findContentMetadata(tileset, contentHeader);
|
||||
}
|
||||
|
||||
const groupMetadata = findGroupMetadata(tileset, contentHeader);
|
||||
if (defined(groupMetadata)) {
|
||||
content.group = new Cesium3DContentGroup({
|
||||
metadata: groupMetadata,
|
||||
});
|
||||
}
|
||||
return content;
|
||||
return finishContent(tile, resource, preprocessed, contentHeader, index);
|
||||
} catch (error) {
|
||||
handleInnerContentFailed(multipleContents, index, error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
import defined from "../Core/defined.js";
|
||||
import Cesium3DTileContentFactory from "./Cesium3DTileContentFactory.js";
|
||||
import findContentMetadata from "./findContentMetadata.js";
|
||||
import findGroupMetadata from "./findGroupMetadata.js";
|
||||
import Cesium3DContentGroup from "./Cesium3DContentGroup.js";
|
||||
|
||||
/**
|
||||
* Finalize the creation of a <code>Cesium3DTileContent</code> object.
|
||||
*
|
||||
* This takes the information from the tile and the preprocessed content
|
||||
* data that was fetched from the resource, creates the proper
|
||||
* <code>Cesium3DTileContent</code> instance, and assigns the
|
||||
* content- and group metadata to it.
|
||||
*
|
||||
* @function
|
||||
*
|
||||
* @param {Cesium3DTile} tile The tile that contained the content
|
||||
* @param {Resource} resource The resource
|
||||
* @param {PreprocessedContent} preprocessed The return value of <code>preprocess3DTileContent</code>
|
||||
* @param {object} contentHeader the JSON header for a {@link Cesium3DTileContent}
|
||||
* @param {number} index The content index. This is 0 for a single content, or the index of the inner content for multiple contents.
|
||||
* @return {Cesium3DTileContent} The finished <code>Cesium3DTileContent</code>
|
||||
* @private
|
||||
*/
|
||||
async function finishContent(
|
||||
tile,
|
||||
resource,
|
||||
preprocessed,
|
||||
contentHeader,
|
||||
index,
|
||||
) {
|
||||
const tileset = tile._tileset;
|
||||
const contentFactory = Cesium3DTileContentFactory[preprocessed.contentType];
|
||||
let content;
|
||||
if (defined(preprocessed.binaryPayload)) {
|
||||
content = await Promise.resolve(
|
||||
contentFactory(
|
||||
tileset,
|
||||
tile,
|
||||
resource,
|
||||
preprocessed.binaryPayload.buffer,
|
||||
0,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// JSON formats
|
||||
content = await Promise.resolve(
|
||||
contentFactory(tileset, tile, resource, preprocessed.jsonPayload),
|
||||
);
|
||||
}
|
||||
|
||||
if (tile.hasImplicitContentMetadata) {
|
||||
const subtree = tile.implicitSubtree;
|
||||
const coordinates = tile.implicitCoordinates;
|
||||
content.metadata = subtree.getContentMetadataView(coordinates, index);
|
||||
} else if (!tile.hasImplicitContent) {
|
||||
content.metadata = findContentMetadata(tileset, contentHeader);
|
||||
}
|
||||
|
||||
const groupMetadata = findGroupMetadata(tileset, contentHeader);
|
||||
if (defined(groupMetadata)) {
|
||||
content.group = new Cesium3DContentGroup({
|
||||
metadata: groupMetadata,
|
||||
});
|
||||
}
|
||||
return content;
|
||||
}
|
||||
export default finishContent;
|
||||
|
|
@ -84,6 +84,15 @@ function preprocess3DTileContent(arrayBuffer) {
|
|||
};
|
||||
}
|
||||
|
||||
if (defined(json.dynamicContents)) {
|
||||
// If this is not dynamic content, someone must have
|
||||
// added that 'dynamicContents' property maliciously.
|
||||
return {
|
||||
contentType: Cesium3DTileContentType.DYNAMIC_CONTENTS,
|
||||
jsonPayload: json,
|
||||
};
|
||||
}
|
||||
|
||||
throw new RuntimeError("Invalid tile content.");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -131,7 +131,9 @@ void doThreePointDepthTest(float eyeDepth, bool applyTranslate) {
|
|||
}
|
||||
#endif
|
||||
|
||||
void doDepthTest() {
|
||||
// Extra manual depth testing is done to allow more control over how a billboard is occluded
|
||||
// by the globe when near and far from the camera.
|
||||
void doDepthTest(float globeDepth) {
|
||||
float temp = v_compressed.y;
|
||||
temp = temp * SHIFT_RIGHT1;
|
||||
float temp2 = (temp - floor(temp)) * SHIFT_LEFT1;
|
||||
|
|
@ -156,14 +158,10 @@ void doDepthTest() {
|
|||
}
|
||||
#endif
|
||||
|
||||
// Automatic depth testing of billboards is disabled (@see BillboardCollection#update).
|
||||
// Instead, we do one of two types of manual depth tests (potentially in addition to the test above), depending on the camera's distance to the billboard fragment.
|
||||
// If we're far away, we just compare against a flat, camera-facing depth-plane at the ellipsoid's center.
|
||||
// If we're close, we compare against the globe depth texture (which includes depth from the 3D tile pass).
|
||||
vec2 fragSt = gl_FragCoord.xy / czm_viewport.zw;
|
||||
float globeDepth = getGlobeDepthAtCoords(fragSt);
|
||||
if (globeDepth == 0.0) return; // Not on globe
|
||||
|
||||
|
||||
if (globeDepth == 0.0) return; // Not on globe
|
||||
float distanceToEllipsoidCenter = -length(czm_viewerPositionWC); // depth is negative by convention
|
||||
float testDistance = (eyeDepth > -u_coarseDepthTestDistance) ? globeDepth : distanceToEllipsoidCenter;
|
||||
if (eyeDepth < testDistance) {
|
||||
|
|
@ -175,7 +173,10 @@ void main()
|
|||
{
|
||||
if (v_splitDirection < 0.0 && gl_FragCoord.x > czm_splitPosition) discard;
|
||||
if (v_splitDirection > 0.0 && gl_FragCoord.x < czm_splitPosition) discard;
|
||||
doDepthTest();
|
||||
|
||||
vec2 fragSt = gl_FragCoord.xy / czm_viewport.zw;
|
||||
float globeDepth = getGlobeDepthAtCoords(fragSt);
|
||||
doDepthTest(globeDepth);
|
||||
|
||||
vec4 color = texture(u_atlas, v_textureCoordinates);
|
||||
|
||||
|
|
@ -243,6 +244,18 @@ void main()
|
|||
out_FragColor = color;
|
||||
|
||||
#ifdef LOG_DEPTH
|
||||
czm_writeLogDepth();
|
||||
// If we've made it here, we passed our manual depth test, above. But the automatic depth test will
|
||||
// still run, and some fragments of the billboard may clip against the globe. To prevent that,
|
||||
// ensure the depth value we write out is in front of the globe depth.
|
||||
float depthArg = v_depthFromNearPlusOne;
|
||||
|
||||
if (globeDepth != 0.0) { // On the globe
|
||||
float globeDepthFromNearPlusOne = (-globeDepth - czm_currentFrustum.x) + 1.0;
|
||||
float nudge = max(globeDepthFromNearPlusOne * 5e-6, czm_epsilon7);
|
||||
float globeOnTop = max(1.0, globeDepthFromNearPlusOne - nudge);
|
||||
depthArg = min(depthArg, globeOnTop);
|
||||
}
|
||||
|
||||
czm_writeLogDepth(depthArg);
|
||||
#endif
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue