Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ThreeJS - how to set current time in Animation

I'm using skinning / skeletal animation in ThreeJS. I have an animation, and I want to be able to move backward and forward through it, and jump to different locations within it, rather than the usual looping behaviour.

The animation is created like this, as in the example:

var animation = new THREE.Animation( mesh, geometry.animation.name );

I have tried updating the animation with negative deltas, as well as setting animation.currentTime directly:

animation.currentTime = animationLocation;

These appear to work only if I move forward in time, but if I go backward the animation breaks and I get an error:

THREE.Animation.update: Warning! Scale out of bounds: ... on bone ... 

One thing that does actually work without error is to call stop() and then play() with a new start time:

animation.stop();
animation.play( true, animationLocation );

...however when I look at what these functions are actually doing, they involve many many function calls, looping, resetting transforms etc. This seems like a horrible way to do it, even if it works as a hack.

It may be that this functionality does not exist yet, in which case I'll try to dig in and create a function that does a minimal amount of work, but I'm hoping there is another way that I haven't found.

Can anyone help with this?

[UPDATE]

As an update on my progress, I'll post the best solution I have at this time...

I pulled out the contents of the stop() and play() functions, and stripped out everything I could, making some assumptions about certain values having already been set by 'play()'.

This still seems like it is probably not the best way to do it, but it is doing a bit less work than by just calling stop() then play().

This is what I was able to get it down to:

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;

}

The main limitation of the function in terms of usefulness is that you can't interpolate from one arbitrary time to another. You can basically just scrub around in the animation.

like image 837
null Avatar asked Oct 25 '12 08:10

null


1 Answers

You can use THREE.Clock and assign startTime, oldTime, elapsedTime.

like image 150
Valay Avatar answered Sep 19 '22 03:09

Valay