princeton.edu:pub/Graphics/GraphicsGems/GemsII http://spiw.com/cgi-bin/matrixtest http://cs-www.bu.edu/faculty/sclaroff/courses/cs480-95/s1/s1.html http://www.vream.com/jasonj/vutils.html http://www.clark.net/pub/pjs/vrml.html http://www.crispen.org/src/rotations.c http://spiw.com/rotateSeries.class http://bambi.ccs.fau.edu/~tomh/fractals/quatmath.html http://lobelia.physics.wisc.edu/jnb/intro/node8.html Date: Thu, 21 Nov 1996 13:52:58 -0800 From: Chris Laurel Organization: Dimension X To: Adrian Cook CC: lr-discuss@dimensionx.com Subject: Re: Math: SFRotations. References: <3293BEF0.1082@plugged.net.au> Sender: owner-lr-discuss@dimensionx.com Reply-To: Chris Laurel Conceptually, this is straightforward . . . Let A be the matrix representing the rotation of the viewpoint at the the last position and B be the rotation at the next position. You want to find the rotation R which is the change between A and B: B = R A To solve for R, we just right multiply by the inverse of A: -1 -1 B A = R A A -1 R = B A The rotation you want is just the product of the next rotation and the inverse of the last rotation. Unfortunately, the VRML scripting API doesn't contain anything to make computing the products of rotations convenient. If you're not concerned about portability to other browsers, you can use LR's Quaternion class from the dnx.geom package. Quaternions are often used to represent orientations in 3 dimensions (LR uses them internally to represent SFRotations.) The following code will compute the rotation you want using LR's Quaternion class: Quaternion qNext = new Quaternion(); Quaternion qInvLast = new Quaternion(); Quaternion qDiff = new Quaternion(); // Convert rotations from VRML's axis-angle representation // to quaternions; note that we compute the quaternion // representing the inverse of last qNext.rotation(nextValue[0], nextValue[1], nextValue[2], nextValue[3]); qInvLast.rotation(lastValue[0], lastValue[1], lastValue[2], -lastValue[3]); // Compute the change in orientation qDiff.multiply(qNext, qInvLast); // Convert back to the axis-angle representation which // VRML understands Vector3 axis = new Vector3(); float angle; angle = toAxisAngle(axis); difference.setValue(axis.x, axis.y, axis.z, angle); (I've sacrificed efficiency for readability in the code--you probably don't want to allocate new Quaternion and Vector3 objects every time the routine is called.) If using LR specific classes isn't an option for you, you'll have to write your own functions for quaternion multiplication and converting between axis-angle and quaternion representations. I can send you the code for these operations if you like--it only amounts to about 30 lines of Java. From: "Crispen, Bob" To: "'gavin@acm.org'" Cc: "'www-vrml@vag.vrml.org'" Subject: RE: Merging Multiple Rotations Date: Tue, 3 Dec 1996 08:09:06 -0600 Sender: owner-www-vrml@vag.vrml.org X-info: To remove yourself from this list, send the command "unsubscribe" to www-vrml-request@vag.vrml.org. Gavin Bell[SMTP:gavin@mailbag.com] sez: > >Crispen, Bob wrote: >> Or, just stack your rotation parameters in your >> Transform node: >> Transform { >> rotation 1 0 0 1.570795 >> rotation 0 1 0 1.570795 > >Ack, no! Specifying the same field multiple times is undefined-- there >might actually be a browser that combines them all (I doubt it, though), >but browsers can take any of the values. > >Don't do this. Double Ack! I discovered last night in trying to assemble Zap's squirrel that the world I tried this on at work only gave the illusion of rotating the elements properly (it was spinning spheres, which was the only VRML 2.0 world I had on hand, and depending on when it first fired up, or perhaps when I first looked at it, it seemed to tilt one of the orbits). In mitigation, Gavin's own rotations.c, which gives the right answers is at http://www.crispen.org/src/rotations.c or here: Sorry for the brain fart. Translation *does* work the way I said, dammit! Though I'm in serious doubt about whether it's proper VRML 2.0. I'm unable to find words that make either the translation or rotation fields legal or illegal. One example that would accommodate something I do all the time is: translation 10 0 0 rotation 0 1 0 1.570795 translation -10 0 0 just as you see it, corresponding to the VRML 1.0 Transform { translation 10 0 0 } Transform { rotation 0 1 0 1.570795 } Transform { translation -10 0 0 } That seems to be not only useful, but essential. I now defer to the VRML Court to pronounce on it. In the meantime, I'll check it out with a better test case. Bob Crispen bob.crispen@boeing.com speaking for myself, not my company /* * A little program to figure out the single axis+angle that * corresponds to a series of rotations. * * Written by Gavin Bell. */ #include #include /* * Convert axis/angle to quaternion form: * Assumes axis is unit-length, angle is in radians. */ void to_quaternion(float axis[3], float angle, float result[4]) { int i; for (i = 0; i < 3; i++) { result[i] = axis[i]*sin(angle / 2.0); } result[3] = cos(angle / 2.0); } /* * Convert unit-length quaternion to axis/angle. */ void from_quaternion(float q[4], float axis[3], float *angle) { int i; *angle = acos(q[3]) * 2.0; for (i = 0; i < 3; i++) { axis[i] = q[i] / sin(*angle / 2.0); } } /* * Multiply two quaternions together. Result may be the same as * either input: */ void multiply_quaternion(float q1[4], float q2[4], float result[4]) { float tmp[4]; tmp[0] = q2[3] * q1[0] + q2[0] * q1[3] + q2[1] * q1[2] - q2[2] * q1[1]; tmp[1] = q2[3] * q1[1] + q2[1] * q1[3] + q2[2] * q1[0] - q2[0] * q1[2]; tmp[2] = q2[3] * q1[2] + q2[2] * q1[3] + q2[0] * q1[1] - q2[1] * q1[0]; tmp[3] = q2[3] * q1[3] - q2[0] * q1[0] - q2[1] * q1[1] - q2[2] * q1[2]; result[0] = tmp[0]; result[1] = tmp[1]; result[2] = tmp[2]; result[3] = tmp[3]; } #define MAX_ROTATIONS 10 main(int argc, char **argv) { float axis[MAX_ROTATIONS][3], angle[MAX_ROTATIONS]; float q[4], result_axis[3], result_angle; int num_entered = 0; int i; printf("\nThis program allows you to enter a series of rotations\n" "about the X, Y, or Z axes. It will combine them into\n" "one rotation about an arbitrary axis, suitable for\n" "use in a VRML camera's orientation field or any other\n" "VRML SFRotation field.\n" "Enter rotations as if you are rotating the object or\n" "camera (the opposite order from nested transformations in\n" "a VRML file):\n\n"); for (i = 0; i < MAX_ROTATIONS; i++) { char axisString[10]; int done = 0; float angle_degrees; axis[i][0] = 0.0; axis[i][1] = 0.0; axis[i][2] = 0.0; angle[i] = 0.0; if (i == 0) { printf("Axis (X, Y or Z): "); } else { printf("Axis [XYZ or Q]: "); } fflush(stdout); scanf("%s", axisString); switch(axisString[0]) { case 'X': case 'x': axis[i][0] = 1.0; break; case 'Y': case 'y': axis[i][1] = 1.0; break; case 'Z': case 'z': axis[i][2] = 1.0; break; default: done = 1; break; } if (done) { num_entered = i; break; } printf("Angle in degrees: "); fflush(stdout); scanf("%f", &angle_degrees); angle[i] = angle_degrees * 3.141592653 / 180.0; printf(" You entered: (%g %g %g), %g\n", axis[i][0], axis[i][1], axis[i][2], angle[i]); } to_quaternion(axis[0], angle[0], q); for (i = 1; i < num_entered; i++) { float q1[4]; to_quaternion(axis[i], angle[i], q1); multiply_quaternion(q, q1, q); } from_quaternion(q, result_axis, &result_angle); printf("\nCombined rotation is:\n"); printf("Transform { rotation %g %g %g %g }\n\n", result_axis[0], result_axis[1], result_axis[2], result_angle); return 0; }