Same Transform3D calculations yielding different results on multiples instances of the same type

Godot Version

v4.5.1.stable.official

Question

`Hello, I’m currently trying to code a seamless portal system for a little non-euclidean game I’m currently making.
For testing purposes, I have set up area A and area B each containing a portal linked to the other.

My problem is the following : Portal A’s camera works perfectly fine, while Portal B’s transform seems to be calculated from the wrong point.

I feel like I’m missing on something very trivial but I can’t find out what it is.
Can someone help me figure this out ?

The calculation is as following (pseudocode) :

(this makes Portal A work, but breaks Portal B)
transform = exit.global_transform.affine_inverse * self.global_transform * player_camera.global_transform

(this makes Portal B work, but breaks Portal A)
transform = self.global_transform.affine_inverse * exit.global_transform * player_camera.global_transform

Portal A and B when using the first formula :


My scene tree containing the portals :


Printing out self/exit on the ready function
Screenshot 2025-11-10 194700

Ah well at this point I might aswell just share what my code looks like :

I dont get it, someone please help me I’m losing faith in my own sanity lmao

`

A thing about portals that is easy to miss is that enter and exit are faced in opposite directions. The portal cam should resolve to the same exact global position as the player cam when the portals are placed back to back. I think it sould be something along the lines of transform = mirror(exit.global_transform) * self.global_transorm * player_camera.global_transform

Isn’t the internet full of portal recipes? I would imagine there exists a working solution somewhere already.

That’s the thing, I actually did take that formula from a pre-existing project and I have no idea why It does not work the same way

You can’t use the same calculation for both directions. Matrix multiplication is not commutative.

In general, the thing that should work is:

to_transform * from_transform.inverse() * whatever_is_being_transformed

I just did further testing. And this is effectively not a math problem at all as I was thinking.

I changed things up a bit. Now each portal has a portal_a and portal_b pair of values.
You’d rightfully think setting [portal_a, portal_b] and [portal_b, portal_a] would work ?

Now here’s the curiosity :
if portal b is set to [B, A] then portal a works but not b
BUT if portal b is set to [A, B] just like portal a… now portal b works but not portal a ???
mind you, this testing step had me not change a single thing about portal a

What I’m getting out of this is that portal b’s variables seem to replace portal a’s as if both are using shared variables instead of their own.

So yeah, the math was right, something else is wrong.

Screenshot 2025-11-12 211057

I am not trying to make it commutative at all, each portal has it’s own camera and viewport.

Whatever you’re trying to do, the order matters. from left to right: first goest the destination matrix, then the source matrix and then the object that’s being teleported matrix.

Please, again. This is not a math problem. THE MATH WORKS FOR EACH PORTAL INDIVIDUALLY.

What are the “different results” then?

As explained in my previous comment about my further testing results, it appears that portal b is overwriting the data of portal a as if both are using shared variables.

What data would that be?

I genuinely cannot make it any more clear but I’ll rephrase I guess

Each portal has a pair of variables A and B (see the screenshot I attached in said comment)
They essentially replace the self and exit_portal members to make things more clear for debugging purposes.

For some reason, portal b’s values overwrite portal a’s

So let’s say I have [A, B] and [B, A], in reality I actually have [B, A] and [B, A] making the calculation for both portals mirror what’s set for portal b.

Hope this clears your confusion

I’m not the one confused here :wink:

Show your teleportation code. Or better yet make a MRP if you think something insane is happening.

Oh I’m not confused either lmao

Let’s make things even clearer.
Now both portals are completely independent from each other.

I’ve added a node3d called “exit_point” which - as its name suggests - serves as a replacement for the exit portal. (see attached screenshot one)
To make problems impossible to miss, both points are placed somewhat randomly.

The pattern still occurs here. One portal works while the other does not.
The orange portal’s camera is at the right place and orientation while the blue one is not.
(see attached screenshot two and three)

I will also attach screenshots of the “new” code (still the same ol’ code but eh) and of the scene tree.

Hope this helps.

screenshot one

screenshot two

screenshot three

screenshot four

screenshot five

ah one more for the visual representation, showing where the exits are