setUserTransformsWorld - Model just a blob

There have been a few threads on properly using setUserTransformsWorld and I have gone through all of them. I can’t seem to make anything work that doesn’t have my model end up as a blob on the ground with every joint at (0, 0, 0). I have a model and skeleton that looks like this:

World bind position

Then I use the following code:

In graphics state initialize

       // Setup the avatar
        avatarNode = (Node) assetManager.loadModel("Models/BaseBody/BaseBody.mesh.j3o");
        Material matAvatar = new Material(assetManager, "MatDefs/Lighting.j3md");
        matAvatar.setTexture("DiffuseMap", assetManager.loadTexture("Textures/flatGrey.png"));
        avatarNode.setMaterial(matAvatar);
        animControl = avatarNode.getControl(AnimControl.class);
        animControl.setEnabled(false);
        avatarNode.scale(2.2f);
        rootNode.attachChild(avatarNode);
        boneList = new TreeSet<String>();
        
        // Setup the skeleton
        skeleton = animControl.getSkeleton();
        skelDebug = new SkeletonDebugger("skeleton", skeleton);
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.getAdditionalRenderState().setWireframe(true);
        mat.setColor("Color", ColorRGBA.Green);
        mat.getAdditionalRenderState().setDepthTest(false);
        skelDebug.setMaterial(mat);
        avatarNode.attachChild(skelDebug);

Note that I grab the AnimControl and disable it so I can take control of the bones.

update loop

    @Override
    public void update(float tpf) {
        super.update(tpf);
        Bone rootBone[] = skeleton.getRoots();
        List<Bone> bones = new ArrayList<Bone>(rootBone[0].getChildren());
        for(Bone bone : bones) {
            bone.setUserControl(true);
            Vector3f worldPos = bone.getWorldBindPosition();
            Quaternion worldRot = bone.getWorldBindRotation();
            bone.setUserTransformsWorld(worldPos, worldRot);
            bone.updateWorldVectors();
        }
    }

Here, all I do is loop through all the bones in the skeleton, grab the world bind position and world bind rotation and set the bone to the rotation and position. When I do this, I get the following:

Model blob

In every thread, people have recommended looking at KinematicRagdollControl.java because this shows how to use setUserTransformsWorld. I have examined this code and run it, but I can’t see what I am doing differently. One part of KinematicRagdollControl that is a bit hard for me to parse is the use of TempVars which appears to be a way to store variables you want to use in multiple threads. The boneList that is extracted from this variable is kind of an unknown to me. Does anyone have any insights to what I might be doing wrong?

Why are you setting world transforms to the local bone transform? What do you expect to happen when you do that?

Also, not related to your problem, but is there a reason you copy the bones into an array list just to iterate over it? You don’t have to do that.

And finally, TempVars has nothing to do with multithreading. It’s a way to avoid object creation for code that is called frequently and just needs some temp vars.

Thanks for your response. My ultimate goal is to set the rotations of all the bones in the global coordinate system instead of local. I am doing some motion capture stuff I need to display and my data is a quaternion for each bone in the world reference frame. My goal is to apply these global rotations.

The reason I have started with setting the bones to the world bind transforms is I expect to see picture 1 pop out. If I can “rebuild” the skeleton by controlling each bone, I can easily apply the rotations I want.

You say that I am using the local bone transforms; I didn’t know that. The word world in the getter implied I would be getting the transform in the world sense. Is that not true?

@P-Worm said: Thanks for your response. My ultimate goal is to set the rotations of all the bones in the global coordinate system instead of local. I am doing some motion capture stuff I need to display and my data is a quaternion for each bone in the world reference frame. My goal is to apply these global rotations.

The reason I have started with setting the bones to the world bind transforms is I expect to see picture 1 pop out. If I can “rebuild” the skeleton by controlling each bone, I can easily apply the rotations I want.

You say that I am using the local bone transforms; I didn’t know that. The word world in the getter implied I would be getting the transform in the world sense. Is that not true?

Ah, I missed the “world” on the setter. I’ve never used that so I have no idea what it does. It might be worth poking into the Bone code to see what’s going on. I remember some funky ju-ju the last time I looked in that class.

Actually in 3.0 the setUserTransformWorld is absolutely misleading. It does NOT set the transforms in world space, it does in model space. Model space is relative to the root bone of the model.
If you have a world space rotation, you need to combine it with the inverse world rotation of the model before using setUserTransformWorld.

An other thing, getWorldBindPosition and Rotation does NOT return anything related to world coordinate. The naming is completely wrong. It returns the bind transform of the bone in LOCAL space (relative to its parent bone). This is the position of the bone when the skeleton is in rest pose. That’s probably why you have a small blobby model, because you set local bind transforms as model space transforms.

You need to use, getModelSpacePosition / Rotation / Scale then use setUserTransformWorld to have transforms in the same space.

Note that I reworked the naming of these methods in latest version of the engine. I don’t recommend to switch to it though but the deprecation javadoc of the methods might help you to find your way into this.

Thanks for your reply. I am looking at the values produced by both getWorldBindPosition() and getModelSpacePosition(). World bind position provides vectors and quaternions with actual values whereas model space position provides identity vectors and quaternions. Your description of the two doesn’t match what I’m seeing. Any ideas?

Also, I can’t seem to move the bones at all with setUserTransformsWorld. Even when I manually set things to new Vector3f(0, 3, 0), etc. I still get that same blob. And for the record I am doing updateWorldVectors() on each bone. Any idea what I am missing?

If there aren’t any ideas, maybe I can show you what is holding me up from applying my rotations. I have tried applying rotations to my bones and my model’s bone axis do not align with the global coordinate frame. For example, here’s the left shoulder when using “show axes” in Blender:

Left shoulder

Here, X is pointing forward (jMonkey’s Z axis). When I rotate a bone with setUserTransforms(), it will rotate around this local axis instead of the global one. My first thought was that I should reorient these axes in Blender, but unfortunately my Blender skills (or any 3D modeler) are nonexistent. I did some reading and it appears that you can change the roll of the axis in Blender, but the bones Y axis is always inherited from the parent.

My motion capture system I am working with outputs quaternions matched up with jMonkey’s world frame. I don’t know how to get around the rotations being done around the axes inherited by the parent bone.

I still have yet to get setUserTransformsWorld() to do anything meaningful. I’ve looked at the source code and I can’t parse exactly what is going on behind the scenes.

I’m continuing trying to apply rotations to bones around the global axes instead of the local axes at the joints. In my mind, the ultimate problem is that I can’t rotate the local coordinate axes of the joints to be aligned with the global frame without also rotating the bone in the process. I thought that what I could do is “pretend” that the local joint axes are the same as the global axes by converting my quaternion (in the global coordinate frame) to Euler angles and then make the correct rotation by applying the Euler rotations about the joint’s local frame axis by axis in the correct order. This would treat the local frame the same as the global frame if the order was correct.

Example:

rotX.fromAngleAxis(pitch, Vector3f.UNIT_X);
rotY.fromAngleAxis(-roll, Vector3f.UNIT_Y);
rotZ.fromAngleAxis(yaw, Vector3f.UNIT_Z);

combinedRotation = combinedRotation.mult(rotZ);
combinedRotation = combinedRotation.mult(rotY);
combinedRotation = combinedRotation.mult(rotX);

Basically, I figure out which angle (roll, pitch, or yaw) needs to applied to which axis and then combine the rotations in the right order.

After trying this I have run into two main problems. First, this is an incredibly hack-y solution to my problem. And I worry that going about this in this manner will really come back to bite me in the future. Second, If I do just rotations about global Z, I get incorrect results. I think this has to do with the dreaded gimbal lock in the conversion between quaternion to Euler angles. When I look at the combined rotation I am getting undesirable behavior and I think it is directly related to this.

I have to say, I’m pretty blown away that there doesn’t seem to be a straight-forward baked-in solution to applying global rotations to joints in jMonkey, but this topic has come up infrequently enough that it must not be a desirable feature.

I still think my key to this is figuring out how to use setUserTransformsWorld even if the name is misleading. In my case, the root bone of my skeleton is aligned with the global axis, so setUserTransformsWorld should do exactly what I am looking for. I have gone through the Bone.java, Skeleton.java, RagdollUtils.java, and the infamous KinematicRagdollControl.java that seems to come up whenever someone wants to do what I am doing. I can’t see what I am doing wrong or see why my model always comes out to a blob when I use setUserTransformsWorld.

One question I do have with setUserTransformsWorld is why it requires a rotation and a position. The skeleton has access to the lengths of the joints so I don’t know why the positions aren’t calculated automatically when the skeleton is updated. Is that where I am going wrong? Do the positions of the bones need to match what they should be in order for setUserTransformsWorld to work?

The documentation is really sparse on how setUserTransforms is supposed to work. I will keep working to try and figure this out so that I can post the solution here, but a little assistance would be greatly appreciated.

To get a world transform converted to local space you have to multiply by the inverse accumulated transform of that local space. Then you can multiply that by the local transform to get it rotated as if you were using world rotation.

You will probably have more fun deciphering that as regular JME nodes/geometry to see what I mean. Test it, etc. in a safer/easier to visualize environment before taking lessons learned back to the skeleton.