Smoothly rotate bone based on X, Y and Z in degrees

Godot Version

4.2.2

Question

Currently I’m trying to rotate a bone, which I get with skeleton.find_bone("Neck"), smoothly based on a set of data I get externally.
My data is represented as X, Y and Z angles going from roughly -90 to +90.

So as an example if I have an arrow and the coords X = 0, Y = 45, Z = 0, I’d expect it to point center up at a 45 degree angle. If I then change the Z to 90 I’d expect the arrow to still point at the same angle upwards, but now pointing towards the right. (I hope this makes sense)

So far the closest working code I found was:

var bone_pose:Transform3D = skeleton.global_transform * skeleton.get_bone_global_pose(neckBone)
bone_pose = bone_pose.looking_at(Vector3(0, 0, 0))
skeleton.set_bone_global_pose_override(neckBone, skeleton.global_transform.affine_inverse() * bone_pose, 1.0, true)

However this is neither smooth, nor am I able to find a good way to convert my angles to a Vector3.

Is there a way to do this in a simple and clean manner?

Edit: After thinking about it a little I thought of a little hack that could work, but I also don’t know how to pull it off. Basically instead of rotating the bone with the data directly I could rotate an object that is ankered to position of the bone and then have the bone face that object. Would that work and if so, how would that be pulled off smoothly? Is there some kind of trace / follow method for bones?

Edit 2: After trying it out I went with the option from my edit, which while being jank, still works well enough for what is essentially a V0.1. Here is how it looks for anyone interested:

@onready var skeleton: Skeleton3D = get_node("StoneModel/Armature/Skeleton3D")
@onready var neckBone = skeleton.find_bone("Neck")
@onready var lookPoint: Marker3D = get_node("LookPoint")

var cords = {"x": 0, "y": 0, "z": 0}

func _process(delta):
	# Data is being served from a UDP server
	var udpAnswer = udp.get_packet().get_string_from_utf8().split(",")
	cords["x"] = int(udpAnswer[0])
	cords["y"] = int(udpAnswer[1])
	cords["z"] = int(udpAnswer[2])

	lookPoint.position.y = -cords["x"]
	lookPoint.position.x = -cords["z"]
			
	var headRotation: Transform3D = skeleton.get_bone_global_pose_no_override(neckBone)
	headRotation = headRotation.looking_at(lookPoint.global_transform.origin)
	skeleton.set_bone_global_pose_override(neckBone, headRotation, 1.0, true)

1 Like

Use tweens to make it smooth.

1 Like

Also, I believe you may end up finding using x/y/z degrees insufficient at some point in your game dev. You will want to look up how to use quaternions / transforms at that point.

1 Like

Tweens do look to be good for the smoothing part, but it still feels like another step which comes after I figure out how to properly rotate the bone itself.

I’ve stumbled onto quaternions, while trying to figure this out and it definitely seems to be the way to go. My current data however comes from an offsite sensor that delivers coordinates in degrees, making it difficult to work with anything else. So the conversion still needs to happen at some point.