PDA

View Full Version : About Software Implementation of glRotate()?

suo_alex
02-26-2003, 12:47 PM
Hi everyone, sorry for such a simple question :P

I have implemented glLookAt(), glTranslatef() as well as glScalef() (they are quite simple). But for this one I am not quite sure what I should do.

I tried to do it like this:

given (x, y, z), generate (x, y, 0) and (y, z, 0) to form a basis of the 3D space. Call this matrix A.

then use the matrix series inv(A) * (standard rotation) * A to implement the rotation.

I am wondering if I have done this correctly? The problem is the output is not right now, and I am wondering if this is the problem of my implementation or the idea.

Thank you very much.

Yours Alex

knackered
02-26-2003, 01:46 PM
This is from the mesa source code ( http://www.mesa.org/ )

void
_math_matrix_rotate( GLmatrix *mat,
GLfloat angle, GLfloat x, GLfloat y, GLfloat z )
{
/* This function contributed by Erich Boleyn (erich@uruk.org) */
GLfloat mag, s, c;
GLfloat xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;
GLfloat m[16];

s = sin( angle * DEG2RAD );
c = cos( angle * DEG2RAD );

mag = GL_SQRT( x*x + y*y + z*z );

if (mag <= 1.0e-4) {
/* generate an identity matrix and return */
MEMCPY(m, Identity, sizeof(GLfloat)*16);
return;
}

x /= mag;
y /= mag;
z /= mag;

#define M(row,col) m[col*4+row]

/*
* Arbitrary axis rotation matrix.
*
* This is composed of 5 matrices, Rz, Ry, T, Ry', Rz', multiplied
* like so: Rz * Ry * T * Ry' * Rz'. T is the final rotation
* (which is about the X-axis), and the two composite transforms
* Ry' * Rz' and Rz * Ry are (respectively) the rotations necessary
* from the arbitrary axis to the X-axis then back. They are
* all elementary rotations.
*
* Rz' is a rotation about the Z-axis, to bring the axis vector
* into the x-z plane. Then Ry' is applied, rotating about the
* Y-axis to bring the axis vector parallel with the X-axis. The
* rotation about the X-axis is then performed. Ry and Rz are
* simply the respective inverse transforms to bring the arbitrary
* axis back to it's original orientation. The first transforms
* Rz' and Ry' are considered inverses, since the data from the
* arbitrary axis gives you info on how to get to it, not how
* to get away from it, and an inverse must be applied.
*
* The basic calculation used is to recognize that the arbitrary
* axis vector (x, y, z), since it is of unit length, actually
* represents the sines and cosines of the angles to rotate the
* X-axis to the same orientation, with theta being the angle about
* Z and phi the angle about Y (in the order described above)
* as follows:
*
* cos ( theta ) = x / sqrt ( 1 - z^2 )
* sin ( theta ) = y / sqrt ( 1 - z^2 )
*
* cos ( phi ) = sqrt ( 1 - z^2 )
* sin ( phi ) = z
*
* Note that cos ( phi ) can further be inserted to the above
* formulas:
*
* cos ( theta ) = x / cos ( phi )
* sin ( theta ) = y / sin ( phi )
*
* ...etc. Because of those relations and the standard trigonometric
* relations, it is pssible to reduce the transforms down to what
* is used below. It may be that any primary axis chosen will give the
* same results (modulo a sign convention) using thie method.
*
* Particularly nice is to notice that all divisions that might
* have caused trouble when parallel to certain planes or
* axis go away with care paid to reducing the expressions.
* After checking, it does perform correctly under all cases, since
* in all the cases of division where the denominator would have
* been zero, the numerator would have been zero as well, giving
* the expected result.
*/

xx = x * x;
yy = y * y;
zz = z * z;
xy = x * y;
yz = y * z;
zx = z * x;
xs = x * s;
ys = y * s;
zs = z * s;
one_c = 1.0F - c;

M(0,0) = (one_c * xx) + c;
M(0,1) = (one_c * xy) - zs;
M(0,2) = (one_c * zx) + ys;
M(0,3) = 0.0F;

M(1,0) = (one_c * xy) + zs;
M(1,1) = (one_c * yy) + c;
M(1,2) = (one_c * yz) - xs;
M(1,3) = 0.0F;

M(2,0) = (one_c * zx) - ys;
M(2,1) = (one_c * yz) + xs;
M(2,2) = (one_c * zz) + c;
M(2,3) = 0.0F;

M(3,0) = 0.0F;
M(3,1) = 0.0F;
M(3,2) = 0.0F;
M(3,3) = 1.0F;

#undef M

matrix_multf( mat, m, MAT_FLAG_ROTATION );
}

antipop
02-27-2003, 05:10 AM
I think the formulas are present in the red book or Foley-VanDam. I do not have them at hand, so I cannot check it.

However, rotating about an axis are rather easy if you use quaterniations:

{
float M[16];
float q[4];
float a[3] = /* axis */
float d = /* amount */

create_q( q,a,d );
q_to_glrot_mat( q,m );

/* M contains the matrix */
}

/* q is a quaternion, v is a normalised vector, d is amount */
create_q( *q, *v, d )
{
float s,c;

s = sin( angle/2 );
c = cos( angle/2 );

q[0] = v[0]*s;
q[1] = v[1]*s;
q[2] = v[2]*s;
q[3] = c;
}

/* a slight rewrite of the code
presented in matrix-quat-faq */

q_to_glrot_mat( float *q, float *m)
{
float xx,xy,xz,xw,yy,yz,yw,zz,zw;
int i;

for(i=0; i<16; i++)
m[i] = 0.0;
m[0] = m[5] = m[10] = m[15] = 1.0;

xx = q[0]*q[0];
xy = q[0]*q[1];
xz = q[0]*q[2];
xw = q[0]*q[3];
yy = q[1]*q[1];
yz = q[1]*q[2];
yw = q[1]*q[3];
zz = q[2]*q[2];
zw = q[2]*q[3];

m[ 0] = 1.0-2.0*(yy+zz);
m[ 1] = 2.0*(xy+zw);
m[ 2] = 2.0*(xz-yw);

m[ 4] = 2.0*(xy-zw);
m[ 5] = 1.0-2.0*(xx+zz);
m[ 6] = 2.0*(yz+xw);

m[ 8] = 2.0*(xz+yw);
m[ 9] = 2.0*(yz-xw);
m[10] = 1.0-2.0*(xx+yy);

m[ 3] = 0.0;
m[ 7] = 0.0;
m[11] = 0.0;
m[12] = 0.0;
m[13] = 0.0;
m[14] = 0.0;
m[15] = 1.0;
}

Chuck0
02-27-2003, 11:00 AM
Originally posted by suo_alex:
Hi everyone, sorry for such a simple question :P

I have implemented glLookAt(), glTranslatef() as well as glScalef() (they are quite simple). But for this one I am not quite sure what I should do.

I tried to do it like this:

given (x, y, z), generate (x, y, 0) and (y, z, 0) to form a basis of the 3D space. Call this matrix A.

then use the matrix series inv(A) * (standard rotation) * A to implement the rotation.

I am wondering if I have done this correctly? The problem is the output is not right now, and I am wondering if this is the problem of my implementation or the idea.

Thank you very much.

Yours Alex

the idea sounds correct... i guess your problem is, that the basis for your 3d space isnt perpendicular to each other...
in order to get a perpendicular basis you can do something like this:
v=(1,0,0) (if a is parallel to this take another value)
x,y,z ... basis vectors for your space
z=a
x=v*z;
y=x*z;
(those * are cross products)