Three.js Skeleton animation is not displayed correctly

skeleton
skeletal-animation

#1

I’m trying to show the animation of a skeleton with Three.js SkeletonHelper and made up of 25 joints from a JSON. This JSON has been created from a CSV file where it is only specified how the x, y, z coordinates of each joints of the skeleton changes over time. Here is an image of the skeleton joints. The content of the JSON is the hierarchy of the bones and the keyframes for each of them.

The hierarchy of bones is as follows:

"bones": [{
					"parent": -1,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "0_Spine_Base"
				}, {
					"parent": 0,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "1_Spine_Mid"
				}, {
					"parent": 20,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "2_Neck"
				}, {
					"parent": 2,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "3_Head"
				}, {
					"parent": 20,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "4_Shoulder_L"
				}, {
					"parent": 4,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "5_Elbow_L"
				}, {
					"parent": 5,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "6_Wrist_L"
				}, {
					"parent": 6,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "7_Hand_L"
				}, {
					"parent": 20,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "8_Shoulder_R"
				}, {
					"parent": 8,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "9_Elbow_R"
				}, {
					"parent": 9,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "10_Wrist_R"
				}, {
					"parent": 10,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "11_Hand_R"
				}, {
					"parent": 0,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "12_Hip_L"
				}, {
					"parent": 12,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "13_Knee_L"
				}, {
					"parent": 13,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "14_Ankle_L"
				}, {
					"parent": 14,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "15_Foot_L"
				}, {
					"parent": 0,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "16_Hip_R"
				}, {
					"parent": 16,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "17_Knee_R"
				}, {
					"parent": 17,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "18_Ankle_R"
				}, {
					"parent": 18,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "19_Foot_R"
				}, {
					"parent": 1,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "20_Spine_Shoulder"
				}, {
					"parent": 7,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "21_Hand_Tip_L"
				}, {
					"parent": 7,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "22_Thumb_L"
				}, {
					"parent": 11,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "23_Hand_Tip_R"
				}, {
					"parent": 11,
					"pos": [0, 0, 0],
					"rotq": [0, 0, 0, 1.0],
					"scl": [1, 1, 1],
					"name": "24_Thumb_R"
				}

And each of the keyframes looks like this:

{
		"pos": [-0.0827139, -0.3065069, -0.08963513],
		"time": 34.1326514,
		"scl": [1, 1, 1]
}

Although the data that contains the keyframes for each joint is correct, the skeleton is not displayed correctly. For example, the arms are shown above their position, the feet appear straight… In this image you can see how they are currently shown and in the following how they should be shown.

In this link to a Codepen you can see how it is displayed.
What could be the reason that it is displayed like this? Thank you in advance!


#2

Instead of adding a skinned mesh to the skeleton helper, do it like in this example and see if it works:

https://threejs.org/examples/webgl_loader_bvh.html


#3

First of all, thanks for answering. I do not know if I understood very well what you mean. I have done the following and it continues displaying in the same way.

new THREE.ObjectLoader().load(url, function (loadedObject) {
    loadedObject.traverse(function (child) {
        if (child instanceof THREE.SkinnedMesh) {
            mesh = child;
            console.log(mesh);
        }
    });
    if (mesh === undefined) {
        alert('Unable to find a SkinnedMesh in this place:\n\n' + url + '\n\n');
        return;
    }
    // Add mesh and skeleton helper to scene
    mesh.rotation.y = -135 * Math.PI / 180;

    skeletonHelper = new THREE.SkeletonHelper(mesh.skeleton.bones[0]);
    skeletonHelper.skeletonHelper = mesh.skeleton;


    var boneContainer = new THREE.Group();
    boneContainer.add( mesh.skeleton.bones[ 0 ] );

    scene.add(skeletonHelper);
    scene.add(boneContainer);

    // Initialize camera and camera controls
    var radius = mesh.geometry.boundingSphere.radius;
    var aspect = window.innerWidth / window.innerHeight;
    camera = new THREE.PerspectiveCamera(45, aspect, 1, 10000);
    camera.position.set(0.0, radius, radius * 9);
    controls = new THREE.OrbitControls(camera, renderer.domElement);
    controls.addEventListener('change', function () {//Cuando se mueva la camara se llamará a onCameraChange
        onCameraChange();
    });
    controls.target.set(0, radius, 0);
    controls.update();

    // Create the control panel
    createPanel();

    // Initialize mixer and clip actions
    mixer = new THREE.AnimationMixer(mesh);
    console.log(mixer);
    walkAction = mixer.clipAction('walk').setEffectiveWeight(1.0).play();
 
    // Listen on window resizing and start the render loop
    window.addEventListener('resize', onWindowResize, false);
    animate();
});

#4

It should be:

skeletonHelper.skeleton = mesh.skeleton;

#5

Right. I’ve updated it but it still does not change anything. Any other idea?


#6

Can you share the code in a new codepen? Maybe in a way so I can edit it? In your last codepen I could only view your code.


#7

I have not managed to make it editable from Codepen, I share it in JSFiddle, where you can edit it. Thanks for your help.


#8

I can’t find a problem in the code. Are you sure the animation data and the bone hierarchy are correct?

I suggest you try to convert your CSV data to BVH instead and import the file in Blender. In this way, you can ensure the correctness of your data without three.js.

Loading a BVH file in three.js can be done via BVHLoader.


#9

Thanks for the recommendation! I remembered that I also tried to create the skeleton with the information of a specific frame, and the skeleton wasn’t displayed correctly either, so I don’t know if the problem can be in the hierarchy, what do you think?
https://jsfiddle.net/asierta/q5snxa16/#&togetherjs=DKonyugMky

On the other hand, I would not know how to convert CSV to BVH since I do not have offset information.


#10

I have a question about your code. When you do this:

joint3.position.set(0.04242629, 0.7160538, -0.1223311);

Are these position values from your CSV file in world or model space?


#11

I’m not sure, but I think they’re in model space, since joint 0 is in position (0, 0, 0) and the rest of joints are in positions that are relative to that.


#12

Okay. I’ve asked that since world space would be wrong :innocent:


#13

So, what do you think. Would the hierarchy be the problem?


#14

Possibly. That’s why I suggested to work with BVH so you can import in other tools and see what’s happen.


#15

I’m trying to test different hierarchies in this example. What bone do I have to put here, the root or the first bone of the array of bones?

    bones[0].updateMatrixWorld(true);
    var helper = new THREE.SkeletonHelper(bones[0]);

#16

The root bone. It should be always the first bone of the array (for consistency reasons).