Setting the current time in an Animation using ThreeJS

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.

Answer №1

Utilize the THREE.Clock feature by setting up variables such as startTime, oldTime, and elapsedTime.

Answer №2

After struggling to set the animation time directly, I decided to embrace a hack that I had initially thought of during my research process 😄. It seems pretty optimized as it only requires one function call that should be executed every frame anyway. So far, I haven't encountered any issues with this approach.

The main concept behind my method is to loop the animation and utilize the update() function to adjust the animation position when necessary, rather than updating it based on delta time as intended. Additionally, the code below refrains from rendering or updating anything unless absolutely necessary.

I developed this code to directly control the animation position using a slider.

let time = 0;
let lastSliderPos = 0;
const slider = document.getElementById("slider");
let mixer = null;

mixer = new THREE.AnimationMixer(yourModelHereLikeGLTFForExample); 
//Call this inside the mesh file load callback

function render(t) {
    requestAnimationFrame( render );
    
    let newframe = lastSliderPos != slider.value;
    
    if (newframe){
        if (mixer){
            mixer.update( anim.duration + (slider.value - lastSliderPos));
            lastSliderPos = slider.value;
        }

        // Optimizing performance by not rendering every frame when not moving               
        if (camera){
            renderer.render(scene, camera);
        }
    }
}

In theory, this method may accumulate floating point precision errors because it's relative to the previous frame's float rather than absolute time.

A more generalized solution that should work with any position and maintain precision:

// Call these four lines inside the model load callback
anim = gltf.animations[0];
anim.optimize();
animAction = mixer.clipAction(anim);
animAction.play();

function setAnimTime(t){
    if ( mixer ){
        mixer.update( anim.duration + (t - animAction.time));
    }
}

Alternatively, if you wish to control multiple animation clips (albeit less efficiently):

function setAnimTime(a, t){
    aAction = mixer.clipAction(a);
    if ( mixer ){
        mixer.update( a.duration + (t - aAction.time));
    }
}

Ensure that the animation is playing before calling setAnimTime(). Play/Stop functionality can be incorporated into the function, but that would negate its advantages over OP's second solution.

Feel free to provide any suggestions or enhancements 🙂

Edit: In theory, you could also call the update based on delta time as usual, allowing the animation to play while still being able to scrub to a new position without any additional steps. This doesn't offer any significant benefits, just an observation that might come in handy...

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Struggling to retrieve data from Firebase in React Native?

It's been a challenge for me as a newcomer to React Native trying to retrieve data from a Firebase database. This is the process flow of how my data is handled: 1. A user selects locations and trip details (name, startDate, endDate) --> stored in ...

What is the best way to obtain the output produced by a function when a button is clicked

When I click on a button, the desired action is to trigger a function that adds a new property inside an object within a large array of multiple objects. This function then eventually returns a new array. How can I access and utilize this new array? I am ...

The battle between Iteration and Recursion: Determining the position of a point in a sequence based

One of the challenges I'm facing involves a recursive function that takes a point labeled {x,y} and then calculates the next point in the sequence, recursively. The function in question has the following structure: var DECAY = 0.75; var LENGTH = 150 ...

What causes the "Invalid hook call" error to occur when the useQuery function is invoked?

Encountering an issue while trying to use the useQuery() function from react-admin within a custom component. Despite the clear error message, I'm struggling to determine the right course of action. Visiting the provided website and following the inst ...

How can I make a checkbox checked in AngularJS?

Hello, I am currently working on a web application using AngularJS. Within this application, I have a form where I am populating values into a multi-select dropdown. <li ng-repeat="p in locations"> <input type="checkbox" ng-checked="master" n ...

Is it possible to utilize the output of a function nested within a method in a different method?

I am currently facing a challenge with my constructor function. It is supposed to return several methods, but I'm having trouble using the value from this section of code: var info = JSON.parse(xhr.responseText); Specifically, I can't figure ou ...

Placing files in the "Dist" folder is causing an issue by disrupting the functionality of the Angular 2 app

For testing my login component in Angular2, I am using a mockBackend within my app. Initially, the standalone version of the login worked perfectly fine. However, when trying to integrate it into my ongoing development project, I encountered an issue. Duri ...

The themes showcased in the Bespoke.js documentation and demo exhibit unique designs

Recently, I stumbled upon an incredible HTML5 framework for presentations. For more information, you can check out the documentation here. You can also view a demo of the framework by visiting this link. However, I was disappointed to find that the caro ...

Feeling trapped by the endless stream of AJAX calls

As I was working on building a scheduler with jQuery and AJAX, I encountered a problem with multiple AJAX requests. Although most of the time everything works fine and returns the correct values, occasionally, out of more than 50 requests, I come across so ...

The array element is not being shown in the id "main" when using a for loop with the onchange function

I've been using document.write to display specific content, but it seems to be removing other elements from the page. Is there a way for me to display this loop inside the element with id="main" without losing any content? When I attempt to use docume ...

Protection of Angular expressions

I have been following the PhoneCat tutorial for AngularJS and found it very helpful up until step 6 where links are generated dynamically from angular expressions: http://localhost:8000/app/{{phone.imageUrl}} Although the tutorial mentions that ngSrc pre ...

Tips for adjusting the horizontal position of a grid item within a map() loop

I am trying to align the text in my Timeline component from Material Ui always towards the center of the timeline. The TimelineContent contains Paper, Typography (for title and description), and an image. Currently, I have multiple TimelineContent element ...

Issue with making requests across origins in Vuejs using axios

Currently, I am utilizing Vuejs and axios for handling web requests. So far, I have successfully managed to perform GET, POST, and PUT operations. However, the new challenge that I am facing is uploading media to the server. The server necessitates sending ...

Guide for extracting the first matching group with regex in node.js

Extracting a specific value from a string in a text file can be tricky. In the example below, the goal is to get the value of valuetext: <CONFIG_entry name="Konfiguration:Allgemeine Einstellungen:CMS-Server:Port" valuetext="15000" value="15000" adr="CL ...

Access the system by logging in with a stored Google account

I have experience integrating "Login via Google account" on various websites. However, some sites like Zomato always display the option to login via Google as soon as you open them. They even show a list of Google accounts that I have previously logged i ...

The getJSON API functions properly on a local machine but encounters issues when deployed on a

I have a vision to create a web application that can display the weather of any city based on user input. I've divided my project into three files - index.html for collecting user input, index2.html for retrieving and displaying the data, and a CSS fi ...

SSR with Material UI Drawer encounters issue during webpack production build meltdown

When I attempted to utilize the Material UI SSR example provided by Material-UI (Link), it worked successfully. However, my next step was to integrate the Material-UI Drawer into this example. To do so, I utilized the Persistent drawer example code also pr ...

Unleashing the Power of Dynamic JSON Data Access

I am facing an issue with my React Child component. Here is the code snippet: const SingleProject =(props)=>{ let data = projectData.VARIABLE_FROM_PROPS.projectDetails; let asideData = projectData.VARIABLE_FROM_PROPS.projectSideBar; useEffe ...

Attempting to create a multi-page slider using a combination of CSS and JavaScript

Looking for help with creating a slider effect that is triggered when navigating to page 2. Ideally, the div should expand with a width of 200% or similar. Any assistance would be greatly appreciated! http://jsfiddle.net/juxzg6fn/ <html> <head& ...

What is the best way to retrieve the value of the "Observer" object?

I am a user of Vue.js and I have the following data in this.suspendedReserveMemo: this.suspendedReserveMemo [__ob__: Observer]650: "memo3"651: "memo4"652: ""653: ""654: ""655: ""656: ""657: ""658: ""659: ""660:""661: ""662: ""length: 663__ob__: Observer {v ...