Looking for a way to animate model with bones and joints rotation infos

collada-format
collada-loader
animation

#1

Hi guys,

First of all, let me explain you what I’m trying to do here. I have a collada file (dae) and my friend are animating it using bones and joints rotation infos on iOS using SceneKit. What I want to do is to animate the same rig with the same infos already stored in a DB.

However, I found that it’s impossible to have acces to bones and joints with Collada Loader (correct me if I’m wrong). Therefore, I’m looking for another way to do it and I don’t care if I have to import then export my model to another format using Blender.

Please take note that my model doesn’t have any animation infos in it, neither any kinematics infos! It’s just a static rig and we are animating it using bones and joints infos that is stored in the DB like I said before.

If I’m missing any infos please let me know.

Thank you!


#2

If the loader detects a skin and bone hierarchy, it returns the corresponding mesh as a SkinnedMesh. This object has a skeleton property that provides access to all bones.


#3

My model doesn’t seems to have any SkinnedMesh?


#4

The SkinnedMesh is in the second level of the object hierarchy:


#5

Ok got it. And is there any example somewhere on how to animate this?


#6

Such an example is not known to me, sry.


#7

OK I have access to bones and I can rotate them. However, I still don’t know how to use the infos I got to animate my model. These are infos about I about rotation (pitch, roll and yaw) but also duration and frame number. I tried to only use pitch roll and yaw data but as you probably know the animation is executing blazing fast since it’s not using any times. Anyone have an idea on how to use those data to replicate the animation in Three.js?


#8

This is a good place to start: Animation system


#9

Sorry for the delay I was busy with other things at school.

So if I get it right, I will have to construct an array with all the times and another with 3 corresponding value (x,y,z, which are in my case pitch, roll and yaw) for each time?

And since all my movement seems to be rotation it will looks like that:

const kf = new THREE.NumberKeyframeTrack( ‘.rotation’, timeArray[], valueArray[]);

If so, what’s the order to add the value? Is it like that:

const kf = new THREE.NumberKeyframeTrack( ‘.rotation’, [ 0, 1, 2 ], [ x1, y1, z1, x2, y2, z2, x3, y3, z3 ] );


#10

That looks right. If your animation is complex, it will be easier to create keyframes in Blender and export them with the model. See: http://unboring.net/workflows/animation.html


#11

Sadly, this is not an option. :frowning:


#12

Unfortunately you can’t animate the .rotation property, you’ll have to use .quaternion

This is another reason why the three.js animation system is very beginner unfriendly and I raised an issue about it on github, but it was decided to keep it this way to avoid gimbal lock.

So if you want to use three.js to animate rotation you’ll have to learn about quaternions.
Alternatively you could use tween.js or GSAP libraries instead, I think they are better for authoring your own animations.


#13

Ok now I got 4 questions:

  1. I took a look at this example:
    https://threejs.org/examples/#misc_animation_keys

If I understand well, since I want to animate the skeleton of my object, I will have to name my KeyframeTrack like this:

“.children[1].skeleton.bones[x].quaternion”

instead of only “.quaternion”?

  1. Also, I’m wondering what’s the difference between NumberKeyframeTrack, QuaternionKeyframeTrack and VectorKeyframeTrack if the name is containing the property that you want to animate? In the previous example they are using VectorKeyframeTrack and QuaternionKeyFrameTrack but in a previous post, you seemed to only use NumberKeyframeTrack @looeee :

https://github.com/mrdoob/three.js/issues/11297

  1. After looking in the documentation I saw 3 functions to get a Quaternion (setFromAxisAngle, setFromRotationMatrix and setFromEuler) and I’m wondering which one I sould use according to the data I have (pitch, roll, yaw).

  2. Every examples I see online seems to use 0, 1, 2 and so on for the value passed as “times” to the KeyframeTrack constructor. As I have values for duration in my database, can I use them or I really have to pass incrementing integer value?

Thanks in advance guys, you’ve been of a great help.


#14

.children[1].skeleton.bones[x].quaternion

When you create an AnimationMixer you specify a root object - in this case it looks like you want to animate children[1] so you would do

const mixer = new THREE.AnimationMixer( children[1] )

Then in the KeyFrameTrack you can reference set the name as bones[x].quaternion

in a previous post, you seemed to only use NumberKeyframeTrack

You should use the Quaternion track for quaternions. Internally it uses ‘slerp’ instead of ‘lerp’ to interpolate between values. If you use NumberKeyframeTrack it will still work, but interpolation will probably be wrong - this was a mistake on my part.

3 functions to get a Quaternion (setFromAxisAngle, setFromRotationMatrix and setFromEuler)

  1. setFromAxisAngle

Rotate around an axis. This is the method used in the misc animation keyframes example you linked and should be fairly intuitive. You set up an axis (below, the x-axis, but this can be any normalised vector), and then the object will rotate around this axis.

// set up rotation about x axis
var xAxis = new THREE.Vector3( 1, 0, 0 );

var qInitial = new THREE.Quaternion().setFromAxisAngle( xAxis, 0 );
var qFinal = new THREE.Quaternion().setFromAxisAngle( xAxis, Math.PI );

var quaternionKF = new THREE.QuaternionKeyframeTrack( '.quaternion', [ 0, 1, 2 ], [ qInitial.x, qInitial.y, qInitial.z, 
qInitial.w, qFinal.x, qFinal.y, qFinal.z, qFinal.w, qInitial.x, qInitial.y, qInitial.z, qInitial.w ] );
  1. setFromRotationMatrix - you probably don’t want to use this, unless you are already using transformation matrices to specify things.

  2. setFromEuler

This is also an intuitive way to use the rotation, since you can use Euler angles to specify the rotations, and just convert them to quaternions as you use them.
In general, you can think of the Euler angle as a ‘normal’ angle, where you specify the amount to rotate around the x, y, z axes.

Every examples I see online seems to use 0, 1, 2 and so on for the value passed as “times” to the KeyframeTrack constructor.

This is just for simplicity - 0, 1, 2 here means 0 seconds, 1 second, 2 seconds. You can use any incrementing values you like, they don’t have to be integers.


#15

Now I managed to create my animation without any errors. Just to let people know, I used model.children[1] for my root:

mixer = new THREE.AnimationMixer(nao.children[1]);

And for my KeyframeTrack name I used:

'.skeleton.bones[' + index + '].quaternion'

I’m now looking for the proper way to play the animation. In the previously stated example (https://github.com/mrdoob/three.js/blob/master/examples/misc_animation_keys.html), the mesh is created with Three.js. In my example, I’m using ColladaLoader, so I have to be sure my model is loaded before creating my animation. For right now, I have function to get my movement from the database that callback the function init() that load the model.

The prepareAnimation function is where I put all the code for creating my Keyframe, then I have code for the clip code itself.

The animate is exactly the same as the example and the render function is a little bit modified to be sure my model is loaded:

    function render() {
        var delta = clock.getDelta();
        if ( model !== undefined ) {

            if (mixer) {
                mixer.update(delta);
            }
            renderer.render(scene, camera);
        }
    }

I don’t get any error, but the page freeze and I have no other choice but to close the tab. Any ideas?


#17

Maybe the data in your keyframes array are not correct. See this issue for more information:

https://github.com/mrdoob/three.js/issues/11302


#18

I verified all my KeyframeTracks and I also validated them with the validate() function. Everything seems to be fine and if I comment the call to animate(), I don’t have any errors. Also, if I only add one KeyframeTrack in my clip, it seems to be fine. Here is my code for animate() and render():

function animate() {
    requestAnimationFrame( animate );
    render();
}

function render() {
    var delta = clock.getDelta();
    if ( model !== undefined ) {

        if (mixer) {
            mixer.update(delta);
        }
        renderer.render(scene, camera);
    }
}

Also, here is my keyframes objects:

Then I use this to create the clip:

var clip = new THREE.AnimationClip('Animation', -1, keyframes);
mixer = new THREE.AnimationMixer(model.children[1]);
mixer.clipAction( clip ).play();

#19

(post withdrawn by author, will be automatically deleted in 24 hours unless flagged)


#20

Found my issue! :expressionless:

I was creating my QuaternionKeyframeTrack with 3 values (x,y,z) instead of 4 (x,y,z,w). So I was getting the same issue as @looeee (not enough quaternion value compared to my time value). I can also confirm I was not getting any error in the console and I was observing the same behavior (browser crash). Furthermore, the validate function was not flagging me anything a about keyframes being wrong. So my question is, should the original issue be open back?

Otherwise, my animation is now playing! :smiley: Still have some tweaking to do with my animation (camera position, etc.), but at least the animation system is working! Thank you all!