Browse Source

Morph targets added

Mikael Emtinger 14 years ago
parent
commit
b4b730c07d

File diff suppressed because it is too large
+ 239 - 238
build/Three.js


+ 4 - 2
src/animation/Animation.js

@@ -8,6 +8,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 = 1;
 	this.isPlaying = false;
 	this.isPaused = true;
 	this.loop = true;
@@ -92,6 +93,7 @@ THREE.Animation.prototype.pause = function() {
 THREE.Animation.prototype.stop = function() {
 
 	this.isPlaying = false;
+	this.isPaused  = false;
 	THREE.AnimationHandler.removeFromUpdate( this );
 	
 	
@@ -146,7 +148,7 @@ THREE.Animation.prototype.update = function( deltaTimeMS ) {
 
 	// update
 	
-	this.currentTime += deltaTimeMS;
+	this.currentTime += deltaTimeMS * this.timeScale;
 
 	unloopedCurrentTime = this.currentTime;
 	currentTime         = this.currentTime = this.currentTime % this.data.length;
@@ -216,7 +218,7 @@ THREE.Animation.prototype.update = function( deltaTimeMS ) {
 
 					// did we loop?
 
-					if ( currentTime <= unloopedCurrentTime ) {
+					if ( currentTime < unloopedCurrentTime ) {
 
 						if ( this.loop ) {
 

+ 51 - 8
src/animation/AnimationHandler.js

@@ -116,10 +116,6 @@ THREE.AnimationHandler = (function() {
  
 		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
@@ -134,10 +130,6 @@ 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 );
-
-
 				// create quaternions
 
 				if( data.hierarchy[ h ].keys[ k ].rot !== undefined &&
@@ -147,6 +139,57 @@ THREE.AnimationHandler = (function() {
 					data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion( quat[0], quat[1], quat[2], quat[3] );
 
 				}
+			}
+
+
+			// prepare morph target keys
+			
+			if( data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) {
+
+				// get all used
+
+				var usedMorphTargets = {};
+				
+				for( var k = 0; k < data.hierarchy[ h ].keys.length; k++ ) {
+	
+					for( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m++ ) {
+						
+						var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ];
+						usedMorphTargets[ morphTargetName ] = -1;
+					}					
+				
+				}
+				
+				data.hierarchy[ h ].usedMorphTargets = usedMorphTargets;
+				
+				
+				// set all used on all frames
+				
+				for( var k = 0; k < data.hierarchy[ h ].keys.length; k++ ) {
+	
+					var influences = {};
+	
+					for( var morphTargetName in usedMorphTargets ) {
+			
+						for( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m++ ) {
+	
+							if( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) {
+								
+								influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ];
+								break;	
+							}
+					
+						}
+						
+						if( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) {
+							
+							influences[ morphTargetName ] = 0;
+						}
+	
+					}
+				
+					data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences;
+				}
 			
 			}
 			

+ 218 - 0
src/animation/AnimationMorphTarget.js

@@ -0,0 +1,218 @@
+/**
+ * @author mikael emtinger / http://gomo.se/
+ */
+
+THREE.AnimationMorphTarget = function( root, data ) {
+
+	this.root = root;
+	this.data = THREE.AnimationHandler.get( data );
+	this.hierarchy = THREE.AnimationHandler.parse( root );
+	this.currentTime = 0;
+	this.timeScale = 1;
+	this.isPlaying = false;
+	this.isPaused = true;
+	this.loop = true;
+	this.influence = 1;
+}
+
+/*
+ * Play
+ */
+
+THREE.AnimationMorphTarget.prototype.play = function( loop, startTimeMS ) {
+
+	if( !this.isPlaying ) {
+
+		this.isPlaying = true;
+		this.loop = loop !== undefined ? loop : true;
+		this.currentTime = startTimeMS !== undefined ? startTimeMS : 0;
+
+
+		// reset key cache
+
+		for ( var h = 0; h < this.hierarchy.length; h++ ) {
+
+			if ( this.hierarchy[ h ].animationCache === undefined ) {
+
+				this.hierarchy[ h ].animationCache = {};
+				this.hierarchy[ h ].animationCache.prevKey = 0;
+				this.hierarchy[ h ].animationCache.nextKey = 0;
+			}
+
+			this.hierarchy[ h ].animationCache.prevKey = this.data.hierarchy[ h ].keys[ 0 ];
+			this.hierarchy[ h ].animationCache.nextKey = this.data.hierarchy[ h ].keys[ 1 ];
+		}
+
+		this.update( 0 );
+	}
+
+	this.isPaused = false;
+	THREE.AnimationHandler.addToUpdate( this );
+}
+
+
+/*
+ * Pause
+ */
+
+THREE.AnimationMorphTarget.prototype.pause = function() {
+
+	if( this.isPaused ) {
+		
+		THREE.AnimationHandler.addToUpdate( this );
+		
+	} else {
+		
+		THREE.AnimationHandler.removeFromUpdate( this );
+		
+	}
+	
+	this.isPaused = !this.isPaused;
+}
+
+
+/*
+ * Stop
+ */
+
+THREE.AnimationMorphTarget.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++ ) {
+		
+		if ( this.hierarchy[ h ].animationCache !== undefined ) {
+			
+			delete this.hierarchy[ h ].animationCache;	
+		}
+
+	}
+
+}
+
+
+/*
+ * Update
+ */
+
+THREE.AnimationMorphTarget.prototype.update = function( deltaTimeMS ) {
+
+	// early out
+
+	if( !this.isPlaying ) return;
+
+
+	// vars
+
+	var scale;
+	var vector;
+	var prevXYZ, nextXYZ;
+	var prevKey, nextKey;
+	var object;
+	var animationCache;
+	var currentTime, unloopedCurrentTime;
+	
+
+	// update time
+	
+	this.currentTime += deltaTimeMS * this.timeScale;
+
+	unloopedCurrentTime = this.currentTime;
+	currentTime         = this.currentTime = this.currentTime % this.data.length;
+
+
+	// update
+
+	for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
+
+		object = this.hierarchy[ h ];
+		animationCache = object.animationCache;
+
+
+		// get keys
+
+		prevKey = animationCache.prevKey;
+		nextKey = animationCache.nextKey;
+
+
+		// switch keys?
+
+		if ( nextKey.time <= unloopedCurrentTime ) {
+
+			// did we loop?
+
+			if ( currentTime < unloopedCurrentTime ) {
+
+				if ( this.loop ) {
+
+					prevKey = this.data.hierarchy[ h ].keys[ 0 ];
+					nextKey = this.data.hierarchy[ h ].keys[ 1 ];
+
+					while( nextKey.time < currentTime ) {
+
+						prevKey = nextKey;
+						nextKey = this.data.hierarchy[ h ].keys[ nextKey.index + 1 ];
+
+					}
+
+				} else {
+
+					this.stop();
+					return;
+
+				}
+
+			} else {
+
+				do {
+
+					prevKey = nextKey;
+					nextKey = this.data.hierarchy[ h ].keys[ nextKey.index + 1 ];
+
+				} while( nextKey.time < currentTime )
+
+			}
+
+			animationCache.prevKey = prevKey;
+			animationCache.nextKey = nextKey;
+
+		}
+
+
+		// calc scale and check for error
+
+		scale = ( currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
+
+		if ( scale < 0 || scale > 1 ) {
+
+			console.log( "THREE.AnimationMorphTarget.update: Warning! Scale out of bounds:" + scale ); 
+			scale = scale < 0 ? 0 : 1;
+
+		}
+
+
+		// interpolate
+		
+		var pi, pmti = prevKey.morphTargetsInfluences;
+		var ni, nmti = nextKey.morphTargetsInfluences;
+		var mt, i;
+		
+		for( mt in pmti ) {
+			
+			pi = pmti[ mt ];
+			ni = nmti[ mt ];
+			i = this.root.getMorphTargetIndexByName( mt );
+			
+			this.root.morphTargetInfluences[ i ] = ( pi + ( ni - pi ) * scale ) * this.influence;
+		}
+
+	}
+
+};
+

+ 21 - 2
src/objects/Mesh.js

@@ -34,12 +34,14 @@ THREE.Mesh = function ( geometry, materials ) {
 
 		if( this.geometry.morphTargets.length ) {
 			
-			this.morphTargetBase = 0;
+			this.morphTargetBase = -1;
 			this.morphTargetInfluences = [];
+			this.morphTargetDictionary = {};
 			
 			for( var m = 0; m < this.geometry.morphTargets.length; m++ ) {
 				
-				this.morphTargetInfluences.push( 0 );	
+				this.morphTargetInfluences.push( 0 );
+				this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m;
 			}
 
 		}
@@ -51,3 +53,20 @@ THREE.Mesh = function ( geometry, materials ) {
 THREE.Mesh.prototype = new THREE.Object3D();
 THREE.Mesh.prototype.constructor = THREE.Mesh;
 THREE.Mesh.prototype.supr = THREE.Object3D.prototype;
+
+
+/*
+ * Get Morph Target Index by Name
+ */
+
+THREE.Mesh.prototype.getMorphTargetIndexByName = function( name ) {
+
+	if( this.morphTargetDictionary[ name ] !== undefined ) {
+		
+		return this.morphTargetDictionary[ name ];
+	}
+	
+
+	console.log( "THREE.Mesh.getMorphTargetIndexByName: morph target " + name + " does not exist. Returning 0." );	
+	return 0;
+}

+ 0 - 86
src/objects/VertexAnimatedMesh.js

@@ -1,86 +0,0 @@
-/**
- * @author Mikael Emtinger
- */
-
-THREE.VertexAnimatedMesh = function( geometry, materials ) {
-
-	THREE.Mesh.call( this, geometry, materials );
-	
-	this.time = 0;
-	this.keyInterpolation = 0.5;
-	this.keyA = 0;
-	this.keyB = 1;
-	this.length = geometry.vertexKeys[ geometry.vertexKeys.length - 1 ].time;
-	this.isPlaying = false;
-	
-	this.animationHandlerAPI = {};
-	this.animationHandlerAPI.that = this;
-	this.animationHandlerAPI.update = this.updateTime;			// this is to not override Object3D.update
-}
-
-
-THREE.VertexAnimatedMesh.prototype = new THREE.Mesh();
-THREE.VertexAnimatedMesh.prototype.constructor = THREE.VertexAnimatedMesh;
-
-
-/*
- * Update time (called through this.animationHandlerAPI by AnimationHandler.updste)
- */
-
-THREE.VertexAnimatedMesh.prototype.updateTime = function( deltaTimeMS ) {
-
-	// update time
-
-	var that = this.that;
-
-	that.time = ( that.time + deltaTimeMS ) % that.length;
-	that.time = Math.max( 0, that.time );
-	
-	
-	// find keys and calculate interpolation
-	
-	var k = 0,
-		keys = that.geometry.vertexKeys,
-		kl = keys.length;
-		
-	while( keys[ k ].time <= that.time && k < kl ) {
-		
-		k++;
-	}
-	
-	if( k !== kl ) {
-		
-		that.keyA = k - 1;
-		that.keyB = k;
-		that.keyInterpolation = ( that.time - keys[ that.keyA ].time ) / ( keys[ that.keyB ].time - keys[ that.keyA ].time );
-	
-	} else {
-		
-		that.keyA = 0;
-		that.keyB = 1;
-		that.keyInterpolation = 0;
-	}
-}
-
-
-/*
- * Play
- */
-
-THREE.VertexAnimatedMesh.prototype.play = function( startTimeMS ) {
-	
-	this.time = Math.max( 0, startTimeMS % this.length );
-	
-	THREE.AnimationHandler.addToUpdate( this.animationHandlerAPI );	
-}
-
-
-/*
- * Stop
- */
-
-THREE.VertexAnimatedMesh.prototype.stop = function() {
-	
-	this.time = 0;
-	THREE.AnimationHandler.removeFromUpdate( this.animationHandlerAPI );
-}

+ 27 - 13
src/renderers/WebGLRenderer.js

@@ -1452,7 +1452,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	this.initMaterial = function ( material, lights, fog, object ) {
 
-		var u, identifiers, i, parameters, maxLightCount, maxBones;
+		var u, a, identifiers, i, parameters, maxLightCount, maxBones;
 
 		if ( material instanceof THREE.MeshDepthMaterial ) {
 
@@ -1499,18 +1499,23 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		material.program = buildProgram( material.fragmentShader, material.vertexShader, parameters );
 
+
+		// load uniforms
+
 		identifiers = [ 'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'objectMatrix', 'cameraPosition',
 						'cameraInverseMatrix', 'boneGlobalMatrices', 'morphTargetInfluences'
 						];
 
+
 		for( u in material.uniforms ) {
 
 			identifiers.push(u);
-
 		}
 
 		cacheUniformLocations( material.program, identifiers );
 		
+
+		// load attributes
 		
 		identifiers = [ "position", "normal", "uv", "uv2", "tangent", "color",
 					    "skinVertexA", "skinVertexB", "skinIndex", "skinWeight" ];
@@ -1520,6 +1525,11 @@ THREE.WebGLRenderer = function ( parameters ) {
 			identifiers.push( "morphTarget" + i );
 		}
 		
+		for( a in material.attributes ) {
+			
+			identifiers.push( a );
+		}
+		
 		cacheAttributeLocations( material.program, identifiers );
 
 
@@ -1704,26 +1714,32 @@ THREE.WebGLRenderer = function ( parameters ) {
 			
 			// set base
 			
-			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webGLMorphTargetsBuffers[ object.morphTargetBase ] );
-			_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
+			if(  object.morphTargetBase !== -1 ) {
+				
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webGLMorphTargetsBuffers[ object.morphTargetBase ] );
+				_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
+				
+			} else {
+				
+				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webGLVertexBuffer );
+				_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
+				
+			}
 			
 			
 			// find most influencing
 			
-			var used = [];
 			var candidateInfluence = -1;
 			var candidate = 0;
 			var influences = object.morphTargetInfluences;
 			var i, il = influences.length;
 			var m = 0;
 
-			used[ object.morphTargetBase ] = 1;
-			
 			while( m < material.numSupportedMorphTargets ) {
 				
 				for( i = 0; i < il; i++ ) {
 					
-					if( !used[ i ] && candidateInfluence < influences[ i ] ) {
+					if( i != object.morphTargetBase && influences[ i ] > candidateInfluence ) {
 						
 						candidate = i;
 						candidateInfluence = influences[ candidate ];
@@ -1735,9 +1751,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 				
 				object.__webGLMorphTargetInfluences[ m ] = candidateInfluence;
 
-				used[ candidate ] = 1;
 				candidateInfluence = -1;
-				
 				m++;
 			}
 			
@@ -3865,6 +3879,7 @@ THREE.Snippets = {
 		"morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];",
 		"morphed *= 0.25;",
 		"morphed += position;",
+		
 		"gl_Position = projectionMatrix * modelViewMatrix * vec4( morphed, 1.0 );",
 
 	"#else",
@@ -3873,7 +3888,7 @@ THREE.Snippets = {
 
 	"#endif"
 
-	].join("\n")	
+	].join("\n")
 
 };
 
@@ -4256,6 +4271,5 @@ THREE.ShaderLib = {
 		].join("\n")
 
 	}
-
-
+	
 };

+ 0 - 1
utils/build.py

@@ -62,7 +62,6 @@ COMMON_FILES = [
 'objects/Ribbon.js',
 'objects/Sound.js',
 'objects/LOD.js',
-'objects/VertexAnimatedMesh.js',
 'scenes/Scene.js',
 'scenes/Fog.js',
 'scenes/FogExp2.js',

Some files were not shown because too many files changed in this diff