mirror of https://github.com/CesiumGS/cesium.git
Compare commits
6 Commits
b4ee10c6df
...
bdcd200d47
| Author | SHA1 | Date |
|---|---|---|
|
|
bdcd200d47 | |
|
|
41a0f18ba9 | |
|
|
8bd8ae7f63 | |
|
|
1a8764265f | |
|
|
020749de32 | |
|
|
f4e49d7b64 |
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -48,8 +48,22 @@ class NDMap {
|
|||
* this constructor.
|
||||
*
|
||||
* @param {string[]} dimensionNames
|
||||
* @throws {DeveloperError} If the given array has length 0 or
|
||||
* contains duplicate elements
|
||||
*/
|
||||
constructor(dimensionNames) {
|
||||
if (dimensionNames.length === 0) {
|
||||
throw new DeveloperError(
|
||||
"The dimensionNames array may not have length 0",
|
||||
);
|
||||
} else {
|
||||
const s = new Set(dimensionNames);
|
||||
if (s.size !== dimensionNames.length) {
|
||||
throw new DeveloperError(
|
||||
`The dimensionNames array may not contain duplicate elements, but is ${dimensionNames}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
this._dimensionNames = dimensionNames;
|
||||
|
||||
/**
|
||||
|
|
@ -75,7 +89,7 @@ class NDMap {
|
|||
* @returns {number} The size
|
||||
*/
|
||||
get size() {
|
||||
return this._lookup.size();
|
||||
return this._lookup.size;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -198,7 +212,7 @@ class NDMap {
|
|||
* @param {any} thisArg A value to use as this when executing the callback
|
||||
*/
|
||||
forEach(callback, thisArg) {
|
||||
this._entries().forEach(callback, thisArg);
|
||||
this.entries().forEach(callback, thisArg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -247,12 +261,18 @@ class LRUCache {
|
|||
/**
|
||||
* Creates a new instance with the given maximum size.
|
||||
*
|
||||
* @param {number} maxSize The maximum size
|
||||
* @param {number} maximumSize The maximum size
|
||||
* @param {Function|undefined} evictionCallback The callback that will
|
||||
* receive the key and value of all evicted entries.
|
||||
* @throws {DeveloperError} If the maximum size is not positive
|
||||
*/
|
||||
constructor(maxSize, evictionCallback) {
|
||||
this._maxSize = maxSize;
|
||||
constructor(maximumSize, evictionCallback) {
|
||||
if (maximumSize <= 0) {
|
||||
throw new DeveloperError(
|
||||
`The maximumSize must be positive, but is ${maximumSize}`,
|
||||
);
|
||||
}
|
||||
this._maximumSize = maximumSize;
|
||||
this._evictionCallback = evictionCallback;
|
||||
|
||||
/**
|
||||
|
|
@ -271,11 +291,17 @@ class LRUCache {
|
|||
* of this cache, then the least recently used elements will
|
||||
* be evicted until the size matches the maximum size.
|
||||
*
|
||||
* @param {number} maxSize The maximum size
|
||||
* @param {number} maximumSize The maximum size
|
||||
* @throws {DeveloperError} If the maximum size is not positive
|
||||
*/
|
||||
setMaximumSize(maxSize) {
|
||||
this._maxSize = maxSize;
|
||||
this._ensureMaxSize();
|
||||
setMaximumSize(maximumSize) {
|
||||
if (maximumSize <= 0) {
|
||||
throw new DeveloperError(
|
||||
`The maximumSize must be positive, but is ${maximumSize}`,
|
||||
);
|
||||
}
|
||||
this._maximumSize = maximumSize;
|
||||
this._ensureMaximumSize();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -296,7 +322,7 @@ class LRUCache {
|
|||
set(key, value) {
|
||||
this._map.delete(key);
|
||||
this._map.set(key, value);
|
||||
this._ensureMaxSize();
|
||||
this._ensureMaximumSize();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -326,8 +352,8 @@ class LRUCache {
|
|||
* This will evict as many entries as necessary, in the
|
||||
* order of their least recent usage.
|
||||
*/
|
||||
_ensureMaxSize() {
|
||||
this.trimToSize(this._maxSize);
|
||||
_ensureMaximumSize() {
|
||||
this.trimToSize(this._maximumSize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -490,6 +516,18 @@ class LoggingRequestListener extends RequestListener {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO If something like the RequestHandle and RequestListener
|
||||
// had to be designed from scratch, then it could be possible
|
||||
// to come up with something that makes sense. Now, there's the
|
||||
// question about how to align the sought-for "nice" solution
|
||||
// with what is already there. A specific example is that
|
||||
// the "requestAttempted" function does not really make sense,
|
||||
// but has to be there, because this was once tracked in some
|
||||
// tileset statistics. (Otherwise, VERY roughly, there could
|
||||
// be the invariant of "cancelled+failed==attempted", but
|
||||
// "attempted" can also mean that nothing really happened
|
||||
// due to throttling, soooo... here we are...)
|
||||
|
||||
/**
|
||||
* A class serving as a convenience wrapper around a request for
|
||||
* a resource.
|
||||
|
|
@ -686,7 +724,7 @@ class RequestHandle {
|
|||
const request = new Request({
|
||||
throttle: true,
|
||||
throttleByServer: true,
|
||||
type: RequestType.TILES3D,
|
||||
type: RequestType.TILES3D, // XXX_DYNAMIC TODO Seems to be unused...
|
||||
priorityFunction: priorityFunction,
|
||||
});
|
||||
return request;
|
||||
|
|
@ -708,6 +746,8 @@ class RequestHandle {
|
|||
const rejectionError = new Error("Request was cancelled");
|
||||
rejectionError.code = RequestState.CANCELLED;
|
||||
this._deferred.reject(rejectionError);
|
||||
this._fireRequestCancelled();
|
||||
this._fireRequestAttempted();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1361,7 +1401,7 @@ class Dynamic3DTileContent {
|
|||
*
|
||||
* @type {number}
|
||||
*/
|
||||
this._loadedContentHandlesMaxSize = 10;
|
||||
this._loadedContentHandlesMaximumSize = 10;
|
||||
|
||||
/**
|
||||
* The mapping from "keys" to arrays(!) of URIs for the dynamic content.
|
||||
|
|
@ -1397,7 +1437,7 @@ class Dynamic3DTileContent {
|
|||
* evicted from the '_loadedContentHandles'.
|
||||
*
|
||||
* This will be called when the size of the '_loadedContentHandles'
|
||||
* is trimmed to the '_loadedContentHandlesMaxSize', and receive
|
||||
* is trimmed to the '_loadedContentHandlesMaximumSize', and receive
|
||||
* the least recently used content handles.
|
||||
*
|
||||
* It will call 'reset()' on the content handle, cancelling all
|
||||
|
|
@ -1998,15 +2038,15 @@ class Dynamic3DTileContent {
|
|||
// Ensure that at least the number of active contents
|
||||
// is retained
|
||||
const numActiveContents = activeContentUris.length;
|
||||
this._loadedContentHandlesMaxSize = Math.max(
|
||||
this._loadedContentHandlesMaxSize,
|
||||
this._loadedContentHandlesMaximumSize = Math.max(
|
||||
this._loadedContentHandlesMaximumSize,
|
||||
numActiveContents,
|
||||
);
|
||||
|
||||
// Trim the LRU cache to the target size, calling the
|
||||
// '_loadedContentHandleEvicted' for the least recently
|
||||
// used content handles.
|
||||
loadedContentHandles.trimToSize(this._loadedContentHandlesMaxSize);
|
||||
loadedContentHandles.trimToSize(this._loadedContentHandlesMaximumSize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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