From gavin@krypton.SGI.COM Thu Jan 26 21:11:01 1989
Path: leah!itsgw!rpi!batcomputer!cornell!mailrus!purdue!decwrl!sgi!gavin@krypton.SGI.COM
From: gavin@krypton.SGI.COM (Gavin Bell)
Newsgroups: comp.graphics
Subject: Re: 3D Rotations/Instancing
Summary: Using Euler paramaters for rotations
Message-ID: <25633@sgi.SGI.COM>
Date: 27 Jan 89 02:11:01 GMT
References: <65@sdcc10.ucsd.EDU> <3390@cloud9.Stratus.COM> <3391@cloud9.Stratus.COM>
Sender: daemon@sgi.SGI.COM
Organization: Silicon Graphics, Inc., Mountain View, CA
Lines: 123


	Here's another way of keeping error out of your rotation matrices:
don't store rotations as a matrix; instead, convert to a matrix only when
you have to, and store rotations as 'Euler paramaters'.
	Euler paramaters take up 1/4 of the room of a rotation matrix (4 floats
instead of 16), and are a lot easier to normalize.
	So, here's the code.  I know that it works; I've used it to spin a cube
at 30 frames/second for over a week, with no deformation of the cube.  The
only thing missing is the vector routines, which are boring anyway, and
are left as an exercise for the reader.  Note that this code is far from
optimized; in my case, optimization wasn't necessary, since most of the
program's time was spent either interacting with the user or drawing the
objects to be displayed.
	If you really want me to, I could dig up the reference to Euler
paramaters.  And, if you are really lazy, I could even be convinced to
send you the source to the vector routines.

/*
 * Given an axis and angle, compute euler paramaters
 * a is the axis (3 floats), phi is angle in radians, and
 * e is where to put the resulting Euler paramaters (4 floats)
 */
void
axis_to_euler(float *a, float phi, float *e)
{
	vnormal(a);	/* Normalize axis */
	vcopy(a, e);
	vscale(e, fsin(phi/2.0));
	e[3] = fcos(phi/2.0);
}
/*
 *	Given two rotations, e1 and e2, expressed as Euler paramaters,
 * figure out the equivalent single rotation and stuff it into dest.
 * 
 * This routine also normalizes the result every COUNT times it is
 * called, to keep error from creeping in.
 */
#define COUNT 100
void
add_eulers(float *e1, float *e2, float *dest)
{
	static int count=0;
	register int i;
	float t1[3], t2[3], t3[3];
	float tf[4];

	vcopy(e1, t1); vscale(t1, e2[3]);
	vcopy(e2, t2); vscale(t2, e1[3]);
	vcross(e2, e1, t3);	/* t3 = e2 cross e1 */
	vadd(t1, t2, tf);	/* tf = t1 + t2, if this was C++ */
	vadd(t3, tf, tf);	/* tf += t3 */
	tf[3] = e1[3] * e2[3] - vdot(e1, e2);

	/* I use tf[] so that e1 or e2 can be the same as dest */
	for (i=0; i < 4; i++) dest[i] = tf[i] ;
	if (++count > COUNT)
	{
		count = 0;
		normalize_euler(dest);
	}
}
/*
 * Euler paramaters always obey:  a^2 + b^2 + c^2 + d^2 = 1.0
 * We'll normalize based on this formula.  Also, normalize greatest
 * component, to avoid problems that occur when the component we're
 * normalizing gets close to zero (and the other components may add up
 * to more than 1.0 because of rounding error).
 */
void
normalize_euler(float *e)
{	/* Normalize result */
	int which, i;
	float gr;

	which = 0;
	gr = e[which];
	for (i = 1 ; i < 4 ; i++)
	{
		if (fabs(e[i]) > fabs(gr))
		{
			gr = e[i];
			which = i;
		}
	}
	e[which] = 0.0;	/* So it doesn't affect next operation */

	e[which] = fsqrt(1.0 - (e[0]*e[0] + e[1]*e[1] +
		e[2]*e[2] + e[3]*e[3]));

	/* Check to see if we need negative square root */
	if (gr < 0.0)
		e[which] = -e[which];
}

/*
 * Build a rotation matrix, given Euler paramaters.
 */
void
build_rotmatrix(Matrix m, float *e)
{
	m[0][0] = 1 - 2.0 * (e[1] * e[1] + e[2] * e[2]);
	m[0][1] = 2.0 * (e[0] * e[1] - e[2] * e[3]);
	m[0][2] = 2.0 * (e[2] * e[0] + e[1] * e[3]);
	m[0][3] = 0.0;

	m[1][0] = 2.0 * (e[0] * e[1] + e[2] * e[3]);
	m[1][1] = 1 - 2.0 * (e[2] * e[2] + e[0] * e[0]);
	m[1][2] = 2.0 * (e[1] * e[2] - e[0] * e[3]);
	m[1][3] = 0.0;

	m[2][0] = 2.0 * (e[2] * e[0] - e[1] * e[3]);
	m[2][1] = 2.0 * (e[1] * e[2] + e[0] * e[3]);
	m[2][2] = 1 - 2.0 * (e[1] * e[1] + e[0] * e[0]);
	m[2][3] = 0.0;

	m[3][0] = 0.0;
	m[3][1] = 0.0;
	m[3][2] = 0.0;
	m[3][3] = 1.0;
}
-------------
Insert Standard Disclaimer here
--gavin    (gavin@sgi.com)


 