Browse Source

introduce the TrackBinding class for better organization.

Ben Houston 10 years ago
parent
commit
8e94b36fa7
3 changed files with 202 additions and 128 deletions
  1. 7 43
      src/animation/AnimationMixer.js
  2. 1 85
      src/animation/AnimationUtils.js
  3. 194 0
      src/animation/TrackBinding.js

+ 7 - 43
src/animation/AnimationMixer.js

@@ -12,7 +12,7 @@ THREE.AnimationMixer = function( root ) {
 
 	this.root = root;
 	this.actions = [];
-	this.trackInfos = {};
+	this.trackBindings = {};
 
 };
 
@@ -25,12 +25,9 @@ THREE.AnimationMixer.prototype = {
 
 		this.actions.push( action );
 
-		foreach( var track in action.tracks ) {
-			if( ! this.trackInfos[track.name] ) {
-				this.trackInfos[track.name] = {
-					node: THREE.AnimationUtils.findNode( this.root, track.nodeName ),
-					propertyName: track.propertyName
-				} 
+		for( var track in action.tracks ) {
+			if( ! this.trackBindings[track.name] ) {
+				this.trackBindings[track.name] = new THREE.TrackBinding( this.root, track.name );
 			}
 		}
 
@@ -86,42 +83,9 @@ THREE.AnimationMixer.prototype = {
 
 			var mixerResult = mixerResults[ name ];
 
-			var trackInfo = this.trackInfos[ name ];
-
-			var node = trackInfo.node;
-
-			if( ! node ) {
-				console.log( "  trying to update node for track: " + name + " but it wasn't found." );
-				continue;
-			}
-
-			var propertyName = trackInfo.propertyName;
-
-			if( ! node[ propertyName ] ) {
-				console.log( "  trying to update property for track: " + name + '.' + propertyName + " but it wasn't found." );				
-				continue;
-			}
-
-			// must use copy for Object3D.Euler/Quaternion
-			if( node[ propertyName ].copy ) {
-				console.log( '  update property ' + name + '.' + propertyName + ' via a set() function.' );				
-				node[ propertyName ].copy( mixerResult.value );
-			}
-			// otherwise just copy across value
-			else {
-				console.log( '  update property ' + name + '.' + propertyName + ' via assignment.' );				
-				node[ propertyName ] = mixerResult.value;	
-			}
-
-
-			// trigger node dirty			
-			if( node.needsUpdate ) { // material
-				node.needsUpdate = true;
-			}			
-			if( node.matrixWorldNeedsUpdate && ! this.matrixAutoUpdate ) { // node transform
-				node.matrixWorldNeedsUpdate = true;
-			}
-
+			var trackBinding = this.trackBindings[ name ];
+			trackBinding.set( mixerResult.value );
+			
 		}
 	}
 

+ 1 - 85
src/animation/AnimationUtils.js

@@ -22,90 +22,6 @@
 			return a * ( 1 - alpha ) + b * alpha;
 			
 		}
-	},
-
-
-
-	// TODO: Cache this at some point
-	findNode: function( root, nodeName ) {
-
-		console.log( 'AnimationUtils.findNode( ' + root.name + ', nodeName: ' + nodeName + ')');
-		
-		if( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 ) {
-
-			console.log( '  root.' );
-			return root;
-
-		}
-
-		// (2) search into skeleton bones.
-		if( root.skeleton ) {
-
-			var searchSkeleton = function( skeleton ) {
-
-				for( var i = 0; i < skeleton.bones.length; i ++ ) {
-
-					var bone = skeleton.bones[i];
-
-					if( bone.name === nodeName ) {
-
-						return childNode;
-
-					}
-				}
-
-				return null;
-
-			};
-
-			var boneNode = searchSkeleton( root.skeleton );
-
-			if( boneNode ) {
-
-				console.log( '  bone: ' + bone.name + '.' );
-				return boneNode;
-
-			}
-		}
-
-		// (3) search into node subtree.
-		if( root.children ) {
-
-			var searchNodeSubtree = function( children ) {
-
-				for( var i = 0; i < children.length; i ++ ) {
-
-					var childNode = children[i];
-
-					if( childNode.name === nodeName ) {
-
-						return childNode;
-
-					}
-
-					var result = searchNodeSubtree( childNode.children );
-
-					if( result ) return result;
-
-				}
-
-				return null;	
-
-			};
-
-			var subTreeNode = searchNodeSubtree( root.children );
-
-			if( subTreeNode ) {
-
-				console.log( '  node: ' + subTreeNode.name + '.' );
-				return subTreeNode;
-
-			}
-
-		}
-
-		console.log( "   <null>.  No node found for name: " + name );
-
-		return null;
 	}
+	
 };

+ 194 - 0
src/animation/TrackBinding.js

@@ -0,0 +1,194 @@
+/**
+ *
+ * A track bound to a real value in the scene graph.
+ * 
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+THREE.TrackBinding = function ( rootNode, trackName ) {
+
+	this.rootNode = rootNode;
+	this.trackName = trackName;
+
+	var parseResults = THREE.TrackBinding.parseTrackName( trackName );
+
+	this.directoryName = parseResults.directoryName || null;
+	this.nodeName = parseResults.nodeName;
+	this.propertyName = parseResults.propertyName || null;
+	this.propertyArrayIndex = parseResults.propertyArrayIndex || -1;
+
+	this.node = THREE.TrackBinding.findNode( rootNode, this.nodeName );
+
+};
+
+THREE.TrackBinding.prototype = {
+
+	constructor: THREE.TrackBinding,
+
+	set: function( value ) {
+
+		 console.log( "TrackBinding.set( " + value + ")" );
+
+ 		// ensure there is a value node
+		if( ! this.node ) {
+			console.log( "  trying to update node for track: " + this.trackName + " but it wasn't found." );
+			continue;
+		}
+
+ 		// ensure there is a value property on the node
+		var nodeProperty = this.node[ this.propertyName ];
+		if( ! nodeProperty ) {
+			console.log( "  trying to update property for track: " + this.nodeName + '.' + this.propertyName + " but it wasn't found." );				
+			continue;
+		}
+
+		// access a sub element of the property array (only primitives are supported right now)
+		if( this.propertyArrayIndex >= 0 ) {
+			console.log( '  update property array ' + this.propertyName + '[' + this.propertyArrayIndex + '] via assignment.' );				
+			nodeProperty[ this.propertyArrayIndex ] = value;
+		}
+		// must use copy for Object3D.Euler/Quaternion		
+		else if( nodeProperty.copy ) {
+			console.log( '  update property ' + this.name + '.' + this.propertyName + ' via a set() function.' );				
+			nodeProperty.copy( value );
+		}
+		// otherwise just set the property directly on the node (do not use nodeProperty as it may not be a reference object)
+		else {
+			console.log( '  update property ' + this.name + '.' + this.propertyName + ' via assignment.' );				
+			node[ this.propertyName ] = value;	
+		}
+
+		// trigger node dirty			
+		if( this.node.needsUpdate ) { // material
+			console.log( '  triggering material as dirty' );
+			this.node.needsUpdate = true;
+		}			
+		if( this.node.matrixWorldNeedsUpdate && ! this.node.matrixAutoUpdate ) { // node transform
+			console.log( '  triggering node as dirty' );
+			this.node.matrixWorldNeedsUpdate = true;
+		}
+
+	},
+
+	get: function() {
+
+		throw new Error( "TODO" );
+
+	}
+
+};
+
+
+THREE.TrackBinding.parseTrackName = function( trackName ) {
+
+	// matches strings in the form of:
+	//    directory/directory/directory/filename.property[index]
+	// created and tested via https://regex101.com/#javascript
+
+	var re = /^(([\w]+\/)*)([\w]+)(\.([\w]+)(\[([\w]+)\])?)?$/; 
+	var matches = re.exec(trackName);
+
+	if( ! matches ) {
+		throw new Error( "cannot parse trackName at all: " + trackName );
+	}
+
+    if (matches.index === re.lastIndex) {
+        re.lastIndex++;
+    }
+
+	var parseResults = {
+		directoryName: m[0],
+		nodeName: m[2],
+		propertyName: m[4],
+		propertySubElement: m[6]
+	};
+
+	console.log( "TrackBinding.parseTrackName", trackName, parseResults );
+
+	return parseResults;
+
+};
+
+// TODO: Cache this at some point
+THREE.TrackBinding.findNode = function( root, nodeName ) {
+
+	console.log( 'AnimationUtils.findNode( ' + root.name + ', nodeName: ' + nodeName + ')');
+	
+	if( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 ) {
+
+		console.log( '  root.' );
+		return root;
+
+	}
+
+	// (2) search into skeleton bones.
+	if( root.skeleton ) {
+
+		var searchSkeleton = function( skeleton ) {
+
+			for( var i = 0; i < skeleton.bones.length; i ++ ) {
+
+				var bone = skeleton.bones[i];
+
+				if( bone.name === nodeName ) {
+
+					return childNode;
+
+				}
+			}
+
+			return null;
+
+		};
+
+		var boneNode = searchSkeleton( root.skeleton );
+
+		if( boneNode ) {
+
+			console.log( '  bone: ' + bone.name + '.' );
+			return boneNode;
+
+		}
+	}
+
+	// (3) search into node subtree.
+	if( root.children ) {
+
+		var searchNodeSubtree = function( children ) {
+
+			for( var i = 0; i < children.length; i ++ ) {
+
+				var childNode = children[i];
+
+				if( childNode.name === nodeName ) {
+
+					return childNode;
+
+				}
+
+				var result = searchNodeSubtree( childNode.children );
+
+				if( result ) return result;
+
+			}
+
+			return null;	
+
+		};
+
+		var subTreeNode = searchNodeSubtree( root.children );
+
+		if( subTreeNode ) {
+
+			console.log( '  node: ' + subTreeNode.name + '.' );
+			return subTreeNode;
+
+		}
+
+	}
+
+	console.log( "   <null>.  No node found for name: " + name );
+
+	return null;
+}