Browse Source

Animation: Refactored update and tightened looping.

tschw 9 years ago
parent
commit
49e181f19e
2 changed files with 108 additions and 78 deletions
  1. 1 1
      examples/webgl_animation_skinning_morph.html
  2. 107 77
      src/animation/AnimationMixer.js

+ 1 - 1
examples/webgl_animation_skinning_morph.html

@@ -377,7 +377,7 @@
 
 							get 'clamp ='() {
 
-								return action !== null ? action.clampWhenFinished : true;
+								return action !== null ? action.clampWhenFinished : false;
 
 							},
 

+ 107 - 77
src/animation/AnimationMixer.js

@@ -128,60 +128,25 @@ THREE.AnimationMixer.prototype = {
 	// advance the time and update apply the animation
 	update: function( deltaTime ) {
 
+		deltaTime *= this.timeScale;
+
 		var actions = this._actions,
 			nActions = this._nActiveActions,
-			mixerDeltaTime = deltaTime * this.timeScale,
-			direction = Math.sign( deltaTime );
-
-		var time = this.time += mixerDeltaTime;
-		var accuIndex = this._accuIndex ^= 1;
-
-		// perform all actions
-
-		for ( var i = 0; i !== nActions; ++ i ) {
-
-			var action = actions[ i ],
-				actionDeltaTime = mixerDeltaTime;
-
-			if ( ! action.enabled ) continue;
-
-			var startTime = action.startTime_;
-
-			if ( startTime !== null ) {
-
-				// check for scheduled start of action
-
-				var timeRunning = ( time - startTime ) * direction;
-				if ( timeRunning < 0 ) continue; // yet to come
-
-				// start
-
-				action.startTime_ = null; // unschedule
-				actionDeltaTime = direction * timeRunning;
-
-			}
-
-			// run this action
-
-			actionDeltaTime *= action.updateTimeScale_( time );
-			var clipTime = action.updateTime_( actionDeltaTime );
 
-			// note: updateTime_ may disable the action resulting in
-			// an effective weight of 0
+			time = this.time += deltaTime,
+			timeDirection = Math.sign( deltaTime ),
 
-			var weight = action.updateWeight_( time );
+			accuIndex = this._accuIndex ^= 1;
 
-			if ( weight > 0 ) {
+		// run active actions
 
-				var interpolants = action._interpolants;
-				var propertyMixers = action._propertyBindings;
+		for ( var i = 0; i !== nActions; ++ i ) {
 
-				for ( var j = 0, m = interpolants.length; j !== m; ++ j ) {
+			var action = actions[ i ];
 
-					interpolants[ j ].evaluate( clipTime );
-					propertyMixers[ j ].accumulate( accuIndex, weight );
+			if ( action.enabled ) {
 
-				}
+				action._update( time, deltaTime, timeDirection, accuIndex );
 
 			}
 
@@ -329,40 +294,43 @@ THREE.AnimationMixer._Action =
 		interpolant.settings = interpolantSettings
 
 	}
+
 	this._interpolantSettings = interpolantSettings;
 
-	this._interpolants = interpolants;
+	this._interpolants = interpolants;	// bound by the mixer
+
+	// inside: PropertyMixer (managed by the mixer)
 	this._propertyBindings = new Array( nTracks );
 
+	this._cacheIndex = null;			// for the memory manager
+	this._byClipCacheIndex = null;		// for the memory manager
+
 	this._timeScaleInterpolant = null;
 	this._weightInterpolant = null;
 
-	this._cacheIndex = null;
-	this._byClipCacheIndex = null;
-
-	this._effectiveTimeScale = 1;
-	this._effectiveWeight = 1;
-
 	this.loop = THREE.LoopRepeat;
 	this._loopCount = -1;
 
 	// global mixer time when the action is to be started
-	// it's set back to 'null' when the mixer has started this action
-	this.startTime_ = null;
+	// it's set back to 'null' upon start of the action
+	this._startTime = null;
 
 	// scaled local time of the action
 	// gets clamped or wrapped to 0..clip.duration according to loop
 	this.time = 0;
 
 	this.timeScale = 1;
+	this._effectiveTimeScale = 1;
+
 	this.weight = 1;
+	this._effectiveWeight = 1;
 
 	this.repetitions = Infinity; 		// no. of repetitions when looping
 
 	this.paused = false;				// false -> zero effective time scale
 	this.enabled = true;				// true -> zero effective weight
 
-	this.clampWhenFinished 	= true;		// keep feeding the last frame?
+	this.clampWhenFinished 	= false;	// keep feeding the last frame?
 
 	this.zeroSlopeAtStart 	= true;		// for smooth interpolation w/o separate
 	this.zeroSlopeAtEnd		= true;		// clips for start, loop and end
@@ -398,7 +366,7 @@ THREE.AnimationMixer._Action.prototype = {
 
 		this.time = 0;			// restart clip
 		this._loopCount = -1;	// forget previous loops
-		this.startTime_ = null;	// forget scheduling
+		this._startTime = null;	// forget scheduling
 
 		return this.stopFading().stopWarping();
 
@@ -406,10 +374,10 @@ THREE.AnimationMixer._Action.prototype = {
 
 	isRunning: function() {
 
-		var start = this.startTime_;
+		var start = this._startTime;
 
 		return this.enabled && ! this.paused && this.timeScale !== 0 &&
-				this.startTime_ === null && this._mixer._isActiveAction( this )
+				this._startTime === null && this._mixer._isActiveAction( this )
 
 	},
 
@@ -422,7 +390,7 @@ THREE.AnimationMixer._Action.prototype = {
 
 	startAt: function( time ) {
 
-		this.startTime_ = time;
+		this._startTime = time;
 
 		return this;
 
@@ -623,9 +591,58 @@ THREE.AnimationMixer._Action.prototype = {
 
 	},
 
-	// Interface used by the mixer:
+	// Interna
+
+	_update: function( time, deltaTime, timeDirection, accuIndex ) {
+		// called by the mixer
+
+		var startTime = this._startTime;
+
+		if ( startTime !== null ) {
 
-	updateWeight_: function( time ) {
+			// check for scheduled start of action
+
+			var timeRunning = ( time - startTime ) * timeDirection;
+			if ( timeRunning < 0 || timeDirection === 0 ) {
+
+				return; // yet to come / don't decide when delta = 0
+
+			}
+
+			// start
+
+			this._startTime = null; // unschedule
+			deltaTime = timeDirection * timeRunning;
+
+		}
+
+		// apply time scale and advance time
+
+		deltaTime *= this._updateTimeScale( time );
+		var clipTime = this._updateTime( deltaTime );
+
+		// note: _updateTime may disable the action resulting in
+		// an effective weight of 0
+
+		var weight = this._updateWeight( time );
+
+		if ( weight > 0 ) {
+
+			var interpolants = this._interpolants;
+			var propertyMixers = this._propertyBindings;
+
+			for ( var j = 0, m = interpolants.length; j !== m; ++ j ) {
+
+				interpolants[ j ].evaluate( clipTime );
+				propertyMixers[ j ].accumulate( accuIndex, weight );
+
+			}
+
+		}
+
+	},
+
+	_updateWeight: function( time ) {
 
 		var weight = 0;
 
@@ -662,7 +679,7 @@ THREE.AnimationMixer._Action.prototype = {
 
 	},
 
-	updateTimeScale_: function( time ) {
+	_updateTimeScale: function( time ) {
 
 		var timeScale = 0;
 
@@ -705,12 +722,13 @@ THREE.AnimationMixer._Action.prototype = {
 
 	},
 
-	updateTime_: function( actionDeltaTime ) {
+	_updateTime: function( deltaTime ) {
 
-		var time = this.time + actionDeltaTime,
-			duration = this._clip.duration,
+		var time = this.time + deltaTime;
 
-			direction = Math.sign( actionDeltaTime ),
+		if ( deltaTime === 0 ) return time;
+
+		var duration = this._clip.duration,
 
 			loop = this.loop,
 			loopCount = this._loopCount,
@@ -747,7 +765,8 @@ THREE.AnimationMixer._Action.prototype = {
 				else this.enabled = false;
 
 				this._mixer.dispatchEvent( {
-					type: 'finished', action: this, direction: direction
+					type: 'finished', action: this,
+					direction: deltaTime < 0 ? -1 : 1
 				} );
 
 				break;
@@ -762,10 +781,23 @@ THREE.AnimationMixer._Action.prototype = {
 
 					// just started
 
-					loopCount = 0;
+					if ( deltaTime > 0 ) {
+
+						loopCount = 0;
 
-					var atStart = direction > 0;
-					this._setEndings( atStart, ! atStart, pingPong );
+						this._setEndings(
+								true, this.repetitions === 0, pingPong );
+
+					} else {
+
+						// when looping in reverse direction, the initial
+						// transition through zero counts as a repetition,
+						// so leave loopCount at -1
+
+						this._setEndings(
+								this.repetitions === 0, true, pingPong );
+
+					}
 
 				}
 
@@ -776,7 +808,7 @@ THREE.AnimationMixer._Action.prototype = {
 					var loopDelta = Math.floor( time / duration ); // signed
 					time -= duration * loopDelta;
 
-					loopCount += loopDelta * direction;
+					loopCount += Math.abs( loopDelta );
 
 					var pending = this.repetitions - loopCount;
 
@@ -787,11 +819,11 @@ THREE.AnimationMixer._Action.prototype = {
 						if ( this.clampWhenFinished ) this.paused = true;
 						else this.enabled = false;
 
-						time = direction < 0 ? 0 : duration;
+						time = deltaTime > 0 ? duration : 0;
 
 						this._mixer.dispatchEvent( {
 							type: 'finished', action: this,
-							direction: direction
+							direction: deltaTime > 0 ? 1 : -1
 						} );
 
 						break;
@@ -800,7 +832,7 @@ THREE.AnimationMixer._Action.prototype = {
 
 						// transition to last round
 
-						var atStart = direction < 0;
+						var atStart = deltaTime < 0;
 						this._setEndings( atStart, ! atStart, pingPong );
 
 					} else {
@@ -837,8 +869,6 @@ THREE.AnimationMixer._Action.prototype = {
 
 	},
 
-	// Interna
-
 	_setEndings: function( atStart, atEnd, pingPong ) {
 
 		var settings = this._interpolantSettings;
@@ -1336,7 +1366,7 @@ Object.assign( THREE.AnimationMixer.prototype, {
 
 			interpolant = new THREE.LinearInterpolant(
 					new Float32Array( 2 ), new Float32Array( 2 ),
-						1, this._controlInterpolantsResultBuffer_ );
+						1, this._controlInterpolantsResultBuffer );
 
 			interpolant.__cacheIndex = lastActiveIndex;
 			interpolants[ lastActiveIndex ] = interpolant;