Browse Source

Reverted changes to Animation class and moved to new KeyFrameAnimation class.

Erik Kitson 13 years ago
parent
commit
cbccd87d57
3 changed files with 573 additions and 153 deletions
  1. 151 153
      src/extras/animation/Animation.js
  2. 421 0
      src/extras/animation/KeyFrameAnimation.js
  3. 1 0
      utils/build.py

+ 151 - 153
src/extras/animation/Animation.js

@@ -10,7 +10,7 @@ THREE.Animation = function( root, data, interpolationType, JITCompile ) {
 	this.data = THREE.AnimationHandler.get( data );
 	this.hierarchy = THREE.AnimationHandler.parse( root );
 	this.currentTime = 0;
-	this.timeScale = 0.001;
+	this.timeScale = 1;
 	this.isPlaying = false;
 	this.isPaused = true;
 	this.loop = true;
@@ -20,37 +20,6 @@ THREE.Animation = function( root, data, interpolationType, JITCompile ) {
 	this.points = [];
 	this.target = new THREE.Vector3();
 
-	// initialize to first keyframes
-
-	for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
-
-		var keys = this.data.hierarchy[h].keys,
-			sids = this.data.hierarchy[h].sids,
-			obj = this.hierarchy[h];
-
-		if ( keys.length ) {
-
-			for ( var s = 0; s < sids.length; s++ ) {
-
-				var sid = sids[ s ],
-					next = this.getNextKeyWith( sid, h, 0 );
-
-				if ( next ) {
-
-					next.apply( sid );
-
-				}
-
-			}
-
-			obj.matrixAutoUpdate = false;
-			this.data.hierarchy[h].node.updateMatrix();
-			obj.matrixWorldNeedsUpdate = true;
-
-		}
-
-	}
-
 };
 
 // Play
@@ -62,21 +31,16 @@ THREE.Animation.prototype.play = function( loop, startTimeMS ) {
 		this.isPlaying = true;
 		this.loop = loop !== undefined ? loop : true;
 		this.currentTime = startTimeMS !== undefined ? startTimeMS : 0;
-		this.startTimeMs = startTimeMS;
-		this.startTime = 10000000;
-		this.endTime = -this.startTime;
 
 
 		// reset key cache
 
 		var h, hl = this.hierarchy.length,
-			object,
-			node;
+			object;
 
 		for ( h = 0; h < hl; h++ ) {
 
 			object = this.hierarchy[ h ];
-			node = this.data.hierarchy[ h ];
 
 			if ( this.interpolationType !== THREE.AnimationHandler.CATMULLROM_FORWARD ) {
 
@@ -84,26 +48,27 @@ THREE.Animation.prototype.play = function( loop, startTimeMS ) {
 
 			}
 
-			if ( node.animationCache === undefined ) {
-
-				node.animationCache = {};
-				node.animationCache.prevKey = null;
-				node.animationCache.nextKey = null;
-				node.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
+			object.matrixAutoUpdate = true;
 
-			}
+			if ( object.animationCache === undefined ) {
 
-			var keys = this.data.hierarchy[h].keys;
+				object.animationCache = {};
+				object.animationCache.prevKey = { pos: 0, rot: 0, scl: 0 };
+				object.animationCache.nextKey = { pos: 0, rot: 0, scl: 0 };
+				object.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
 
-			if (keys.length) {
+			}
 
-				node.animationCache.prevKey = keys[ 0 ];
-				node.animationCache.nextKey = keys[ 1 ];
+			var prevKey = object.animationCache.prevKey;
+			var nextKey = object.animationCache.nextKey;
 
-				this.startTime = Math.min( keys[0].time, this.startTime );
-				this.endTime = Math.max( keys[keys.length - 1].time, this.endTime );
+			prevKey.pos = this.data.hierarchy[ h ].keys[ 0 ];
+			prevKey.rot = this.data.hierarchy[ h ].keys[ 0 ];
+			prevKey.scl = this.data.hierarchy[ h ].keys[ 0 ];
 
-			}
+			nextKey.pos = this.getNextKeyWith( "pos", h, 1 );
+			nextKey.rot = this.getNextKeyWith( "rot", h, 1 );
+			nextKey.scl = this.getNextKeyWith( "scl", h, 1 );
 
 		}
 
@@ -151,25 +116,20 @@ THREE.Animation.prototype.stop = function() {
 
 	for ( var h = 0; h < this.hierarchy.length; h++ ) {
 
-		var obj = this.hierarchy[ h ];
-
-		if ( obj.animationCache !== undefined ) {
+		if ( this.hierarchy[ h ].animationCache !== undefined ) {
 
-			var original = obj.animationCache.originalMatrix;
+			if( this.hierarchy[ h ] instanceof THREE.Bone ) {
 
-			if( obj instanceof THREE.Bone ) {
-
-				original.copy( obj.skinMatrix );
-				obj.skinMatrix = original;
+				this.hierarchy[ h ].skinMatrix = this.hierarchy[ h ].animationCache.originalMatrix;
 
 			} else {
 
-				original.copy( obj.matrix );
-				obj.matrix = original;
+				this.hierarchy[ h ].matrix = this.hierarchy[ h ].animationCache.originalMatrix;
 
 			}
 
-			delete obj.animationCache;
+
+			delete this.hierarchy[ h ].animationCache;
 
 		}
 
@@ -189,13 +149,18 @@ THREE.Animation.prototype.update = function( deltaTimeMS ) {
 
 	// vars
 
+	var types = [ "pos", "rot", "scl" ];
+	var type;
+	var scale;
+	var vector;
+	var prevXYZ, nextXYZ;
 	var prevKey, nextKey;
 	var object;
-	var node;
+	var animationCache;
 	var frame;
 	var JIThierarchy = this.data.JIT.hierarchy;
 	var currentTime, unloopedCurrentTime;
-	var looped;
+	var currentPoint, forwardPoint, angle;
 
 
 	// update
@@ -204,70 +169,15 @@ THREE.Animation.prototype.update = function( deltaTimeMS ) {
 
 	unloopedCurrentTime = this.currentTime;
 	currentTime         = this.currentTime = this.currentTime % this.data.length;
-
-	// if looped around, the current time should be based on the startTime
-	if ( currentTime < this.startTimeMs ) {
-
-		currentTime = this.currentTime = this.startTimeMs + currentTime;
-
-	}
-
 	frame               = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 );
-	looped 				= currentTime < unloopedCurrentTime;
-
-	if ( looped && !this.loop ) {
-
-		// Set the animation to the last keyframes and stop
-		for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
-
-			var keys = this.data.hierarchy[h].keys,
-				sids = this.data.hierarchy[h].sids,
-				end = keys.length-1,
-				obj = this.hierarchy[h];
-
-			if ( keys.length ) {
-
-				for ( var s = 0; s < sids.length; s++ ) {
-
-					var sid = sids[ s ],
-						prev = this.getPrevKeyWith( sid, h, end );
-
-					if ( prev ) {
-
-						prev.apply( sid );
-
-					}
-
-				}
-
-				this.data.hierarchy[h].node.updateMatrix();
-				obj.matrixWorldNeedsUpdate = true;
-
-			}
-
-		}
-
-		this.stop();
-		return;
-
-	}
-
-	// check pre-infinity
-	if ( currentTime < this.startTime ) {
 
-		return;
-
-	}
 
 	// update
 
 	for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
 
 		object = this.hierarchy[ h ];
-		node = this.data.hierarchy[ h ];
-
-		var keys = node.keys,
-			animationCache = node.animationCache;
+		animationCache = object.animationCache;
 
 		// use JIT?
 
@@ -276,39 +186,49 @@ THREE.Animation.prototype.update = function( deltaTimeMS ) {
 			if( object instanceof THREE.Bone ) {
 
 				object.skinMatrix = JIThierarchy[ h ][ frame ];
+
+				object.matrixAutoUpdate = false;
 				object.matrixWorldNeedsUpdate = false;
 
 			} else {
 
 				object.matrix = JIThierarchy[ h ][ frame ];
+
+				object.matrixAutoUpdate = false;
 				object.matrixWorldNeedsUpdate = true;
 
 			}
 
 		// use interpolation
 
-		} else if ( keys.length ) {
+		} else {
 
 			// make sure so original matrix and not JIT matrix is set
 
-			if ( this.JITCompile && animationCache ) {
+			if ( this.JITCompile ) {
 
 				if( object instanceof THREE.Bone ) {
 
-					object.skinMatrix = animationCache.originalMatrix;
+					object.skinMatrix = object.animationCache.originalMatrix;
 
 				} else {
 
-					object.matrix = animationCache.originalMatrix;
+					object.matrix = object.animationCache.originalMatrix;
 
 				}
 
 			}
 
-			prevKey = animationCache.prevKey;
-			nextKey = animationCache.nextKey;
 
-			if ( prevKey && nextKey ) {
+			// loop through pos/rot/scl
+
+			for ( var t = 0; t < 3; t++ ) {
+
+				// get keys
+
+				type    = types[ t ];
+				prevKey = animationCache.prevKey[ type ];
+				nextKey = animationCache.nextKey[ type ];
 
 				// switch keys?
 
@@ -316,42 +236,120 @@ THREE.Animation.prototype.update = function( deltaTimeMS ) {
 
 					// did we loop?
 
-					if ( looped && this.loop ) {
+					if ( currentTime < unloopedCurrentTime ) {
 
-						prevKey = keys[ 0 ];
-						nextKey = keys[ 1 ];
+						if ( this.loop ) {
 
-						while ( nextKey.time < currentTime ) {
+							prevKey = this.data.hierarchy[ h ].keys[ 0 ];
+							nextKey = this.getNextKeyWith( type, h, 1 );
 
-							prevKey = nextKey;
-							nextKey = keys[ prevKey.index + 1 ];
+							while( nextKey.time < currentTime ) {
 
-						}
+								prevKey = nextKey;
+								nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
+
+							}
+
+						} else {
+
+							this.stop();
+							return;
 
-					} else if ( !looped ) {
+						}
 
-						var lastIndex = keys.length - 1;
+					} else {
 
-						while ( nextKey.time < currentTime && nextKey.index !== lastIndex ) {
+						do {
 
 							prevKey = nextKey;
-							nextKey = keys[ prevKey.index + 1 ];
+							nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
 
-						}
+						} while( nextKey.time < currentTime )
 
 					}
 
-					animationCache.prevKey = prevKey;
-					animationCache.nextKey = nextKey;
+					animationCache.prevKey[ type ] = prevKey;
+					animationCache.nextKey[ type ] = nextKey;
 
 				}
 
-				prevKey.interpolate( nextKey, currentTime );
 
-			}
+				object.matrixAutoUpdate = true;
+				object.matrixWorldNeedsUpdate = true;
+
+				scale = ( currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
+				prevXYZ = prevKey[ type ];
+				nextXYZ = nextKey[ type ];
+
+
+				// check scale error
+
+				if ( scale < 0 || scale > 1 ) {
+
+					console.log( "THREE.Animation.update: Warning! Scale out of bounds:" + scale + " on bone " + h );
+					scale = scale < 0 ? 0 : 1;
+
+				}
+
+				// interpolate
+
+				if ( type === "pos" ) {
 
-			this.data.hierarchy[h].node.updateMatrix();
-			object.matrixWorldNeedsUpdate = true;
+					vector = object.position;
+
+					if( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
+
+						vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
+						vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
+						vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
+
+					} else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
+							    this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+
+						this.points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ];
+						this.points[ 1 ] = prevXYZ;
+						this.points[ 2 ] = nextXYZ;
+						this.points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ];
+
+						scale = scale * 0.33 + 0.33;
+
+						currentPoint = this.interpolateCatmullRom( this.points, scale );
+
+						vector.x = currentPoint[ 0 ];
+						vector.y = currentPoint[ 1 ];
+						vector.z = currentPoint[ 2 ];
+
+						if( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+
+							forwardPoint = this.interpolateCatmullRom( this.points, scale * 1.01 );
+
+							this.target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] );
+							this.target.subSelf( vector );
+							this.target.y = 0;
+							this.target.normalize();
+
+							angle = Math.atan2( this.target.x, this.target.z );
+							object.rotation.set( 0, angle, 0 );
+
+						}
+
+					}
+
+				} else if ( type === "rot" ) {
+
+					THREE.Quaternion.slerp( prevXYZ, nextXYZ, object.quaternion, scale );
+
+				} else if( type === "scl" ) {
+
+					vector = object.scale;
+
+					vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
+					vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
+					vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
+
+				}
+
+			}
 
 		}
 
@@ -431,7 +429,7 @@ THREE.Animation.prototype.interpolate = function( p0, p1, p2, p3, t, t2, t3 ) {
 
 // Get next key with
 
-THREE.Animation.prototype.getNextKeyWith = function( sid, h, key ) {
+THREE.Animation.prototype.getNextKeyWith = function( type, h, key ) {
 
 	var keys = this.data.hierarchy[ h ].keys;
 
@@ -448,7 +446,7 @@ THREE.Animation.prototype.getNextKeyWith = function( sid, h, key ) {
 
 	for ( ; key < keys.length; key++ ) {
 
-		if ( keys[ key ].hasTarget( sid ) ) {
+		if ( keys[ key ][ type ] !== undefined ) {
 
 			return keys[ key ];
 
@@ -456,13 +454,13 @@ THREE.Animation.prototype.getNextKeyWith = function( sid, h, key ) {
 
 	}
 
-	return keys[ 0 ];
+	return this.data.hierarchy[ h ].keys[ 0 ];
 
 };
 
 // Get previous key with
 
-THREE.Animation.prototype.getPrevKeyWith = function( sid, h, key ) {
+THREE.Animation.prototype.getPrevKeyWith = function( type, h, key ) {
 
 	var keys = this.data.hierarchy[ h ].keys;
 
@@ -480,7 +478,7 @@ THREE.Animation.prototype.getPrevKeyWith = function( sid, h, key ) {
 
 	for ( ; key >= 0; key-- ) {
 
-		if ( keys[ key ].hasTarget( sid ) ) {
+		if ( keys[ key ][ type ] !== undefined ) {
 
 			return keys[ key ];
 
@@ -488,6 +486,6 @@ THREE.Animation.prototype.getPrevKeyWith = function( sid, h, key ) {
 
 	}
 
-	return keys[ keys.length - 1 ];
+	return this.data.hierarchy[ h ].keys[ keys.length - 1 ];
 
-};
+};

+ 421 - 0
src/extras/animation/KeyFrameAnimation.js

@@ -0,0 +1,421 @@
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author khang duong
+ * @author erik kitson
+ */
+
+THREE.KeyFrameAnimation = function( root, data, JITCompile ) {
+
+	this.root = root;
+	this.data = THREE.AnimationHandler.get( data );
+	this.hierarchy = THREE.AnimationHandler.parse( root );
+	this.currentTime = 0;
+	this.timeScale = 0.001;
+	this.isPlaying = false;
+	this.isPaused = true;
+	this.loop = true;
+	this.JITCompile = JITCompile !== undefined ? JITCompile : true;
+
+	// initialize to first keyframes
+
+	for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
+
+		var keys = this.data.hierarchy[h].keys,
+			sids = this.data.hierarchy[h].sids,
+			obj = this.hierarchy[h];
+
+		if ( keys.length && sids ) {
+
+			for ( var s = 0; s < sids.length; s++ ) {
+
+				var sid = sids[ s ],
+					next = this.getNextKeyWith( sid, h, 0 );
+
+				if ( next ) {
+
+					next.apply( sid );
+
+				}
+
+			}
+
+			obj.matrixAutoUpdate = false;
+			this.data.hierarchy[h].node.updateMatrix();
+			obj.matrixWorldNeedsUpdate = true;
+
+		}
+
+	}
+
+};
+
+// Play
+
+THREE.KeyFrameAnimation.prototype.play = function( loop, startTimeMS ) {
+
+	if( !this.isPlaying ) {
+
+		this.isPlaying = true;
+		this.loop = loop !== undefined ? loop : true;
+		this.currentTime = startTimeMS !== undefined ? startTimeMS : 0;
+		this.startTimeMs = startTimeMS;
+		this.startTime = 10000000;
+		this.endTime = -this.startTime;
+
+
+		// reset key cache
+
+		var h, hl = this.hierarchy.length,
+			object,
+			node;
+
+		for ( h = 0; h < hl; h++ ) {
+
+			object = this.hierarchy[ h ];
+			node = this.data.hierarchy[ h ];
+			object.useQuaternion = true;
+
+			if ( node.animationCache === undefined ) {
+
+				node.animationCache = {};
+				node.animationCache.prevKey = null;
+				node.animationCache.nextKey = null;
+				node.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
+
+			}
+
+			var keys = this.data.hierarchy[h].keys;
+
+			if (keys.length) {
+
+				node.animationCache.prevKey = keys[ 0 ];
+				node.animationCache.nextKey = keys[ 1 ];
+
+				this.startTime = Math.min( keys[0].time, this.startTime );
+				this.endTime = Math.max( keys[keys.length - 1].time, this.endTime );
+
+			}
+
+		}
+
+		this.update( 0 );
+
+	}
+
+	this.isPaused = false;
+
+	THREE.AnimationHandler.addToUpdate( this );
+
+};
+
+
+
+// Pause
+
+THREE.KeyFrameAnimation.prototype.pause = function() {
+
+	if( this.isPaused ) {
+
+		THREE.AnimationHandler.addToUpdate( this );
+
+	} else {
+
+		THREE.AnimationHandler.removeFromUpdate( this );
+
+	}
+
+	this.isPaused = !this.isPaused;
+
+};
+
+
+// Stop
+
+THREE.KeyFrameAnimation.prototype.stop = function() {
+
+	this.isPlaying = false;
+	this.isPaused  = false;
+	THREE.AnimationHandler.removeFromUpdate( this );
+
+
+	// reset JIT matrix and remove cache
+
+	for ( var h = 0; h < this.hierarchy.length; h++ ) {
+
+		var obj = this.hierarchy[ h ];
+
+		if ( obj.animationCache !== undefined ) {
+
+			var original = obj.animationCache.originalMatrix;
+
+			if( obj instanceof THREE.Bone ) {
+
+				original.copy( obj.skinMatrix );
+				obj.skinMatrix = original;
+
+			} else {
+
+				original.copy( obj.matrix );
+				obj.matrix = original;
+
+			}
+
+			delete obj.animationCache;
+
+		}
+
+	}
+
+};
+
+
+// Update
+
+THREE.KeyFrameAnimation.prototype.update = function( deltaTimeMS ) {
+
+	// early out
+
+	if( !this.isPlaying ) return;
+
+
+	// vars
+
+	var prevKey, nextKey;
+	var object;
+	var node;
+	var frame;
+	var JIThierarchy = this.data.JIT.hierarchy;
+	var currentTime, unloopedCurrentTime;
+	var looped;
+
+
+	// update
+
+	this.currentTime += deltaTimeMS * this.timeScale;
+
+	unloopedCurrentTime = this.currentTime;
+	currentTime         = this.currentTime = this.currentTime % this.data.length;
+
+	// if looped around, the current time should be based on the startTime
+	if ( currentTime < this.startTimeMs ) {
+
+		currentTime = this.currentTime = this.startTimeMs + currentTime;
+
+	}
+
+	frame               = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 );
+	looped 				= currentTime < unloopedCurrentTime;
+
+	if ( looped && !this.loop ) {
+
+		// Set the animation to the last keyframes and stop
+		for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
+
+			var keys = this.data.hierarchy[h].keys,
+				sids = this.data.hierarchy[h].sids,
+				end = keys.length-1,
+				obj = this.hierarchy[h];
+
+			if ( keys.length ) {
+
+				for ( var s = 0; s < sids.length; s++ ) {
+
+					var sid = sids[ s ],
+						prev = this.getPrevKeyWith( sid, h, end );
+
+					if ( prev ) {
+
+						prev.apply( sid );
+
+					}
+
+				}
+
+				this.data.hierarchy[h].node.updateMatrix();
+				obj.matrixWorldNeedsUpdate = true;
+
+			}
+
+		}
+
+		this.stop();
+		return;
+
+	}
+
+	// check pre-infinity
+	if ( currentTime < this.startTime ) {
+
+		return;
+
+	}
+
+	// update
+
+	for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
+
+		object = this.hierarchy[ h ];
+		node = this.data.hierarchy[ h ];
+
+		var keys = node.keys,
+			animationCache = node.animationCache;
+
+		// use JIT?
+
+		if ( this.JITCompile && JIThierarchy[ h ][ frame ] !== undefined ) {
+
+			if( object instanceof THREE.Bone ) {
+
+				object.skinMatrix = JIThierarchy[ h ][ frame ];
+				object.matrixWorldNeedsUpdate = false;
+
+			} else {
+
+				object.matrix = JIThierarchy[ h ][ frame ];
+				object.matrixWorldNeedsUpdate = true;
+
+			}
+
+		// use interpolation
+
+		} else if ( keys.length ) {
+
+			// make sure so original matrix and not JIT matrix is set
+
+			if ( this.JITCompile && animationCache ) {
+
+				if( object instanceof THREE.Bone ) {
+
+					object.skinMatrix = animationCache.originalMatrix;
+
+				} else {
+
+					object.matrix = animationCache.originalMatrix;
+
+				}
+
+			}
+
+			prevKey = animationCache.prevKey;
+			nextKey = animationCache.nextKey;
+
+			if ( prevKey && nextKey ) {
+
+				// switch keys?
+
+				if ( nextKey.time <= unloopedCurrentTime ) {
+
+					// did we loop?
+
+					if ( looped && this.loop ) {
+
+						prevKey = keys[ 0 ];
+						nextKey = keys[ 1 ];
+
+						while ( nextKey.time < currentTime ) {
+
+							prevKey = nextKey;
+							nextKey = keys[ prevKey.index + 1 ];
+
+						}
+
+					} else if ( !looped ) {
+
+						var lastIndex = keys.length - 1;
+
+						while ( nextKey.time < currentTime && nextKey.index !== lastIndex ) {
+
+							prevKey = nextKey;
+							nextKey = keys[ prevKey.index + 1 ];
+
+						}
+
+					}
+
+					animationCache.prevKey = prevKey;
+					animationCache.nextKey = nextKey;
+
+				}
+
+				prevKey.interpolate( nextKey, currentTime );
+
+			}
+
+			this.data.hierarchy[h].node.updateMatrix();
+			object.matrixWorldNeedsUpdate = true;
+
+		}
+
+	}
+
+	// update JIT?
+
+	if ( this.JITCompile ) {
+
+		if ( JIThierarchy[ 0 ][ frame ] === undefined ) {
+
+			this.hierarchy[ 0 ].updateMatrixWorld( true );
+
+			for ( var h = 0; h < this.hierarchy.length; h++ ) {
+
+				if( this.hierarchy[ h ] instanceof THREE.Bone ) {
+
+					JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].skinMatrix.clone();
+
+				} else {
+
+					JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].matrix.clone();
+
+				}
+
+			}
+
+		}
+
+	}
+
+};
+
+// Get next key with
+
+THREE.KeyFrameAnimation.prototype.getNextKeyWith = function( sid, h, key ) {
+
+	var keys = this.data.hierarchy[ h ].keys;
+	key = key % keys.length;
+
+	for ( ; key < keys.length; key++ ) {
+
+		if ( keys[ key ].hasTarget( sid ) ) {
+
+			return keys[ key ];
+
+		}
+
+	}
+
+	return keys[ 0 ];
+
+};
+
+// Get previous key with
+
+THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function( sid, h, key ) {
+
+	var keys = this.data.hierarchy[ h ].keys;
+	key = key >= 0 ? key : key + keys.length;
+
+	for ( ; key >= 0; key-- ) {
+
+		if ( keys[ key ].hasTarget( sid ) ) {
+
+			return keys[ key ];
+
+		}
+
+	}
+
+	return keys[ keys.length - 1 ];
+
+};

+ 1 - 0
utils/build.py

@@ -98,6 +98,7 @@ EXTRAS_FILES = [
 'extras/core/TextPath.js',
 'extras/animation/AnimationHandler.js',
 'extras/animation/Animation.js',
+'extras/animation/KeyFrameAnimation.js',
 'extras/cameras/CubeCamera.js',
 'extras/cameras/FirstPersonCamera.js',
 'extras/cameras/PathCamera.js',