glam-rs icon indicating copy to clipboard operation
glam-rs copied to clipboard

Add a method for quats to set their direction

Open 00sos00 opened this issue 2 years ago • 12 comments

I think it would be nice if quats had a method to make them look in a certain direction, for example if you have some direction vector and you want to make the quat look in that vector's direction, instead of making the user having to convert the vector into a rotation matrix and then into a matrix.

00sos00 avatar Jun 11 '22 16:06 00sos00

What would the method signature look like? Is there a method on Mat3 that you think should be on Quat as well?

bitshifter avatar Jun 12 '22 10:06 bitshifter

Probably something like fn look_at(&mut self, eye: Vec3, center: Vec3)

And you would get the direction vector by subtracting the 2 points, you would also need the current quaternion's direction vector and you can get that by just multiplying the quaternion by something like WORLD_FORWARD unit vector.

I also found this neat implementation which creates a quaternion to convert some vector direction into another: http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors

And after creating the quaternion, you would multiply it by the current one.

00sos00 avatar Jun 12 '22 12:06 00sos00

Does https://docs.rs/glam/latest/glam/f32/struct.Quat.html#method.from_rotation_arc do what you want?

bitshifter avatar Jun 15 '22 22:06 bitshifter

That does work if I'm using vectors for directions, however I am using quaternions.

Though there's a way to convert a vector into a quaternion, I think it would be better to just offer a method on quaternions to set their direction

00sos00 avatar Jun 16 '22 06:06 00sos00

I think you will always need two vectors to determine what the rotation should be.

bitshifter avatar Jul 01 '22 23:07 bitshifter

I would also be interested in a look_at function. Something like this maybe?

pub fn from_look_at(forward: Vec3, up: Vec3) -> Self {
    let right = up.cross(forward).normalize();
    let up = forward.cross(right);
    Quat::from_mat3(&Mat3::from_cols(right, up, forward))
}

DGriffin91 avatar Oct 17 '22 19:10 DGriffin91

Resurrecting this because I'd love to see something along these lines as well, and I'm happy to put up a PR if this is something that'll get accepted. Bevy has Transform::looking_to and look_to, and it'd be nice to move that function into glam. The signature is similar to what @DGriffin91 posted: fn look_to(&mut self, direction: Vec3, up: Vec3) and the corresponding looking_to. One possible concern is if direction == up, there's infinite possible Quats that could come out of it. Bevy's solution is just to use glam's any_orthonormal_vector to get an up direction if the given direction and up are equal, and I think that probably puts a reasonable amount of responsibility on the user of the API. Thoughts?

jnhyatt avatar Jan 20 '24 00:01 jnhyatt

bevy has a definted co-ordinate system but glam tries to be coordinate system agnostic,, let right = up.cross(forward).normalize(); looks like it assumes a particular handedness?

bitshifter avatar Jan 20 '24 09:01 bitshifter

Ah, I hadn't thought of that, but that's an excellent reason to omit this function. Would you consider something like look_to_(rh/lh)? I know glm has lookAt(RH/LH) (not sure to what extent the design goals of glam and glm overlap, but it seems like as good a precedent as I'm going to find)

jnhyatt avatar Jan 20 '24 13:01 jnhyatt

Yep I'd be OK with look_to_(rh/lh) variants.

bitshifter avatar Jan 20 '24 20:01 bitshifter

Alright, if nobody beats me to a pr, I'll have it up in the next couple days

jnhyatt avatar Jan 20 '24 22:01 jnhyatt

Popping in here to link this bevy pull request where I implemented a general version of this which looks like this:

    pub fn align(
        &mut self,
        handle: Vec3,
        direction: Vec3,
        weak_handle: Vec3,
        weak_direction: Vec3,
    ) {
        let handle = handle.try_normalize().unwrap_or(Vec3::X);
        let direction = direction.try_normalize().unwrap_or(Vec3::X);
        let weak_handle = weak_handle.try_normalize().unwrap_or(Vec3::Y);
        let weak_direction = weak_direction.try_normalize().unwrap_or(Vec3::Y);

        // The solution quaternion will be constructed in two steps.
        // First, we start with a rotation that takes `handle` to `direction`.
        let first_rotation = Quat::from_rotation_arc(handle, direction);

        // Let's follow by rotating about the `direction` axis so that the image of `weak_handle`
        // is taken to something that lies in the plane of `direction` and `weak_direction`. Since
        // `direction` is fixed by this rotation, the first criterion is still satisfied.
        let weak_image = first_rotation * weak_handle;
        let weak_image_ortho = weak_image.reject_from_normalized(direction).try_normalize();
        let weak_direction_ortho = weak_direction
            .reject_from_normalized(direction)
            .try_normalize();

        // If one of the two weak vectors was parallel to `direction`, then we just do the first part
        self.rotation = match (weak_image_ortho, weak_direction_ortho) {
            (Some(weak_img_ortho), Some(weak_dir_ortho)) => {
                let second_rotation = Quat::from_rotation_arc(weak_img_ortho, weak_dir_ortho);
                second_rotation * first_rotation
            }
            _ => first_rotation,
        };
    }

I would be a bit surprised if there wasn't a more expedient way to implement something equivalent, but I think the construction is at least pretty conceptually clear from this: handle is made to point in direction by a rotation, then a second rotation moves weak_handle into the plane of direction and weak_direction.

If you want to basically take this (and perhaps improve it!) I would be happy to see it or something similar upstreamed into glam.

mweatherley avatar Feb 29 '24 11:02 mweatherley