How to rotate bones via script in 3d - Quaternion conversions and blender to godot axis conversions

Godot Version

4.5.2

Question

I’m trying to rotate some bones via script and I’m having issues figuring out how to do the rotations, understand how the axis work going from blender to Godot, and how to actually manipulate the bones via script.

For context here is my model:

There are 3 gears and I want them to rotate by a certain amount depending on which direction the model is supposed to move in. For now I’d like to focus on this one gear “gear _R”. I’d like it to move by ~19 deg every frame. (sure I need to factor in delta to get the rotation to sync up better with the processing speed but that’s not relevant right now). The idea is to move the bones for the gears rather than use animations. I figured this way I wouldn’t need to keep track of the gears and have to worry about syncing them up as the model moves in different directions. When moving to the right gear R should turn counter clockwise and the other 2 gears should be driven by it. When going left, gear L should turn clockwise and likewise drive the other two gears.

In Godot Gear R looks like:

Manipulating the X rotation component in the UI gets it to rotate on the correct axis. I would like to understand how to convert from blender to Godot, at least conceptually tho. The main gear (the big one in the middle) doesn’t rotate on a single axis unlike L and R. I’m trying to play around with the maths right now to get a better understanding, but I’m having issues with accessing the skeleton.

In Godot I have the exported model in a new inherited scene to a node 3d with the following script attached to the parent node:

@tool
extends Node3D


# skeleton for handling gears
@onready var skeleton_3d: Skeleton3D = $player_paddle_rig/Skeleton3D

# paramaters for turning right
@onready var turn_gear_right := false
# gear rotation deg on bones y axis in blender
const minor_gear_r_rot = 19.222
# amount to rotate by in godot
var minor_gear_r_rot_qnion : Quaternion
# gear bone info
const gear_r_id = Skeleton3D.find_bone('gear_R')
var gear_r_bone_current_rotation = Skeleton3D.get_bone_pose_rotation(gear_r_id)

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	
	# test states
	turn_gear_right = true
	
	
	pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	
	#if turn state true -> turn
	if turn_gear_right == true:
		turn_gears_right()
	pass

# trun to the right function
func turn_gears_right():
	
	print('gear_r_id: ', gear_r_id)
	print('gear_r_bone_current_rotation: ', gear_r_bone_current_rotation)	
	pass

At the moment I just get these errors:

Line 15:Cannot call non-static function “find_bone()” on the class “Skeleton3D” directly. Make an instance instead.
Line 16:Cannot call non-static function “get_bone_pose_rotation()” on the class “Skeleton3D” directly. Make an instance instead.

You’re attempting to use the function on the base class for Skeleton3D, instead of your variable that is called similarly.

const gear_r_id = Skeleton3D.find_bone('gear_R')
var gear_r_bone_current_rotation = Skeleton3D.get_bone_pose_rotation(gear_r_id)

Your variable is skeleton_3d, not Skeleton3D, there is an important distinction.

The correct code should be:

const gear_r_id = skeleton_3d.find_bone('gear_R')
var gear_r_bone_current_rotation = skeleton_3d.get_bone_pose_rotation(gear_r_id)

thanks (i am mortified)

by any chance could you explain to me why when I run the find bone command before ready() i get a null response? It works fine when I run it in the ready()

So now I can fetch the bone ID and the current rotation (0,0,1.0,0), how do I figure out how much to rotate the bone?
I assume u need to use skeleton_3d.set_bone_pose_rotation to apply the rotation.

I tried using:

const minor_gear_r_rot_start = 19.222
var minor_gear_r_rot : float

# turn bones uses qnion 
skeleton_3d.set_bone_pose_rotation(gear_r_id, Quaternion(cos(deg_to_rad(minor_gear_r_rot + minor_gear_r_rot_start*delta)), 0.0, 1.0, 0.0) )
minor_gear_r_rot += minor_gear_r_rot_start*delta

But I get this ramping turning that goes one way slows down then goes the other

	#turn bones using qnion
	gear_r_qnion_x = 1.0
	gear_r_qnion_z = 1.0
	
	gear_r_qnion_w += delta
	if gear_r_qnion_w > PI: gear_r_qnion_w = -PI
	gear_r_qnion_v3 = Vector3(gear_r_qnion_x, gear_r_qnion_y, gear_r_qnion_z).normalized()
	gear_r_qnion = Quaternion(gear_r_qnion_v3, gear_r_qnion_w)
	skeleton_3d.set_bone_pose_rotation(gear_r_id, gear_r_qnion)

I’ve managed to use this to construct the quaternion and rotate the bone, I cant figure out the correct orientation to have the quaternion at tho

Because the data structure is inexistent or not initialized before then, depending on the exact sequence

1 Like

I’ve managed to get the minor gears aligned but the bone for the main gear is confusing me. See the pics of the bones and their axis above.

I need the bone to rotate so the gear does:

Which means I need to find the quaternion where w causes a rotation around the axis of the bone….or I change the bone so its easier to rotate…I’ll try the later first. Unless someone can assist me with figuring out the quaternion components.

how do I find what direction the bone points in before a transform, editor says 0.0, 0.0, 0.707, 0.707. Is that the same thing?

so the parent bone has a transform on 0.5,0.5,0.5,-0.5, i don’t know if that plays a roll here