Skip to content

Commit

Permalink
Merge pull request #6004 from likangning93/multipleAnimations
Browse files Browse the repository at this point in the history
Added clamped and asynchronous loop handling for multiple animations
  • Loading branch information
lilleyse authored Nov 28, 2017
2 parents 7799186 + 8d458cb commit cb632d8
Show file tree
Hide file tree
Showing 14 changed files with 230 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Change Log
* Added ability to support touch event in Imagery Layers Split demo application. [#5948](https://github.com/AnalyticalGraphicsInc/cesium/pull/5948)
* Fixed `Invalid asm.js: Invalid member of stdlib` console error by recompiling crunch.js with latest emscripten toolchain. [#5847](https://github.com/AnalyticalGraphicsInc/cesium/issues/5847)
* Added CZML support for `polyline.depthFailMaterial`, `label.scaleByDistance`, `distanceDisplayCondition`, and `disableDepthTestDistance`. [#5986](https://github.com/AnalyticalGraphicsInc/cesium/pull/5986)
* Fixed a bug where models with animations of different lengths would cause an error. [#5694](https://github.com/AnalyticalGraphicsInc/cesium/issues/5694)
* Added a `clampAnimations` parameter to `Model` and `Entity.model`. Setting this to `false` allows different length animations to loop asynchronously over the duration of the longest animation.

### 1.39 - 2017-11-01

Expand Down
18 changes: 18 additions & 0 deletions Source/Core/CatmullRomSpline.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,24 @@ define([
*/
CatmullRomSpline.prototype.findTimeInterval = Spline.prototype.findTimeInterval;

/**
* Wraps the given time to the period covered by the spline.
* @function
*
* @param {Number} time The time.
* @return {Number} The time, wrapped around to the updated animation.
*/
CatmullRomSpline.prototype.wrapTime = Spline.prototype.wrapTime;

/**
* Clamps the given time to the period covered by the spline.
* @function
*
* @param {Number} time The time.
* @return {Number} The time, clamped to the animation period.
*/
CatmullRomSpline.prototype.clampTime = Spline.prototype.clampTime;

/**
* Evaluates the curve at a given time.
*
Expand Down
18 changes: 18 additions & 0 deletions Source/Core/HermiteSpline.js
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,24 @@ define([
var scratchTimeVec = new Cartesian4();
var scratchTemp = new Cartesian3();

/**
* Wraps the given time to the period covered by the spline.
* @function
*
* @param {Number} time The time.
* @return {Number} The time, wrapped around to the updated animation.
*/
HermiteSpline.prototype.wrapTime = Spline.prototype.wrapTime;

/**
* Clamps the given time to the period covered by the spline.
* @function
*
* @param {Number} time The time.
* @return {Number} The time, clamped to the animation period.
*/
HermiteSpline.prototype.clampTime = Spline.prototype.clampTime;

/**
* Evaluates the curve at a given time.
*
Expand Down
18 changes: 18 additions & 0 deletions Source/Core/LinearSpline.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,24 @@ define([
*/
LinearSpline.prototype.findTimeInterval = Spline.prototype.findTimeInterval;

/**
* Wraps the given time to the period covered by the spline.
* @function
*
* @param {Number} time The time.
* @return {Number} The time, wrapped around to the updated animation.
*/
LinearSpline.prototype.wrapTime = Spline.prototype.wrapTime;

/**
* Clamps the given time to the period covered by the spline.
* @function
*
* @param {Number} time The time.
* @return {Number} The time, clamped to the animation period.
*/
LinearSpline.prototype.clampTime = Spline.prototype.clampTime;

/**
* Evaluates the curve at a given time.
*
Expand Down
18 changes: 18 additions & 0 deletions Source/Core/QuaternionSpline.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,24 @@ define([
*/
QuaternionSpline.prototype.findTimeInterval = Spline.prototype.findTimeInterval;

/**
* Wraps the given time to the period covered by the spline.
* @function
*
* @param {Number} time The time.
* @return {Number} The time, wrapped around to the updated animation.
*/
QuaternionSpline.prototype.wrapTime = Spline.prototype.wrapTime;

/**
* Clamps the given time to the period covered by the spline.
* @function
*
* @param {Number} time The time.
* @return {Number} The time, clamped to the animation period.
*/
QuaternionSpline.prototype.clampTime = Spline.prototype.clampTime;

/**
* Evaluates the curve at a given time.
*
Expand Down
54 changes: 51 additions & 3 deletions Source/Core/Spline.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
define([
'./Check',
'./defaultValue',
'./defined',
'./DeveloperError'
], function(
'./DeveloperError',
'./Math'
], function(
Check,
defaultValue,
defined,
DeveloperError) {
DeveloperError,
CesiumMath) {
'use strict';

/**
Expand Down Expand Up @@ -117,5 +121,49 @@ define([
return i;
};

/**
* Wraps the given time to the period covered by the spline.
* @function
*
* @param {Number} time The time.
* @return {Number} The time, wrapped around the animation period.
*/
Spline.prototype.wrapTime = function(time) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number('time', time);
//>>includeEnd('debug');

var times = this.times;
var timeEnd = times[times.length - 1];
var timeStart = times[0];
var timeStretch = timeEnd - timeStart;
var divs;
if (time < timeStart) {
divs = Math.floor((timeStart - time) / timeStretch) + 1;
time += divs * timeStretch;
}
if (time > timeEnd) {
divs = Math.floor((time - timeEnd) / timeStretch) + 1;
time -= divs * timeStretch;
}
return time;
};

/**
* Clamps the given time to the period covered by the spline.
* @function
*
* @param {Number} time The time.
* @return {Number} The time, clamped to the animation period.
*/
Spline.prototype.clampTime = function(time) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number('time', time);
//>>includeEnd('debug');

var times = this.times;
return CesiumMath.clamp(time, times[0], times[times.length - 1]);
};

return Spline;
});
18 changes: 18 additions & 0 deletions Source/Core/WeightSpline.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,24 @@ define([
*/
WeightSpline.prototype.findTimeInterval = Spline.prototype.findTimeInterval;

/**
* Wraps the given time to the period covered by the spline.
* @function
*
* @param {Number} time The time.
* @return {Number} The time, wrapped around to the updated animation.
*/
WeightSpline.prototype.wrapTime = Spline.prototype.wrapTime;

/**
* Clamps the given time to the period covered by the spline.
* @function
*
* @param {Number} time The time.
* @return {Number} The time, clamped to the animation period.
*/
WeightSpline.prototype.clampTime = Spline.prototype.clampTime;

/**
* Evaluates the curve at a given time.
*
Expand Down
1 change: 1 addition & 0 deletions Source/DataSources/CzmlDataSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -1612,6 +1612,7 @@ define([
processPacketData(Number, model, 'maximumScale', modelData.maximumScale, interval, sourceUri, entityCollection, query);
processPacketData(Boolean, model, 'incrementallyLoadTextures', modelData.incrementallyLoadTextures, interval, sourceUri, entityCollection, query);
processPacketData(Boolean, model, 'runAnimations', modelData.runAnimations, interval, sourceUri, entityCollection, query);
processPacketData(Boolean, model, 'clampAnimations', modelData.clampAnimations, interval, sourceUri, entityCollection, query);
processPacketData(ShadowMode, model, 'shadows', modelData.shadows, interval, sourceUri, entityCollection, query);
processPacketData(HeightReference, model, 'heightReference', modelData.heightReference, interval, sourceUri, entityCollection, query);
processPacketData(Color, model, 'silhouetteColor', modelData.silhouetteColor, interval, sourceUri, entityCollection, query);
Expand Down
12 changes: 12 additions & 0 deletions Source/DataSources/ModelGraphics.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ define([
* @param {Property} [options.maximumScale] The maximum scale size of a model. An upper limit for minimumPixelSize.
* @param {Property} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded.
* @param {Property} [options.runAnimations=true] A boolean Property specifying if glTF animations specified in the model should be started.
* @param {Property} [options.clampAnimations=true] A boolean Property specifying if glTF animations should hold the last pose for time durations with no keyframes.
* @param {Property} [options.nodeTransformations] An object, where keys are names of nodes, and values are {@link TranslationRotationScale} Properties describing the transformation to apply to that node.
* @param {Property} [options.shadows=ShadowMode.ENABLED] An enum Property specifying whether the model casts or receives shadows from each light source.
* @param {Property} [options.heightReference=HeightReference.NONE] A Property specifying what the height is relative to.
Expand Down Expand Up @@ -74,6 +75,7 @@ define([
this._uri = undefined;
this._uriSubscription = undefined;
this._runAnimations = undefined;
this._clampAnimations = undefined;
this._runAnimationsSubscription = undefined;
this._nodeTransformations = undefined;
this._nodeTransformationsSubscription = undefined;
Expand Down Expand Up @@ -179,6 +181,14 @@ define([
*/
runAnimations : createPropertyDescriptor('runAnimations'),

/**
* Gets or sets the boolean Property specifying if glTF animations should hold the last pose for time durations with no keyframes.
* @memberof ModelGraphics.prototype
* @type {Property}
* @default true
*/
clampAnimations : createPropertyDescriptor('clampAnimations'),

/**
* Gets or sets the set of node transformations to apply to this model. This is represented as an {@link PropertyBag}, where keys are
* names of nodes, and values are {@link TranslationRotationScale} Properties describing the transformation to apply to that node.
Expand Down Expand Up @@ -263,6 +273,7 @@ define([
result.shadows = this.shadows;
result.uri = this.uri;
result.runAnimations = this.runAnimations;
result.clampAnimations = this.clampAnimations;
result.nodeTransformations = this.nodeTransformations;
result.heightReference = this._heightReference;
result.distanceDisplayCondition = this.distanceDisplayCondition;
Expand Down Expand Up @@ -296,6 +307,7 @@ define([
this.shadows = defaultValue(this.shadows, source.shadows);
this.uri = defaultValue(this.uri, source.uri);
this.runAnimations = defaultValue(this.runAnimations, source.runAnimations);
this.clampAnimations = defaultValue(this.clampAnimations, source.clampAnimations);
this.heightReference = defaultValue(this.heightReference, source.heightReference);
this.distanceDisplayCondition = defaultValue(this.distanceDisplayCondition, source.distanceDisplayCondition);
this.silhouetteColor = defaultValue(this.silhouetteColor, source.silhouetteColor);
Expand Down
2 changes: 2 additions & 0 deletions Source/DataSources/ModelVisualizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ define([
var defaultScale = 1.0;
var defaultMinimumPixelSize = 0.0;
var defaultIncrementallyLoadTextures = true;
var defaultClampAnimations = true;
var defaultShadows = ShadowMode.ENABLED;
var defaultHeightReference = HeightReference.NONE;
var defaultSilhouetteColor = Color.RED;
Expand Down Expand Up @@ -152,6 +153,7 @@ define([
model.color = Property.getValueOrDefault(modelGraphics._color, time, defaultColor, model._color);
model.colorBlendMode = Property.getValueOrDefault(modelGraphics._colorBlendMode, time, defaultColorBlendMode);
model.colorBlendAmount = Property.getValueOrDefault(modelGraphics._colorBlendAmount, time, defaultColorBlendAmount);
model.clampAnimations = Property.getValueOrDefault(modelGraphics._clampAnimations, time, defaultClampAnimations);

if (model.ready) {
var runAnimations = Property.getValueOrDefault(modelGraphics._runAnimations, time, true);
Expand Down
10 changes: 10 additions & 0 deletions Source/Scene/Model.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ define([
* @param {Boolean} [options.allowPicking=true] When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}.
* @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded.
* @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded.
* @param {Boolean} [options.clampAnimations=true] Determines if the model's animations should hold a pose over frames where no keyframes are specified.
* @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the model casts or receives shadows from each light source.
* @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model.
* @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe.
Expand Down Expand Up @@ -529,6 +530,13 @@ define([
*/
this.activeAnimations = new ModelAnimationCollection(this);

/**
* Determines if the model's animations should hold a pose over frames where no keyframes are specified.
*
* @type {Boolean}
*/
this.clampAnimations = defaultValue(options.clampAnimations, true);

this._defaultTexture = undefined;
this._incrementallyLoadTextures = defaultValue(options.incrementallyLoadTextures, true);
this._asynchronous = defaultValue(options.asynchronous, true);
Expand Down Expand Up @@ -1110,6 +1118,7 @@ define([
* @param {Boolean} [options.allowPicking=true] When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}.
* @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded.
* @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded.
* @param {Boolean} [options.clampAnimations=true] Determines if the model's animations should hold a pose over frames where no keyframes are specified.
* @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the model casts or receives shadows from each light source.
* @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each {@link DrawCommand} in the model.
* @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe.
Expand Down Expand Up @@ -2488,6 +2497,7 @@ define([
// return;
//}
if (defined(spline)) {
localAnimationTime = model.clampAnimations ? spline.clampTime(localAnimationTime) : spline.wrapTime(localAnimationTime);
runtimeNode[targetPath] = spline.evaluate(localAnimationTime, runtimeNode[targetPath]);
runtimeNode.dirtyNumber = model._maxDirtyNumber;
}
Expand Down
42 changes: 42 additions & 0 deletions Specs/Core/SplineSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,48 @@ defineSuite([
}).toThrowDeveloperError();
});

it('wraps time that is out-of-bounds', function() {
var spline = HermiteSpline.createNaturalCubic({
points : [Cartesian3.ZERO, Cartesian3.UNIT_X, Cartesian3.UNIT_Y],
times : [0.0, 1.0, 2.0]
});

expect(spline.wrapTime(-0.5)).toEqual(1.5);
expect(spline.wrapTime(2.5)).toEqual(0.5);
});

it('clamps time that is out-of-bounds', function() {
var spline = HermiteSpline.createNaturalCubic({
points : [Cartesian3.ZERO, Cartesian3.UNIT_X, Cartesian3.UNIT_Y],
times : [0.0, 1.0, 2.0]
});

expect(spline.clampTime(-0.5)).toEqual(0.0);
expect(spline.clampTime(2.5)).toEqual(2.0);
});

it('wrapTime throws without a time', function() {
var spline = HermiteSpline.createNaturalCubic({
points : [Cartesian3.ZERO, Cartesian3.UNIT_X, Cartesian3.UNIT_Y],
times : [0.0, 1.0, 2.0]
});

expect(function() {
spline.wrapTime();
}).toThrowDeveloperError();
});

it('clampTime throws without a time', function() {
var spline = HermiteSpline.createNaturalCubic({
points : [Cartesian3.ZERO, Cartesian3.UNIT_X, Cartesian3.UNIT_Y],
times : [0.0, 1.0, 2.0]
});

expect(function() {
spline.clampTime();
}).toThrowDeveloperError();
});

it('findTimeInterval throws without a time', function() {
var spline = HermiteSpline.createNaturalCubic({
points : [Cartesian3.ZERO, Cartesian3.UNIT_X, Cartesian3.UNIT_Y],
Expand Down
Loading

0 comments on commit cb632d8

Please sign in to comment.