Skip to content

Commit

Permalink
improve animation handling
Browse files Browse the repository at this point in the history
The change was necessary to properly handle user input during spinning/
rock and rolling. Now, the user is still able to rotate/zoom/pan the
camera while spin/rockAndRoll are active.

It's now also possible to have both rockAndRoll and spinning enabled at
the same time without one of the two canceling the effect of the other.

The drawback of this change is that the animation classes are now tied
to the camera. Before, it would have been possible to reuse the same
animation classes to move other objects in the scene.
  • Loading branch information
biasmv committed Feb 14, 2015
1 parent 8fa5691 commit fc911d4
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 82 deletions.
3 changes: 3 additions & 0 deletions doc/viewer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,9 @@ Camera Positioning/Orientation
:param speed: The number of radians per second to rotate. When positive, rotation is in counter-clockwise direction, when negative rotation is in clockwise direction.
:return: true when spinning is enabled, false if not.
Fog and Slab Modes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
123 changes: 87 additions & 36 deletions src/gfx/animation.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,12 @@ function Animation(from, to, duration) {
this._finished = false;
}


Animation.prototype = {
setLooping : function(looping) {
this._looping = looping;
},
step : function() {
step : function(cam) {
var now = Date.now();
var elapsed = now - this._start;
var t;
Expand All @@ -65,13 +66,14 @@ Animation.prototype = {
this._finished = t === 1.0;
}
}
return this._setTo(t);
this.apply(cam, t);
return this._finished;
},

_setTo : function(t) {
apply : function(cam, t) {
var smoothInterval = (1 - Math.cos(t * Math.PI ) ) / 2;
this._current = this._from * (1-smoothInterval) + this._to * smoothInterval;
return this._current;
cam.setZoom(this._current);
},

finished : function() {
Expand All @@ -87,10 +89,10 @@ function Move(from, to, duration) {
}

utils.derive(Move, Animation, {
_setTo : function(t) {
apply : function(cam, t) {
var smoothInterval = (1 - Math.cos(t * Math.PI ) ) / 2;
vec3.lerp(this._current, this._from, this._to, smoothInterval);
return this._current;
cam.setCenter(this._current);
}
});

Expand All @@ -108,69 +110,118 @@ function Rotate(initialRotation, destinationRotation, duration) {
}

utils.derive(Rotate, Animation, {

_setTo : (function() {
apply : (function() {
var quatRot = quat.create();

return function(t) {
return function(cam, t) {
quat.slerp(quatRot, this._from, this._to, t);
mat3.fromQuat(this._current, quatRot);
return this._current;
cam.setRotation(this._current);
};
})()
});

function RockAndRoll(rotation, axis, duration) {
var initial = mat3.create();
mat3.fromMat4(initial, rotation);
Animation.call(this, initial, null, duration);
function RockAndRoll(axis, duration) {
Animation.call(this, null, null, duration);
this._axis = vec3.clone(axis);
this.setLooping(true);
this._current = mat3.create();
this._previousAngle = 0.0;
}

utils.derive(RockAndRoll, Animation, {
_setTo : (function() {
apply : (function() {
var axisRot = mat3.create();
return function(t) {
var rotation = mat3.create();
return function(cam, t) {
mat3.fromMat4(rotation, cam.rotation());
var angle = 0.2 * Math.sin(2 * t * Math.PI);
geom.axisRotation(axisRot, this._axis, angle);
mat3.mul(this._current, this._from, axisRot);
return this._current;
var deltaAngle = angle - this._previousAngle;
this._previousAngle = angle;
geom.axisRotation(axisRot, this._axis, deltaAngle);
mat3.mul(rotation, axisRot, rotation);
cam.setRotation(rotation);
};
})()
});

function Spin(rotation, axis, speed) {
var initial = mat3.create();
mat3.fromMat4(initial, rotation);
function Spin(axis, speed) {
var duration = 1000 * (2 * Math.PI / speed);
console.log(duration);
Animation.call(this, initial, null, duration);
Animation.call(this, null, null, duration);
this._axis = vec3.clone(axis);
this.setLooping(true);
this._speed = speed;
this._current = mat3.create();
this._previousT = 0.0;
}

utils.derive(Spin, Animation, {
_setTo : (function() {
apply : (function() {
var axisRot = mat3.create();
return function(t) {
var angle = Math.PI * 2 * t;
var rotation = mat3.create();
return function(cam, t) {
mat3.fromMat4(rotation, cam.rotation());
var angle = Math.PI * 2 * (t - this._previousT);
this._previousT = t;
geom.axisRotation(axisRot, this._axis, angle);
mat3.mul(this._current, axisRot, this._from, axisRot);
return this._current;
mat3.mul(rotation, axisRot, rotation);
cam.setRotation(rotation);
};
})()
});


function AnimationControl() {
this._animations = [];
}

AnimationControl.prototype = {
// apply all currently active animations to the camera
// returns true if there are pending animations.
run : function(camera) {
var time = Date.now();
this._animations = this._animations.filter(function(anim) {
return !anim.step(camera, time);
});
return this._animations.length > 0;
},
add : function(animation) {
this._animations.push(animation);
},
remove : function(animation) {
this._animations = this._animations.filter(function(a) {
return a !== animation;
});
}
};


function move(from, to, duration) {
return new Move(from, to, duration);
}

function rotate(from, to, duration) {
return new Rotate(from, to, duration);
}

function zoom(from, to, duration) {
return new Animation(from, to, duration);
}


function spin(axis, speed) {
return new Spin(axis, speed);
}

function rockAndRoll() {
return new RockAndRoll([0, 1, 0], 2000);
}

return {
Move : Move,
Rotate : Rotate,
RockAndRoll : RockAndRoll,
Animation : Animation,
Spin : Spin
AnimationControl : AnimationControl,
move : move,
rotate : rotate,
zoom : zoom,
rockAndRoll : rockAndRoll,
spin : spin
};

});
77 changes: 31 additions & 46 deletions src/viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ define([
render,
TextLabel,
CustomMesh,
animation,
anim,
SceneNode) {

"use strict";
Expand Down Expand Up @@ -149,14 +149,14 @@ function PV(domElement, opts) {
this._resize = false;
this._lastTimestamp = null;
this._objectIdManager = new UniqueObjectIdPool();
// these two are set to the animation objects when spin/rockAndRoll
// are active
this._spin = null;
this._rockAndRoll = null;

this.listenerMap = {};

this._camAnim = {
center : null, zoom : null,
rotation : null
};

this._animControl = new anim.AnimationControl();
// NOTE: make sure to only request features supported by all browsers,
// not only browsers that support WebGL in this constructor. WebGL
// detection only happens in PV._initGL. Once this happened, we are
Expand Down Expand Up @@ -564,8 +564,7 @@ PV.prototype = {
} else {
rotation4 = mat4.clone(rotation);
}
this._camAnim.rotation =
new animation.Rotate(this._cam.rotation(), rotation4, ms);
this._animControl.add(anim.rotate(this._cam.rotation(), rotation4, ms));
this.requestRedraw();
},

Expand All @@ -578,28 +577,7 @@ PV.prototype = {

// performs interpolation of current camera position
_animateCam : function() {
var anotherRedraw = false;
if (this._camAnim.center) {
this._cam.setCenter(this._camAnim.center.step());
if (this._camAnim.center.finished()) {
this._camAnim.center = null;
}
anotherRedraw = true;
}
if (this._camAnim.rotation) {
this._cam.setRotation(this._camAnim.rotation.step());
if (this._camAnim.rotation.finished()) {
this._camAnim.rotation = null;
}
anotherRedraw = true;
}
if (this._camAnim.zoom) {
this._cam.setZoom(this._camAnim.zoom.step());
if (this._camAnim.zoom.finished()) {
this._camAnim.zoom = null;
}
anotherRedraw = true;
}
var anotherRedraw = this._animControl.run(this._cam);
if (anotherRedraw) {
this.requestRedraw();
}
Expand Down Expand Up @@ -635,8 +613,8 @@ PV.prototype = {
this._cam.setCenter(center);
return;
}
this._camAnim.center =
new animation.Move(this._cam.center(), vec3.clone(center), ms);
this._animControl.add(anim.move(this._cam.center(),
vec3.clone(center), ms));
this.requestRedraw();
},

Expand All @@ -646,8 +624,7 @@ PV.prototype = {
this._cam.setZoom(zoom);
return;
}
this._camAnim.zoom =
new animation.Animation(this._cam.zoom(), zoom, ms);
this._animControl.add(anim.zoom(this._cam.zoom(), zoom, ms));
this.requestRedraw();
},

Expand Down Expand Up @@ -990,29 +967,37 @@ PV.prototype = {

// enable disable rock and rolling of camera
rockAndRoll : function(enable) {
if (enable === true) {
this._camAnim.rotation =
new animation.RockAndRoll(this._cam.rotation(), [0, 1, 0], 2000);
this.requestRedraw();
} else if (enable === false) {
this._camAnim.rotation = null;
this.requestRedraw();
if (enable === undefined) {
return this._rockAndRoll !== null;
}
return this._camAnim.rotation !== null;
if (!!enable) {
this._rockAndRoll = anim.rockAndRoll();
this._animControl.add(this._rockAndRoll);
this.requestRedraw();
return true;
}
this._animControl.remove(this._rockAndRoll);
this._rockAndRoll = null;
this.requestRedraw();
return false;
},

spin : function(speed, axis) {
if (speed === undefined || speed === false) {
this._camAnim.rotation = null;
if (speed === undefined) {
return this._spin !== null;
}
if (speed === false) {
this._animControl.remove(this._spin);
this._spin = null;
this.requestRedraw();
return false;
}
if (speed === true) {
speed = Math.PI/8;
}
axis = axis || [0, 1, 0];
this._camAnim.rotation =
new animation.Spin(this._cam.rotation(), axis, speed);
this._spin = anim.spin(axis, speed);
this._animControl.add(this._spin);
this.requestRedraw();
return true;
},
Expand Down

0 comments on commit fc911d4

Please sign in to comment.