FBX animations not running properly with three js - javascript

I am having an issue with animations on some fbx models. If I have, for example an animation that lasts 20 secs, the model will stay still for 19 secs and then all changes will happen within the last second or so. On other fbx models the animation runs correctly.
The code that I am using to run the animation is a follows:
The loader.load callback is:
var clock = new THREE.Clock();
var mixers = [];
function(object){
object.position.set(0,0,0);
object.mixer = new THREE.AnimationMixer(object);
mixers.push(object.mixer);
console.log(object);
for (var a = 0; a < object.animations.length; a++){
var action = object.mixer.clipAction(object.animations[a]);
action.play();
console.log(action);
}
scene.add(object);
animate();
}
And the animate code is:
function animate() {
requestAnimationFrame(animate);
for(var i = 0; i < mixers.length; i++){
mixers[i].update(clock.getDelta());
}
render();
stats.update();
}
function render() {
if (mixer) {
mixer.update(clock.getDelta());
}
renderer.render(scene, camera);
}
Any ideas? Thanks!

From experience, I can tell you that the fbx ascii export process (at least for Autodesk Maya) doesn't always give either
the correct start and end times set in Maya or
gives a set of numbers that threejs doesn't import properly.
What you end up getting is -- as you describe -- a lot of time in the animation where nothing happens. As far as I've seen, it's usually trailing at the end, but it could certainly be at the beginning as well.
You could fix the fbx file manually, but it might be easier just add a function to set the beginning time to the time of your first frame (and if the first frame is the issue, start with the second frame).
I have the code for this somewhere, let me find it and then I'll add it to this answer.

Related

Three.js Tweening issue: start value always remains the same

I have a problem for tweening my camera.position . I create a codepen with the minimum of code just to reproduce my issue and I annotate all my code. I also put a lot of console.log() for debugging purpose .
Codepen
the start point is my camera.postion
camera.position.z = 30;
and my tween001
var tween001 = gsap.to(camera.position,{ delay:2,duration:5,z:60,onUpdate:function(){
camera.updateProjectionMatrix();
console.log("play");
},onComplete:function(){console.log("complete");},ease:"elastic"});
so the tween is about to move my camera from the Z = 30 to Z = 60
its work perfectly but ... When the user move the camera when the user move/over/click on the 3d its fire and eventlistener that pause "tween001.pause()" I want the tween001 use the "actual" camera.postion and not when the camera.postion used when the tween 001 get fire .
Cause when the tween001 is played again or it resume from a pause the start point used is the default one x=0 y=0 z=30 .
An idle function play the tween001 again
window.setInterval(checkTime, 1000);// every 1 second launch checktime()
function checkTime() { //idleCounter get 1 every second and at 5 second coz timeout is 5 checktime relaunch the tween001
if (idlecounter < timeout) {
idlecounter++;
//console.log("++ ");
} else if (idlecounter == timeout) {
tween001.play();
console.log('timeout');
}
}
So you have to understand that GSAP assumes it's the only thing that's controlling camera.position. So when you declare gsap.to(camera.position, {z: 60}) it'll store internally the starting position (30) and the ending (60) to build its timeline. It doesn't know that you've changed the z-position with the mousewheel, so when you call .play() it'll still assume you want to go from 30 to 60.
What you have to do is re-initialize a new tween each time so it has to look up the starting position when you want to replay it:
var tween001;
function doTween() {
tween001 = gsap.to(camera.position, { delay:2,duration:5, z:60, ease:"elastic", onComplete:function(){
// camera.updateProjectionMatrix();
console.log("complete");
}});
}
Notice that I declared var tween001 outside the function, in the global scope, so you can still call tween001.pause() whenever you need
There's no need to update the projection matrix while changing position.
Now, when you're ready to start the animation again, instead of using tween001.play() you can call doTween() and it'll build a new timeline that re-reads the camera's current z-position to start the animation:
// ...
else if (idlecounter == timeout) {
doTween();
idlecounter = 0;
}
see here for the updated codepen

Make javascript Animation function loop, Until page loads

I have a piece of javascript that I have copied & edited, that is designed for an animated loading ring but the animation only runs once, I would like it to run every 4 seconds, until the page is loaded, but I can't find the right syntax/script to get it to repeat, i do not want it to reload the page only loop that specific script until i set it to stop.
".radial" is the class of the radials contained inside my css & html files.
there is twelve of them & they do-not rotate only the fluorescent .glow animation part makes it appear as they are rotating. the code is;
const radials = [...document.querySelectorAll('.radial')];
let degrees = 29;
for(i=0; i < radials.length; i++) {
degrees += 13;
radials[i].style.transform = `rotate(${degrees}deg)`;
degrees += 34;
}
radials.forEach((radial, index) => {
setTimeout(function() {
radial.classList.add('glow');
},index * 29);
});
:: Update ::
Having read the comments below and searching on Youtube. I think that wrapping the whole script in a function, would be the best option. Including a call to that function within its self & passing it an argument in the parenthesis of a timeout or delay property. But setInterval() & setTimeOut() both use the unsafe eval() function underneath. Which is supposed to be a security concern.
Also a youtube video I watch a while ago, said that setInterval() & setTimeOut() do not achieve 60fps. requestAnimationFrame() Would be A much better option. I'm not sure how legitamate these claims are, or where his sources were from but I will continue searching the Webs.
The glow part looks good but I just haven't been able to get it to repeat.
I am new to Js please be patient.
is there any other workarounds for the setTimeOut() & setInterval().?
Place this code into a function that is passed to a setInterval() timer call.
function loop() {
const radials = [...document.querySelectorAll('.radial')];
let degrees = 29;
for(i=0; i < radials.length; i++) {
degrees += 13;
radials[i].style.transform = `rotate(${degrees}deg)`;
degrees += 34;
}
radials.forEach((radial, index) => {
setTimeout(function() {
radial.classList.add('glow');
},index * 29);
});
setTimeout(loop, 4000);
}
Use setInterval(). The setInterval takes two parameters, the first is the function you want to run and the second is your repeat time in miliseconds. So to run a function every 4 seconds you would do:
setInterval(function() {
// do something
}, 4000);
You can do it with setInterval, as in the other answers, but I think that the logic is clearer if you have an animate function that keeps calling itself.
You are adding a "glow" class, but you are never removing it. The animate function should toggle it on and off. To make it crystal clear, let's make that a separate function, toggleGlow.
Next, each animation loop we kick off the individual toggleGlow functions with a different delay for each radial.
Finally, the animate function will re-call itself after a short, constant, delay each time, until some stop condition is met (like the page loading).
const radials = [...document.querySelectorAll('.radial')];
function toggleGlow(element) {
if (element.classList.contains("glow")) {
element.classList.remove("glow");
} else {
element.classList.add("glow");
}
}
function animate() {
radials.forEach((radial, index) => {
setTimeout(function() {
toggleGlow(radial);
}, index * 29);
});
if (!stopCondition) {
setTimeout(animate, 200);
}
}
// kick it off
animate();
JSFiddle example here: https://jsfiddle.net/duxhy3Lj/

requestAnimationFrame at beginning or end of function?

If I have a loop using requestAnimationFrame like this:
function render() {
// Rendering code
requestAnimationFrame(render);
}
Will there be any difference if I put the requestAnimationFrame in the beginning of the function, like this:
function render() {
requestAnimationFrame(render);
// Rendering code
}
I haven't noticed any difference, but I have seen both implementations, is one of them better in any way, or are they the same?
Edit:
One thing I have thought about is, if I put it in the beginning, and the render code takes quite long time to run, say 10ms, wouldn't putting it in the end make the frame rate drop with 10ms?
requestAnimationFrame does always call its callback asynchronously, so as long as your rendering code is synchronous and does not throw exceptions it doesn't make any difference.
It's essentially a style choice, choose yourself which approach is cleaner. Putting it at the top may emphasise that render is scheduling itself, and does so even in the presence of errors in the rendering. Putting it in the bottom allows to conditionally break out of the rendering loop (e.g. when you want to pause your game).
It likely won't make a diference. The requestAnimationFrame method is asynchronous, so either way, your render function will work as expected. But... there's a catch when it comes to halting. Say you have the following code:
function render() {
requestAnimationFrame(render);
// Rendering code
}
In order to stop the next render, a call to the cancelAnimationFrame method is needed, like so:
function render() {
requestAnimationFrame(render);
// Rendering code
if (noLongerInterested) {
cancelAnimationFrame();
}
}
Otherwise, the render method will just run indefinitely. Alternatively, you could do:
function render() {
// Rendering code
if (stillInterested) {
requestAnimationFrame(render);
}
}
As for frame dropping, you could look at requestAnimationFrame as being on a fixed schedule (at 60 frames-per-second, it would be approximately 16ms intervals). If your code takes longer than that, the browser will begin to drop frames. Look at Patrick Roberts's answer for instructions on how to take charge of your frames, and use that for more consistent rendering.
I hope that helps!
To answer your question, those two functions will make a difference in the amount of time the asynchronous callback takes to occur only if your rendering code is longer than the animation frame speed (typically around 16 - 33ms depending on browser implementation). However, if you were using this API as intended, even that shouldn't make a difference.
Note that you are opting out of using the optional parameter passed from requestAnimationFrame -- the timestamp.
Make sure to calculate your deltas if you have any delta-time-dependent animations to render. Typically you multiply an animation "velocity" with the timestamp delta (current timestamp minus previous timestamp) in order to get an effective distance an object should travel across the screen. Its effect is particularly noticeable when your rendering code does not consistently take the same amount of time to execute each frame.
Demo
var untimed = 20;
var timed = 20;
function untimedRender() {
var then = performance.now() + Math.random() * 100;
while (performance.now() < then) {}
// estimated velocity
untimed += 50 / 30;
document.querySelector('#untimed').style.left = Math.min(Math.floor(untimed), 200) + 'px';
if (untimed < 200) {
requestAnimationFrame(untimedRender);
} else {
last = performance.now();
requestAnimationFrame(timedRender);
}
}
var last;
function timedRender(timestamp) {
var delta = timestamp - last;
var then = timestamp + Math.random() * 100;
last = timestamp;
while (performance.now() < then) {}
// calculated velocity
timed += delta / 30;
document.querySelector('#timed').style.left = Math.min(Math.floor(timed), 200) + 'px';
if (timed < 200) {
requestAnimationFrame(timedRender);
}
}
requestAnimationFrame(untimedRender);
div {
position: absolute;
left: 20px;
width: 10px;
height: 10px;
}
#untimed {
background-color: #F00;
top: 20px;
}
#timed {
background-color: #00F;
top: 50px;
}
<div id="untimed"></div>
<div id="timed"></div>
Notice how the blue square appears to maintain a more consistent velocity overall. That is the intention.
The MDN description states that:
The window.requestAnimationFrame() method tells the browser that you wish to perform an animation and requests that the browser call a specified function to update an animation before the next repaint.
When that repaint occurs is largely up to the browser. There shouldn't be any difference in behavior unless your JS is still running when the repaint would have occurred.
The WhatWG spec does not mention waiting for the JS call stack to clear or anything of the sort, although an exceptionally long-running function will block the UI thread and therefore should prevent animation frames from being called.

Resolving latency with setTimeout for Drum Machine

I am currently working on a drum machine and I am using setTimeout to make it run. Here is the heart of the code:
var sequencerRun = function(){
var currentTime = 0
var starting = 200;
for(var k = 0; k < 16; k++){
$(".instrument td .beat" + k).each(function(){
setTimeout(blinker, currentTime,$(this));
})
currentTime += starting;
}
}
var timerId, setInt;
var runSeq = function(){
setInt = setInterval(sequencerRun,3200);
}
$('.play').click(function(){
stopped = false
sequencerRun();
runSeq();
})
$('.stop').click(function(){
clearInterval(setInt);
stopped = true;
})
The drum machine has a matrix HTML structure built using a table. When .play is clicked a scheduling process occurs, which is encapsulated in sequencerRun. This involves a run through the columns of my matrix to determine whether there should be a drum hit or not. This is done through blinker. The scheduling creates a check on each column 1 - 16 at times 0,200,...,3200 respectively. This is what creates the effect of a sequencer. I also have a setInterval that reruns this process every 3200, which is how it takes for a run to finish.
Programmatically my code seems to make sense and my hope was that it would execute on time. The thing is that my actual app tends to stutter a lot and is stuttering even more since I deployed it. Here is a deployed version of my app.
This stuttering side effect can be best heard when you click on a full row. My question here is can anyone tell if this side effect is a result of setTimeout's timing inconsistency and if so how could I go about fixing this? Or is this related to something else that I am missing?
I think the stuttering issue has more to do with you not preloading the instruments but rather loading them on every hit, more than it has to do with settimeout.
In any case, I think I would have solved this differently. Rather than setting a fresh timeout for each beat, create one beat timeout and put the logic in there. Something like (pseudo-code-ish, lots of stuff missing just the general idea):
var MAX_BEATS = 16; // I think you had 16 beats in your example?
var BPM = 200;
var preloadedInstruments = [];
function preloadInstruments(){
for(i of myInstruments) { // myInstruments would be a list of your instruments (probably just strings with a filename)
preloadedInstruments.push(new instrument(i)); // You need an instrument class, that would preload the sound and provide a play function
}
}
var currentbeat = 1;
function beat() {
var activeInstruments = getActiveInstruments(currentbeat); // You could cache this also for every beat, but I think a simple loop here would be quick enough
for(instrument of activeInstruments) {
preloadedInstruments[instrument].play(); // play method of the instrument class called
}
currentbeat++;
if (currentbeat > MAX_BEATS) {
currentbeat = 1;
}
}
setInterval(beat, 60e3 / BPM);

ThreeJS Stop Rendering

I am working with ThreeJS on a basic 3d scene that has OrbitControls. Everything works great, except it causes my entire site to lag, as it is looping itself even when the user is not looking at it. I want a function that I can call to start and stop the rendering when certain conditions are met (in this case, the user isn't viewing the canvas). I have a start function that works just fine, but the stop function does not seem to be working, as my site goes unbearably slow after ThreeJS has initialized.
I have looked and looked for a solution to this problem, and have found a couple 'solutions', but for whatever reason they do not work with my application. My assumption is that these solutions are from old versions of ThreeJS.
Here is my code in my main.js file:
var scene,
camera,
controls,
render,
requestId = undefined;
function init() {
scene = new THREE.Scene();
var threeJSCanvas = document.getElementById("threeJS");
camera = new THREE.PerspectiveCamera( 75, threeJSCanvas.width / threeJSCanvas.height, 0.1, 1000 );
controls = new THREE.OrbitControls( camera );
// Controls and Camera settings
// Create Geometry.
}
function render() {
requestId = requestAnimationFrame(render);
renderer.render(scene, camera);
}
function start() {
render();
}
function stop() {
window.cancelAnimationFrame(requestId);
requestId = undefined;
}
In my other javascript file, there is a conditional inside of my pageChange function (this is a multipage app), that looks like the following:
if (page == 5) { // The page with the canvas on it
if (!locationsRendered) {
init();
locationsRendered = true;
}
} else { // If the page is not the page with the canvas on it
if (locationsRendered) {
stop();
}
}
locationsRendered is initialized earlier in this second javascript file in the local scope.
Any help would be much appreciated, as I can not let this simple 3D scene lag my entire app after it has been loaded. It's just not realistic.
If your scene is static, there is no reason for an animation loop. You only need to re-render when the camera moves due to a mouse or touch event.
Just use this pattern:
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render );
function render() {
renderer.render( scene, camera );
}
three.js r.67
I was using trackball controls in my scene and therefore couldn't use the solution above (as the trackball controls continue updating after mouse events finish triggering).
To solve this problem, I used:
function animate() {
renderer.render(scene, camera);
controls.update();
}
renderer.setAnimationLoop(animate);
That runs the animation loop indefinitely. To pause the animation, one can then specify null as the animation loop:
renderer.setAnimationLoop(null); // pause the animation
And to resume the animation, just pass the animation loop again:
renderer.setAnimationLoop(animate); // resume the animation
An alternative solution to completely stopping the render loop is to reduce the frames per second rate and thereby reducing resource consumption.
This approach is particularly useful if you need responsive update on your scene while not necessarily animating, but also need to snap back normal speeds when you need to.
a simple setTimout() achieves this nicely.
var fps 10;
function render() {
//reduce framerate
setTimeout(()=>{
requestAnimationFrame(render);
//must be called to enable rotating
controls.update();
renderer.render(scene, camera);
}, 1000/fps)
};

Categories

Resources