-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add tile loading events for Voxels #12430
base: main
Are you sure you want to change the base?
Changes from all commits
3a4f140
f36ad9c
ceaf56c
bfe4c11
fcd9a9c
61f4771
335414d
156d050
3c79fad
5f9a85a
2b38dbb
f589fb0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -25,6 +25,7 @@ import CustomShader from "./Model/CustomShader.js"; | |||||
import Cartographic from "../Core/Cartographic.js"; | ||||||
import Ellipsoid from "../Core/Ellipsoid.js"; | ||||||
import VerticalExaggeration from "../Core/VerticalExaggeration.js"; | ||||||
import Cesium3DTilesetStatistics from "./Cesium3DTilesetStatistics.js"; | ||||||
|
||||||
/** | ||||||
* A primitive that renders voxel data from a {@link VoxelProvider}. | ||||||
|
@@ -70,6 +71,12 @@ function VoxelPrimitive(options) { | |||||
*/ | ||||||
this._traversal = undefined; | ||||||
|
||||||
/** | ||||||
* @type {Cesium3DTilesetStatistics} | ||||||
* @private | ||||||
*/ | ||||||
this._statistics = new Cesium3DTilesetStatistics(); | ||||||
|
||||||
/** | ||||||
* This member is not created until the provider is ready. | ||||||
* | ||||||
|
@@ -450,6 +457,136 @@ function VoxelPrimitive(options) { | |||||
} | ||||||
} | ||||||
|
||||||
/** | ||||||
* The event fired to indicate that a tile's content was loaded. | ||||||
* <p> | ||||||
* The loaded tile is passed to the event listener. | ||||||
* </p> | ||||||
* <p> | ||||||
* This event is fired during the tileset traversal while the frame is being rendered | ||||||
* so that updates to the tile take effect in the same frame. Do not create or modify | ||||||
* Cesium entities or primitives during the event listener. | ||||||
* </p> | ||||||
* | ||||||
* @type {Event} | ||||||
* @default new Event() | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I don't think this should be needed. Strictly speaking, we should probably prevent the public API from reassigning this at all by providing only a public getter. But this can remain consistent with what we have over in |
||||||
* | ||||||
* @example | ||||||
* voxelPrimitive.tileLoad.addEventListener(function() { | ||||||
* console.log('A tile was loaded.'); | ||||||
* }); | ||||||
*/ | ||||||
this.tileLoad = new Event(); | ||||||
|
||||||
/** | ||||||
* This event fires once for each visible tile in a frame. | ||||||
* <p> | ||||||
* This event is fired during the traversal while the frame is being rendered. | ||||||
* | ||||||
* @type {Event} | ||||||
* @default new Event() | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Same as above. |
||||||
* | ||||||
* @example | ||||||
* voxelPrimitive.tileVisible.addEventListener(function() { | ||||||
* console.log('A tile is visible.'); | ||||||
* }); | ||||||
* | ||||||
*/ | ||||||
this.tileVisible = new Event(); | ||||||
|
||||||
/** | ||||||
* The event fired to indicate that a tile's content failed to load. | ||||||
* <p> | ||||||
* If there are no event listeners, error messages will be logged to the console. | ||||||
* </p> | ||||||
Comment on lines
+499
to
+501
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this accurate? Please remove this line if not. |
||||||
* | ||||||
* @type {Event} | ||||||
* @default new Event() | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
* | ||||||
* @example | ||||||
* voxelPrimitive.tileFailed.addEventListener(function() { | ||||||
* console.log('An error occurred loading tile.'); | ||||||
* }); | ||||||
*/ | ||||||
this.tileFailed = new Event(); | ||||||
|
||||||
/** | ||||||
* The event fired to indicate that a tile's content was unloaded. | ||||||
* | ||||||
* @type {Event} | ||||||
* @default new Event() | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
* | ||||||
* @example | ||||||
* voxelPrimitive.tileUnload.addEventListener(function() { | ||||||
* console.log('A tile was unloaded from the cache.'); | ||||||
* }); | ||||||
* | ||||||
*/ | ||||||
this.tileUnload = new Event(); | ||||||
|
||||||
/** | ||||||
* The event fired to indicate progress of loading new tiles. This event is fired when a new tile | ||||||
* is requested, when a requested tile is finished downloading, and when a downloaded tile has been | ||||||
* processed and is ready to render. | ||||||
* <p> | ||||||
* The number of pending tile requests, <code>numberOfPendingRequests</code>, and number of tiles | ||||||
* processing, <code>numberOfTilesProcessing</code> are passed to the event listener. | ||||||
* </p> | ||||||
* <p> | ||||||
* This event is fired at the end of the frame after the scene is rendered. | ||||||
* </p> | ||||||
* | ||||||
* @type {Event} | ||||||
* @default new Event() | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
* | ||||||
* @example | ||||||
* voxelPrimitive.loadProgress.addEventListener(function(numberOfPendingRequests, numberOfTilesProcessing) { | ||||||
* if ((numberOfPendingRequests === 0) && (numberOfTilesProcessing === 0)) { | ||||||
* console.log('Stopped loading'); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick:
Suggested change
|
||||||
* return; | ||||||
* } | ||||||
* | ||||||
* console.log(`Loading: requests: ${numberOfPendingRequests}, processing: ${numberOfTilesProcessing}`); | ||||||
* }); | ||||||
*/ | ||||||
this.loadProgress = new Event(); | ||||||
|
||||||
/** | ||||||
* The event fired to indicate that all tiles that meet the screen space error this frame are loaded. The voxel | ||||||
* primitive is completely loaded for this view. | ||||||
* <p> | ||||||
* This event is fired at the end of the frame after the scene is rendered. | ||||||
* </p> | ||||||
* | ||||||
* @type {Event} | ||||||
* @default new Event() | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
* | ||||||
* @example | ||||||
* voxelPrimitive.allTilesLoaded.addEventListener(function() { | ||||||
* console.log('All tiles are loaded'); | ||||||
* }); | ||||||
*/ | ||||||
this.allTilesLoaded = new Event(); | ||||||
|
||||||
/** | ||||||
* The event fired to indicate that all tiles that meet the screen space error this frame are loaded. This event | ||||||
* is fired once when all tiles in the initial view are loaded. | ||||||
* <p> | ||||||
* This event is fired at the end of the frame after the scene is rendered. | ||||||
* </p> | ||||||
* | ||||||
* @type {Event} | ||||||
* @default new Event() | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
* | ||||||
* @example | ||||||
* voxelPrimitive.initialTilesLoaded.addEventListener(function() { | ||||||
* console.log('Initial tiles are loaded'); | ||||||
* }); | ||||||
* | ||||||
* @see Cesium3DTileset#allTilesLoaded | ||||||
*/ | ||||||
this.initialTilesLoaded = new Event(); | ||||||
|
||||||
// If the provider fails to initialize the primitive will fail too. | ||||||
const provider = this._provider; | ||||||
initialize(this, provider); | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -143,6 +143,12 @@ function VoxelTraversal( | |
*/ | ||
this._binaryTreeKeyframeWeighting = new Array(keyframeCount); | ||
|
||
/** | ||
* @type {boolean} | ||
* @private | ||
*/ | ||
this._initialTilesLoaded = false; | ||
|
||
const binaryTreeKeyframeWeighting = this._binaryTreeKeyframeWeighting; | ||
binaryTreeKeyframeWeighting[0] = 0; | ||
binaryTreeKeyframeWeighting[keyframeCount - 1] = 0; | ||
|
@@ -316,13 +322,17 @@ VoxelTraversal.prototype.update = function ( | |
const timestamp1 = getTimestamp(); | ||
generateOctree(this, sampleCount, levelBlendFactor); | ||
const timestamp2 = getTimestamp(); | ||
|
||
if (this._debugPrint) { | ||
const checkEventListeners = | ||
primitive.loadProgress.numberOfListeners > 0 || | ||
primitive.allTilesLoaded.numberOfListeners > 0 || | ||
primitive.initialTilesLoaded.numberOfListeners > 0; | ||
if (this._debugPrint || checkEventListeners) { | ||
const loadAndUnloadTimeMs = timestamp1 - timestamp0; | ||
const generateOctreeTimeMs = timestamp2 - timestamp1; | ||
const totalTimeMs = timestamp2 - timestamp0; | ||
printDebugInformation( | ||
postPassesUpdate( | ||
this, | ||
frameState, | ||
loadAndUnloadTimeMs, | ||
generateOctreeTimeMs, | ||
totalTimeMs, | ||
|
@@ -418,6 +428,18 @@ function requestData(that, keyframeNode) { | |
} | ||
|
||
const provider = that._primitive._provider; | ||
const { keyframe, spatialNode } = keyframeNode; | ||
if (spatialNode.level >= provider._implicitTileset.availableLevels) { | ||
return; | ||
} | ||
|
||
const requestOptions = { | ||
tileLevel: spatialNode.level, | ||
tileX: spatialNode.x, | ||
tileY: spatialNode.y, | ||
tileZ: spatialNode.z, | ||
keyframe: keyframe, | ||
}; | ||
|
||
function postRequestSuccess(result) { | ||
that._simultaneousRequestCount--; | ||
|
@@ -443,34 +465,33 @@ function requestData(that, keyframeNode) { | |
keyframeNode.metadata[i] = data; | ||
// State is received only when all metadata requests have been received | ||
keyframeNode.state = KeyframeNode.LoadState.RECEIVED; | ||
that._primitive.tileLoad.raiseEvent(); | ||
} else { | ||
keyframeNode.state = KeyframeNode.LoadState.FAILED; | ||
break; | ||
} | ||
} | ||
} | ||
if (keyframeNode.state === KeyframeNode.LoadState.FAILED) { | ||
that._primitive.tileFailed.raiseEvent(); | ||
} | ||
} | ||
|
||
function postRequestFailure() { | ||
that._simultaneousRequestCount--; | ||
keyframeNode.state = KeyframeNode.LoadState.FAILED; | ||
that._primitive.tileFailed.raiseEvent(); | ||
} | ||
|
||
const { keyframe, spatialNode } = keyframeNode; | ||
const promise = provider.requestData({ | ||
tileLevel: spatialNode.level, | ||
tileX: spatialNode.x, | ||
tileY: spatialNode.y, | ||
tileZ: spatialNode.z, | ||
keyframe: keyframe, | ||
}); | ||
const promise = provider.requestData(requestOptions); | ||
|
||
if (defined(promise)) { | ||
that._simultaneousRequestCount++; | ||
keyframeNode.state = KeyframeNode.LoadState.RECEIVING; | ||
promise.then(postRequestSuccess).catch(postRequestFailure); | ||
} else { | ||
keyframeNode.state = KeyframeNode.LoadState.FAILED; | ||
that._primitive.tileFailed.raiseEvent(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may be a question beyond the scope of this PR, but when a provider returns CC @jjhembd |
||
} | ||
} | ||
|
||
|
@@ -645,6 +666,7 @@ function loadAndUnload(that, frameState) { | |
destroyedCount++; | ||
|
||
const discardNode = keyframeNodesInMegatexture[addNodeIndex]; | ||
that._primitive.tileUnload.raiseEvent(); | ||
discardNode.spatialNode.destroyKeyframeNode( | ||
discardNode, | ||
that.megatextures, | ||
|
@@ -703,8 +725,9 @@ function keyframePriority(previousKeyframe, keyframe, nextKeyframe, traversal) { | |
* | ||
* @private | ||
*/ | ||
function printDebugInformation( | ||
function postPassesUpdate( | ||
that, | ||
frameState, | ||
loadAndUnloadTimeMs, | ||
generateOctreeTimeMs, | ||
totalTimeMs, | ||
|
@@ -758,6 +781,55 @@ function printDebugInformation( | |
} | ||
traverseRecursive(rootNode); | ||
|
||
const numberOfPendingRequests = | ||
loadStateByCount[KeyframeNode.LoadState.RECEIVING]; | ||
const numberOfTilesProcessing = | ||
loadStateByCount[KeyframeNode.LoadState.RECEIVED]; | ||
|
||
const progressChanged = | ||
numberOfPendingRequests !== | ||
that._primitive._statistics.numberOfPendingRequests || | ||
numberOfTilesProcessing !== | ||
that._primitive._statistics.numberOfTilesProcessing; | ||
|
||
if (progressChanged) { | ||
frameState.afterRender.push(function () { | ||
that._primitive.loadProgress.raiseEvent( | ||
numberOfPendingRequests, | ||
numberOfTilesProcessing, | ||
); | ||
|
||
return true; | ||
}); | ||
} | ||
|
||
that._primitive._statistics.numberOfPendingRequests = numberOfPendingRequests; | ||
that._primitive._statistics.numberOfTilesProcessing = numberOfTilesProcessing; | ||
|
||
const tilesLoaded = | ||
numberOfPendingRequests === 0 && numberOfTilesProcessing === 0; | ||
|
||
// Events are raised (added to the afterRender queue) here since promises | ||
// may resolve outside of the update loop that then raise events, e.g., | ||
// model's readyEvent | ||
if (progressChanged && tilesLoaded) { | ||
frameState.afterRender.push(function () { | ||
that._primitive.allTilesLoaded.raiseEvent(); | ||
return true; | ||
}); | ||
if (!that._initialTilesLoaded) { | ||
that._initialTilesLoaded = true; | ||
frameState.afterRender.push(function () { | ||
that._primitive.initialTilesLoaded.raiseEvent(); | ||
return true; | ||
}); | ||
} | ||
} | ||
|
||
if (!that._debugPrint) { | ||
return; | ||
} | ||
|
||
const loadedKeyframeStatistics = `KEYFRAMES: ${ | ||
loadStatesByKeyframe[KeyframeNode.LoadState.LOADED] | ||
}`; | ||
|
@@ -892,6 +964,7 @@ function generateOctree(that, sampleCount, levelBlendFactor) { | |
} else { | ||
// Store the leaf node information instead | ||
// Recursion stops here because there are no renderable children | ||
that._primitive.tileVisible.raiseEvent(); | ||
if (useLeafNodes) { | ||
const baseIdx = leafNodeCount * 5; | ||
const keyframeNode = node.renderableKeyframeNodePrevious; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is raised with no arguments since the nodes are still private API, correct?