Anton
Anton

Reputation: 508

Getting the global position of a bone

I'm trying to build a component which can display the position and orientation of the bones of a SkinMesh and has run into a bit of a snag. somehow I cannot determine the global position of a bone.

I have tried:

this.updateMatrixWorld(true);
this.traverse(function (bone) {
    var twinGlobalPos = new THREE.Vector3().getPositionFromMatrix(bone.matrixWorld);
    console.log(typeof (bone), "GlobalPos", twinGlobalPos.x, twinGlobalPos.y, twinGlobalPos.z);
});

and obvious variations on that, unfortunately this doesn't seem to work, all bones, except for the root report a position of 0,0,0. The root bone reports a global position identical to the SkinMesh

What am I doing wrong? is there some alternative way?

Upvotes: 5

Views: 2996

Answers (2)

Pawel
Pawel

Reputation: 18212

The chosen answer might be outdated because in current version multiple animations are now supported. Don't edit the original answer because people use different versions of three.js

this line (from Anton's code):

var twinGlobalPos = new THREE.Vector3().setFromMatrixPosition(bone.skinMatrix).add(meshGlobal);

should be now looking like that:

 var twinGlobalPos = new THREE.Vector3().setFromMatrixPosition(bone.animationCache.animations[animation.data.name].originalMatrix).add(meshGlobal);

or just

var twinGlobalPos = new THREE.Vector3().setFromMatrixPosition(bone.position).add(meshGlobal);

Upvotes: 0

Anton
Anton

Reputation: 508

Ok, I found the answer on my own.

SkinMesh overloads the implementation of updateMatrixWorld, which does not perform updates on any instances of THREE.Bone. Instead it delegates to the bones' update method, which maintains a matrix 'skinmatrix'.

Skinmatrix works similar to the worldMatrix but uses the SkinnedMesh as the global reference rather than the scene. Combining the worldMatrix of the SkinnedMesh and the SkinMatrix of the bone seems to do the trick.

this.updateMatrixWorld(true);
// assuming this is a SkinnedMesh
var meshGlobal = new THREE.Vector3().setFromMatrixPosition(this.matrixWorld);
this.traverse(function (bone) {
    if(bone instanceof THREE.Bone) {
        var twinGlobalPos = new THREE.Vector3().setFromMatrixPosition(bone.skinMatrix).add(meshGlobal);
        console.log(typeof (bone), "GlobalPos", twinGlobalPos.x, twinGlobalPos.y, twinGlobalPos.z);
    }
});

The above code seems to do the trick. I can see why this design has been chosen, but it does present a gotcha, perhaps overriding the localToWorld and worldToLocal methods on THREE.Bone would be a semi solution?

Upvotes: 5

Related Questions