如何从 Godot 中的两个变换获得欧拉角

How to get Euler angles from two transforms in Godot

我有一个 Transform 在轴上旋转了一个角度。是否可以使用旋转前的Transform和旋转后的Transform来获得旋转的欧拉角?我尝试使用 Transform.basis.get_euler() 来获得 Transform 前后的欧拉角。但是我不认为减去它们会起作用。这将如何完成?

解决方案

我想到了这个:

var A := start.basis.get_rotation_quat()
var B := end.basis.get_rotation_quat()
var R := A.inverse() * B
print(R.get_euler())

还建议始终使用 normalize 四元数,以尽量减少累积浮点错误。不过我相信这里不是问题。


说明

想法是你有一个旋转四元数 A,经过一些旋转 R 变成 B。换句话说:

B = A * R

如果我们求解 R,我们会得到:

B = A * R

B * R.inverse() = A * R * R.inverse()

B * R.inverse() = A

B.inverse() * B * R.inverse() = B.inverse() * A

R.inverse() = B.inverse() * A

R = (B.inverse() * A).inverse()

R = A.inverse() * B

测试

这是我用来测试的:

tool
extends MeshInstance


func _enter_tree() -> void:
    set_notify_transform(true)


func _notification(what: int) -> void:
    if what == NOTIFICATION_TRANSFORM_CHANGED:
        var parent := get_parent() as Spatial
        var end := global_transform.basis.get_rotation_quat()
        var start := parent.global_transform.basis.get_rotation_quat()
        var A := start
        var B := end
        var R := A.inverse() * B
        var S := transform.basis.get_rotation_quat()
        if R.w < 0: R = -R
        if S.w < 0: S = -S
        if not S.is_equal_approx(R):
            prints("FAIL", rotation_degrees, R, S)

将其附加到 SpatialMeshInstance 子级。

代码将 运行 放在编辑器上。它启用对其 Transform 中的任何更改的通知。当它收到通知时,它会根据其 global_transform 及其父 global_transform 计算四元数。旋转它看看是否打印出来。

顺便说一句,我最初忘记了有两种方法可以用四元数表示相同的旋转。所以它没有通过测试。这些行修复它:

if R.w < 0: R = -R
if S.w < 0: S = -S

不,normalize 没有解决这个问题。缺少 normalize 也没有导致测试失败。

我还测试了您不需要这样做就可以从 get_euler 中获取正确的值。我的意思是,像这样它可以正常工作:

var end := global_transform.basis.get_rotation_quat()
var start := parent.global_transform.basis.get_rotation_quat()
var A := start
var B := end
var R := A.inverse() * B
var S := transform.basis.get_rotation_quat()
if not S.get_euler().is_equal_approx(R.get_euler()):
    prints("FAIL", rotation_degrees, R, S)

类似地,它可以直接与 Transform 一起正常工作,如下所示:

var end := global_transform
var start := parent.global_transform
var A := start
var B := end
var R := A.affine_inverse() * B
var S := transform
if not S.is_equal_approx(R):
    prints("FAIL", rotation_degrees, R, S)