7

I am making a game and I need the projectile to face which direction its going. I know which direction its going and I need to make a transformation matrix that would allow me to align the projectile model direction (1, 0, 0) or the positive X axis, to any arbitrary vector. How could I do this in glm?

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Jerfov2
  • 5,264
  • 5
  • 30
  • 52

3 Answers3

7

What are you using to represent the direction exactly?

You could do something like this:

glm::mat4 transform = glm::eulerAngleYXZ(euler.y, euler.x, euler.z);

Or via quaternions:

glm::quat rot = glm::angleAxis(glm::radians(angle_in_degrees), glm::vec3(x, y, z));
glm::mat4 rotMatrix = glm::mat4_cast(rot);

Unless you were looking for something as simple as glm::lookAt ?

detail::tmat4x4<T> glm::gtc::matrix_transform::lookAt   
(   
    detail::tvec3< T > const &  eye,     // from
    detail::tvec3< T > const &  center,  // to
    detail::tvec3< T > const &  up       // up
)
Mr_Pouet
  • 4,061
  • 8
  • 36
  • 47
7

Hey I figured the answer out, which was close to @Mr_Pouet but here it is:

const glm::vec3 a = ...;
const glm::vec3 b = ...; // in my case (1, 0, 0)
glm::vec3 v = glm::cross(b, a);
float angle = acos(glm::dot(b, a) / (glm::length(b) * glm::length(a)));
glm::mat4 rotmat = glm::rotate(angle, v);

You can replace a or b with anything you want, where a is the vector you want to translate to and b is where you are. We can optimize this if b is (1, 0, 0) or the x-axis, like in my case:

glm::vec3 v = glm::vec3(0, -a.z, a.y);
float angle = acos(a.x / glm::length(a));
glm::mat4 rotmat = glm::rotate(angle, v);

I hope this helps someone!

Jerfov2
  • 5,264
  • 5
  • 30
  • 52
0

None of the other answers are really complete answers because they don't treat special cases like cross(.,.)=0 or up||at-eye.

Here is a rather complete solution that should work in every situation where you don't care about the orientation of the rotated object (ie. lines or cylinders etc.):

glm::vec3 from;
glm::vec3 to;

glm::vec3 v = glm::cross(to, from);
float angle = acos(glm::dot(to, from) / (glm::length(to) * glm::length(from)));
glm::mat4 rotmat = glm::rotate(angle, v);

// special cases lead to NaN values in the rotation matrix
if (glm::any(glm::isnan(rotmat * glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)))) {
    if (angle < 0.1f) {
        rotmat = glm::mat4(1.0f);
    }
    else if (angle > 3.1f) {
        // rotate about any perpendicular vector
        rotmat = glm::rotate(angle, glm::cross(from,
                                      glm::vec3(from.y, from.z, from.x)));
    }
    else {
        assert(false);
    }
}

Another drawback of this solution is that it is very 'snappy', ie. the special case only triggers when the matrix blows up. This could lead to artifacts when processing cases close to the parallel case. Usually, one would want to transition between overlapping regions and apply continuous solutions in each. For that, a breakdown into Euler angles would be pereferable I guess. If someone steps over any such algorithm, please post. I mean that would also be a decent homework for students.

user1050755
  • 11,218
  • 4
  • 45
  • 56