Browse Source

Merge remote branch 'empaempa/master'

Mr.doob 14 years ago
parent
commit
4ba3695dbc
3 changed files with 337 additions and 120 deletions
  1. 243 109
      src/animation/Animation.js
  2. 94 9
      src/animation/AnimationHandler.js
  3. 0 2
      src/renderers/WebGLRenderer.js

+ 243 - 109
src/animation/Animation.js

@@ -2,56 +2,31 @@
  * @author mikael emtinger / http://gomo.se/
  */
 
-THREE.Animation = function( root, data ) {
+THREE.Animation = function( root, data, interpolationType, JITCompile ) {
 
 	this.root = root;
-	this.data = data;
-	this.hierarchy = [];
-
-	this.startTime = 0;
+	this.data = THREE.AnimationHandler.get( data );
+	this.hierarchy = THREE.AnimationHandler.parse( root );
+	this.currentTime = 0;
 	this.isPlaying = false;
+	this.isPaused = true;
 	this.loop = true;
-
-	this.offset = 0;
-
-	// need to initialize data?
-
-	if( !this.data.initialized ) {
-
-		THREE.AnimationHandler.initData( this.data );
-
-	}
-
-
-	// setup hierarchy
-
-	if ( root instanceof THREE.SkinnedMesh ) {
-
-		for( var b = 0; b < this.root.bones.length; b++ ) {
-
-			this.hierarchy.push( this.root.bones[ b ] );
-
-		}
-
-	} else {
-
-		// parse hierarchy and match against animation (somehow)
-
-	}
-
+	this.interpolationType = interpolationType !== undefined ? interpolationType : THREE.AnimationHandler.LINEAR;
+	this.JITCompile = JITCompile !== undefined ? JITCompile : true;
 }
 
-
 /*
  * Play
  */
 
-THREE.Animation.prototype.play = function( loop ) {
+THREE.Animation.prototype.play = function( loop, startTimeMS ) {
 
 	if( !this.isPlaying ) {
 
 		this.isPlaying = true;
-		this.startTime = new Date().getTime() * 0.001;
+		this.loop = loop !== undefined ? loop : true;
+		this.currentTime = startTimeMS !== undefined ? startTimeMS : 0;
+
 
 		// reset key cache
 
@@ -60,28 +35,33 @@ THREE.Animation.prototype.play = function( loop ) {
 			this.hierarchy[ h ].useQuaternion    = true;
 			this.hierarchy[ h ].matrixAutoUpdate = true;
 
-			if ( this.hierarchy[ h ].prevKey === undefined ) {
-
-				this.hierarchy[ h ].prevKey = { pos: 0, rot: 0, scl: 0 };
-				this.hierarchy[ h ].nextKey = { pos: 0, rot: 0, scl: 0 };
+			if ( this.hierarchy[ h ].animationCache === undefined ) {
 
+				this.hierarchy[ h ].animationCache = {};
+				this.hierarchy[ h ].animationCache.prevKey = { pos: 0, rot: 0, scl: 0 };
+				this.hierarchy[ h ].animationCache.nextKey = { pos: 0, rot: 0, scl: 0 };
+				this.hierarchy[ h ].animationCache.originalMatrix = this.hierarchy[ h ] instanceof THREE.Bone ? this.hierarchy[ h ].skinMatrix : this.hierarchy[ h ].matrix;
 			}
 
-			this.hierarchy[ h ].prevKey.pos = this.data.hierarchy[ h ].keys[ 0 ];
-			this.hierarchy[ h ].prevKey.rot = this.data.hierarchy[ h ].keys[ 0 ];
-			this.hierarchy[ h ].prevKey.scl = this.data.hierarchy[ h ].keys[ 0 ];
 
-			this.hierarchy[ h ].nextKey.pos = this.getNextKeyWith( "pos", h, 1 );
-			this.hierarchy[ h ].nextKey.rot = this.getNextKeyWith( "rot", h, 1 );
-			this.hierarchy[ h ].nextKey.scl = this.getNextKeyWith( "scl", h, 1 );
-		}
+			var prevKey = this.hierarchy[ h ].animationCache.prevKey;
+			var nextKey = this.hierarchy[ h ].animationCache.nextKey;
 
-		this.update();
+			prevKey.pos = this.data.hierarchy[ h ].keys[ 0 ];
+			prevKey.rot = this.data.hierarchy[ h ].keys[ 0 ];
+			prevKey.scl = this.data.hierarchy[ h ].keys[ 0 ];
 
-		THREE.AnimationHandler.add( this );
+			nextKey.pos = this.getNextKeyWith( "pos", h, 1 );
+			nextKey.rot = this.getNextKeyWith( "rot", h, 1 );
+			nextKey.scl = this.getNextKeyWith( "scl", h, 1 );
+		}
 
+		this.update( 0 );
 	}
 
+	this.isPaused = false;
+
+	THREE.AnimationHandler.addToUpdate( this );
 };
 
 
@@ -91,9 +71,17 @@ THREE.Animation.prototype.play = function( loop ) {
 
 THREE.Animation.prototype.pause = function() {
 
-	THREE.AnimationHandler.remove( this );
-
-	// todo
+	if( this.isPaused ) {
+		
+		THREE.AnimationHandler.addToUpdate( this );
+		
+	} else {
+		
+		THREE.AnimationHandler.removeFromUpdate( this );
+		
+	}
+	
+	this.isPaused = !this.isPaused;
 }
 
 
@@ -104,7 +92,29 @@ THREE.Animation.prototype.pause = function() {
 THREE.Animation.prototype.stop = function() {
 
 	this.isPlaying = false;
-	THREE.AnimationHandler.remove( this );
+	THREE.AnimationHandler.removeFromUpdate( this );
+	
+	
+	// reset JIT matrix and remove cache
+	
+	for ( var h = 0; h < this.hierarchy.length; h++ ) {
+		
+		if ( this.hierarchy[ h ].animationCache !== undefined ) {
+			
+			if( this.hierarchy[ h ] instanceof THREE.Bone ) {
+			
+				this.hierarchy[ h ].skinMatrix = this.hierarchy[ h ].animationCache.originalMatrix;
+				
+			} else {
+				
+				this.hierarchy[ h ].matrix = this.hierarchy[ h ].animationCache.originalMatrix;
+			}
+			
+			
+			delete this.hierarchy[ h ].animationCache;	
+		}
+	}
+ 	
 }
 
 
@@ -112,9 +122,7 @@ THREE.Animation.prototype.stop = function() {
  * Update
  */
 
-THREE.Animation.prototype.update = function( time ) {
-
-	// TODO: add input time
+THREE.Animation.prototype.update = function( deltaTimeMS ) {
 
 	// early out
 
@@ -124,60 +132,80 @@ THREE.Animation.prototype.update = function( time ) {
 	// vars
 
 	var types = [ "pos", "rot", "scl" ];
+	var type;
 	var scale;
-	var relative;
 	var vector;
 	var prevXYZ, nextXYZ;
+	var prevKey, nextKey;
 	var object;
+	var animationCache;
 	var frame;
 	var JIThierarchy = this.data.JIT.hierarchy;
+	var currentTime, unloopedCurrentTime;
+	
 
 	// update
+	
+	this.currentTime += deltaTimeMS;
 
-	var currentTime         = new Date().getTime() * 0.001 - this.startTime + this.offset;
-	var unloopedCurrentTime = currentTime;
-
-
-	// looped?
-
-	if ( currentTime > this.data.length ) {
+	unloopedCurrentTime = this.currentTime;
+	currentTime         = this.currentTime = this.currentTime % this.data.length;
+	frame               = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 );
 
-		while( currentTime > this.data.length ) {
-
-			currentTime -= this.data.length;
-
-		}
-
-		this.startTime = new Date().getTime() * 0.001 - currentTime;
-		currentTime    = new Date().getTime() * 0.001 - this.startTime;
-
-	}
-
-	frame = Math.min( parseInt( currentTime * this.data.fps ), parseInt( this.data.length * this.data.fps ) );
 
 	// update
 
 	for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
 
 		object = this.hierarchy[ h ];
-
+		animationCache = object.animationCache;
+	
+		// use JIT?
+	
 		if ( JIThierarchy[ h ][ frame ] !== undefined ) {
 
-			object.skinMatrix = JIThierarchy[ h ][ frame ];
-			object.matrixAutoUpdate = false;
-			object.matrixNeedsUpdate = false;
+			if( object instanceof THREE.Bone ) {
+				
+				object.skinMatrix = JIThierarchy[ h ][ frame ];
+				object.skinMatrix.flattenToArrayOffset( this.root.boneMatrices, h * 16 );
+				
+				object.matrixAutoUpdate = false;
+				object.matrixNeedsUpdate = false;
+			}
+			else {
+			
+				object.matrix = JIThierarchy[ h ][ frame ];
+				
+				object.matrixAutoUpdate = false;
+				object.matrixNeedsUpdate = true;
+			}
+			
+		// use interpolation
+	
+		} else {
 
-			object.skinMatrix.flattenToArrayOffset( this.root.boneMatrices, h * 16 );
+			// make sure so original matrix and not JIT matrix is set
 
-		} else {
+			if( object instanceof THREE.Bone ) {
+				
+				object.skinMatrix = object.animationCache.originalMatrix;
+				
+			} else {
+				
+				object.matrix = object.animationCache.originalMatrix;
+				
+			}
+
+
+			// loop through pos/rot/scl
 
 			for ( var t = 0; t < 3; t++ ) {
 
 				// get keys
 
-				var type    = types[ t ];
-				var prevKey = object.prevKey[ type ];
-				var nextKey = object.nextKey[ type ];
+				type    = types[ t ];
+				prevKey = animationCache.prevKey[ type ];
+				nextKey = animationCache.nextKey[ type ];
 
 				// switch keys?
 
@@ -210,12 +238,11 @@ THREE.Animation.prototype.update = function( time ) {
 
 					}
 
-					object.prevKey[ type ] = prevKey;
-					object.nextKey[ type ] = nextKey;
+					animationCache.prevKey[ type ] = prevKey;
+					animationCache.nextKey[ type ] = nextKey;
 
 				}
 
-				// interpolate rot (quaternion slerp)
 
 				object.matrixAutoUpdate = true;
 				object.matrixNeedsUpdate = true;
@@ -224,24 +251,53 @@ THREE.Animation.prototype.update = function( time ) {
 				prevXYZ = prevKey[ type ];
 				nextXYZ = nextKey[ type ];
 
-				if ( type === "rot" ) {
+				
+				// check scale error
 
-					if ( scale < 0 || scale > 1 ) {
+				if ( scale < 0 || scale > 1 ) {
 
-						console.log( "Scale out of bounds:" + scale ); 
-						scale = scale < 0 ? 0 : 1;
+					console.log( "THREE.Animation.update: Warning! Scale out of bounds:" + scale ); 
+					scale = scale < 0 ? 0 : 1;
 
-					}
+				}
 
-					THREE.Quaternion.slerp( prevXYZ, nextXYZ, object.quaternion, scale );
+				// interpolate
+
+				if ( type === "pos" ) {
 
+					if( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
+						
+						vector   = object.position; 
+						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 {
+						
+						var points = [];
+		
+						points[ 0 ] = this.getPrevKeyWith( type, h, prevKey.index - 1 )[ type ];
+						points[ 1 ] = prevXYZ;
+						points[ 2 ] = nextXYZ;
+						points[ 3 ] = this.getNextKeyWith( type, h, nextKey.index + 1 )[ type ];
+
+						scale = scale * 0.33 + 0.33;
+
+						var result = this.interpolateCatmullRom( points, scale );
+						
+						object.position.x = result[ 0 ];
+						object.position.y = result[ 1 ];
+						object.position.z = result[ 2 ];
+					}
 				}
+				else if ( type === "rot" ) {
 
-				// lerp pos/scl 
+					THREE.Quaternion.slerp( prevXYZ, nextXYZ, object.quaternion, scale );
 
-				else {
+				}
+				else if( type === "scl" ) {
 
-					vector   = type === "pos" ? object.position : object.scale; 
+					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;
@@ -256,14 +312,26 @@ THREE.Animation.prototype.update = function( time ) {
 
 	// update JIT?
 
-	if ( JIThierarchy[ 0 ][ frame ] === undefined ) {
-
-		this.hierarchy[ 0 ].update( undefined, true );
-
-		for ( var h = 0; h < this.hierarchy.length; h++ ) {
-
-			JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].skinMatrix.clone();
-
+	if ( this.JITCompile ) {
+		
+		if ( JIThierarchy[ 0 ][ frame ] === undefined ) {
+	
+			this.hierarchy[ 0 ].update( undefined, 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();
+				
+				}
+	
+			}
+	
 		}
 
 	}
@@ -271,16 +339,64 @@ THREE.Animation.prototype.update = function( time ) {
 };
 
 
-/*
- * Update Object
+/**
+ * Spline from Tween.js, slightly optimized
+ * Modified to fit to THREE.Animation.js
+ * 
+ * http://sole.github.com/tween.js/examples/05_spline.html
+ *
+ * @author mrdoob / http://mrdoob.com/
  */
 
-THREE.Animation.prototype.updateObject = function( h, currentTime, unloopedCurrentTime ) {}
+ 
+THREE.Animation.prototype.interpolateCatmullRom = function ( points, scale ) {
+
+	var c = [], v3 = [],
+	point, intPoint, weight, w2, w3,
+	pa, pb, pc, pd;
+	
+	point = ( points.length - 1 ) * scale;
+	intPoint = Math.floor( point );
+	weight = point - intPoint;
+
+	c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
+	c[ 1 ] = intPoint;
+	c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1;
+	c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2;
+
+	pa = points[ c[ 0 ] ];
+	pb = points[ c[ 1 ] ];
+	pc = points[ c[ 2 ] ];
+	pd = points[ c[ 3 ] ];
+
+	w2 = weight * weight;
+	w3 = weight * w2;
+	
+	v3[ 0 ] = this.interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 );
+	v3[ 1 ] = this.interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 );
+	v3[ 2 ] = this.interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 );
+	
+	return v3;
+}
 
 
-THREE.Animation.prototype.getNextKeyWith = function( type, h, key ) {
+THREE.Animation.prototype.interpolate = function( p0, p1, p2, p3, t, t2, t3 ) {
+
+	var v0 = ( p2 - p0 ) * 0.5,
+		v1 = ( p3 - p1 ) * 0.5;
+
+	return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
+}
+
 
+/*
+ * Get next key with
+ */
+
+THREE.Animation.prototype.getNextKeyWith = function( type, h, key ) {
+	
 	var keys = this.data.hierarchy[ h ].keys;
+	key      = key % keys.length;
 
 	for ( ; key < keys.length; key++ ) {
 
@@ -295,3 +411,21 @@ THREE.Animation.prototype.getNextKeyWith = function( type, h, key ) {
 	return this.data.hierarchy[ h ].keys[ 0 ];
 
 }
+
+THREE.Animation.prototype.getPrevKeyWith = function( type, h, key ) {
+	
+	var keys = this.data.hierarchy[ h ].keys;
+	key      = key >= 0 ? key : key + keys.length;
+
+	for ( ; key >= 0; key-- ) {
+
+		if ( keys[ key ][ type ] !== undefined ) {
+
+			return keys[ key ];
+
+		}
+
+	}
+
+	return this.data.hierarchy[ h ].keys[ keys.length - 1 ];
+}

+ 94 - 9
src/animation/AnimationHandler.js

@@ -5,22 +5,23 @@
 THREE.AnimationHandler = (function() {
 
 	var playing = [];
+	var library = {};
 	var that    = {};
 
 
 	//--- update ---
 
-	that.update = function( time ) {
+	that.update = function( deltaTimeMS ) {
 
 		for( var i = 0; i < playing.length; i++ )
-			playing[ i ].update( time );
+			playing[ i ].update( deltaTimeMS );
 
 	};
 
 
 	//--- add ---
 
-	that.add = function( animation ) {
+	that.addToUpdate = function( animation ) {
 
 		if( playing.indexOf( animation ) === -1 )
 			playing.push( animation );
@@ -30,23 +31,97 @@ THREE.AnimationHandler = (function() {
 
 	//--- remove ---
 
-	that.remove = function( animation ) {
+	that.removeFromUpdate = function( animation ) {
 
 		var index = playing.indexOf( animation );
 
 		if( index !== -1 )
-			playing.splice( childIndex, 1 );
+			playing.splice( index, 1 );
 
 	};
 
 
-	//--- init data ---
+	//--- add ---
+	
+	that.add = function( data ) {
+
+		if( library[ data.name ] !== undefined )
+			console.log( "THREE.AnimationHandler.add: Warning! " + data.name + " already exists in library. Overwriting." );
+
+		library[ data.name ] = data;
+		initData( data );
+	};
+
 
-	that.initData = function( data ) {
+	//--- get ---
+	
+	that.get = function( name ) {
+		
+		if( typeof name === "string" ) {
+			
+			if( library[ name ] ) {
+				
+				return library[ name ];
+			
+			} else {
+				
+				console.log( "THREE.AnimationHandler.get: Couldn't find animation " + name );
+				return null;	
+			}
+		}
+		else {
+			
+			// todo: add simple tween library
+		}
+		
+	};
 
+	//--- parse ---
+	
+	that.parse = function( root ) {
+		
+		// setup hierarchy
+
+		var hierarchy = [];
+	
+		if ( root instanceof THREE.SkinnedMesh ) {
+	
+			for( var b = 0; b < root.bones.length; b++ ) {
+	
+				hierarchy.push( root.bones[ b ] );
+	
+			}
+	
+		} else {
+	
+			parseRecurseHierarchy( root, hierarchy );
+	
+		}
+		
+		return hierarchy;
+	};
+
+	var parseRecurseHierarchy = function( root, hierarchy ) {
+		
+		hierarchy.push( root );
+		
+		for( var c = 0; c < root.children.length; c++ ) 
+			parseRecurseHierarchy( root.children[ c ], hierarchy );
+	}
+
+
+	//--- init data ---
+
+	var initData = function( data ) {
+ 
 		if( data.initialized === true )
 			return;
 
+		// THIS SHOULD BE REMOVED WHEN LENGTH IS UPDATED TO MS IN EXPORT FORMAT!
+		data.length = parseInt( data.length * 1000, 10 );	
+		data.fps   *= 0.001;
+		
+
 		// loop through all keys
 
 		for( var h = 0; h < data.hierarchy.length; h++ ) {
@@ -59,6 +134,9 @@ THREE.AnimationHandler = (function() {
 					data.hierarchy[ h ].keys[ k ].time = 0;
 
 
+				// THIS SHOULD BE REMOVED WHEN LENGTH IS UPDATED TO MS IN EXPORT FORMAT!
+				data.hierarchy[ h ].keys[ k ].time = parseInt( data.hierarchy[ h ].keys[ k ].time * 1000, 10 );
+
 				// set index
 
 				data.hierarchy[ h ].keys[ k ].index = k;
@@ -67,7 +145,7 @@ THREE.AnimationHandler = (function() {
 				// create quaternions
 
 				if( data.hierarchy[ h ].keys[ k ].rot !== undefined &&
-				  !( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) {
+				 !( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) {
 
 					var quat = data.hierarchy[ h ].keys[ k ].rot;
 					data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion( quat[0], quat[1], quat[2], quat[3] );
@@ -79,7 +157,7 @@ THREE.AnimationHandler = (function() {
 
 		// JIT
 
-		var lengthInFrames = parseInt( data.length * data.fps, 10 );
+		var lengthInFrames = parseInt( data.length * data.fps * 0.001, 10 );
 
 		data.JIT = {};
 		data.JIT.hierarchy = [];
@@ -94,5 +172,12 @@ THREE.AnimationHandler = (function() {
 
 	};
 
+
+	// interpolation typess
+
+	that.LINEAR = 0;
+	that.CATMULLROM = 1;
+	that.CATMULLROM_FORWARD = 2;
+
 	return that;
 }());

+ 0 - 2
src/renderers/WebGLRenderer.js

@@ -1867,8 +1867,6 @@ THREE.WebGLRenderer = function ( parameters ) {
 		_projScreenMatrix.multiply( camera.projectionMatrix, camera.matrixWorldInverse );
 		computeFrustum( _projScreenMatrix );
 
-		if( THREE.AnimationHandler ) THREE.AnimationHandler.update();
-
 		scene.update( undefined, false, camera );
 
 		this.initWebGLObjects( scene, camera );