最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - ThreeJS - how to set current time in Animation - Stack Overflow

programmeradmin0浏览0评论

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.

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.

Share Improve this question edited Nov 19, 2012 at 10:19 null asked Oct 25, 2012 at 8:55 nullnull 1,1971 gold badge8 silver badges21 bronze badges 2
  • Very interesting question... I was working on an animation imported from collada, and wanted to reset the animation to frame 0 immediately after stopping it. I mistakenly tried to do so by using -> animation.update(0) -> animation.stop(), but after reading your question, tried animation.update(0) -> animation.play(false, 0) -> animation.stop()... And it's appears to work consistently. As for your problem (wanting to play animations in reverse), the first thought I had, was to just animate the reverse frames, and playback from the start of that... – Charlie Commented Mar 9, 2013 at 19:03
  • Thanks for your ment. My goal was not so much to play it forward and backward, but to be able to jump around from pose to pose, interpolating between them without having to go through the frames in between. I think you can do this with morph target animations already. I noticed the other day that the morph target animations now support playing in reverse as well. Skinning is a lot more involved I think, so I guess it will take longer to have the same features. – null Commented Mar 18, 2013 at 3:03
Add a ment  | 

2 Answers 2

Reset to default 3

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

I just pletely gave up on trying to set the animation time directly, and finally accepted that I'd have to use the hack I had thought of early in my google journey

发布评论

评论列表(0)

  1. 暂无评论