|
@@ -1,8 +1,6 @@
|
|
|
/**
|
|
|
*
|
|
|
* Reusable set of Tracks that represent an animation.
|
|
|
- *
|
|
|
- * TODO: MUST add support for importing AnimationClips from JSONLoader data files.
|
|
|
*
|
|
|
* @author Ben Houston / http://clara.io/
|
|
|
* @author David Sarno / http://lighthaus.us/
|
|
@@ -14,6 +12,11 @@ THREE.AnimationClip = function ( name, duration, tracks ) {
|
|
|
this.tracks = tracks;
|
|
|
this.duration = duration || 1;
|
|
|
|
|
|
+ // TODO: maybe only do these on demand, as doing them here could potentially slow down loading
|
|
|
+ // but leaving these here during development as this ensures a lot of testing of these functions
|
|
|
+ this.trim();
|
|
|
+ this.optimize();
|
|
|
+
|
|
|
};
|
|
|
|
|
|
THREE.AnimationClip.prototype = {
|
|
@@ -38,41 +41,28 @@ THREE.AnimationClip.prototype = {
|
|
|
return results;
|
|
|
},
|
|
|
|
|
|
- clean: function() {
|
|
|
- // sort all keys by time
|
|
|
- // move any keys before 0 to zero
|
|
|
-
|
|
|
- // remove minus times
|
|
|
+ trim: function() {
|
|
|
|
|
|
- if ( data.hierarchy[ h ].keys[ k ].time < 0 ) {
|
|
|
+ for( var trackIndex in this.tracks ) {
|
|
|
|
|
|
- data.hierarchy[ h ].keys[ k ].time = 0;
|
|
|
+ this.tracks[ trackIndex ].trim( this.duration );
|
|
|
|
|
|
}
|
|
|
|
|
|
- // remove second key if there is more than one key at the same time
|
|
|
- // remove all keys that are on the same time
|
|
|
-
|
|
|
- for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) {
|
|
|
+ },
|
|
|
|
|
|
- if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) {
|
|
|
+ optimize: function() {
|
|
|
|
|
|
- data.hierarchy[ h ].keys.splice( k, 1 );
|
|
|
- k --;
|
|
|
+ for( var trackIndex in this.tracks ) {
|
|
|
|
|
|
- }
|
|
|
+ this.tracks[ trackIndex ].optimize();
|
|
|
|
|
|
}
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- // set index
|
|
|
-
|
|
|
- for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
|
|
|
+};
|
|
|
|
|
|
- data.hierarchy[ h ].keys[ k ].index = k;
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
/*
|
|
|
"animation" : {
|
|
@@ -90,360 +80,117 @@ THREE.AnimationClip.prototype = {
|
|
|
"scl" :[1,1,1]
|
|
|
},
|
|
|
*/
|
|
|
- importFromData: function( data ) {
|
|
|
-
|
|
|
- var convertTrack = function( trackName, dataKeys, dataKeyToValueFunc ) {
|
|
|
-
|
|
|
- var keys = [];
|
|
|
-
|
|
|
- for( var k = 0; k < dataKeys.length; k ++ ) {
|
|
|
-
|
|
|
- var dataKey = dataKeys[k];
|
|
|
- keys.push( { time: dataKey.time, value: dataKeyToValueFunc( dataKey ) } );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return new THREE.KeyframeTrack( trackName, keys );
|
|
|
|
|
|
- };
|
|
|
+THREE.AnimationClip.FromJSONLoaderAnimation = function( jsonLoader ) {
|
|
|
|
|
|
- var clipName = data.name;
|
|
|
- var duration = data.length;
|
|
|
- var fps = data.fps;
|
|
|
-
|
|
|
- var tracks = [];
|
|
|
-
|
|
|
- var dataTracks = data.hierarchy;
|
|
|
-
|
|
|
- for ( var h = 0; h < dataTracks.length; h ++ ) {
|
|
|
-
|
|
|
- var boneName = '.bone[' + h + ']';
|
|
|
- var dataKeys = dataTracks[ h ].keys;
|
|
|
-
|
|
|
- // skip empty tracks
|
|
|
- if( ! dataKeys || dataKeys.length == 0 ) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- // process morph targets in a way exactly compatible with AnimationHandler.init( data )
|
|
|
- if( dataKeys[0].morphTargets ) {
|
|
|
-
|
|
|
- // figure out all morph targets used in this track
|
|
|
- var morphTargetNames = {};
|
|
|
- for( var k = 0; k < dataKeys.length; k ++ ) {
|
|
|
-
|
|
|
- if( dataKeys[k].morphTargets ) {
|
|
|
- for( var m = 0; m < dataKeys[k].morphTargets.length; m ++ ) {
|
|
|
-
|
|
|
- morphTagetNames[ dataKeys[k].morphTargets[m] ] = -1;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- // create a track for each morph target with all zero morphTargetInfluences except for the keys in which the morphTarget is named.
|
|
|
- for( var morphTargetName in morphTargetNames ) {
|
|
|
-
|
|
|
- var keys = [];
|
|
|
-
|
|
|
- for( var m = 0; m < dataKeys[k].morphTargets.length; m ++ ) {
|
|
|
+ var animation = jsonLoader['animation'];
|
|
|
+ if( ! animation ) {
|
|
|
+ console.error( " no animation in JSONLoader data" );
|
|
|
+ return null;
|
|
|
+ }
|
|
|
|
|
|
- var dataKey = dataKeys[k];
|
|
|
+ var convertTrack = function( trackName, animationKeys, animationKeyToValueFunc ) {
|
|
|
|
|
|
- keys.push( {
|
|
|
- time: dataKey.time,
|
|
|
- value: (( dataKey.morphTarget === morphTargetName ) ? 1 : 0 )
|
|
|
- });
|
|
|
-
|
|
|
- }
|
|
|
+ var keys = [];
|
|
|
|
|
|
- tracks.push( new THREE.KeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', keys ) );
|
|
|
+ for( var k = 0; k < animationKeys.length; k ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ var animationKey = animationKeys[k];
|
|
|
+ keys.push( { time: animationKey.time, value: animationKeyToValueFunc( animationKey ) } );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- // track contains positions...
|
|
|
- if( dataKeys[0].pos ) {
|
|
|
+ return new THREE.KeyframeTrack( trackName, keys );
|
|
|
|
|
|
- tracks.push( convertTracks( boneName + '.position', dataKeys, function( dataValue ) {
|
|
|
- return new THREE.Vector3().fromArray( dataKey.pos )
|
|
|
- } );
|
|
|
+ };
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- // track contains quaternions...
|
|
|
- if( dataKeys[0].rot ) {
|
|
|
+ var clipName = animation.name;
|
|
|
+ var duration = animation.length;
|
|
|
+ var fps = animation.fps;
|
|
|
|
|
|
- tracks.push( convertTracks( boneName + '.quaternion', dataKeys, function( dataValue ) {
|
|
|
- return new THREE.Quaternion().fromArray( dataKey.rot )
|
|
|
- } );
|
|
|
+ var tracks = [];
|
|
|
|
|
|
- }
|
|
|
+ var animationTracks = animation.hierarchy;
|
|
|
|
|
|
- // track contains quaternions...
|
|
|
- if( dataKeys[0].scl ) {
|
|
|
+ for ( var h = 0; h < animationTracks.length; h ++ ) {
|
|
|
|
|
|
- tracks.push( convertTracks( boneName + '.quaternion', dataKeys, function( dataValue ) {
|
|
|
- return new THREE.Vector3().fromArray( dataKey.scl )
|
|
|
- } );
|
|
|
+ var boneName = '.bone[' + h + ']';
|
|
|
+ var animationKeys = animationTracks[ h ].keys;
|
|
|
|
|
|
- }
|
|
|
+ // skip empty tracks
|
|
|
+ if( ! animationKeys || animationKeys.length == 0 ) {
|
|
|
+ continue;
|
|
|
}
|
|
|
|
|
|
- var clip = new THREE.AnimationClip( clipName, duration, tracks );
|
|
|
- console.log( 'clipFromHierarchy', clip );
|
|
|
-
|
|
|
- return clip;
|
|
|
-
|
|
|
- }
|
|
|
+ // process morph targets in a way exactly compatible with AnimationHandler.init( animation )
|
|
|
+ if( animationKeys[0].morphTargets ) {
|
|
|
|
|
|
- // loop through all keys
|
|
|
+ // figure out all morph targets used in this track
|
|
|
+ var morphTargetNames = {};
|
|
|
+ for( var k = 0; k < animationKeys.length; k ++ ) {
|
|
|
|
|
|
- for ( var h = 0; h < data.hierarchy.length; h ++ ) {
|
|
|
-
|
|
|
- for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
|
|
|
-
|
|
|
- // create quaternions
|
|
|
-
|
|
|
- if ( data.hierarchy[ h ].keys[ k ].rot !== undefined &&
|
|
|
- ! ( 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().fromArray( quat );
|
|
|
+ if( animationKeys[k].morphTargets ) {
|
|
|
+ for( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) {
|
|
|
|
|
|
+ morphTagetNames[ animationKeys[k].morphTargets[m] ] = -1;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- // prepare morph target keys
|
|
|
-
|
|
|
- if ( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) {
|
|
|
-
|
|
|
- // get all used
|
|
|
-
|
|
|
- var usedMorphTargets = {};
|
|
|
-
|
|
|
- for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
|
|
|
+ // create a track for each morph target with all zero morphTargetInfluences except for the keys in which the morphTarget is named.
|
|
|
+ for( var morphTargetName in morphTargetNames ) {
|
|
|
|
|
|
- for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
|
|
|
+ var keys = [];
|
|
|
|
|
|
- var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ];
|
|
|
- usedMorphTargets[ morphTargetName ] = - 1;
|
|
|
+ for( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ var animationKey = animationKeys[k];
|
|
|
|
|
|
+ keys.push( {
|
|
|
+ time: animationKey.time,
|
|
|
+ value: (( animationKey.morphTarget === morphTargetName ) ? 1 : 0 )
|
|
|
+ });
|
|
|
+
|
|
|
}
|
|
|
|
|
|
- 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;
|
|
|
-
|
|
|
- }
|
|
|
+ tracks.push( new THREE.KeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', keys ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
+
|
|
|
+ // track contains positions...
|
|
|
+ if( animationKeys[0].pos ) {
|
|
|
|
|
|
- data.initialized = true;
|
|
|
-
|
|
|
- return data;
|
|
|
-
|
|
|
- }*/
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
-// TODO: Fix this for loops.
|
|
|
-// TODO: Test this
|
|
|
-THREE.AnimationClip.CreateMorphAnimation = function( morphTargets, duration ) {
|
|
|
-
|
|
|
- var tracks = [];
|
|
|
- var frameStep = duration / morphTargets.length;
|
|
|
-
|
|
|
- for( var i = 0; i < morphTargets.length; i ++ ) {
|
|
|
-
|
|
|
- var keys = [];
|
|
|
-
|
|
|
- if( ( i - 1 ) >= 0 ) {
|
|
|
-
|
|
|
- keys.push( { time: ( i - 1 ) * frameStep, value: 0 } );
|
|
|
+ tracks.push( convertTracks( boneName + '.position', animationKeys, function( dataValue ) {
|
|
|
+ return new THREE.Vector3().fromArray( animationKey.pos )
|
|
|
+ } );
|
|
|
|
|
|
}
|
|
|
+
|
|
|
+ // track contains quaternions...
|
|
|
+ if( animationKeys[0].rot ) {
|
|
|
|
|
|
- keys.push( { time: i * frameStep, value: 1 } );
|
|
|
-
|
|
|
- if( ( i + 1 ) <= morphTargets.length ) {
|
|
|
-
|
|
|
- keys.push( { time: ( i + 1 ) * frameStep, value: 0 } );
|
|
|
+ tracks.push( convertTracks( boneName + '.quaternion', animationKeys, function( dataValue ) {
|
|
|
+ return new THREE.Quaternion().fromArray( animationKey.rot )
|
|
|
+ } );
|
|
|
|
|
|
}
|
|
|
|
|
|
- var morphName = morphTargets[i].name;
|
|
|
- var trackName = '.morphTargetInfluences[' + morphName + ']';
|
|
|
- var track = new THREE.KeyframeTrack( trackName, keys );
|
|
|
-
|
|
|
- tracks.push( track );
|
|
|
- }
|
|
|
-
|
|
|
- var clip = new THREE.AnimationClip( 'morphAnimation', duration, tracks );
|
|
|
- //console.log( 'morphAnimationClip', clip );
|
|
|
-
|
|
|
- return clip;
|
|
|
-};
|
|
|
-
|
|
|
-THREE.AnimationClip.CreateRotationAnimation = function( period, axis ) {
|
|
|
-
|
|
|
- var keys = [];
|
|
|
- keys.push( { time: 0, value: 0 } );
|
|
|
- keys.push( { time: period, value: 360 } );
|
|
|
-
|
|
|
- axis = axis || 'x';
|
|
|
- var trackName = '.rotation[' + axis + ']';
|
|
|
-
|
|
|
- var track = new THREE.KeyframeTrack( trackName, keys );
|
|
|
-
|
|
|
- var clip = new THREE.AnimationClip( 'rotate.x', 10, [ track ] );
|
|
|
- //console.log( 'rotateClip', clip );
|
|
|
-
|
|
|
- return clip;
|
|
|
-};
|
|
|
-
|
|
|
-THREE.AnimationClip.CreateScaleAxisAnimation = function( period, axis ) {
|
|
|
-
|
|
|
- var keys = [];
|
|
|
- keys.push( { time: 0, value: 0 } );
|
|
|
- keys.push( { time: period, value: 360 } );
|
|
|
-
|
|
|
- axis = axis || 'x';
|
|
|
- var trackName = '.scale[' + axis + ']';
|
|
|
-
|
|
|
- var track = new THREE.KeyframeTrack( trackName, keys );
|
|
|
-
|
|
|
- var clip = new THREE.AnimationClip( 'scale.x', 10, [ track ] );
|
|
|
- //console.log( 'scaleClip', clip );
|
|
|
-
|
|
|
- return clip;
|
|
|
-};
|
|
|
-
|
|
|
-THREE.AnimationClip.CreateShakeAnimation = function( duration, shakeScale ) {
|
|
|
-
|
|
|
- var keys = [];
|
|
|
-
|
|
|
- for( var i = 0; i < duration * 10; i ++ ) {
|
|
|
-
|
|
|
- keys.push( {
|
|
|
- time: ( i / 10.0 ),
|
|
|
- value: new THREE.Vector3( Math.random() * 2.0 - 1.0, Math.random() * 2.0 - 1.0, Math.random() * 2.0 - 1.0 ).multiply( shakeScale )
|
|
|
- } );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- var trackName = '.position';
|
|
|
-
|
|
|
- var track = new THREE.KeyframeTrack( trackName, keys );
|
|
|
+ // track contains quaternions...
|
|
|
+ if( animationKeys[0].scl ) {
|
|
|
|
|
|
- var clip = new THREE.AnimationClip( 'shake' + duration, duration, [ track ] );
|
|
|
- //console.log( 'shakeClip', clip );
|
|
|
-
|
|
|
- return clip;
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
-THREE.AnimationClip.CreatePulsationAnimation = function( duration, pulseScale ) {
|
|
|
-
|
|
|
- var keys = [];
|
|
|
-
|
|
|
- for( var i = 0; i < duration * 10; i ++ ) {
|
|
|
-
|
|
|
- var scaleFactor = Math.random() * pulseScale;
|
|
|
- keys.push( {
|
|
|
- time: ( i / 10.0 ),
|
|
|
- value: new THREE.Vector3( scaleFactor, scaleFactor, scaleFactor )
|
|
|
- } );
|
|
|
+ tracks.push( convertTracks( boneName + '.quaternion', animationKeys, function( dataValue ) {
|
|
|
+ return new THREE.Vector3().fromArray( animationKey.scl )
|
|
|
+ } );
|
|
|
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- var trackName = '.scale';
|
|
|
-
|
|
|
- var track = new THREE.KeyframeTrack( trackName, keys );
|
|
|
-
|
|
|
- var clip = new THREE.AnimationClip( 'scale' + duration, duration, [ track ] );
|
|
|
- //console.log( 'scaleClip', clip );
|
|
|
+ var clip = new THREE.AnimationClip( clipName, duration, tracks );
|
|
|
+ console.log( 'clipFromJSONLoaderAnimation', clip );
|
|
|
|
|
|
return clip;
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
-THREE.AnimationClip.CreateVisibilityAnimation = function( duration ) {
|
|
|
-
|
|
|
- var keys = [];
|
|
|
- keys.push( {
|
|
|
- time: 0,
|
|
|
- value: true
|
|
|
- } );
|
|
|
- keys.push( {
|
|
|
- time: duration - 1,
|
|
|
- value: false
|
|
|
- } );
|
|
|
- keys.push( {
|
|
|
- time: duration,
|
|
|
- value: true
|
|
|
- } );
|
|
|
-
|
|
|
- var trackName = '.visible';
|
|
|
|
|
|
- var track = new THREE.KeyframeTrack( trackName, keys );
|
|
|
-
|
|
|
- var clip = new THREE.AnimationClip( 'visible' + duration, duration, [ track ] );
|
|
|
- //console.log( 'scaleClip', clip );
|
|
|
-
|
|
|
- return clip;
|
|
|
};
|
|
|
-
|
|
|
-
|
|
|
-THREE.AnimationClip.CreateMaterialColorAnimation = function( duration, colors, loop ) {
|
|
|
-
|
|
|
- var timeStep = duration / colors.length;
|
|
|
- var keys = [];
|
|
|
- for( var i = 0; i <= colors.length; i ++ ) {
|
|
|
- keys.push( { time: i * timeStep, value: colors[ i % colors.length ] } );
|
|
|
- }
|
|
|
-
|
|
|
- var trackName = '.material[0].color';
|
|
|
-
|
|
|
- var track = new THREE.KeyframeTrack( trackName, keys );
|
|
|
-
|
|
|
- var clip = new THREE.AnimationClip( 'colorDiffuse', 10, [ track ] );
|
|
|
- //console.log( 'diffuseClip', clip );
|
|
|
-
|
|
|
- return clip;
|
|
|
-};
|
|
|
-
|