Getting direction vectors for an isometry

I asked this on the Amethyst forum, but haven’t gotten any responses. Given an Isometry, I’m trying to get the direction of travel, as well as the direction of local up/left/right/behind. After taking an example I was given, logging my position/direction of travel, and changing it until I got values that made sense, I have:

            let mut direction = transform.isometry().rotation * Vector::z();
            direction *= -1.;
            info!("Direction: {:?}", direction.data);

Initially when I instantiate an object and set its Euler angles to 0,0,0, I get:

[INFO][onslaught] Direction: [-0.0, -0.0, -1.0]

And this is correct. If I fly in the direction of travel, my Z coordinate does indeed decrease, and rotation seems to set these correctly. So, things I’m confused about:

  1. The first example I was given in the Amethyst thread has me do the following to get a forward vector:
            let mut direction = transform.isometry().rotation.inverse() * Vector::z();

But the presence or absence of .inverse() makes absolutely no difference here. It’s only by multiplying the rotation by -1 that I get a rotation in the correct direction. I’d have thought multiplying by -1 was an inverse. Am I misunderstanding why that’s there? I haven’t done this level of math in 20 years and wasn’t good at it then, unfortunately.

  1. Another example had me calling transform.isometry().rotation.matrix().row(2), and that looks a bit cleaner in that I’m just getting data without cloning or modifying anything. Unfortunately, there’s no .matrix() method on the rotation. I got this to compile:
            let rotation = transform.isometry().rotation;
            let mut direction = rotation * Vector::z();
            direction *= -1.;
            info!("Direction: {:?}", direction.data);
            let direction2 = &rotation.coords.row(2);
            info!("Direction2: {}, {}, {}", direction2[0], direction2[1], direction2[2]);

And I wanted to see if the values equalled, but I get a matrix index out of bounds error.

So in short, my code seems to work, but I’m not sure why. Calling .inverse() on the rotation has no effect, and it seems like inverting a rotation should do something to the direction vector, even if it isn’t what I ultimately want. The second example looks a bit cleaner, and I’d like to compare their results, but I can’t make it work. Anyhow, I don’t like writing code that I don’t understand. I’m also a bit confused on why I may have been told to call .inverse(). Given that the direction I get is in fact opposite of the one I need, it seems like an inverse method should give me what I want. But that it does nothing makes me wonder. :slight_smile:

Thanks.

Now that I’ve had a bit of coffee and started my day, I think I can make more sense of things.

  1. I suspect the initial rotation.inverse() suggestion was incorrect. I’m still curious about why that had no effect on my code, even if it was just swapping out one bad result for another. :slight_smile:2. I’m getting the Z vector of my rotation. In this case the Z vector is the local back, so to get local forward I multiply Z by -1. I’d do the same for left/right with either Vector::x() and Vector::x()*-1.

If that’s accurate, then I at least feel comfortable that I understand the code I wrote, even if I’m kicking the math down the road to Nalgebra. :slight_smile:

Hi!

First, some remarks (in no particular order):

  1. If rotation does not have a .matrix() method, that’s because it’s represented as an unit quaternion instead of a rotation matrix. You may call .to_rotation_matrix().into_inner() instead.
  2. Multiplying rotation * z is the same as extracting the third column of the rotation seen as a matrix.
  3. Doing transform.isometry().rotation.matrix().row(2) (with the call to .matrix() replaced by the correct one given in 1.) is the same as extracting the third row of the rotation matrix, which itself is equivalent to extracting the third column of the inverse rotation. Thus it’s equivalent to rotation.inverse() * Vector::z().

Why calling inverse() did not change anything for you?

That’s probably because you tested some special cases where it did not change anything. For example with Euler angles (0.0, 0.0, 0.0), you get an identity rotation. And the inverse of the identity is the identity itself. Similarly, there are quite a few rotations which are their own inverse. For example the rotation with the following matrix is equal to its own inverse:

 -1.0 0.0  0.0
  0.0 1.0  0.0
  0.0 0.0 -1.0

So from a mathematical point of view, you can’t assume inverting will change signs.

Why were you suggested to call inverse()?

People from the computer graphics community are used to work with the MVP matrix (Model-View-Projection matrix) which is the common way of projecting things into the screen on the graphics card. In particular, the camera is modelled as the View matrix. This view matrix is actually the inverse of the camera world-space position and orientation. Therefore, to get the forward vector of the camera (i.e. direction toward which the camera looks at in world-space), one has to invert the view matrix first: view.inverse() * -Vector::z().

Why did I multiply by -Vector::z() above?

Another conversion from the computer graphics community (and OpenGL in particular) is to use the -z vector as the forward camera vector. The flip of axis from z to -z is generally performed by the projection matrix. You way want to read the section about projections on the nalgebra user guide.

So if the transform you are working with here is the camera view matrix, then you should use:

let forward_direction = transform.inverse() * -Vector::z();
// Or equivalently:
let forward_direction = transform.inverse_transform_vector(&-Vector::z())

Finally note that multiplying by -Vector::z() is equivalent to multiplying by Vector::z() and doing direction *= -1.; afterward like you did.

Let me know if anything is not clear or if you need more explanations :slight_smile:

This is great, thanks! The code is much cleaner and I appreciate the
explanation. Things seem to be working now.

OK, one oddity here. I’m creating a ray with the following values:


[INFO][onslaught] Ray: Ray { origin: Point { coords: Matrix { data: 
[0.0, 0.0, 0.0] } }, dir: Matrix { data: [0.0, 0.0, -1.0] } }

My play area has virtual walls, and here’s where it hits:


[INFO][onslaught] Point: [0.0, 0.0, 150.0]

A more complete code snippet:


             let translation = transform.translation();
             let origin = Point::new(translation.x, translation.y, 
translation.z);
             let direction = transform.isometry().inverse() * -Vector::z();
             info!("Forward direction: {:?}", direction.data);
             let ray = Ray::new(origin, direction);
             info!("Ray: {:?}", ray);
             for (_obj, intersection) in 
world.interferences_with_ray(&ray, &collision_groups) {
                 let hit_point = ray.point_at(intersection.toi);
                 bumper_coords.push(hit_point); // bumper_coords[0]
             }
...

         for (_bumper, _emitter, transform) in (&front_bumper, 
&emitters, &mut transforms).join() {
             let point = bumper_coords[0];
             info!("Point: {:?}", point.coords.data);
             transform.set_translation_xyz(point.x, point.y, point.z);
         }

Not clear on how a ray at (0, 0, 0) heading toward (0, 0, -1) can hit a
point (0, 0, 150). Should be (0, 0, -150), unless I’m misremembering
that a ray starts at the origin and continues infinitely in the
indicated direction.

Thanks a bunch.

That’s surprising. What is the value of intersection.toi in this case?

Just figured out my issue a few minutes ago. Was a combination of bad
values for barriers, and bad logic in how I was storing/testing hit
points in my radar system. Now I’m having other issues, but I suspect
I’ll get to the bottom of these with a bit more work.

Sorry for the noise, and again, thanks for all the patient support. This
stuff is hard when you can’t just render it to the screen and see what’s
what. :slight_smile: