2

I am loading a model of a mechanism (e.g. a robot arm) in Three.js. Sadly the models I am using don't have a skeleton, but I have the locations, axes and so on of the joints. In order to use e.g. inverse kinematic solvers like Three-IK, I want to create a skeleton from these parameters. Since I want to use many different models I would prefer to not create the skeletons by hand but in code.

I have been trying for over a week now to create a valid bone structure from these values that reflects the model, but nothing succeeded. For example, if I create a chain of bones using the positions of the joints I get a very long skeleton which in no way matches the positions I used.

let boneParent;
let bonepos = [];
let bones = [];
model.traverse(child => {
    switch(child.type) {
        case "joint":
            let p = new Vector3();
            child.getWorldPosition(p);
            bonepos.push(p);

            let bone = new Bone();
            boneParent && boneParent.add(p);
            bone.worldToLocal(p.clone());
            bone.position.copy(p);
            bone.rotation.copy(child.rotation);
            bone.scale.copy(child.scale);

            boneParent = bone;
            bones.push(bone);
            break;
    }
});
showPoints(scene, bonepos, 0xff0000);

const skeletonHelper = new SkeletonHelper(bones[0]);
skeletonHelper.visible = true;
scene.add(skeletonHelper);

The code above results in the screenshot below. The red markers are the positions I get from the robot joints, the line snaking into the distance is the skeleton as visualized by the SkeletonHelper.

Robot with very bad bones

So my question is this: it seems like I don't understand well enough how bones are handled in Three.js. How can I create a skeleton that reflects my existing model from its joint locations and orientations?

Thanks in advance!

For future visitors: Rocksi is open source!

Managarm
  • 1,070
  • 3
  • 12
  • 25
  • Hello Managarm, I am currently working on similar problem using a URDF robot model, and I am trying to use off the shelf IK solvers but they use bones, by any chance do you have code that you could share ? – Jorge Vilchis Dec 20 '22 at 17:37
  • Hey @JorgeVilchis, I do indeed :D Check out https://github.com/ndahn/Rocksi for the latest version! – Managarm Dec 21 '22 at 00:04
  • Managarm, I took a look into above git repo, I was nicely surprised , the whole reason I am trying to incorporate three js bones and skeleton to a URDF robot model is to use CCD and FABRIK IK solvers ! – Jorge Vilchis Dec 21 '22 at 19:39
  • Do you have have linkedin account ? Do you mind if I send you direct message over there ? – Jorge Vilchis Dec 21 '22 at 20:01

2 Answers2

1

After some fiddling around I found a solution:

let root = new Bone();
let parent = root;
let pos = new Vector3();

for (let joint of robot.arm.movable) {
    let link = robot.getLinkForJoint(joint);
    link.getWorldPosition(pos);
    
    let bone = new Bone();
    parent.add(bone);
    parent.lookAt(pos);
    parent.updateMatrixWorld();  // crucial for worldToLocal!
    bone.position.copy(bone.worldToLocal(pos));
        
    parent = bone;
}

The important part is to call updateMatrixWOrld() after lookAt() so that bone.worldToLocal() works correctly. Also lookAt() saves a lot of matrix hassles :)

Managarm
  • 1,070
  • 3
  • 12
  • 25
  • @Managam can you please refer to the documentation of `getLinkForJoint` I can't find this function documentation! – Bilal Mar 08 '23 at 05:04
  • 1
    @Bilal That line is part of my project, see [here](https://github.com/ndahn/Rocksi/blob/fb37baf7f808f91c4e9785c9c00e74cb643375a6/src/simulator/robots/robotbase.js#L270). [Rocksi](https://github.com/ndahn/Rocksi) is open source! – Managarm Mar 13 '23 at 17:21
0

child.getWorldPosition(p);

I'm afraid it's incorrect to apply the position in world space to Bone.position which represents the position in local space.

boneParent = bone;

This line looks problematic, too. A bone can have multiple child elements. It seems to me that this use case is not considered of your code.

Mugen87
  • 28,829
  • 4
  • 27
  • 50
  • Local space is global space if there is no parent yet :) But I tried parenting first and translating to local space, too. Single parents are okay as long as it's a single kinematic chain! – Managarm Dec 14 '20 at 14:22
  • But you are introducing a hierarchy when using `add()` so your code is definitely not correct. I suggest you demonstrate the issue with a live example. – Mugen87 Dec 14 '20 at 15:14
  • Thanks for your feedback, I have added a more complete example and a screenshot! – Managarm Dec 17 '20 at 21:23