Skip to content

Commit

Permalink
Merge pull request #4136 from AnalyticalGraphicsInc/3d-tiles-expire
Browse files Browse the repository at this point in the history
3D Tiles - Support tile expiration
  • Loading branch information
pjcozzi authored May 10, 2017
2 parents 802a93a + b5c719d commit 68e2aa5
Show file tree
Hide file tree
Showing 15 changed files with 599 additions and 74 deletions.
148 changes: 125 additions & 23 deletions Source/Scene/Cesium3DTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ define([
'../Core/getStringFromTypedArray',
'../Core/Intersect',
'../Core/joinUrls',
'../Core/JulianDate',
'../Core/loadArrayBuffer',
'../Core/Matrix3',
'../Core/Matrix4',
Expand Down Expand Up @@ -60,6 +61,7 @@ define([
getStringFromTypedArray,
Intersect,
joinUrls,
JulianDate,
loadArrayBuffer,
Matrix3,
Matrix4,
Expand Down Expand Up @@ -118,8 +120,6 @@ define([
*/
this.computedTransform = computedTransform;

this._transformDirty = true;

this._boundingVolume = this.createBoundingVolume(header.boundingVolume, computedTransform);
this._boundingVolume2D = undefined;

Expand Down Expand Up @@ -212,6 +212,7 @@ define([
this._contentState = contentState;
this._contentReadyToProcessPromise = undefined;
this._contentReadyPromise = undefined;
this._expiredContent = undefined;

this._requestServer = requestServer;

Expand Down Expand Up @@ -261,6 +262,30 @@ define([
*/
this.replacementNode = undefined;

var expire = header.expire;
var expireDuration;
var expireDate;
if (defined(expire)) {
expireDuration = expire.duration;
if (defined(expire.date)) {
expireDate = JulianDate.fromIso8601(expire.date);
}
}

/**
* The time in seconds after the tile's content is ready when the content expires and new content is requested.
*
* @type {Number}
*/
this.expireDuration = expireDuration;

/**
* The date when the content expires and new content is requested.
*
* @type {JulianDate}
*/
this.expireDate = expireDate;

// Members that are updated every frame for tree traversal and rendering optimizations:

/**
Expand Down Expand Up @@ -393,26 +418,28 @@ define([
},

/**
* Whether the computedTransform has changed this frame.
*
* @memberof Cesium3DTile.prototype
*
* @type {Boolean}
* @readonly
* @private
*/
transformDirty : {
requestServer : {
get : function() {
return this._transformDirty;
return this._requestServer;
}
},

/**
* Determines if the tile has available content to render. <code>true</code> if the tile's
* content is ready or if it has expired content that renders while new content loads; otherwise,
* <code>false</code>.
*
* @memberof Cesium3DTile.prototype
*
* @type {Boolean}
* @readonly
* @private
*/
requestServer : {
contentAvailable : {
get : function() {
return this._requestServer;
return this.contentReady || (defined(this._expiredContent) && this._contentState !== Cesium3DTileContentState.FAILED);
}
},

Expand Down Expand Up @@ -446,6 +473,21 @@ define([
}
},

/**
* Determines if the tile's content is expired. <code>true</code> if tile's
* content is expired; otherwise, <code>false</code>.
*
* @memberof Cesium3DTile.prototype
*
* @type {Boolean}
* @readonly
*/
contentExpired : {
get : function() {
return this._contentState === Cesium3DTileContentState.EXPIRED;
}
},

/**
* Gets the promise that will be resolved when the tile's content is ready to process.
* This happens after the content is downloaded but before the content is ready
Expand Down Expand Up @@ -487,6 +529,38 @@ define([
}
});

var scratchJulianDate = new JulianDate();

/**
* Update whether the tile has expired.
*
* @private
*/
Cesium3DTile.prototype.updateExpiration = function() {
if (defined(this.expireDate) && this.contentReady && !this.hasEmptyContent) {
var now = JulianDate.now(scratchJulianDate);
if (JulianDate.lessThan(this.expireDate, now)) {
this._contentState = Cesium3DTileContentState.EXPIRED;
this._expiredContent = this._content;
}
}
};

function updateExpireDate(tile) {
if (defined(tile.expireDuration)) {
var expireDurationDate = JulianDate.now(scratchJulianDate);
JulianDate.addSeconds(expireDurationDate, tile.expireDuration, expireDurationDate);

if (defined(tile.expireDate)) {
if (JulianDate.lessThan(tile.expireDate, expireDurationDate)) {
JulianDate.clone(expireDurationDate, tile.expireDate);
}
} else {
tile.expireDate = JulianDate.clone(expireDurationDate);
}
}
}

/**
* Requests the tile's content.
* <p>
Expand All @@ -506,9 +580,16 @@ define([
return false;
}

var url = this._contentUrl;
if (defined(this.expireDate)) {
// Append a query parameter of the tile expiration date to prevent caching
var timestampQuery = '?expired=' + this.expireDate.toString();
url = joinUrls(url, timestampQuery, false);
}

var distance = this.distanceToCamera;
var promise = RequestScheduler.schedule(new Request({
url : this._contentUrl,
url : url,
server : this._requestServer,
requestFunction : loadArrayBuffer,
type : RequestType.TILES3D,
Expand All @@ -525,7 +606,8 @@ define([

promise.then(function(arrayBuffer) {
if (that.isDestroyed()) {
return when.reject('tileset is destroyed');
// Tile is unloaded before the content finishes loading
return when.reject('tile is destroyed');
}
var uint8Array = new Uint8Array(arrayBuffer);
var magic = getMagic(uint8Array);
Expand All @@ -546,6 +628,16 @@ define([
that._contentReadyToProcessPromise.resolve(content);

content.readyPromise.then(function(content) {
if (that.isDestroyed()) {
// Tile is unloaded before the content finishes processing
return when.reject('tile is destroyed');
}
updateExpireDate(that);

// Refresh style for expired content
that.lastSelectedFrameNumber = 0;
that.lastStyleTime = 0;

that._contentState = Cesium3DTileContentState.READY;
that._contentReadyPromise.resolve(content);
}).otherwise(function(error) {
Expand Down Expand Up @@ -595,10 +687,6 @@ define([

this.replacementNode = undefined;

// Restore properties set per frame to their defaults
this.distanceToCamera = 0;
this.visibilityPlaneMask = 0;
this.selected = false;
this.lastSelectedFrameNumber = 0;
this.lastStyleTime = 0;

Expand Down Expand Up @@ -788,9 +876,8 @@ define([
Cesium3DTile.prototype.updateTransform = function(parentTransform) {
parentTransform = defaultValue(parentTransform, Matrix4.IDENTITY);
var computedTransform = Matrix4.multiply(parentTransform, this.transform, scratchTransform);
var transformDirty = !Matrix4.equals(computedTransform, this.computedTransform);
if (transformDirty) {
this._transformDirty = true;
var transformChanged = !Matrix4.equals(computedTransform, this.computedTransform);
if (transformChanged) {
Matrix4.clone(computedTransform, this.computedTransform);

// Update the bounding volumes
Expand Down Expand Up @@ -853,6 +940,20 @@ define([
}
}

function updateContent(tile, tileset, frameState) {
var content = tile._content;
var expiredContent = tile._expiredContent;

if (defined(expiredContent) && !tile.contentReady) {
// Render the expired content while the content loads
expiredContent.update(tileset, frameState);
return;
}

tile._expiredContent = tile._expiredContent && tile._expiredContent.destroy();
content.update(tileset, frameState);
}

/**
* Get the draw commands needed to render this tile.
*
Expand All @@ -861,8 +962,7 @@ define([
Cesium3DTile.prototype.update = function(tileset, frameState) {
var initCommandLength = frameState.commandList.length;
applyDebugSettings(this, tileset, frameState);
this._content.update(tileset, frameState);
this._transformDirty = false;
updateContent(this, tileset, frameState);
this._commandsLength = frameState.commandList.length - initCommandLength;
};

Expand Down Expand Up @@ -897,7 +997,9 @@ define([
* @private
*/
Cesium3DTile.prototype.destroy = function() {
// For the interval between new content being requested and downloaded, expiredContent === content, so don't destroy twice
this._content = this._content && this._content.destroy();
this._expiredContent = this._expiredContent && !this._expiredContent.isDestroyed() && this._expiredContent.destroy();
this._debugBoundingVolume = this._debugBoundingVolume && this._debugBoundingVolume.destroy();
this._debugContentBoundingVolume = this._debugContentBoundingVolume && this._debugContentBoundingVolume.destroy();
this._debugViewerRequestVolume = this._debugViewerRequestVolume && this._debugViewerRequestVolume.destroy();
Expand Down
5 changes: 3 additions & 2 deletions Source/Scene/Cesium3DTileContentState.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ define([
LOADING : 1, // Is waiting on a pending request
PROCESSING : 2, // Request received. Contents are being processed for rendering. Depending on the content, it might make its own requests for external data.
READY : 3, // Ready to render.
FAILED : 4 // Request failed.
EXPIRED : 4, // Is expired and will be unloaded once new content is loaded.
FAILED : 5 // Request failed.
};

return freezeObject(Cesium3DTileContentState);
});
});
Loading

0 comments on commit 68e2aa5

Please sign in to comment.