提交 1be1d051 编写于 作者: B Ben Houston

memeoization of lerp. add optimize, trim, validate, sort to KeyframeTrack. ...

memeoization of lerp.  add optimize, trim, validate, sort to KeyframeTrack.  Create separate AnimationClipCreator class to get demo code out of ThreeJS src/ directory.
上级 0e5c35fd
/**
*
* Creator of typical test AnimationClips / KeyframeTracks
*
* @author Ben Houston / http://clara.io/
* @author David Sarno / http://lighthaus.us/
*/
THREE.AnimationClipCreator = function() {
};
THREE.AnimationClipCreator.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 } );
}
keys.push( { time: i * frameStep, value: 1 } );
if( ( i + 1 ) <= morphTargets.length ) {
keys.push( { time: ( i + 1 ) * frameStep, value: 0 } );
}
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.AnimationClipCreator.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.AnimationClipCreator.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.AnimationClipCreator.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 );
var clip = new THREE.AnimationClip( 'shake' + duration, duration, [ track ] );
//console.log( 'shakeClip', clip );
return clip;
};
THREE.AnimationClipCreator.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 )
} );
}
var trackName = '.scale';
var track = new THREE.KeyframeTrack( trackName, keys );
var clip = new THREE.AnimationClip( 'scale' + duration, duration, [ track ] );
//console.log( 'scaleClip', clip );
return clip;
};
THREE.AnimationClipCreator.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.AnimationClipCreator.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;
};
......@@ -46,8 +46,8 @@
<script src="js/Detector.js"></script>
<script src="js/libs/stats.min.js"></script>
<script src="js/libs/dat.gui.min.js"></script>
<script src="js/AnimationClipCreator.js"></script>
<script>
......@@ -257,21 +257,21 @@
mixer = new THREE.AnimationMixer( mesh );
var clip1 = THREE.AnimationClip.CreateShakeAnimation( 10, new THREE.Vector3( 10, 10, 10 ) );
var clip1 = THREE.AnimationClipCreator.CreateShakeAnimation( 10, new THREE.Vector3( 10, 10, 10 ) );
//mixer.addAction( new THREE.AnimationAction( clip1, 0, 1, 1, true ) );
var clip2 = THREE.AnimationClip.CreatePulsationAnimation( 10, 100 );
var clip2 = THREE.AnimationClipCreator.CreatePulsationAnimation( 10, 100 );
//mixer.addAction( new THREE.AnimationAction( clip2, 0, 1, 1, true ) );
var clip3 = THREE.AnimationClip.CreateRotationAnimation( 100, 'y' );
var clip3 = THREE.AnimationClipCreator.CreateRotationAnimation( 100, 'y' );
//mixer.addAction( new THREE.AnimationAction( clip3, 0, 1, 1, true ) );
var clip4 = THREE.AnimationClip.CreateScaleAxisAnimation( 10, 'x' );
var clip4 = THREE.AnimationClipCreator.CreateScaleAxisAnimation( 10, 'x' );
//mixer.addAction( new THREE.AnimationAction( clip4, 0, 1, 1, true ) );
var clip5 = THREE.AnimationClip.CreateMaterialColorAnimation( 10, [ new THREE.Color( 0xffffff ), new THREE.Color( 0xff0000 ), new THREE.Color( 0xff00ff ) ] );
var clip5 = THREE.AnimationClipCreator.CreateMaterialColorAnimation( 10, [ new THREE.Color( 0xffffff ), new THREE.Color( 0xff0000 ), new THREE.Color( 0xff00ff ) ] );
//mixer.addAction( new THREE.AnimationAction( clip5, 0, 1, 1, true ) );
//var clip6 = THREE.AnimationClip.CreateVisibilityAnimation( 10 );
//mixer.addAction( new THREE.AnimationAction( clip6, 0, 1, 1, true ) );
var clip7 = THREE.AnimationClip.CreateMorphAnimation( mesh.geometry.morphTargets, 3 );
var clip7 = THREE.AnimationClipCreator.CreateMorphAnimation( mesh.geometry.morphTargets, 3 );
mixer.addAction( new THREE.AnimationAction( clip7, 0, 1, 1, true ) );
}
......
/**
*
* 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;
};
......@@ -5,43 +5,79 @@
THREE.AnimationUtils = {
// TODO/OPTIMIZATION: do the switch statement once per user of this and cache the resulting function and call it directly.
equalsFunc: function( exemplarValue ) {
if( exemplarValue.equals ) {
return function( a, b ) {
return a.equals( b );
}
}
return function( a, b ) {
return ( a === b );
};
},
lerp: function( a, b, alpha, interTrack ) {
var lerpFunc = THREE.AnimationUtils.getLerpFunc( a, interTrack );
return lerpFunc( a, b, alpha );
},
// TODO/OPTIMIZATION: Accumulator should be writable and it will get rid of the *.clone() calls that are likely slow.
lerp: function( accumulator, b, alpha, interTrack ) {
getLerpFunc: function( exemplarValue, interTrack ) {
var typeName = typeof accumulator;
var typeName = typeof exemplarValue;
switch( typeName ) {
case "object": {
if( accumulator.lerp ) {
if( exemplarValue.lerp ) {
return accumulator.clone().lerp( b, alpha );
return function( a, b, alpha ) {
return a.clone().lerp( b, alpha );
}
}
if( accumulator.slerp ) {
if( exemplarValue.slerp ) {
return accumulator.clone().slerp( b, alpha );
return function( a, b, alpha ) {
return a.clone().slerp( b, alpha );
}
}
break;
}
case "number": {
return accumulator * ( 1 - alpha ) + b * alpha;
return function( a, b, alpha ) {
return a * ( 1 - alpha ) + b * alpha;
}
}
case "boolean": {
if( interTrack ) {
return ( alpha < 0.5 ) ? accumulator : b;
return function( a, b, alpha ) {
return ( alpha < 0.5 ) ? a : b;
}
}
else {
return accumulator;
return function( a, b, alpha ) {
return a;
}
}
}
case "string": {
if( interTrack ) {
return ( alpha < 0.5 ) ? accumulator : b;
return function( a, b, alpha ) {
return ( alpha < 0.5 ) ? a : b;
}
}
else {
return accumulator;
return function( a, b, alpha ) {
return a;
}
}
}
};
......
......@@ -13,9 +13,9 @@ THREE.KeyframeTrack = function ( name, keys ) {
this.name = name;
this.keys = keys || []; // time in seconds, value as value
// TODO: sort keys via their times
//this.keys.sort( function( a, b ) { return a.time < b.time; } );
this.sort();
this.validate();
this.optimize();
};
THREE.KeyframeTrack.prototype = {
......@@ -52,7 +52,7 @@ THREE.KeyframeTrack.prototype = {
// linear interpolation to start with
var alpha = ( time - this.keys[ i - 1 ].time ) / ( this.keys[ i ].time - this.keys[ i - 1 ].time );
var interpolatedValue = THREE.AnimationUtils.lerp( this.keys[ i - 1 ].value, this.keys[ i ].value, alpha );
var interpolatedValue = this.lerp( this.keys[ i - 1 ].value, this.keys[ i ].value, alpha );
/*console.log( ' interpolated: ', {
value: interpolatedValue,
......@@ -70,6 +70,139 @@ THREE.KeyframeTrack.prototype = {
throw new Error( "should never get here." );
},
// memoization of the lerp function for speed.
// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
lerp: function( value0, value1, alpha ) {
this.lerp = THREE.AnimationUtils.getLerpFunc( value0, false );
return this.lerp( value0, value1, alpha );
},
// sort in ascending order
sort: function() {
var keyComparator = function(key0, key1) {
return key0.time - key1.time;
};
return function() {
this.keys.sort( keyComparator );
}
}();
// ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
// TODO: ensure that all key.values in a track are all of the same type (otherwise interpolation makes no sense.)
validate: function() {
var prevKey = null;
if( this.keys.length === 0 ) {
console.error( " track is empty, no keys", this );
return;
}
for( var i = 0; i < this.keys.length; i ++ ) {
var currKey = this.keys[i];
if( ! currKey ) {
console.error( " key is null in track", this, i );
return;
}
if( ( typeof currKey.time ) !== 'Number' || currKey.time == NaN ) {
console.error( " key.time is not a valid number", this, i, currKey );
return;
}
if( currKey.value === undefined || currKey.value === null) {
console.error( " key.value is null in track", this, i, currKey );
return;
}
if( prevKey && prevKey.time > currKey.time ) {
console.error( " key.time is less than previous key time, out of order keys", this, i, currKey, prevKey );
return;
}
prevKey = currKey;
}
},
// currently only removes equivalent sequential keys (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0), which are common in morph target animations
// TODO: linear based interpolation optimization with an error threshold.
optimize: function() {
var newKeys = [];
var prevKey = this.keys[0];
newKeys.push( prevKey );
var equalsFunc = THREE.AnimationUtils.equalsFunc( prevKey.value );
for( var i = 1; i < this.keys.length - 1; i ++ ) {
var currKey = this.keys[i];
var nextKey = this.keys[i+1];
// if prevKey & currKey are the same time, remove currKey. If you want immediate adjacent keys, use an epsilon offset
// it is not possible to have two keys at the same time as we sort them. The sort is not stable on keys with the same time.
if( ( prevKey.time === currKey.time ) ) {
console.log( 'removing key at the same time', currKey );
continue;
}
// remove completely unnecessary keyframes that are the same as their prev and next keys
if( equalsFunc( prevKey.value, currKey.value ) && equals( currKey.value, nextKey.value ) ) {
console.log( 'removing key identical to prev and next', currKey );
continue;
}
// TODO:add here a check for linear interpolation optimization.
newKeys.push( currKey );
prevKey = currKey;
}
console.log( ' track optimization removed keys:', ( this.keys.length - newKeys.length ), this.name );
this.keys = newKeys;
},
// removes keyframes before and after animation without changing any values within the range [0,duration].
// IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values
trim: function( duration ) {
var firstKeysToRemove = 0;
for( var i = 1; i < this.keys.length; i ++ ) {
if( this.keys[i] <= 0 ) {
firstKeysToRemove ++;
}
}
var lastKeysToRemove = 0;
for( var i = this.keys.length - 2; i > 0; i ++ ) {
if( this.keys[i] >= duration ) {
lastKeysToRemove ++;
}
else {
break;
}
}
// remove last keys first because it doesn't affect the position of the first keys (the otherway around doesn't work as easily)
// TODO: Figure out if there is an array subarray function... might be faster
var keysWithoutLastKeys = this.keys.splice( this.keys.length - lastKeysToRemove, lastKeysToRemove );
var keysWithoutFirstKeys = keysWithoutLastKeys.splice( 0, firstKeysToRemove );
this.keys = keysWithoutFirstKeys;
}
};
};
\ No newline at end of file
......@@ -40,22 +40,28 @@ THREE.PropertyBinding.prototype = {
accumulate: function( value, weight ) {
if( this.cumulativeWeight === 0 ) {
var lerp = THREE.AnimationUtils.getLerpFunc( this.cumulativeValue, true );
this.cumulativeValue = value;
this.cumulativeWeight = weight;
this.accumulate = function( value, weight ) {
}
else {
if( this.cumulativeWeight === 0 ) {
this.cumulativeValue = value;
this.cumulativeWeight = weight;
}
else {
var lerpAlpha = weight / ( this.cumulativeWeight + weight );
this.cumulativeValue = THREE.AnimationUtils.lerp( this.cumulativeValue, value, lerpAlpha );
this.cumulativeWeight += weight;
var lerpAlpha = weight / ( this.cumulativeWeight + weight );
this.cumulativeValue = lerp( this.cumulativeValue, value, lerpAlpha );
this.cumulativeWeight += weight;
}
}
},
this.accumulate( value, weight );
},
apply: function() {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册