How do I rotate a transform's basis by the difference between to other transforms basis?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By iwilliams

I need some help working on some transforms. I’m making a teleport mechanic where I have two transforms that are facing the direction the player will be moving when they hit the teleporter and where they should be facing when they leave the teleporter. I have this working with the below code using Euler’s but, this doesn’t scale to other axis of rotation… I’m looking for a way to correctly do the following but without get_euler():

    var to_rot = tp["to_transform"].basis.get_euler()
    var from_rot = tp["from_transform"].basis.get_euler()
    var rot_diff = to_rot + from_rot
                
    var new_t = Transform()
    # The origin here is offset by the player's distance from the teleporter's origin so that 
    # when we rotate the transform that offset can be preserved
    new_t.origin = state.get_transform().origin - tp["from_transform"].origin
    # The player's basis as they pass through the teleporter
    new_t.basis = state.get_transform().basis

   # What I want to do is rotate new_t by the difference between tp["to_transform"].basis and tp["from_transform"].basis. I don't know how to do this without first converting to Euler's and doing it one axis at a time
    if rot_diff.y != 0.0:
        new_t = new_t.rotated(Vector3(0, 1, 0), -rot_diff.y)
             
        state.set_linear_velocity(state.get_linear_velocity().rotated(Vector3(0, 1, 0), -rot_diff.y))
            
        state.set_angular_velocity(state.get_angular_velocity().rotated(Vector3(0, 1, 0), -rot_diff.y))
                        
    new_t.origin += tp["from_transform"].origin
    new_t.origin -= tp["from_transform"].origin - tp["to_transform"].origin
    state.set_transform(new_t)

I’ve read the docs on using transforms, but I just lack the fundamental understanding to apply it to my situation here. The problem with my code here is that rot_diff if in Eulers and can get all messed up, especially when adding another axis.I know there’s some sort of math I can do on new_t.basis to get what I want, but I just don’t know the proper functions to call.

enter image description here

:bust_in_silhouette: Reply From: klaas

Hi,
if i understand correctly you want the player to face in the relative same direction he enters the portal 1 after leaving portal 2. Right?

# this is the global direction vector the player is facing, one unit forward minus the players global offset
var global_player_direction = player.to_global( Vector3.FORWARD ) - player.global_transform.origin

# now add this direction to the global position of the portal and transform this into local coordinate system of the portal. this is the relative (to the portal) viewing vector of the player.
var relative_player_direction = portal1.to_local( portal1.global_transform.origin + global_player_direction)

# transform this relative direction to global from the coordinate system of the other portal
var new_direction = portal2.to_global( relative_player_direction ) - portal2.global_tranform.origin

this should do it.

Thank you for answering, but this doesn’t actually account for the player’s transform.basis only the origin and translation. This would probably work fine for a physical teleporter where the exit should always be a direction, but I suppose I should have made it clearer that this is for an endless hallway type teleportation and should be seamless no matter what direction the player is facing as they walk through the hallway.

My above code works, I just know that there’s a way to do it involving the matrix math functions that I don’t have a grasp on.

iwilliams | 2020-07-28 01:52

Actually my code only deals with the “basis” (you propably mean the rotation) and does not deal with the translation.
Its just vector math. The endresult is the viewing direction transformed from one portal to another.

klaas | 2020-07-28 08:04

Hm okay, sorry for misunderstanding… I’ll have to give it another go. It’s my understanding that thetransform.origin is a vector representing the xyz cords of the transform and the transform.basis is a matrix composed of vectors representing the transforms rotation around the three axis. I don’t see where the rotation happens in your example, I’ll have to adapt it to my function to test it out properly.

iwilliams | 2020-07-28 08:20

The basis is the rotation matrix which is usualy created from a quaternion.
The rotation component in my code comes from the vector.forward which defines the orientation.
If you are not familar with vector math you should defintly dive into it. Its absolute supirior tot euler math.

klaas | 2020-07-28 09:16

I adapted your function to work with my variables, and am comparing it to the transform that my original code has constructed. The below screenshot of my debugger shows new_t with the correct transform, reached with Euler math, and new_direction, the vector calculated from your example. On the right you can see transform, which is the player’s current transform when entering the teleport plane. It appears that new_direction is close to the z component of new_t, but new_direction.y is negative when it should be positive. I also still need the x and y components of the transform with your method.

Thank you for helping, I know it’s my lack of understanding that’s the problem here. If I knew more about the subject I know I could take your advice and apply it properly :frowning:

enter image description here

var to_rot = tp["to_transform"].basis.get_euler()
var from_rot = tp["from_transform"].basis.get_euler()
var rot_diff = to_rot + from_rot
        
var new_t = Transform()
new_t.origin = state.get_transform().origin - tp["from_transform"].origin
new_t.basis = state.get_transform().basis

if rot_diff.y != 0.0:
    new_t = new_t.rotated(Vector3(0, 1, 0), -rot_diff.y)
    state.set_linear_velocity(state.get_linear_velocity().rotated(Vector3(0, 1, 0), -rot_diff.y))
    state.set_angular_velocity(state.get_angular_velocity().rotated(Vector3(0, 1, 0), -rot_diff.y))

if rot_diff.z != 0.0:
    new_t = new_t.rotated(Vector3(0, 0, 1), rot_diff.z)
    state.set_linear_velocity(state.get_linear_velocity().rotated(Vector3(0, 0, 1), rot_diff.z))
    state.set_angular_velocity(state.get_angular_velocity().rotated(Vector3(0, 0, 1), rot_diff.z))

if rot_diff.x != 0.0:
    new_t = new_t.rotated(Vector3(1, 0, 0), -rot_diff.x)
    state.set_linear_velocity(state.get_linear_velocity().rotated(Vector3(1, 0, 0), -rot_diff.x))
    state.set_angular_velocity(state.get_angular_velocity().rotated(Vector3(0, 0, 0), -rot_diff.x))
                
new_t.origin += tp["from_transform"].origin
new_t.origin -= tp["from_transform"].origin - tp["to_transform"].origin  


# this is the global direction vector the player is facing, one unit forward minus the players global offset
var global_player_direction = body.to_global( Vector3.FORWARD ) - body.global_transform.origin
# now add this direction to the global position of the portal and transform this into local coordinate system of the portal. this is the relative (to the portal) viewing vector of the player.
var relative_player_direction = tp["from_body"].to_local( tp["from_transform"].origin + global_player_direction)
# transform this relative direction to global from the coordinate system of the other portal
var new_direction = tp["to_body"].to_global( relative_player_direction ) - tp["to_transform"].origin

# debug screenshot point
state.set_transform(new_t)

iwilliams | 2020-07-28 16:43

:bust_in_silhouette: Reply From: elvisish

A bit late, but here’s mine:

func teleport(body):
	var transform_it = transform.xform_inv(body.transform.origin)
	var transform_point = other_tele.to_global(transform_it)
	body.transform.origin = transform_point
	
	var rotation = Quat(global_transform.basis).inverse()
	var player_rotation = Quat(body.global_transform.basis)
	var other_rotation = Quat(other_tele.global_transform.basis)
	body.global_transform.basis = Basis(rotation * player_rotation * other_rotation)