3

I have a Three.js project with an animation going on and I would like to find the skeleton bones position at different times.

If I go for example to: https://modelviewer.dev/examples/animation/index.html and find the Three.js scene:

  const modelViewer = document.querySelector("model-viewer");
  const scene = modelViewer[Object.getOwnPropertySymbols(modelViewer)[14]];

I can then access for example the position of the LowerArmR which is animated in this case:

scene.children[0].children[0].children[0].children[0].children[1].children[0].skeleton.bones.find(b => b.name == "LowerArmR").position

If I do that, at different points of the animation, I always get the same position:

{x: 1.86264503820865e-10, y: 0.00575238140299916, z: -1.02445485428149e-9}

How can I access the current skeletal positions during an animation?

If I perform this process on a humanoid avatar animating, and then try to plot the positions of the bones, I just get the T-pose, but not the actual positions in every given time: enter image description here

From here I can see that:

Skeletal Animations are handled on the GPU so this data is not exactly accessible in js code at runtime.

Perhaps someone knows some workaround, but as far as I am aware there is none.

And I am looking for that workaround or canonical way to do it.

Amit
  • 4,458
  • 5
  • 31
  • 70

1 Answers1

2

Skeletal animation (or "skinning") is applied to the individual mesh vertices on the GPU1, because there are often many more vertices than there are bones, and updating them all on the CPU would be computationally expensive.

However, the transformations of the bones themselves are computed on the CPU in three.js. The distinctions that may not be obvious here are:

  1. The .position property of the bone, inherited from THREE.Object3D's .position property, is a local position, relative to the position of its parent, and its parent, and so on.
  2. Most skeletal animation operates by rotating, not translating, individual bones. For example, a rotation of the shoulder will have the effect of both translating and rotating the descendants of that bone (i.e. the rest of the arm).

Putting all of this together, what you want to find is the world position rather than the local position of a particular bone. The Object3D parent class has a method to help with this, object.getWorldPosition:

const position = new THREE.Vector3();

bone.getWorldPosition( position );

Note that this method incurs a bit of a performance overhead, because it first updates the bone's world matrix — traversing its ancestors — before computing the current world position. If you need the positions of many bones, or other properties like world rotation and scale, it's better to ensure all global transforms are up to date once (scene.updateMatrixWorld(), if needed) and then decompose the object's .matrixWorld:

const position = new THREE.Vector3();

position.setFromMatrixPosition( bone.matrixWorld );

1 If you do happen to need to compute the position of a particular vertex on the CPU, the three.js SkinnedMesh class has a helper method for this: .boneTransform().

Don McCurdy
  • 7,980
  • 1
  • 25
  • 50