[SOLVED] How do I turn a rigidbody's velocity into a local velocity (relative to itself)?

Hello!

I’ve been breaking my brain over this all morning.

In Unity, I have a line of code that turns a car’s world velocity into a local velocity from the perspective of the wheel:

Vector3 localVelocity = transform.InverseTransformDirection( carBody.rb.velocity );

The entity hierarchy is pretty simple, and I’ve recreated it in PlayCanvas:

- carBody
– wheel (script is here)
– wheel
– wheel
– wheel

So I’ve been looking through the forums and the API for hours trying to figure this out myself, and so far I’ve got the following PlayCanvas code:

// Create a 3-dimensional vector
var globalVelocity = this.carBody.rigidbody.linearVelocity;

// Create a 4x4 rotation matrix
var localTransform = new pc.Mat4();        
localTransform.clone( this.entity.getLocalTransform() );
localTransform.invert();

// Get Local Velocity
var localVelocity = localTransform.transformVector(globalVelocity);

I’ve never had to work with transform matrices before, so I’m a bit confused how to use them, but to me this is the most sensible code I’ve been able to come up with.

You get the local transform matrix of the wheel, you invert it, and then you transform (?) the global velocity based on that matrix to get the local velocity?

Anyway, It doesn’t do anything:

2023-06-20_15-05-58_(chrome)_Notable_Microvenator

localVelocity always remains exactly the same as globalVelocity, even though in the screenshot above the carBody is diagonally zooming through the world while spinning around. localVelocity should constantly be changing based on the angle of the carBody relative to the world space. Instead, nothing.

I’ve tried switching stuff around, using getWorldTransform() instead of local, commenting out invert(), all sorts of things, but it does nothing. I’ve hit a wall.

Does anyone know what I’m doing wrong?

Thanks!


Here’s my project: PlayCanvas 3D HTML5 Game Engine
Scene is “Car”

The Car can be controlled with arrow keys, and shows a blue line denoting localVelocity.z and a red line denoting localVelocity.x

If you press F12 and go to console you can also see log lines spitting out global velocity and local velocity

Hi @Yannic,

I haven’t that something similar before, but I’m trying to understand what exactly Unity is doing behind the original method.

I’ve found this post from the Unity forums in case it’s of help on the operations used to replicate that behavior (mainly using matrix setTRS):

https://forum.unity.com/threads/transform-inversetransformdirection-for-ecs.652543/#post-4776839

You were almost there. You take the world transform, invert it, and then modify the global velocity vector with it. The problem in your code is that this is not how you clone a matrix. Use this:

const localVelocity = new pc.Vec3();
const m4 = new pc.Mat4();

m4.copy(this.entity.getWorldTransform());
// or const m4 = this.entity.getWorldTransform().clone();

m4.invert();
m4.transformVector(globalVelocity, localVelocity);

Edit:
Sorry, had .getLocalTransform() from copy-paste. Corrected.

2 Likes

Wow, that’s exactly it!
Thank you so much!

So if I understand it correctly:

variable1.copy( variable2 );

Makes variable1’s value the same as variable 2.

But clone is instead used like:

variable1 = variable2.clone();

Is that right?

Coming from C# I’m still a bit confused why the following code wouldn’t work:

m4 = this.entity.getWorldTransform();

Yes, correct. You can always check the API docs, or go to the sources directly, e.g:

It doesn’t work, because .getWorldTransform() returns original matrix object, which you then invert. Instead, you want the original matrix to be as is, but use its inverted values to modify the vector. For that, you need a copy of it, so you could invert the copy, not the original.

2 Likes

Ahh I see!

Awesome, yeah, that makes a lot of sense.

Thanks a lot!

1 Like