Search code examples
godotgdscript

.translated function not working in Godot / best way to change position of bone


I'm working on a program in Godot using Gdscript, and I ran into a problem when trying to use the Transform.translated(Vector3) function. My code is supposed to move a bone to (0,0,0) by translating it by its current coordinates but with negative sign. Example: (1,2,3) would be translated by (-1,-2,-3) so it would end up at (0,0,0). For some reason when I do this, the end position of the bone is not (0,0,0), but some other coordinate. In the Godot documents, it says the .translated function is "relative to the transform's basis vectors", so maybe that's why? Also if there is a better way to change a bones position than using the Transform.translated(Vector3) function that would be helpful too. Thanks!

My Code:

            bonePose = skel.get_bone_global_pose(bone)
            var globalBonePose = skel.to_global(bonePose.origin)
            translateVector = -globalBonePose
            var newPose = bonePose.translated(translateVector)
            skel.set_bone_pose(bone, newPose)

Code Output / Results:

bonePose (the original position of the bone) is around (-0.82,0.49,0.50)

translateVector (the amount the bone will be translated) is around (0.82,-0.49,-0.50)

newPose (the final position of the bone -- should be [0,0,0]) is around (0.82,-0.66,-0.46). Even when I call skel.to_global(newPose.origin) to see the global coordinates, it's (-0.76,0.44,0.42), which is not (0,0,0)


Solution

  • In Godot a Transform is composed of a basis (a Basis) and an origin (a Vector3). Where the origin handles the translation part of the transform, and the Basis the rest.

    A Basis is the set of vectors that define the coordinate system. There is a vector that defines the x axis, another for the y axis, and another for the z axis. And this is the way Godot will encode rotation and scaling transformations.

    When the documentation says "relative to the transform's basis vectors" it means the Basis will be applied to the vector you pass in. Thus, in your case, you are getting a translation on the local space of the bone. Which implies that if the bone is rotated or scaled (or something like that), that will affect the translation.

    If you don't want to deal with rotation, scaling, et.al. I suggest you work with the origin of the Transform instead.

    If you have a Transform and you want another that is otherwise equal but located at (0, 0, 0), you do this:

    var new_transform = Transform(transform.basis, Vector.ZERO)
    

    Or replace Vector.ZERO with whatever origin you want to give the new transform.


    I also need to remind you that get_bone_global_pose and set_bone_pose do not operate on the same thing. On one hand set_bone_pose is relative to the parent bone, on the other get_bone_global_pose is relative to the Skeleton. Thus, I suggest you use set_bone_global_pose_override instead.


    The final piece you need is the opposite of Spatial.to_global. Because setting the pose like as follows…

    bonePose = skel.get_bone_global_pose(bone)
    var newPose = Transform(bonePose.basis, Vector.ZERO)
    skel.set_bone_global_pose_override(bone, newPose, 1.0)
    

    … Would place it at the origin of the Skeleton.

    Well, the opposite of Spatial.to_global is Spatial.to_local, and you would use it like this:

    bonePose = skel.get_bone_global_pose(bone)
    var newPose = Transform(bonePose.basis, skel.to_local(Vector.ZERO))
    skel.set_bone_global_pose_override(bone, newPose, 1.0)
    

    Here skel.to_local(Vector.ZERO) should give the origin of the world relative to the Skeleton. And given that set_bone_global_pose_override wants a Transform relative to the Skeleton, the result should be that the bone is placed at the origin of the world. With its rotation and scaling preserved.