I'm utilizing skinning and skeletal animation within ThreeJS. My goal is to navigate through an animation by moving backward and forward, as well as jumping to specific locations within the sequence instead of the usual looping behavior.
The animation setup follows a structure similar to this example:
var animation = new THREE.Animation( mesh, geometry.animation.name );
I have experimented with updating the animation using negative deltas and setting the currentTime property directly:
animation.currentTime = animationLocation;
These methods seem to function properly when moving forward in time, however, attempting to go backward results in an error:
THREE.Animation.update: Warning! Scale out of bounds: ... on bone ...
One workaround that works without triggering errors is calling stop()
followed by play()
with a new start time:
animation.stop();
animation.play( true, animationLocation );
Although functional, this approach involves numerous function calls, loops, and transforms which may not be ideal. I am interested in exploring alternative solutions.
If anyone has suggestions or insights on how to address this issue, please feel free to share!
[UPDATE]
To provide an update on my progress, I would like to share the most effective solution I have devised thus far...
I extracted the core functions from stop() and play(), streamlining the process by eliminating unnecessary steps while making assumptions about preset values by 'play()'
Despite these optimizations, there may still be room for improvement. However, the current method seems more efficient than solely using stop() followed by play().
Here is the refined solution:
THREE.Animation.prototype.gotoTime = function( time ) {
//clamp to duration of the animation:
time = THREE.Math.clamp( time, 0, this.length );
this.currentTime = time;
// reset key cache
var h, hl = this.hierarchy.length,
object;
for ( h = 0; h < hl; h ++ ) {
object = this.hierarchy[ h ];
var prevKey = object.animationCache.prevKey;
var nextKey = object.animationCache.nextKey;
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 );
}
//isPlaying must be true for update to work due to "early out"
//so remember the current play state:
var wasPlaying = this.isPlaying;
this.isPlaying = true;
//update with a delta time of zero:
this.update( 0 );
//reset the play state:
this.isPlaying = wasPlaying;
}
One limitation of this function is its inability to interpolate between arbitrary times. It primarily allows scrubbing through the animation rather than smooth transitions.