• 3D
  • How to find transform rotation

If I have a transform in global space, and a desired rotation, how do I find out how much to rotate the object using a transform basis? For instance, I have a camera that is a child of a skeletal bone via BoneAttachment. The camera a slight roll when animations are played. I want the camera to be level and I know the transform for this desired rotation but don't know how to rotate the camera with the rotated() function in order to fix it. I know there is a simple way but I'm just not thinking of it.

I can move things around with transforms by multiplying them but how does it work for rotations?

You should be able to apply the transformation directly, I don’t think you necessarily have to go through a built in function

I made a mistake in typing the question, I have to convert to local space and that's where I'm stumbling, otherwise I would just set the rotation/position.

If I recall correctly, you can convert a transform to local space by multiplying it by the inverse of the transform whose space you want to make it local to, if that makes sense. Here's an example in code:

var global_transform = Transform(Basis(), Vector3(10, 10, 0))

# the node we want to make the transform local/relative to
var node = get_node("Example")

var local_transform = global_transform * node.global_transform.inverse()

I think the code above should work and will change a transform so it's relative to the other, but I'm not 100% positive. I know its doable though as I've done it before, I just don't remember 100% how I did it right off.

Thanks guys this is helping me out. I took a zeroed out transform I'm trying to reset a bone's rotation to be aligned with the local x axis of the character, and this is getting me close to it. So right now I'm going Node -> Skeleton Space -> Bone Space to try to find the right local transform of the bone that can get it aligned properly.

You can also just set the "global_transform" directly, if that is easier for your application.

I can only set a global pose override i think, doesn't seem to work correctly, I'll keep tweaking and see if I can get it to work.

I've only grazed this comment thread, but do be aware that each node that has transform, has both global and local transforms (global_transform and transform).

transform gives you the transform of the node wrt its scene parent. global transform is wrt to world.

and as mentioned above, you can use global_transform.xform_inv() to take a global vector to the same reference frame as a node's transform property.

a node also supports some local transform operations conveniently with rotation and rotation_degrees etc.

Refer to documentation for Spatial and Node2D respectively for the precise calls.

@spirit_toaster said: I can only set a global pose override i think, doesn't seem to work correctly, I'll keep tweaking and see if I can get it to work.

Oh, well a global pose in a Skeleton is not, oddly enough, a global transform like in Spatial Nodes. A global pose in a Skeleton is actually just a transform relative to the Skeleton node itself, similar to a transform of a child node of a Skeleton.

To take a global transform in a Spatial node and convert it to a global pose, or vice versa, you’ll need to multiply by the Skeleton’s global transform. I think this the code, but this is off memory:

func global_pose_to_world_transform(global_pose, skeleton_node):
	return skeleton_node.global_transform * global_pose

func world_transform_to_global_pose(world_transform, skeleton_node):
	return skeleton_node.global_transform.inverse() * world_transform

I think that’s what the code is, but I’ll double check and will make another post.

Its C++ code, but here’s the full set of conversions for all the transforms in a Skeleton:

Transform Skeleton3D::global_pose_to_world_transform(Transform p_global_pose) {
 	return get_global_transform() * p_global_pose;
 }

Transform Skeleton3D::world_transform_to_global_pose(Transform p_world_transform) {
 	return get_global_transform().affine_inverse() * p_world_transform;
 }

 Transform Skeleton3D::global_pose_to_local_pose(int p_bone_idx, Transform p_global_pose) {
 	if (bones[p_bone_idx].parent >= 0) {
 		int parent_bone_idx = bones[p_bone_idx].parent;
 		Transform conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest);
 		return conversion_transform.affine_inverse() * p_global_pose;
 	} else {
 		return p_global_pose;
 	}
 }

 Transform Skeleton3D::local_pose_to_global_pose(int p_bone_idx, Transform p_local_pose) {
 	if (bones[p_bone_idx].parent >= 0) {
 		int parent_bone_idx = bones[p_bone_idx].parent;
 		Transform conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest);
 		return conversion_transform * p_local_pose;
 	} else {
 		return p_local_pose;
 	}
 }

Looking at the code, I think the snippets I posted previously should give the same conversion from a global pose to a world transform, and vice versa, in GDScript.

Here is the code I put in, this code completely removes any sort of bobbing and shaking when a camera is attached to the bone:

var head_idx = $Armature/Skeleton.find_bone("Head")
var xform = $Armature/Skeleton.global_transform.inverse() * transform
var head_xform = $Armature/Skeleton.get_bone_global_pose(head_idx)
xform.origin = head_xform.origin
$Armature/Skeleton.set_bone_global_pose_override(head_idx, xform, 1, true)

The transform is the player's node root itself as this keeps the head facing straight forward. This is a great solution! I just have to make it so the head can tilt up and down and this is a perfect solution.

Thank you so much!

I have one more question. Is there a way to turn my pose computed in Skeleton space to bone space? (i.e. if I want to use:

$Armature/Skeleton.set_bone_bose()

instead?

Nevermind, I was able to get it to work thanks to your code snippets!