Browse Source

Fixed Quaternion::slerp.

Mr.doob 14 years ago
parent
commit
d1aff49533
3 changed files with 133 additions and 143 deletions
  1. 4 3
      build/Three.js
  2. 105 116
      src/animation/Animation.js
  3. 24 24
      src/core/Quaternion.js

+ 4 - 3
build/Three.js

@@ -48,9 +48,10 @@ this.matrix.n12=this.matrixRotation.n12;this.matrix.n13=this.matrixRotation.n13;
 b,c){if(this.visible){this.matrixAutoUpdate&&(b|=this.updateMatrix());if(b||this.matrixNeedsUpdate){a?this.matrixWorld.multiply(a,this.matrix):this.matrixWorld.copy(this.matrix);this.matrixNeedsUpdate=!1;b=!0}var d=this.children.length;for(a=0;a<d;a++)this.children[a].update(this.matrixWorld,b,c)}}};THREE.Object3DCounter={value:0};THREE.Quaternion=function(a,b,c,d){this.set(a||0,b||0,c||0,d!==undefined?d:1)};
 THREE.Quaternion.prototype={set:function(a,b,c,d){this.x=a;this.y=b;this.z=c;this.w=d;return this},setFromEuler:function(a){var b=0.5*Math.PI/360,c=a.x*b,d=a.y*b,f=a.z*b;a=Math.cos(d);d=Math.sin(d);b=Math.cos(-f);f=Math.sin(-f);var g=Math.cos(c);c=Math.sin(c);var h=a*b,k=d*f;this.w=h*g-k*c;this.x=h*c+k*g;this.y=d*b*g+a*f*c;this.z=a*f*g-d*b*c;return this},calculateW:function(){this.w=-Math.sqrt(Math.abs(1-this.x*this.x-this.y*this.y-this.z*this.z));return this},inverse:function(){this.x*=-1;this.y*=
 -1;this.z*=-1;return this},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},normalize:function(){var a=Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w);if(a==0)this.w=this.z=this.y=this.x=0;else{a=1/a;this.x*=a;this.y*=a;this.z*=a;this.w*=a}return this},multiplySelf:function(a){var b=this.x,c=this.y,d=this.z,f=this.w,g=a.x,h=a.y,k=a.z;a=a.w;this.x=b*a+f*g+c*k-d*h;this.y=c*a+f*h+d*g-b*k;this.z=d*a+f*k+b*h-c*g;this.w=f*a-b*g-c*h-d*k;return this},
-multiplyVector3:function(a,b){b||(b=a);var c=a.x,d=a.y,f=a.z,g=this.x,h=this.y,k=this.z,j=this.w,m=j*c+h*f-k*d,n=j*d+k*c-g*f,w=j*f+g*d-h*c;c=-g*c-h*d-k*f;b.x=m*j+c*-g+n*-k-w*-h;b.y=n*j+c*-h+w*-g-m*-k;b.z=w*j+c*-k+m*-h-n*-g;return b},slerp:function(a,b,c,d){var f=a.w*b.w+a.x*b.x+a.y*b.y+a.z*b.z;if(Math.abs(f)>=1){c.w=a.w;c.x=a.x;c.y=a.y;c.z=a.z;return c}var g=Math.acos(f),h=Math.sqrt(1-f*f);if(Math.abs(h)<0.001){c.w=0.5*(a.w+b.w);c.x=0.5*(a.x+b.x);c.y=0.5*(a.y+b.y);c.z=0.5*(a.z+b.z);return c}f=Math.sin((1-
-d)*g)/h;d=Math.sin(d*g)/h;c.w=a.w*f+b.w*d;c.x=a.x*f+b.x*d;c.y=a.y*f+b.y*d;c.z=a.z*f+b.z*d;return c}};THREE.Vertex=function(a,b){this.position=a||new THREE.Vector3;this.positionWorld=new THREE.Vector3;this.positionScreen=new THREE.Vector4;this.normal=b||new THREE.Vector3;this.normalWorld=new THREE.Vector3;this.normalScreen=new THREE.Vector3;this.tangent=new THREE.Vector4;this.__visible=!0};
-THREE.Vertex.prototype={toString:function(){return"THREE.Vertex ( position: "+this.position+", normal: "+this.normal+" )"}};THREE.Face3=function(a,b,c,d,f){this.a=a;this.b=b;this.c=c;this.centroid=new THREE.Vector3;this.normal=d instanceof THREE.Vector3?d:new THREE.Vector3;this.vertexNormals=d instanceof Array?d:[];this.materials=f instanceof Array?f:[f]};THREE.Face3.prototype={toString:function(){return"THREE.Face3 ( "+this.a+", "+this.b+", "+this.c+" )"}};
+multiplyVector3:function(a,b){b||(b=a);var c=a.x,d=a.y,f=a.z,g=this.x,h=this.y,k=this.z,j=this.w,m=j*c+h*f-k*d,n=j*d+k*c-g*f,w=j*f+g*d-h*c;c=-g*c-h*d-k*f;b.x=m*j+c*-g+n*-k-w*-h;b.y=n*j+c*-h+w*-g-m*-k;b.z=w*j+c*-k+m*-h-n*-g;return b}};
+THREE.Quaternion.slerp=function(a,b,c,d){var f=a.w*b.w+a.x*b.x+a.y*b.y+a.z*b.z;if(Math.abs(f)>=1){c.w=a.w;c.x=a.x;c.y=a.y;c.z=a.z;return c}var g=Math.acos(f),h=Math.sqrt(1-f*f);if(Math.abs(h)<0.001){c.w=0.5*(a.w+b.w);c.x=0.5*(a.x+b.x);c.y=0.5*(a.y+b.y);c.z=0.5*(a.z+b.z);return c}f=Math.sin((1-d)*g)/h;d=Math.sin(d*g)/h;c.w=a.w*f+b.w*d;c.x=a.x*f+b.x*d;c.y=a.y*f+b.y*d;c.z=a.z*f+b.z*d;return c};
+THREE.Vertex=function(a,b){this.position=a||new THREE.Vector3;this.positionWorld=new THREE.Vector3;this.positionScreen=new THREE.Vector4;this.normal=b||new THREE.Vector3;this.normalWorld=new THREE.Vector3;this.normalScreen=new THREE.Vector3;this.tangent=new THREE.Vector4;this.__visible=!0};THREE.Vertex.prototype={toString:function(){return"THREE.Vertex ( position: "+this.position+", normal: "+this.normal+" )"}};
+THREE.Face3=function(a,b,c,d,f){this.a=a;this.b=b;this.c=c;this.centroid=new THREE.Vector3;this.normal=d instanceof THREE.Vector3?d:new THREE.Vector3;this.vertexNormals=d instanceof Array?d:[];this.materials=f instanceof Array?f:[f]};THREE.Face3.prototype={toString:function(){return"THREE.Face3 ( "+this.a+", "+this.b+", "+this.c+" )"}};
 THREE.Face4=function(a,b,c,d,f,g){this.a=a;this.b=b;this.c=c;this.d=d;this.centroid=new THREE.Vector3;this.normal=f instanceof THREE.Vector3?f:new THREE.Vector3;this.vertexNormals=f instanceof Array?f:[];this.materials=g instanceof Array?g:[g]};THREE.Face4.prototype={toString:function(){return"THREE.Face4 ( "+this.a+", "+this.b+", "+this.c+" "+this.d+" )"}};THREE.UV=function(a,b){this.set(a||0,b||0)};
 THREE.UV.prototype={set:function(a,b){this.u=a;this.v=b;return this},copy:function(a){this.set(a.u,a.v);return this},toString:function(){return"THREE.UV ("+this.u+", "+this.v+")"}};THREE.Geometry=function(){this.id="Geometry"+THREE.GeometryIdCounter++;this.vertices=[];this.faces=[];this.uvs=[];this.uvs2=[];this.colors=[];this.skinWeights=[];this.skinIndices=[];this.boundingSphere=this.boundingBox=null;this.geometryChunks={};this.hasTangents=!1};
 THREE.Geometry.prototype={computeCentroids:function(){var a,b,c;a=0;for(b=this.faces.length;a<b;a++){c=this.faces[a];c.centroid.set(0,0,0);if(c instanceof THREE.Face3){c.centroid.addSelf(this.vertices[c.a].position);c.centroid.addSelf(this.vertices[c.b].position);c.centroid.addSelf(this.vertices[c.c].position);c.centroid.divideScalar(3)}else if(c instanceof THREE.Face4){c.centroid.addSelf(this.vertices[c.a].position);c.centroid.addSelf(this.vertices[c.b].position);c.centroid.addSelf(this.vertices[c.c].position);

+ 105 - 116
src/animation/Animation.js

@@ -3,33 +3,38 @@
  */
 
 THREE.Animation = function( root, data ) {
-	
-	this.root      = root;
-	this.data      = data;
+
+	this.root = root;
+	this.data = data;
 	this.hierarchy = [];
 
 	this.startTime = 0;
 	this.isPlaying = false;
-	this.loop      = true;
-	
-	this.offset    = 0;
+	this.loop = true;
 
+	this.offset = 0;
 
 	// need to initialize data?
 
-	if( !this.data.initialized )
+	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++ )
+	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)
 
 	}
@@ -44,36 +49,35 @@ THREE.Animation = function( root, data ) {
 THREE.Animation.prototype.play = function( loop ) {
 
 	if( !this.isPlaying ) {
-		
+
 		this.isPlaying = true;
 		this.startTime = new Date().getTime() * 0.001;
 
-		
 		// reset key cache
-		
-		for( var h = 0; h < this.hierarchy.length; h++ ) {
-			
+
+		for ( var h = 0; h < this.hierarchy.length; h++ ) {
+
 			this.hierarchy[ h ].useQuaternion    = true;
 			this.hierarchy[ h ].matrixAutoUpdate = true;
-			
-			if( this.hierarchy[ h ].prevKey === undefined ) {
-				
+
+			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 };
 
 			}
-			
+
 			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 );
-		}	
-		
+		}
+
 		this.update();
-		
+
 		THREE.AnimationHandler.add( this );
 
 	}
@@ -86,9 +90,9 @@ THREE.Animation.prototype.play = function( loop ) {
  */
 
 THREE.Animation.prototype.pause = function() {
-	
+
 	THREE.AnimationHandler.remove( this );
-	
+
 	// todo
 }
 
@@ -98,7 +102,7 @@ THREE.Animation.prototype.pause = function() {
  */
 
 THREE.Animation.prototype.stop = function() {
-	
+
 	this.isPlaying = false;
 	THREE.AnimationHandler.remove( this );
 }
@@ -110,15 +114,15 @@ THREE.Animation.prototype.stop = function() {
 
 THREE.Animation.prototype.update = function( time ) {
 
-	// todo: add input time
+	// TODO: add input time
 
 	// early out
-	
+
 	if( !this.isPlaying ) return;
 
 
 	// vars
-	
+
 	var types = [ "pos", "rot", "scl" ];
 	var scale;
 	var relative;
@@ -127,117 +131,116 @@ THREE.Animation.prototype.update = function( time ) {
 	var object;
 	var frame;
 	var JIThierarchy = this.data.JIT.hierarchy;
-	
-	
+
 	// update
-	
+
 	var currentTime         = new Date().getTime() * 0.001 - this.startTime + this.offset;
 	var unloopedCurrentTime = currentTime;
 
 
 	// looped?
-	
-	if( currentTime > this.data.length ) {
-		
-		while( currentTime > this.data.length )
+
+	if ( currentTime > this.data.length ) {
+
+		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++ ) {
+
+	for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
 
 		object = this.hierarchy[ h ];
-	
-		if( JIThierarchy[ h ][ frame ] !== undefined ) {
 
-			object.skinMatrix         = JIThierarchy[ h ][ frame ];
-			object.matrixAutoUpdate   = false;
-			object.matrixNeedsUpdate  = false;
+		if ( JIThierarchy[ h ][ frame ] !== undefined ) {
+
+			object.skinMatrix = JIThierarchy[ h ][ frame ];
+			object.matrixAutoUpdate = false;
+			object.matrixNeedsUpdate = false;
 
 			object.skinMatrix.flattenToArrayOffset( this.root.boneMatrices, h * 16 );
-			
-		}
-		else {
-		
-			for( var t = 0; t < 3; t++ ) {
-				
+
+		} else {
+
+			for ( var t = 0; t < 3; t++ ) {
+
 				// get keys
-				
+
 				var type    = types[ t ];
 				var prevKey = object.prevKey[ type ];
 				var nextKey = object.nextKey[ type ];
-				
+
 				// switch keys?
 
-				if( nextKey.time < unloopedCurrentTime ) {
-		
+				if ( nextKey.time < unloopedCurrentTime ) {
+
 					// did we loop?
-		
-					if( currentTime < unloopedCurrentTime ) {
-						
-						if( this.loop ) {
-							
+
+					if ( currentTime < unloopedCurrentTime ) {
+
+						if ( this.loop ) {
+
 							prevKey = this.data.hierarchy[ h ].keys[ 0 ];
 							nextKey = this.getNextKeyWith( type, h, 1 );
 
 						} else {
-							
+
 							this.stop();
 							return;
 
 						}
 
 					} else {
-						
+
 						do {
-							
+
 							prevKey = nextKey;
 							nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
 
-						}
-						while( nextKey.time < currentTime )
+						} while( nextKey.time < currentTime )
+
 					}
-		
+
 					object.prevKey[ type ] = prevKey;
 					object.nextKey[ type ] = nextKey;
 
 				}
-				
-				
+
 				// interpolate rot (quaternion slerp)
-				
+
 				object.matrixAutoUpdate = true;
 				object.matrixNeedsUpdate = true;
-				
-				scale   = ( currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
+
+				scale = ( currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
 				prevXYZ = prevKey[ type ];
 				nextXYZ = nextKey[ type ];
-		
-				if( type === "rot" ) {
-		
-					if( scale < 0 || scale > 1 ) {
-						
+
+				if ( type === "rot" ) {
+
+					if ( scale < 0 || scale > 1 ) {
+
 						console.log( "Scale out of bounds:" + scale ); 
 						scale = scale < 0 ? 0 : 1;
-					
+
 					}
-		
+
 					THREE.Quaternion.slerp( prevXYZ, nextXYZ, object.quaternion, scale );
 
 				}
-				
+
 				// lerp pos/scl 
 
 				else {
-					
+
 					vector   = type === "pos" ? object.position : object.scale; 
 					vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
 					vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
@@ -250,59 +253,45 @@ THREE.Animation.prototype.update = function( time ) {
 		}
 
 	}
-	
-	
+
 	// update JIT?
-	
-	if( JIThierarchy[ 0 ][ frame ] === undefined ) {
-		
+
+	if ( JIThierarchy[ 0 ][ frame ] === undefined ) {
+
 		this.hierarchy[ 0 ].update( undefined, true );
-	
-		for( var h = 0; h < this.hierarchy.length; h++ ) 
+
+		for ( var h = 0; h < this.hierarchy.length; h++ ) {
+
 			JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].skinMatrix.clone();
 
+		}
+
 	}
 
 };
 
 
-
 /*
  * Update Object
- */	
-
-THREE.Animation.prototype.updateObject = function( h, currentTime, unloopedCurrentTime ) {
-	
-
-}
+ */
 
+THREE.Animation.prototype.updateObject = function( h, currentTime, unloopedCurrentTime ) {}
 
 
 THREE.Animation.prototype.getNextKeyWith = function( type, h, key ) {
-	
-	var keys = this.data.hierarchy[ h ].keys;
-	
-	for( ; key < keys.length; key++ ) {
-		
-		if( keys[ key ][ type ] !== undefined )
-			return keys[ key ];
-
-	}
-
-	return this.data.hierarchy[ h ].keys[ 0 ];
-
-}
-
-
-
-
-
-
 
+	var keys = this.data.hierarchy[ h ].keys;
 
+	for ( ; key < keys.length; key++ ) {
 
+		if ( keys[ key ][ type ] !== undefined ) {
 
+			return keys[ key ];
 
+		}
 
+	}
 
+	return this.data.hierarchy[ h ].keys[ 0 ];
 
+}

+ 24 - 24
src/core/Quaternion.js

@@ -139,43 +139,43 @@ THREE.Quaternion.prototype = {
 
 		return dest;
 
-	},
+	}
 
-	slerp : function ( qa, qb, qm, t ) {
+}
 
-		var cosHalfTheta = qa.w * qb.w + qa.x * qb.x + qa.y * qb.y + qa.z * qb.z;
+THREE.Quaternion.slerp = function ( qa, qb, qm, t ) {
 
-		if ( Math.abs( cosHalfTheta ) >= 1.0 ) {
+	var cosHalfTheta = qa.w * qb.w + qa.x * qb.x + qa.y * qb.y + qa.z * qb.z;
 
-			qm.w = qa.w; qm.x = qa.x; qm.y = qa.y; qm.z = qa.z;
-			return qm;
+	if ( Math.abs( cosHalfTheta ) >= 1.0 ) {
 
-		}
+		qm.w = qa.w; qm.x = qa.x; qm.y = qa.y; qm.z = qa.z;
+		return qm;
 
-		var halfTheta = Math.acos( cosHalfTheta ),
-		sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
+	}
 
-		if ( Math.abs( sinHalfTheta ) < 0.001 ) { 
+	var halfTheta = Math.acos( cosHalfTheta ),
+	sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
 
-			qm.w = 0.5 * ( qa.w + qb.w );
-			qm.x = 0.5 * ( qa.x + qb.x );
-			qm.y = 0.5 * ( qa.y + qb.y );
-			qm.z = 0.5 * ( qa.z + qb.z );
+	if ( Math.abs( sinHalfTheta ) < 0.001 ) { 
 
-			return qm;
+		qm.w = 0.5 * ( qa.w + qb.w );
+		qm.x = 0.5 * ( qa.x + qb.x );
+		qm.y = 0.5 * ( qa.y + qb.y );
+		qm.z = 0.5 * ( qa.z + qb.z );
 
-		}
+		return qm;
 
-		var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
-		ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; 
+	}
 
-		qm.w = ( qa.w * ratioA + qb.w * ratioB );
-		qm.x = ( qa.x * ratioA + qb.x * ratioB );
-		qm.y = ( qa.y * ratioA + qb.y * ratioB );
-		qm.z = ( qa.z * ratioA + qb.z * ratioB );
+	var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
+	ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; 
 
-		return qm;
+	qm.w = ( qa.w * ratioA + qb.w * ratioB );
+	qm.x = ( qa.x * ratioA + qb.x * ratioB );
+	qm.y = ( qa.y * ratioA + qb.y * ratioB );
+	qm.z = ( qa.z * ratioA + qb.z * ratioB );
 
-	}
+	return qm;
 
 }