You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
816 lines
25 KiB
JavaScript
816 lines
25 KiB
JavaScript
import arrayFill from "../Core/arrayFill.js";
|
|
import Check from "../Core/Check.js";
|
|
import combine from "../Core/combine.js";
|
|
import defaultValue from "../Core/defaultValue.js";
|
|
import defined from "../Core/defined.js";
|
|
import destroyObject from "../Core/destroyObject.js";
|
|
import Event from "../Core/Event.js";
|
|
import getTimestamp from "../Core/getTimestamp.js";
|
|
import JulianDate from "../Core/JulianDate.js";
|
|
import CesiumMath from "../Core/Math.js";
|
|
import Matrix4 from "../Core/Matrix4.js";
|
|
import Resource from "../Core/Resource.js";
|
|
import when from "../ThirdParty/when.js";
|
|
import ClippingPlaneCollection from "./ClippingPlaneCollection.js";
|
|
import PointCloud from "./PointCloud.js";
|
|
import PointCloudEyeDomeLighting from "./PointCloudEyeDomeLighting.js";
|
|
import PointCloudShading from "./PointCloudShading.js";
|
|
import SceneMode from "./SceneMode.js";
|
|
import ShadowMode from "./ShadowMode.js";
|
|
|
|
/**
|
|
* Provides playback of time-dynamic point cloud data.
|
|
* <p>
|
|
* Point cloud frames are prefetched in intervals determined by the average frame load time and the current clock speed.
|
|
* If intermediate frames cannot be loaded in time to meet playback speed, they will be skipped. If frames are sufficiently
|
|
* small or the clock is sufficiently slow then no frames will be skipped.
|
|
* </p>
|
|
*
|
|
* @alias TimeDynamicPointCloud
|
|
* @constructor
|
|
*
|
|
* @param {Object} options Object with the following properties:
|
|
* @param {Clock} options.clock A {@link Clock} instance that is used when determining the value for the time dimension.
|
|
* @param {TimeIntervalCollection} options.intervals A {@link TimeIntervalCollection} with its data property being an object containing a <code>uri</code> to a 3D Tiles Point Cloud tile and an optional <code>transform</code>.
|
|
* @param {Boolean} [options.show=true] Determines if the point cloud will be shown.
|
|
* @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] A 4x4 transformation matrix that transforms the point cloud.
|
|
* @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the point cloud casts or receives shadows from light sources.
|
|
* @param {Number} [options.maximumMemoryUsage=256] The maximum amount of memory in MB that can be used by the point cloud.
|
|
* @param {Object} [options.shading] Options for constructing a {@link PointCloudShading} object to control point attenuation and eye dome lighting.
|
|
* @param {Cesium3DTileStyle} [options.style] The style, defined using the {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/Styling|3D Tiles Styling language}, applied to each point in the point cloud.
|
|
* @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the point cloud.
|
|
*/
|
|
function TimeDynamicPointCloud(options) {
|
|
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
|
|
|
|
//>>includeStart('debug', pragmas.debug);
|
|
Check.typeOf.object("options.clock", options.clock);
|
|
Check.typeOf.object("options.intervals", options.intervals);
|
|
//>>includeEnd('debug');
|
|
|
|
/**
|
|
* Determines if the point cloud will be shown.
|
|
*
|
|
* @type {Boolean}
|
|
* @default true
|
|
*/
|
|
this.show = defaultValue(options.show, true);
|
|
|
|
/**
|
|
* A 4x4 transformation matrix that transforms the point cloud.
|
|
*
|
|
* @type {Matrix4}
|
|
* @default Matrix4.IDENTITY
|
|
*/
|
|
this.modelMatrix = Matrix4.clone(
|
|
defaultValue(options.modelMatrix, Matrix4.IDENTITY)
|
|
);
|
|
|
|
/**
|
|
* Determines whether the point cloud casts or receives shadows from light sources.
|
|
* <p>
|
|
* Enabling shadows has a performance impact. A point cloud that casts shadows must be rendered twice, once from the camera and again from the light's point of view.
|
|
* </p>
|
|
* <p>
|
|
* Shadows are rendered only when {@link Viewer#shadows} is <code>true</code>.
|
|
* </p>
|
|
*
|
|
* @type {ShadowMode}
|
|
* @default ShadowMode.ENABLED
|
|
*/
|
|
this.shadows = defaultValue(options.shadows, ShadowMode.ENABLED);
|
|
|
|
/**
|
|
* The maximum amount of GPU memory (in MB) that may be used to cache point cloud frames.
|
|
* <p>
|
|
* Frames that are not being loaded or rendered are unloaded to enforce this.
|
|
* </p>
|
|
* <p>
|
|
* If decreasing this value results in unloading tiles, the tiles are unloaded the next frame.
|
|
* </p>
|
|
*
|
|
* @type {Number}
|
|
* @default 256
|
|
*
|
|
* @see TimeDynamicPointCloud#totalMemoryUsageInBytes
|
|
*/
|
|
this.maximumMemoryUsage = defaultValue(options.maximumMemoryUsage, 256);
|
|
|
|
/**
|
|
* Options for controlling point size based on geometric error and eye dome lighting.
|
|
* @type {PointCloudShading}
|
|
*/
|
|
this.shading = new PointCloudShading(options.shading);
|
|
|
|
/**
|
|
* The style, defined using the
|
|
* {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/Styling|3D Tiles Styling language},
|
|
* applied to each point in the point cloud.
|
|
* <p>
|
|
* Assign <code>undefined</code> to remove the style, which will restore the visual
|
|
* appearance of the point cloud to its default when no style was applied.
|
|
* </p>
|
|
*
|
|
* @type {Cesium3DTileStyle}
|
|
*
|
|
* @example
|
|
* pointCloud.style = new Cesium.Cesium3DTileStyle({
|
|
* color : {
|
|
* conditions : [
|
|
* ['${Classification} === 0', 'color("purple", 0.5)'],
|
|
* ['${Classification} === 1', 'color("red")'],
|
|
* ['true', '${COLOR}']
|
|
* ]
|
|
* },
|
|
* show : '${Classification} !== 2'
|
|
* });
|
|
*
|
|
* @see {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/Styling|3D Tiles Styling language}
|
|
*/
|
|
this.style = options.style;
|
|
|
|
/**
|
|
* The event fired to indicate that a frame failed to load. A frame may fail to load if the
|
|
* request for its uri fails or processing fails due to invalid content.
|
|
* <p>
|
|
* If there are no event listeners, error messages will be logged to the console.
|
|
* </p>
|
|
* <p>
|
|
* The error object passed to the listener contains two properties:
|
|
* <ul>
|
|
* <li><code>uri</code>: the uri of the failed frame.</li>
|
|
* <li><code>message</code>: the error message.</li>
|
|
* </ul>
|
|
*
|
|
* @type {Event}
|
|
* @default new Event()
|
|
*
|
|
* @example
|
|
* pointCloud.frameFailed.addEventListener(function(error) {
|
|
* console.log('An error occurred loading frame: ' + error.uri);
|
|
* console.log('Error: ' + error.message);
|
|
* });
|
|
*/
|
|
this.frameFailed = new Event();
|
|
|
|
/**
|
|
* The event fired to indicate that a new frame was rendered.
|
|
* <p>
|
|
* The time dynamic point cloud {@link TimeDynamicPointCloud} is passed to the event listener.
|
|
* </p>
|
|
* @type {Event}
|
|
* @default new Event()
|
|
*
|
|
* @example
|
|
* pointCloud.frameChanged.addEventListener(function(timeDynamicPointCloud) {
|
|
* viewer.camera.viewBoundingSphere(timeDynamicPointCloud.boundingSphere);
|
|
* });
|
|
*/
|
|
this.frameChanged = new Event();
|
|
|
|
this._clock = options.clock;
|
|
this._intervals = options.intervals;
|
|
this._clippingPlanes = undefined;
|
|
this.clippingPlanes = options.clippingPlanes; // Call setter
|
|
this._pointCloudEyeDomeLighting = new PointCloudEyeDomeLighting();
|
|
this._loadTimestamp = undefined;
|
|
this._clippingPlanesState = 0;
|
|
this._styleDirty = false;
|
|
this._pickId = undefined;
|
|
this._totalMemoryUsageInBytes = 0;
|
|
this._frames = [];
|
|
this._previousInterval = undefined;
|
|
this._nextInterval = undefined;
|
|
this._lastRenderedFrame = undefined;
|
|
this._clockMultiplier = 0.0;
|
|
this._readyPromise = when.defer();
|
|
|
|
// For calculating average load time of the last N frames
|
|
this._runningSum = 0.0;
|
|
this._runningLength = 0;
|
|
this._runningIndex = 0;
|
|
this._runningSamples = arrayFill(new Array(5), 0.0);
|
|
this._runningAverage = 0.0;
|
|
}
|
|
|
|
Object.defineProperties(TimeDynamicPointCloud.prototype, {
|
|
/**
|
|
* The {@link ClippingPlaneCollection} used to selectively disable rendering the point cloud.
|
|
*
|
|
* @memberof TimeDynamicPointCloud.prototype
|
|
*
|
|
* @type {ClippingPlaneCollection}
|
|
*/
|
|
clippingPlanes: {
|
|
get: function () {
|
|
return this._clippingPlanes;
|
|
},
|
|
set: function (value) {
|
|
ClippingPlaneCollection.setOwner(value, this, "_clippingPlanes");
|
|
},
|
|
},
|
|
|
|
/**
|
|
* The total amount of GPU memory in bytes used by the point cloud.
|
|
*
|
|
* @memberof TimeDynamicPointCloud.prototype
|
|
*
|
|
* @type {Number}
|
|
* @readonly
|
|
*
|
|
* @see TimeDynamicPointCloud#maximumMemoryUsage
|
|
*/
|
|
totalMemoryUsageInBytes: {
|
|
get: function () {
|
|
return this._totalMemoryUsageInBytes;
|
|
},
|
|
},
|
|
|
|
/**
|
|
* The bounding sphere of the frame being rendered. Returns <code>undefined</code> if no frame is being rendered.
|
|
*
|
|
* @memberof TimeDynamicPointCloud.prototype
|
|
*
|
|
* @type {BoundingSphere}
|
|
* @readonly
|
|
*/
|
|
boundingSphere: {
|
|
get: function () {
|
|
if (defined(this._lastRenderedFrame)) {
|
|
return this._lastRenderedFrame.pointCloud.boundingSphere;
|
|
}
|
|
return undefined;
|
|
},
|
|
},
|
|
|
|
/**
|
|
* Gets the promise that will be resolved when the point cloud renders a frame for the first time.
|
|
*
|
|
* @memberof TimeDynamicPointCloud.prototype
|
|
*
|
|
* @type {Promise.<TimeDynamicPointCloud>}
|
|
* @readonly
|
|
*/
|
|
readyPromise: {
|
|
get: function () {
|
|
return this._readyPromise.promise;
|
|
},
|
|
},
|
|
});
|
|
|
|
function getFragmentShaderLoaded(fs) {
|
|
return "uniform vec4 czm_pickColor;\n" + fs;
|
|
}
|
|
|
|
function getUniformMapLoaded(stream) {
|
|
return function (uniformMap) {
|
|
return combine(uniformMap, {
|
|
czm_pickColor: function () {
|
|
return stream._pickId.color;
|
|
},
|
|
});
|
|
};
|
|
}
|
|
|
|
function getPickIdLoaded() {
|
|
return "czm_pickColor";
|
|
}
|
|
|
|
/**
|
|
* Marks the point cloud's {@link TimeDynamicPointCloud#style} as dirty, which forces all
|
|
* points to re-evaluate the style in the next frame.
|
|
*/
|
|
TimeDynamicPointCloud.prototype.makeStyleDirty = function () {
|
|
this._styleDirty = true;
|
|
};
|
|
|
|
/**
|
|
* Exposed for testing.
|
|
*
|
|
* @private
|
|
*/
|
|
TimeDynamicPointCloud.prototype._getAverageLoadTime = function () {
|
|
if (this._runningLength === 0) {
|
|
// Before any frames have loaded make a best guess about the average load time
|
|
return 0.05;
|
|
}
|
|
return this._runningAverage;
|
|
};
|
|
|
|
var scratchDate = new JulianDate();
|
|
|
|
function getClockMultiplier(that) {
|
|
var clock = that._clock;
|
|
var isAnimating = clock.canAnimate && clock.shouldAnimate;
|
|
var multiplier = clock.multiplier;
|
|
return isAnimating ? multiplier : 0.0;
|
|
}
|
|
|
|
function getIntervalIndex(that, interval) {
|
|
return that._intervals.indexOf(interval.start);
|
|
}
|
|
|
|
function getNextInterval(that, currentInterval) {
|
|
var intervals = that._intervals;
|
|
var clock = that._clock;
|
|
var multiplier = getClockMultiplier(that);
|
|
|
|
if (multiplier === 0.0) {
|
|
return undefined;
|
|
}
|
|
|
|
var averageLoadTime = that._getAverageLoadTime();
|
|
var time = JulianDate.addSeconds(
|
|
clock.currentTime,
|
|
averageLoadTime * multiplier,
|
|
scratchDate
|
|
);
|
|
var index = intervals.indexOf(time);
|
|
|
|
var currentIndex = getIntervalIndex(that, currentInterval);
|
|
if (index === currentIndex) {
|
|
if (multiplier >= 0) {
|
|
++index;
|
|
} else {
|
|
--index;
|
|
}
|
|
}
|
|
|
|
// Returns undefined if not in range
|
|
return intervals.get(index);
|
|
}
|
|
|
|
function getCurrentInterval(that) {
|
|
var intervals = that._intervals;
|
|
var clock = that._clock;
|
|
var time = clock.currentTime;
|
|
var index = intervals.indexOf(time);
|
|
|
|
// Returns undefined if not in range
|
|
return intervals.get(index);
|
|
}
|
|
|
|
function reachedInterval(that, currentInterval, nextInterval) {
|
|
var multiplier = getClockMultiplier(that);
|
|
var currentIndex = getIntervalIndex(that, currentInterval);
|
|
var nextIndex = getIntervalIndex(that, nextInterval);
|
|
|
|
if (multiplier >= 0) {
|
|
return currentIndex >= nextIndex;
|
|
}
|
|
return currentIndex <= nextIndex;
|
|
}
|
|
|
|
function handleFrameFailure(that, uri) {
|
|
return function (error) {
|
|
var message = defined(error.message) ? error.message : error.toString();
|
|
if (that.frameFailed.numberOfListeners > 0) {
|
|
that.frameFailed.raiseEvent({
|
|
uri: uri,
|
|
message: message,
|
|
});
|
|
} else {
|
|
console.log("A frame failed to load: " + uri);
|
|
console.log("Error: " + message);
|
|
}
|
|
};
|
|
}
|
|
|
|
function requestFrame(that, interval, frameState) {
|
|
var index = getIntervalIndex(that, interval);
|
|
var frames = that._frames;
|
|
var frame = frames[index];
|
|
if (!defined(frame)) {
|
|
var transformArray = interval.data.transform;
|
|
var transform = defined(transformArray)
|
|
? Matrix4.fromArray(transformArray)
|
|
: undefined;
|
|
var uri = interval.data.uri;
|
|
frame = {
|
|
pointCloud: undefined,
|
|
transform: transform,
|
|
timestamp: getTimestamp(),
|
|
sequential: true,
|
|
ready: false,
|
|
touchedFrameNumber: frameState.frameNumber,
|
|
};
|
|
frames[index] = frame;
|
|
Resource.fetchArrayBuffer({
|
|
url: uri,
|
|
})
|
|
.then(function (arrayBuffer) {
|
|
// PERFORMANCE_IDEA: share a memory pool, render states, shaders, and other resources among all
|
|
// frames. Each frame just needs an index/offset into the pool.
|
|
frame.pointCloud = new PointCloud({
|
|
arrayBuffer: arrayBuffer,
|
|
cull: true,
|
|
fragmentShaderLoaded: getFragmentShaderLoaded,
|
|
uniformMapLoaded: getUniformMapLoaded(that),
|
|
pickIdLoaded: getPickIdLoaded,
|
|
});
|
|
return frame.pointCloud.readyPromise;
|
|
})
|
|
.otherwise(handleFrameFailure(that, uri));
|
|
}
|
|
return frame;
|
|
}
|
|
|
|
function updateAverageLoadTime(that, loadTime) {
|
|
that._runningSum += loadTime;
|
|
that._runningSum -= that._runningSamples[that._runningIndex];
|
|
that._runningSamples[that._runningIndex] = loadTime;
|
|
that._runningLength = Math.min(
|
|
that._runningLength + 1,
|
|
that._runningSamples.length
|
|
);
|
|
that._runningIndex = (that._runningIndex + 1) % that._runningSamples.length;
|
|
that._runningAverage = that._runningSum / that._runningLength;
|
|
}
|
|
|
|
function prepareFrame(that, frame, updateState, frameState) {
|
|
if (frame.touchedFrameNumber < frameState.frameNumber - 1) {
|
|
// If this frame was not loaded in sequential updates then it can't be used it for calculating the average load time.
|
|
// For example: selecting a frame on the timeline, selecting another frame before the request finishes, then selecting this frame later.
|
|
frame.sequential = false;
|
|
}
|
|
|
|
var pointCloud = frame.pointCloud;
|
|
|
|
if (defined(pointCloud) && !frame.ready) {
|
|
// Call update to prepare renderer resources. Don't render anything yet.
|
|
var commandList = frameState.commandList;
|
|
var lengthBeforeUpdate = commandList.length;
|
|
renderFrame(that, frame, updateState, frameState);
|
|
|
|
if (pointCloud.ready) {
|
|
// Point cloud became ready this update
|
|
frame.ready = true;
|
|
that._totalMemoryUsageInBytes += pointCloud.geometryByteLength;
|
|
commandList.length = lengthBeforeUpdate; // Don't allow preparing frame to insert commands.
|
|
if (frame.sequential) {
|
|
// Update the values used to calculate average load time
|
|
var loadTime = (getTimestamp() - frame.timestamp) / 1000.0;
|
|
updateAverageLoadTime(that, loadTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
frame.touchedFrameNumber = frameState.frameNumber;
|
|
}
|
|
|
|
var scratchModelMatrix = new Matrix4();
|
|
|
|
function getGeometricError(that, pointCloud) {
|
|
var shading = that.shading;
|
|
if (defined(shading) && defined(shading.baseResolution)) {
|
|
return shading.baseResolution;
|
|
} else if (defined(pointCloud.boundingSphere)) {
|
|
return CesiumMath.cbrt(
|
|
pointCloud.boundingSphere.volume() / pointCloud.pointsLength
|
|
);
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
function getMaximumAttenuation(that) {
|
|
var shading = that.shading;
|
|
if (defined(shading) && defined(shading.maximumAttenuation)) {
|
|
return shading.maximumAttenuation;
|
|
}
|
|
|
|
// Return a hardcoded maximum attenuation. For a tileset this would instead be the maximum screen space error.
|
|
return 10.0;
|
|
}
|
|
|
|
var defaultShading = new PointCloudShading();
|
|
|
|
function renderFrame(that, frame, updateState, frameState) {
|
|
var shading = defaultValue(that.shading, defaultShading);
|
|
var pointCloud = frame.pointCloud;
|
|
var transform = defaultValue(frame.transform, Matrix4.IDENTITY);
|
|
pointCloud.modelMatrix = Matrix4.multiplyTransformation(
|
|
that.modelMatrix,
|
|
transform,
|
|
scratchModelMatrix
|
|
);
|
|
pointCloud.style = that.style;
|
|
pointCloud.time = updateState.timeSinceLoad;
|
|
pointCloud.shadows = that.shadows;
|
|
pointCloud.clippingPlanes = that._clippingPlanes;
|
|
pointCloud.isClipped = updateState.isClipped;
|
|
pointCloud.attenuation = shading.attenuation;
|
|
pointCloud.backFaceCulling = shading.backFaceCulling;
|
|
pointCloud.normalShading = shading.normalShading;
|
|
pointCloud.geometricError = getGeometricError(that, pointCloud);
|
|
pointCloud.geometricErrorScale = shading.geometricErrorScale;
|
|
pointCloud.maximumAttenuation = getMaximumAttenuation(that);
|
|
|
|
pointCloud.update(frameState);
|
|
frame.touchedFrameNumber = frameState.frameNumber;
|
|
}
|
|
|
|
function loadFrame(that, interval, updateState, frameState) {
|
|
var frame = requestFrame(that, interval, frameState);
|
|
prepareFrame(that, frame, updateState, frameState);
|
|
}
|
|
|
|
function getUnloadCondition(frameState) {
|
|
return function (frame) {
|
|
// Unload all frames that aren't currently being loaded or rendered
|
|
return frame.touchedFrameNumber < frameState.frameNumber;
|
|
};
|
|
}
|
|
|
|
function unloadFrames(that, unloadCondition) {
|
|
var frames = that._frames;
|
|
var length = frames.length;
|
|
for (var i = 0; i < length; ++i) {
|
|
var frame = frames[i];
|
|
if (defined(frame)) {
|
|
if (!defined(unloadCondition) || unloadCondition(frame)) {
|
|
var pointCloud = frame.pointCloud;
|
|
if (frame.ready) {
|
|
that._totalMemoryUsageInBytes -= pointCloud.geometryByteLength;
|
|
}
|
|
if (defined(pointCloud)) {
|
|
pointCloud.destroy();
|
|
}
|
|
if (frame === that._lastRenderedFrame) {
|
|
that._lastRenderedFrame = undefined;
|
|
}
|
|
frames[i] = undefined;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function getFrame(that, interval) {
|
|
var index = getIntervalIndex(that, interval);
|
|
var frame = that._frames[index];
|
|
if (defined(frame) && frame.ready) {
|
|
return frame;
|
|
}
|
|
}
|
|
|
|
function updateInterval(that, interval, frame, updateState, frameState) {
|
|
if (defined(frame)) {
|
|
if (frame.ready) {
|
|
return true;
|
|
}
|
|
loadFrame(that, interval, updateState, frameState);
|
|
return frame.ready;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function getNearestReadyInterval(
|
|
that,
|
|
previousInterval,
|
|
currentInterval,
|
|
updateState,
|
|
frameState
|
|
) {
|
|
var i;
|
|
var interval;
|
|
var frame;
|
|
var intervals = that._intervals;
|
|
var frames = that._frames;
|
|
var currentIndex = getIntervalIndex(that, currentInterval);
|
|
var previousIndex = getIntervalIndex(that, previousInterval);
|
|
|
|
if (currentIndex >= previousIndex) {
|
|
// look backwards
|
|
for (i = currentIndex; i >= previousIndex; --i) {
|
|
interval = intervals.get(i);
|
|
frame = frames[i];
|
|
if (updateInterval(that, interval, frame, updateState, frameState)) {
|
|
return interval;
|
|
}
|
|
}
|
|
} else {
|
|
// look forwards
|
|
for (i = currentIndex; i <= previousIndex; ++i) {
|
|
interval = intervals.get(i);
|
|
frame = frames[i];
|
|
if (updateInterval(that, interval, frame, updateState, frameState)) {
|
|
return interval;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If no intervals are ready return the previous interval
|
|
return previousInterval;
|
|
}
|
|
|
|
function setFramesDirty(that, clippingPlanesDirty, styleDirty) {
|
|
var frames = that._frames;
|
|
var framesLength = frames.length;
|
|
for (var i = 0; i < framesLength; ++i) {
|
|
var frame = frames[i];
|
|
if (defined(frame) && defined(frame.pointCloud)) {
|
|
frame.pointCloud.clippingPlanesDirty = clippingPlanesDirty;
|
|
frame.pointCloud.styleDirty = styleDirty;
|
|
}
|
|
}
|
|
}
|
|
|
|
var updateState = {
|
|
timeSinceLoad: 0,
|
|
isClipped: false,
|
|
clippingPlanesDirty: false,
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
TimeDynamicPointCloud.prototype.update = function (frameState) {
|
|
if (frameState.mode === SceneMode.MORPHING) {
|
|
return;
|
|
}
|
|
|
|
if (!this.show) {
|
|
return;
|
|
}
|
|
|
|
if (!defined(this._pickId)) {
|
|
this._pickId = frameState.context.createPickId({
|
|
primitive: this,
|
|
});
|
|
}
|
|
|
|
if (!defined(this._loadTimestamp)) {
|
|
this._loadTimestamp = JulianDate.clone(frameState.time);
|
|
}
|
|
|
|
// For styling
|
|
var timeSinceLoad = Math.max(
|
|
JulianDate.secondsDifference(frameState.time, this._loadTimestamp) * 1000,
|
|
0.0
|
|
);
|
|
|
|
// Update clipping planes
|
|
var clippingPlanes = this._clippingPlanes;
|
|
var clippingPlanesState = 0;
|
|
var clippingPlanesDirty = false;
|
|
var isClipped = defined(clippingPlanes) && clippingPlanes.enabled;
|
|
|
|
if (isClipped) {
|
|
clippingPlanes.update(frameState);
|
|
clippingPlanesState = clippingPlanes.clippingPlanesState;
|
|
}
|
|
|
|
if (this._clippingPlanesState !== clippingPlanesState) {
|
|
this._clippingPlanesState = clippingPlanesState;
|
|
clippingPlanesDirty = true;
|
|
}
|
|
|
|
var styleDirty = this._styleDirty;
|
|
this._styleDirty = false;
|
|
|
|
if (clippingPlanesDirty || styleDirty) {
|
|
setFramesDirty(this, clippingPlanesDirty, styleDirty);
|
|
}
|
|
|
|
updateState.timeSinceLoad = timeSinceLoad;
|
|
updateState.isClipped = isClipped;
|
|
|
|
var shading = this.shading;
|
|
var eyeDomeLighting = this._pointCloudEyeDomeLighting;
|
|
|
|
var commandList = frameState.commandList;
|
|
var lengthBeforeUpdate = commandList.length;
|
|
|
|
var previousInterval = this._previousInterval;
|
|
var nextInterval = this._nextInterval;
|
|
var currentInterval = getCurrentInterval(this);
|
|
|
|
if (!defined(currentInterval)) {
|
|
return;
|
|
}
|
|
|
|
var clockMultiplierChanged = false;
|
|
var clockMultiplier = getClockMultiplier(this);
|
|
var clockPaused = clockMultiplier === 0;
|
|
if (clockMultiplier !== this._clockMultiplier) {
|
|
clockMultiplierChanged = true;
|
|
this._clockMultiplier = clockMultiplier;
|
|
}
|
|
|
|
if (!defined(previousInterval) || clockPaused) {
|
|
previousInterval = currentInterval;
|
|
}
|
|
|
|
if (
|
|
!defined(nextInterval) ||
|
|
clockMultiplierChanged ||
|
|
reachedInterval(this, currentInterval, nextInterval)
|
|
) {
|
|
nextInterval = getNextInterval(this, currentInterval);
|
|
}
|
|
|
|
previousInterval = getNearestReadyInterval(
|
|
this,
|
|
previousInterval,
|
|
currentInterval,
|
|
updateState,
|
|
frameState
|
|
);
|
|
var frame = getFrame(this, previousInterval);
|
|
|
|
if (!defined(frame)) {
|
|
// The frame is not ready to render. This can happen when the simulation starts or when scrubbing the timeline
|
|
// to a frame that hasn't loaded yet. Just render the last rendered frame in its place until it finishes loading.
|
|
loadFrame(this, previousInterval, updateState, frameState);
|
|
frame = this._lastRenderedFrame;
|
|
}
|
|
|
|
if (defined(frame)) {
|
|
renderFrame(this, frame, updateState, frameState);
|
|
}
|
|
|
|
if (defined(nextInterval)) {
|
|
// Start loading the next frame
|
|
loadFrame(this, nextInterval, updateState, frameState);
|
|
}
|
|
|
|
var that = this;
|
|
if (defined(frame) && !defined(this._lastRenderedFrame)) {
|
|
frameState.afterRender.push(function () {
|
|
that._readyPromise.resolve(that);
|
|
});
|
|
}
|
|
|
|
if (defined(frame) && frame !== this._lastRenderedFrame) {
|
|
if (that.frameChanged.numberOfListeners > 0) {
|
|
frameState.afterRender.push(function () {
|
|
that.frameChanged.raiseEvent(that);
|
|
});
|
|
}
|
|
}
|
|
|
|
this._previousInterval = previousInterval;
|
|
this._nextInterval = nextInterval;
|
|
this._lastRenderedFrame = frame;
|
|
|
|
var totalMemoryUsageInBytes = this._totalMemoryUsageInBytes;
|
|
var maximumMemoryUsageInBytes = this.maximumMemoryUsage * 1024 * 1024;
|
|
|
|
if (totalMemoryUsageInBytes > maximumMemoryUsageInBytes) {
|
|
unloadFrames(this, getUnloadCondition(frameState));
|
|
}
|
|
|
|
var lengthAfterUpdate = commandList.length;
|
|
var addedCommandsLength = lengthAfterUpdate - lengthBeforeUpdate;
|
|
|
|
if (
|
|
defined(shading) &&
|
|
shading.attenuation &&
|
|
shading.eyeDomeLighting &&
|
|
addedCommandsLength > 0
|
|
) {
|
|
eyeDomeLighting.update(
|
|
frameState,
|
|
lengthBeforeUpdate,
|
|
shading,
|
|
this.boundingSphere
|
|
);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns true if this object was destroyed; otherwise, false.
|
|
* <br /><br />
|
|
* If this object was destroyed, it should not be used; calling any function other than
|
|
* <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
|
|
*
|
|
* @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
|
|
*
|
|
* @see TimeDynamicPointCloud#destroy
|
|
*/
|
|
TimeDynamicPointCloud.prototype.isDestroyed = function () {
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
|
|
* release of WebGL resources, instead of relying on the garbage collector to destroy this object.
|
|
* <br /><br />
|
|
* Once an object is destroyed, it should not be used; calling any function other than
|
|
* <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
|
|
* assign the return value (<code>undefined</code>) to the object as done in the example.
|
|
*
|
|
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
|
|
*
|
|
* @example
|
|
* pointCloud = pointCloud && pointCloud.destroy();
|
|
*
|
|
* @see TimeDynamicPointCloud#isDestroyed
|
|
*/
|
|
TimeDynamicPointCloud.prototype.destroy = function () {
|
|
unloadFrames(this);
|
|
this._clippingPlanes = this._clippingPlanes && this._clippingPlanes.destroy();
|
|
this._pickId = this._pickId && this._pickId.destroy();
|
|
return destroyObject(this);
|
|
};
|
|
export default TimeDynamicPointCloud;
|