[SOLVED] Rotate Joint around World Axis

Hi all,
I’m trying to rotate a joint around the Vector3.UNIT_Y axis. However, the result does not correspond to my expectations, and I cannot seem to get it right.

Transform transform = selectedJoint.getModelTransform().clone()
   .combineWithParent(skinningControl.getSpatial().getWorldTransform());

arrowX.setLocalTranslation(transform.getTranslation());
arrowX.setLocalRotation(transform.getRotation());

arrowY.setLocalTranslation(transform.getTranslation());
arrowY.setLocalRotation(transform.getRotation());

arrowZ.setLocalTranslation(transform.getTranslation());
arrowZ.setLocalRotation(transform.getRotation());
            
Quaternion newRotation = new Quaternion().fromAngleAxis(1f * tpf, Vector3f.UNIT_Y).mult(selectedJoint.getLocalRotation());
selectedJoint.setLocalRotation(newRotation);

Initial joint transform:



Which is a bit weird imho, as the joint’s ‘forward direction’ is not the Z-axis, but the X-axis. Is the mistake that I made coming from wrongly importing from Blender using GLTF? However, I would still expect if I rotate the joint using the above code, it would rotate around world Y axis .

For further debugging purposes:

INFO: Hardware skinning engaged for Armature (Node)
-----------------------
Selected Joint : L Elbow02 in armature Armature_Armature
Root Bone : false
-----------------------
Local translation: (113.12236, -0.06864291, -8.303824)
Local rotation: (0.0, 0.8509035, 0.0, 0.52532196)
Local scale: (1.0, 0.9999999, 1.0)
---
Model translation: (181.49194, 181.21202, -71.80898)
Model rotation: (0.40202725, -0.57666767, -0.5766674, -0.41627333)
Model scale: (0.9999994, 0.9999997, 0.9999981)
---
Bind inverse Transform: 
Matrix4f
[
 -33.018204  1.6428379  -94.37752  -10.823269 
 -94.377495  1.165594  33.03866  192.89973 
 1.6428365  99.97969  1.1656089  -183.3205 
 0.0  0.0  0.0  1.0 
]

sorry i dont understand problem.

but i need ask one question:

  • do you aware that Y Axis is UP Axis?
    (in blender Axis could be Z as Up, but in JME Y is as Up)

Yes, I’m aware that the Y-axis is the up axis. So if I rotate a neck joint around the world Y (up) axis, I’m expecting a look right/left effect from the character if it is positioned on a flat surface. However it seems to rotate around the local Y axes which is not in the direction of world UP.

Please note i also dont see Scene Graph here.

For example you could have:

Node (90 deg, -90 deg, 0 deg) → Node(0 deg, 90 deg, 0 deg) → Geom

thats also why i always in Blender do “ctrl a → apply all transforms” before export.
(even skeleton as i remember was affected by it, not locally, but globally)

Local Y, you are right, but it can be AFFECTED by parent nodes/etc
(im not sure what data exactly your debug print show)

Also lets assume you rotate bone A locally, but bone B(parent of A) was transformed already, it will affect bone A so globally axis will be different too. (since i dont know here if selectedJoint is root bone). If you want rotate rootBone then anyway you could just rotate Node, not bone.

i need to again say sorry, since i dont see real issue, i just see its about some wrong axis bone rotation (am i right?). But still, every GLTF model was working for me, i also were rotating bones earlier and was workign fine, so i guess its some “parent transform” issue

is it also possible for you to provide some video so maybe i will better understand the issue you have?

Please note i also dont see Scene Graph here.
For example you could have:
Node (90 deg, -90 deg, 0 deg) → Node(0 deg, 90 deg, 0 deg) → Geom

The imported scene has the following properties:

-----------------------
Selected scene : Scene
-----------------------
Local translation: (0.0, 0.0, 0.0)
Local rotation: (0.0, 0.0, 0.0, 1.0)
Local scale: (1.0, 1.0, 1.0)
---
Scene translation: (0.0, 0.0, 0.0)
Scene rotation: (0.0, 0.0, 0.0, 1.0)
Scene scale: (1.0, 1.0, 1.0)
---

Is this what you mean?

thats also why i always in Blender do “ctrl a → apply all transforms” before export.
(even skeleton as i remember was affected by it, not locally, but globally)

I have uploaded a video in which I show how I export from blender and the steps that I take.

(im not sure what data exactly your debug print show)

The data that I showed in my first post is from the selected yellow Joint.

(since i dont know here if selectedJoint is root bone)

The selected Joint is not the root joint, the root joint is a joint somewhere in the middle of the spine.

i just see its about some wrong axis bone rotation (am i right?)

Correct, but I don’t know how to get it right :stuck_out_tongue:

i also were rotating bones earlier and was workign fine, so i guess its some “parent
transform” issue

It might indeed be a parent transform issue. I would like to know how I can solve that issue if that’s possible.

is it also possible for you to provide some video so maybe i will better understand the
issue you have?

As requested, I have uploaded a video. 2021-02-27 18-37-14.mp4 on Vimeo

The root joint however, does have a ‘weird’ transform properties, see the image below. As you explained this might cause issues. I’m not really sure how to go about it, and what exactly its influence is on rotations in child joints.

After some serious thinking and drawing quaternions on my shower window, I have finally found the answer. In case someone else in the future faces the same problem, below are the calculations:

Get the complete Transformation matrix from the model to the specific joint (multiplying all the transformation matrices on the path between the joint and the root joint of the model).

Transform transform = selectedJoint.getModelTransform().clone()
    .combineWithParent(skinningControl.getSpatial().getWorldTransform());

We don’t want to rotate around the joint’s local axis, but around the world axis. So we need to get the rotation of the world axis in local space.

Vector3f worldYInLocalSpace = new Vector3f();
transform
    .getRotation()
    .inverse()
    .mult(Vector3f.UNIT_Y, worldYInLocalSpace);

Now that we have the World Y axis projected in local space coordinates we can multiply it with the local rotation of the joint:

selectedJoint
    .getLocalRotation()
    .multLocal(new Quaternion().fromAngleAxis(tpf, worldYInLocalSpace));
5 Likes