Skip to content

Instantly share code, notes, and snippets.

@json2d
Last active September 17, 2023 14:51
Show Gist options
  • Save json2d/c48b4fee505e9397c9a56467d39f4d11 to your computer and use it in GitHub Desktop.
Save json2d/c48b4fee505e9397c9a56467d39f4d11 to your computer and use it in GitHub Desktop.
static func Basis(from: Vector3, to: Vector3) -> Basis:
if from.is_equal_approx(to): # then let it be
return
var axis = to.cross(from).normalized() # right-handed
var angle = -to.angle_to(from) # negates to force right-handed orientation
return Basis(axis, angle)
# elsewhere
var t: Tranform3D = ...
var next_up: Vector3 = ...
t.basis = Maff.Basis(t.basis.y, next_up) * t.basis
# works the same as
t.quaternion = Quaternion(t.basis.y, next_up) * t.quaternion

background

i ended up here trying to do rotations w/ Godot quaternions like this:

var target: Transform3D = ...
target.quaternion *= Quaternion(from: Vector3, to: Vector3)

the result i was getting was pretty wacky and seemed inexplicably to carry some extra rotation along the to axis

so i started digging...

breakdown

handle rotations from one Vector3 to another Vector3 this way in the snippet works more as expected versus applying it w/ Godot quaternions eg. via Quaternion(from: Vector3, to: Vector3)

to be sure, simply changing the way at the end the rotation is derived and applied (w/ axis and angle) to instead go through their quaternions (ie. target.quaternion *= Quaternion(axis, angle)) gets us back to the wrong result again.

w/o quaternions

so what's going on - what makes the non-quaternion implementation work correctly?

[πŸ”Ž Node3D.global_rotate(axis: Vector3, angle: float)...]

ok so its just using Basis to handle the rotation

[πŸ”Ž Basis.rotate(axis: Vector3, angle: float)...]

[πŸ”Ž Basis(axis: Vector3, angle: float)...] https://github.com/godotengine/godot/blob/master/core/math/basis.cpp#L826

so it uses trigonometry to derive...

[πŸ”Ž Basis multiplication...]

then it uses matrix multiplcation to apply...

w/ quaternions

for comparison, what's going on w/ the corresponding quaternion functions that seem bugged?

[πŸ”Ž Quaternion(axis: Vector3, angle: float)...]

https://github.com/godotengine/godot/blob/ba54c34551d1bda5139515d74e7d614ccbc43cea/core/math/quaternion.cpp#L295

[πŸ”Ž Quaternion multiplication...]

now full circle back to initial problematic quaternion function that kicked off this whole discovery... [πŸ”Ž Quaternion(from: Vector3, to: Vector3)...]

https://github.com/godotengine/godot/blob/16a93563bfd3b02ca0a8f6df2026f3a3217f5571/core/math/quaternion.h#L144

TL;DR

these two are not equivalent:

target.basis *= Basis(axis, angle)
target.quaternion *= Quaternion(axis, angle)

the former produces the correct result, while the latter does not.

TL;DR 2

turns out their quaternions are fine and it was a mistake w/ applying them, so disregard conclusions made above!

what went wrong

instead of this:

quaternion *= Quaternion(axis, angle)

it should've been:

quaternion = Quaternion(axis, angle) * quaternion

quaternion multiplication is not commutative https://chat.openai.com/c/16e051c8-14d3-4a4d-923e-f6330cc1b707 and i would've have realize this until digging into the source files above πŸ₯² gg friends.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment