From cbccd87d57b21a21046dd1b5daeb8e78cac9f231 Mon Sep 17 00:00:00 2001 From: Erik Kitson Date: Mon, 21 Nov 2011 14:07:47 -0800 Subject: [PATCH] Reverted changes to Animation class and moved to new KeyFrameAnimation class. --- src/extras/animation/Animation.js | 304 ++++++++-------- src/extras/animation/KeyFrameAnimation.js | 421 ++++++++++++++++++++++ utils/build.py | 1 + 3 files changed, 573 insertions(+), 153 deletions(-) create mode 100644 src/extras/animation/KeyFrameAnimation.js diff --git a/src/extras/animation/Animation.js b/src/extras/animation/Animation.js index 2caa337bd1..d682f8b4e0 100644 --- a/src/extras/animation/Animation.js +++ b/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 ]; -}; +}; \ No newline at end of file diff --git a/src/extras/animation/KeyFrameAnimation.js b/src/extras/animation/KeyFrameAnimation.js new file mode 100644 index 0000000000..406961afc0 --- /dev/null +++ b/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 ]; + +}; diff --git a/utils/build.py b/utils/build.py index 0e75addcbd..67b68b92c4 100644 --- a/utils/build.py +++ b/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', -- GitLab