Rotation Parallel to Vector

Hi everyone. The problem is as follows:

I have a line drawn between two vertices and I would like to rotate a gluCylinder so that it is pointing in the same direction (parallel to the line).
I have used the vertex coordinates of the line to calculate the components of a vector and I have calculated the magnitude of this vector (which is the length of the line).

For example: vector = (dx/length, dy/length, dz/length)

With only this information, vector components and magnitude, is it possible to use glRotate to rotate the gluCylinder to match the orientation of the line?

Thanks for any help.

Hello,

is it really neccessary to do it using glRotate? If not I’d suggest another way which is to construct a rotation matrix. In fact a rotation matrix is defined as an orthonormal matrix (and with det > 0).
In a transformation matrix the column vectors represent the axis vectors of the new system relative to the old system. Orthonormal means that all three axes are orthogonal to each other and have a length of 1.
What we know is that your object shall point in the direction between the two vectors, so you can say for the beginning:

z = vec2 - vec1

(where vec1 and vec2 are your two vectors)
you then need some kind of “up-vector” just as in gluLookAt. It must be a vector which is not parallel to Z. Let’s use (0,1,0), which works until z is parallel to (0,1,0). In this case you should use some other vector. However, you can now write:

x = cross( (0,1,0), z)

which gives you an orthogonal vector to Z. The command “cross” in my example performs the cross-product on two vectors and returns the result which is then stored in the “x” vector. This function must be implemented by you. Now you need an orthogonal vector to X and Z to have a 3D orthogonal system. This can be created by:

y = cross(z, x)

These 3 vectors form your new system. You can now construct a 3x3 matrix by first norming them:
xn = normalize(x)
yn = normalize(y)
zn = normalize(z)
and then writing them column wise in a matrix. The function “normalize” creates a vector which is parallel to the provided vector but with a length of 1. It is basically: “return vec/length(vec)”. Finally you can multiply the new matrix with the actual modelview matrix by using glMultMatrix.

Regards

KjuEnnDee, thanks for the reply.

So are you saying I build my own rotation matrix and then load that into the MODELVIEW matrix?

If so then I will try the math approach you suggested.

Thanks very much for your help! - Mike

Yes, that’s what I meant.
The approach above only creates a 3x3 matrix but you need to construct a 4x4 matrix. You can fill the missing 4th row with (0, 0, 0, 1) and the missing fourth column also with (0, 0, 0, 1) where the last “1” of the column and row are the same field of course.

Regards

Here’s another way to do the rotation without using rotation matrices.
It assumes your cylinder is created with its axis parallel to the X axis.


    const float RADDEG = 57.29578;

    float az = atan2 (dy, dx)       * RADDEG;
    float el = asin  (dz / length ) * RADDEG;

    glPushMatrix ();
       glRotatef ( az, 0,0,1);
       glRotatef (-el, 0,1,0);
       Draw_Cylinder ();
    glPopMatrix ()


MaxH,

Thanks very much, I am going to try both your method and the one suggested by KjuEnnDee and see how it goes.

My WorldUp vector is Z so I will modify accordingly. It took me a few minutes to figure out az and el means azimuth and elevation - thanks very much for the help guys!

Mike

It’s true that az and el are short for azimuth and elevation, but it’s also irrelevant.
It seems to be distracting you, so I’ve rewritten my code below.
You should not have to modify this code at all as long as the initial cylinder is along the X axis.
It does not matter what your world up vector is. Don’t make this harder than it really is.

Good luck.


    const float RADDEG = 57.29578;

    float Q = atan2 (dy, dx)       * RADDEG;
    float P = asin  (dz / length ) * RADDEG;

    glPushMatrix ();
       glRotatef ( Q, 0,0,1);
       glRotatef (-P, 0,1,0);
       Draw_Cylinder ();
    glPopMatrix ()


I think the problem is related to my original question. I want to draw a gluCylinder, which defaults to being drawn with its height along the Z-axis starting at the origin. Sorry, I forgot to mention that that in my original question.

I don’t think there’s any way I can change that unless there is something I’m not realizing about drawing the cylinder.
Do you mean I should draw the gluCylinder and then rotate it so that it is laying on the x-axis before applying your method?

If so then I see what you mean.

Thanks again!

You are correct. I forgot that gluCylinder generates cylinders along the Z-axis only. In that case the easiest fix is to add one statement to the code I posted - glRotatef (90, 0,1,0) just before the Draw_Cylinder line. This will orient the cylinder along the X-axis, which is what my equations expect. If you want to do an exercise in trig. try rewriting my equations so they work starting with a cylinder along the Z-axis.

Thanks very much! I understand, I will put it to good use very soon.

Thanks again,
Mike

Here’s another way to solve your problem using ONE call to glRotate and no matrices. But you have to use vector operations, specifically - cross product and dot product. Perhaps you already have subroutines to do matrix operations. If you don’t, you should. Say you want to rotate vector A to vector B. In your case vector A would be the Z axis (0,0,1). Let vector C = A x B (cross product routine). Next get the angle (theta) between A and B. The formula for this is: theta = acos(dot(A,B)), where ‘dot’ means dot product, and acos means arccos. Finally, call glRotate with the angle theta and vector C just before the gluCylinder call. In case you forgot, the vector C is normal to the plane containing A and B. So you’re rotating A around C until it lines up with B.

OK, we started with this

float az = atan2 (dy, dx)       * RADDEG;
float el = asin  (dz / length ) * RADDEG;

glPushMatrix ();
   glRotatef ( az, 0,0,1);
   glRotatef (-el, 0,1,0);
   Draw_Cylinder ();
glPopMatrix ();

Then I tried the modification

float az = atan2 (dy, dx)       * RADDEG;
float el = asin  (dz / length ) * RADDEG;

glPushMatrix ();
   glRotatef ( az, 0,0,1);
   glRotatef (-el, 0,1,0);
   glRotatef (90, 0,1,0);
   Draw_Cylinder ();
glPopMatrix ();

I realized the last two rotations could be combined to get this
(since they’re around the same axis and consecutive)

float az = atan2 (dy, dx)       * RADDEG;
float el = asin  (dz / length ) * RADDEG;

glPushMatrix ();
   glRotatef ( az, 0,0,1);
   glRotatef (-el+90, 0,1,0);
   Draw_Cylinder ();
glPopMatrix ();

and then realized the final answer would be

float az = atan2 (dy, dx)       * RADDEG;
float el = acos  (dz / length ) * RADDEG;

glPushMatrix ();
   glRotatef (az, 0,0,1);
   glRotatef (el, 0,1,0);
   Draw_Cylinder ();
glPopMatrix ();

This works perfectly for any object drawn along the z-axis.

I am using this in the program and I am going to take the next step and try to build a single matrix and see how that goes.

Thanks again for all your help! - Mike

There you go! Throw a little math in there. Thanks for posting your solution.