Skip to content

Commit

Permalink
Merge pull request #8769 from CesiumGS/anim-start-stop
Browse files Browse the repository at this point in the history
Clamp non-looped animations to start and stop times.
  • Loading branch information
lilleyse authored Apr 23, 2020
2 parents 70f8445 + bb6ac69 commit 4ca9e1b
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 24 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

- Fixed several problems with polylines when the logarithmic depth buffer is enabled, which is the default on most systems. [#8706](https://github.com/CesiumGS/cesium/pull/8706)
- Fixed an issue with glTF skinning support where an optional property `skeleton` was considered required by Cesium. [#8175](https://github.com/CesiumGS/cesium/issues/8175)
- Fixed an issue with clamping of non-looped glTF animations. Subscribers to animation `update` events should expect one additional event firing as an animation stops. [#7387](https://github.com/CesiumGS/cesium/issues/7387)
- Fixed a bug with very long view ranges requiring multiple frustums even with the logarithmic depth buffer enabled. Previously, such scenes could resolve depth incorrectly. [#8727](https://github.com/CesiumGS/cesium/pull/8727)
- Fixed a bug where the elevation contour material's alpha was not being applied. [#8749](https://github.com/CesiumGS/cesium/pull/8749)

Expand Down
36 changes: 23 additions & 13 deletions Source/Scene/ModelAnimationCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,16 @@ ModelAnimationCollection.prototype.update = function (frameState) {
duration !== 0.0
? JulianDate.secondsDifference(sceneTime, startTime) / duration
: 0.0;

// Clamp delta to stop time, if defined.
if (
duration !== 0.0 &&
defined(stopTime) &&
JulianDate.greaterThan(sceneTime, stopTime)
) {
delta = JulianDate.secondsDifference(stopTime, startTime) / duration;
}

var pastStartTime = delta >= 0.0;

// Play animation if
Expand All @@ -418,9 +428,10 @@ ModelAnimationCollection.prototype.update = function (frameState) {
(delta <= 1.0 || repeat) &&
(!defined(stopTime) || JulianDate.lessThanOrEquals(sceneTime, stopTime));

if (play) {
// If it IS, or WAS, animating...
if (play || scheduledAnimation._state === ModelAnimationState.ANIMATING) {
// STOPPED -> ANIMATING state transition?
if (scheduledAnimation._state === ModelAnimationState.STOPPED) {
if (play && scheduledAnimation._state === ModelAnimationState.STOPPED) {
scheduledAnimation._state = ModelAnimationState.ANIMATING;
if (scheduledAnimation.start.numberOfListeners > 0) {
frameState.afterRender.push(scheduledAnimation._raiseStartEvent);
Expand Down Expand Up @@ -458,18 +469,17 @@ ModelAnimationCollection.prototype.update = function (frameState) {
frameState.afterRender.push(scheduledAnimation._raiseUpdateEvent);
}
animationOccured = true;
} else if (
pastStartTime &&
scheduledAnimation._state === ModelAnimationState.ANIMATING
) {
// ANIMATING -> STOPPED state transition?
scheduledAnimation._state = ModelAnimationState.STOPPED;
if (scheduledAnimation.stop.numberOfListeners > 0) {
frameState.afterRender.push(scheduledAnimation._raiseStopEvent);
}

if (scheduledAnimation.removeOnStop) {
animationsToRemove.push(scheduledAnimation);
if (!play) {
// ANIMATING -> STOPPED state transition?
scheduledAnimation._state = ModelAnimationState.STOPPED;
if (scheduledAnimation.stop.numberOfListeners > 0) {
frameState.afterRender.push(scheduledAnimation._raiseStopEvent);
}

if (scheduledAnimation.removeOnStop) {
animationsToRemove.push(scheduledAnimation);
}
}
}
}
Expand Down
139 changes: 128 additions & 11 deletions Specs/Scene/ModelSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,7 @@ describe(
});
});

it("Applies the right render state", function () {
it("applies the right render state", function () {
spyOn(RenderState, "fromCache").and.callThrough();

// Simulate using procedural glTF as opposed to loading it from a file
Expand Down Expand Up @@ -1773,7 +1773,7 @@ describe(
).then(function () {
expect(spyStart).toHaveBeenCalledWith(animBoxesModel, a);

expect(spyUpdate.calls.count()).toEqual(4);
expect(spyUpdate.calls.count()).toEqual(5);
expect(spyUpdate.calls.argsFor(0)[0]).toBe(animBoxesModel);
expect(spyUpdate.calls.argsFor(0)[1]).toBe(a);
expect(spyUpdate.calls.argsFor(0)[2]).toEqualEpsilon(
Expand All @@ -1792,14 +1792,18 @@ describe(
3.0,
CesiumMath.EPSILON14
);
expect(spyUpdate.calls.argsFor(4)[2]).toEqualEpsilon(
3.708, // Expect animation to have reached its final value.
CesiumMath.EPSILON3
);

expect(spyStop).toHaveBeenCalledWith(animBoxesModel, a);
expect(animations.length).toEqual(0);
animBoxesModel.show = false;
});
});

it("Animates with a delay", function () {
it("animates with a delay", function () {
var time = JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC"));

var animations = animBoxesModel.activeAnimations;
Expand All @@ -1822,7 +1826,7 @@ describe(
animBoxesModel.show = false;
});

it("Animates with an explicit stopTime", function () {
it("animates with an explicit stopTime", function () {
var time = JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC"));
var stopTime = JulianDate.fromDate(
new Date("January 1, 2014 12:00:01 UTC")
Expand All @@ -1841,9 +1845,9 @@ describe(
animBoxesModel.show = true;
scene.renderForSpecs(time);
scene.renderForSpecs(JulianDate.addSeconds(time, 1.0, new JulianDate()));
scene.renderForSpecs(JulianDate.addSeconds(time, 2.0, new JulianDate())); // Does not fire update
scene.renderForSpecs(JulianDate.addSeconds(time, 2.0, new JulianDate()));

expect(spyUpdate.calls.count()).toEqual(2);
expect(spyUpdate.calls.count()).toEqual(3);
expect(spyUpdate.calls.argsFor(0)[2]).toEqualEpsilon(
0.0,
CesiumMath.EPSILON14
Expand All @@ -1852,11 +1856,15 @@ describe(
1.0,
CesiumMath.EPSILON14
);
expect(spyUpdate.calls.argsFor(2)[2]).toEqualEpsilon(
1.0,
CesiumMath.EPSILON14
);
expect(animations.remove(a)).toEqual(true);
animBoxesModel.show = false;
});

it("Animates with a multiplier", function () {
it("animates with a multiplier", function () {
var time = JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC"));
var animations = animBoxesModel.activeAnimations;
var a = animations.add({
Expand Down Expand Up @@ -1890,7 +1898,116 @@ describe(
animBoxesModel.show = false;
});

it("Animates in reverse", function () {
it("finishes an animation after the stop time", function () {
var time = JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC"));
var stopTime = JulianDate.fromDate(
new Date("January 1, 2014 12:00:01 UTC")
);

var animations = animBoxesModel.activeAnimations;
var a = animations.add({
name: "animation_1",
startTime: time,
stopTime: stopTime,
});

var spyUpdate = jasmine.createSpy("listener");
a.update.addEventListener(spyUpdate);

animBoxesModel.show = true;
scene.renderForSpecs(time);
scene.renderForSpecs(JulianDate.addSeconds(time, 0.5, new JulianDate())); // Midpoint of designated interval
scene.renderForSpecs(JulianDate.addSeconds(time, 2.0, new JulianDate())); // Past designated stop time

expect(spyUpdate.calls.count()).toEqual(3);
expect(spyUpdate.calls.argsFor(0)[2]).toEqualEpsilon(
0.0,
CesiumMath.EPSILON14
);
expect(spyUpdate.calls.argsFor(1)[2]).toEqualEpsilon(
0.5,
CesiumMath.EPSILON14
);
expect(spyUpdate.calls.argsFor(2)[2]).toEqualEpsilon(
1.0, // Expect clamping to designated stop time.
CesiumMath.EPSILON14
);
expect(animations.remove(a)).toEqual(true);
animBoxesModel.show = false;
});

it("finishes an animation after it runs off the end", function () {
var time = JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC"));
var animations = animBoxesModel.activeAnimations;
var a = animations.add({
name: "animation_1",
startTime: time,
});

var spyUpdate = jasmine.createSpy("listener");
a.update.addEventListener(spyUpdate);

animBoxesModel.show = true;
scene.renderForSpecs(time);
scene.renderForSpecs(JulianDate.addSeconds(time, 0.5, new JulianDate())); // Somewhere inside animation
scene.renderForSpecs(JulianDate.addSeconds(time, 10.0, new JulianDate())); // Way past end of animation

expect(spyUpdate.calls.count()).toEqual(3);
expect(spyUpdate.calls.argsFor(0)[2]).toEqualEpsilon(
0.0,
CesiumMath.EPSILON14
);
expect(spyUpdate.calls.argsFor(1)[2]).toEqualEpsilon(
0.5,
CesiumMath.EPSILON14
);
expect(spyUpdate.calls.argsFor(2)[2]).toEqualEpsilon(
3.708, // Expect animation to have reached its final value.
CesiumMath.EPSILON3
);
expect(animations.remove(a)).toEqual(true);
animBoxesModel.show = false;
});

it("halts an animation before the start time", function () {
var time = JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC"));
var stopTime = JulianDate.fromDate(
new Date("January 1, 2014 12:00:01 UTC")
);

var animations = animBoxesModel.activeAnimations;
var a = animations.add({
name: "animation_1",
startTime: time,
stopTime: stopTime,
});

var spyUpdate = jasmine.createSpy("listener");
a.update.addEventListener(spyUpdate);

animBoxesModel.show = true;
scene.renderForSpecs(time);
scene.renderForSpecs(JulianDate.addSeconds(time, 0.5, new JulianDate())); // Midpoint of animation
scene.renderForSpecs(JulianDate.addSeconds(time, -1.0, new JulianDate())); // Before start of animation

expect(spyUpdate.calls.count()).toEqual(3);
expect(spyUpdate.calls.argsFor(0)[2]).toEqualEpsilon(
0.0,
CesiumMath.EPSILON14
);
expect(spyUpdate.calls.argsFor(1)[2]).toEqualEpsilon(
0.5,
CesiumMath.EPSILON14
);
expect(spyUpdate.calls.argsFor(2)[2]).toEqualEpsilon(
0.0,
CesiumMath.EPSILON14
);
expect(animations.remove(a)).toEqual(true);
animBoxesModel.show = false;
});

it("animates in reverse", function () {
var time = JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC"));
var animations = animBoxesModel.activeAnimations;
var a = animations.add({
Expand Down Expand Up @@ -1929,7 +2046,7 @@ describe(
animBoxesModel.show = false;
});

it("Animates with REPEAT", function () {
it("animates with REPEAT", function () {
var time = JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC"));
var animations = animBoxesModel.activeAnimations;
var a = animations.add({
Expand Down Expand Up @@ -1983,7 +2100,7 @@ describe(
animBoxesModel.show = false;
});

it("Animates with MIRRORED_REPEAT", function () {
it("animates with MIRRORED_REPEAT", function () {
var time = JulianDate.fromDate(new Date("January 1, 2014 12:00:00 UTC"));
var animations = animBoxesModel.activeAnimations;
var a = animations.add({
Expand Down Expand Up @@ -2037,7 +2154,7 @@ describe(
animBoxesModel.show = false;
});

it("Animates and renders", function () {
it("animates and renders", function () {
return loadModel(animBoxesUrl, {
scale: 2.0,
}).then(function (m) {
Expand Down

0 comments on commit 4ca9e1b

Please sign in to comment.