PRO_Muffy 122 Report post Posted April 6, 2007 Hi all, this is my first post on these forums. I've been searching about a bit to get a feel for the forum and so wasnt sure whether it would be welcomed if I landed a big chunk of code in my first post. I noticed another closed thread on something similar here on the same topic as this on this forum: http://www.gamedev.net/community/forums/topic.asp?topic_id=432027 For the last week or so, I've been trying to write my own rotation matrix. I do mean day in, day out staring at the same 30 or so lines of code. At first I tried to use an algorithm that would rotate a point around an arbitrary axis by combining three rotation matrices, one for each of the X, Y and Z axes into one matrix calculation but I couldn't get this to work. I then had a look at some of the pixel shader examples in RenderMonkey 1.62 and tried to port across an algorithm using Euler angles from GLSL to C++, and this had pretty much the same result. I then had another look at the problem and broke it down into the elements so that I was creating the X, Y and Z rotation matrices seperately. I've emailed my lecturers with code and so far I've had two responses, one saying "can't see anything obvious" and to the second suggesting a few alterations to the code. I've performed these alterations but I'm getting the same effect. For now I'm using a simple sphere object extended from a base graphic object class where the vertices are stored in a vertex array (*)(3). Using each matrix individually yields different results. Using the Y axis rotation matrix, the sphere rotates around the Y axis (it spins) and shrinks in size gradually over time. Using the X axis rotation matrix, the sphere rotates around the X axis but the sphere coordinates are flattened on the Z axis. Again over time it scales. Using the Z axis rotation matrix, a similar thing happens but rotation takes place around the Z axis and flattening occurs to the X coordinate. I wasnt sure if it was a good idea to post a lot of C++ on my first go. It's not so much about the code as the underlying algorithm that's causing problems. I feel I'm so close to the code I can't see the problem and I have tried working out the algorithm seperately. Does anyone have any suggestions? Quote: /*********************************************************************** Update the player - This is performed before GraphicObject::Rotate() so that the value to rotate by is set ***********************************************************************/ void Player::Update(void) { // Update dt value dt = (float)timer.GetDT(); float rotationInDegrees = 30.0f; float rotationInRadians = rotationInDegrees*dt*(PI/180.0f); rotation.xyzw[0] = rotationInRadians; rotation.xyzw[1] = rotationInRadians; rotation.xyzw[2] = rotationInRadians; SetRotation(rotation); } /******************************************************************************* Rotate this graphicobject's vertices *******************************************************************************/ void GraphicObject::Rotate(const float rotateX, const float rotateY, const float rotateZ) { // Rotation about an arbitrary axis Matrix33f rotationMatrixX; Matrix33f rotationMatrixY; Matrix33f rotationMatrixAll; rotationMatrixX.MakeRotationMatrixX(rotation.xyzw[0]); rotationMatrixY.MakeRotationMatrixY(rotation.xyzw[1]); rotationMatrixAll.MakeRotationMatrixZ(rotation.xyzw[2]); rotationMatrixAll.MultiplyThis(rotationMatrixX); rotationMatrixAll.MultiplyThis(rotationMatrixY); // Update vertices Vector3f temp; for (int i = 0; i < numberOfVertices; i++) { temp.Set3f(vertexArray[i][0], vertexArray[i][1], vertexArray[i][2]); temp.MultiplyThis33f(rotationMatrixAll); vertexArray[i][0] = temp.xyz[0]; vertexArray[i][1] = temp.xyz[1]; vertexArray[i][2] = temp.xyz[2]; } // TODO: update normals! } /******************************************************************************* Creates a rotation matrix which vector3fs can be multiplied to rotate around the X axis *******************************************************************************/ void Matrix33f::MakeRotationMatrixX(float angle) { float fCos = cosf(angle); float fSin = sinf(angle); m[0][0] = 1.0f; m[1][0] = 0.0f; m[2][0] = 0.0f; m[0][1] = 0.0f; m[1][1] = fCos; m[2][1] = fSin; m[0][2] = 0.0f; m[1][2] = -fSin; m[2][2] = fCos; } /******************************************************************************* Creates a rotation matrix which vector3fs can be multiplied to rotate around the Y axis *******************************************************************************/ void Matrix33f::MakeRotationMatrixY(float angle) { float fCos = cosf(angle); float fSin = sinf(angle); m[0][0] = fCos; m[1][0] = 0.0f; m[2][0] = -fSin; m[0][1] = 0.0f; m[1][1] = 1.0f; m[2][1] = 0.0f; m[0][2] = fSin; m[1][2] = 0.0f; m[2][2] = fCos; } /******************************************************************************* Creates a rotation matrix which vector3fs can be multiplied to rotate around the Z axis *******************************************************************************/ void Matrix33f::MakeRotationMatrixZ(float angle) { float fCos = cosf(angle); float fSin = sinf(angle); m[0][0] = fCos; m[1][0] = fSin; m[2][0] = 0.0f; m[0][1] = -fSin; m[1][1] = fCos; m[2][1] = 0.0f; m[0][2] = 0.0f; m[1][2] = 0.0f; m[2][2] = 1.0f; } /******************************************************************************* Multiply this Matrix33f by another Matrix33f *******************************************************************************/ void Matrix33f::MultiplyThis(Matrix33f& a) { // Calculate the dot products m[0][0] = m[0][0]*a.m[0][0] + m[0][1]*a.m[1][0] + m[0][2]*a.m[2][0]; m[1][0] = m[0][0]*a.m[0][1] + m[0][1]*a.m[1][1] + m[0][2]*a.m[2][1]; m[2][0] = m[0][0]*a.m[0][2] + m[0][1]*a.m[1][2] + m[0][2]*a.m[2][2]; m[0][1] = m[1][0]*a.m[0][0] + m[1][1]*a.m[1][0] + m[1][2]*a.m[2][0]; m[1][1] = m[1][0]*a.m[0][1] + m[1][1]*a.m[1][1] + m[1][2]*a.m[2][1]; m[2][1] = m[1][0]*a.m[0][2] + m[1][1]*a.m[1][2] + m[1][2]*a.m[2][2]; m[0][2] = m[2][0]*a.m[0][0] + m[2][1]*a.m[1][0] + m[2][2]*a.m[2][0]; m[1][2] = m[2][0]*a.m[0][1] + m[2][1]*a.m[1][1] + m[2][2]*a.m[2][1]; m[2][2] = m[2][0]*a.m[0][2] + m[2][1]*a.m[1][2] + m[2][2]*a.m[2][2]; } /******************************************************************************* Multiply this vector by a matrix33f *******************************************************************************/ void Vector3f::MultiplyThis33f(Matrix33f& mat) { xyz[0] = mat.m[0][0]*xyz[0] + mat.m[0][1]*xyz[1] + mat.m[0][2]*xyz[2]; xyz[1] = mat.m[1][0]*xyz[0] + mat.m[1][1]*xyz[1] + mat.m[1][2]*xyz[2]; xyz[2] = mat.m[2][0]*xyz[0] + mat.m[2][1]*xyz[1] + mat.m[2][2]*xyz[2]; } 0 Share this post Link to post Share on other sites
jyk 2095 Report post Posted April 6, 2007 Only have time for a quick look at the moment, but here:m[0][0] = m[0][0]*a.m[0][0] + m[0][1]*a.m[1][0] + m[0][2]*a.m[2][0];m[1][0] = m[0][0]*a.m[0][1] + m[0][1]*a.m[1][1] + m[0][2]*a.m[2][1];m[2][0] = m[0][0]*a.m[0][2] + m[0][1]*a.m[1][2] + m[0][2]*a.m[2][2];m[0][1] = m[1][0]*a.m[0][0] + m[1][1]*a.m[1][0] + m[1][2]*a.m[2][0];m[1][1] = m[1][0]*a.m[0][1] + m[1][1]*a.m[1][1] + m[1][2]*a.m[2][1];m[2][1] = m[1][0]*a.m[0][2] + m[1][1]*a.m[1][2] + m[1][2]*a.m[2][2];m[0][2] = m[2][0]*a.m[0][0] + m[2][1]*a.m[1][0] + m[2][2]*a.m[2][0];m[1][2] = m[2][0]*a.m[0][1] + m[2][1]*a.m[1][1] + m[2][2]*a.m[2][1];m[2][2] = m[2][0]*a.m[0][2] + m[2][1]*a.m[1][2] + m[2][2]*a.m[2][2];It looks like you're overwriting the elements of m as you go, which means the results of the operation will be more or less meaningless (this may be why your objects are being scaled as they rotate).To correct this, you'll need to store the result of the multiplication in a temporary matrix, and then assign this matrix to m (or just store individual elements as needed).As for constructing a matrix to rotate one vector about another, it's not necessary to compose such a matrix from individual rotations. A Google search for 'axis angle matrix' should turn up plenty of info on the correct (or at least most straightforward) way to do this.Here's some code you can use as a reference:/** Build a rotation matrix from an axis-angle pair */template < typename E, class A, class B, class L, class VecT > voidmatrix_rotation_axis_angle(matrix<E,A,B,L>& m, const VecT& axis, E angle){ typedef matrix<E,A,B,L> matrix_type; typedef typename matrix_type::value_type value_type; /* Checking */ detail::CheckMatLinear3D(m); detail::CheckVec3(axis); identity_transform(m); value_type s = std::sin(angle); value_type c = std::cos(angle); value_type omc = value_type(1) - c; value_type xomc = axis[0] * omc; value_type yomc = axis[1] * omc; value_type zomc = axis[2] * omc; value_type xxomc = axis[0] * xomc; value_type yyomc = axis[1] * yomc; value_type zzomc = axis[2] * zomc; value_type xyomc = axis[0] * yomc; value_type yzomc = axis[1] * zomc; value_type zxomc = axis[2] * xomc; value_type xs = axis[0] * s; value_type ys = axis[1] * s; value_type zs = axis[2] * s; m.set_basis_element(0,0, xxomc + c ); m.set_basis_element(0,1, xyomc + zs); m.set_basis_element(0,2, zxomc - ys); m.set_basis_element(1,0, xyomc - zs); m.set_basis_element(1,1, yyomc + c ); m.set_basis_element(1,2, yzomc + xs); m.set_basis_element(2,0, zxomc + ys); m.set_basis_element(2,1, yzomc - xs); m.set_basis_element(2,2, zzomc + c );} 0 Share this post Link to post Share on other sites
David Neubelt 866 Report post Posted April 6, 2007 Quote:Original post by jyk....It looks like you're overwriting the elements of m as you go, which means the results of the operation will be more or less meaningless (this may be why your objects are being scaled as they rotate).I remember getting bit by that same bug on my first time implementing matrix operations in code. Don't feel bad :)Though, as far as debugging math problems you should take a different approach to solving this problem. I believe if you did you would have figured this out on your own very quickly.1) Determine the problem - You knew the problem already so this was easy. The problem was that after a rotation the sphere scales2) Find a simple test case - Well, a simple test case would be to multiply a point, say at (1,0,0) by a rotation of 90 degree about the z-axis. We know in our head the result of this operation to be <0,1,0>. 3) Show each step on paper - This would be to calculate all the steps of the matrix multiply one line at a time.4) Debug the simple test case - You already have the correct answer on paper so stepping through the problem in the debugger one line at a time would have shown the case where the values were being stomped.Good luck!-= Dave 0 Share this post Link to post Share on other sites
PRO_Muffy 122 Report post Posted April 7, 2007 Jyk, you're an absolute star! I knew it was somewhere in my algorithm. I had actually worked it out for a 30 degree rotation on paper, but couldnt work out what was causing it to go wrong. I didnt check the individual values during the multiplication which with hindsight was a bit daft. I think I'd been staring at it for too long. I wrote the "MultiplyThis" functions into my vector and matrix classes more recently and completely forgot the issue of temporary variables. They werent actually necessary either. I changed one line in my code and it now has the desired effect.In the Rotate() function...Quote:temp.MultiplyThis33f(rotationMatrixZ);becomes Quote:temp = temp.Multiply33f(rotationMatrixZ);Multiplying the X, Y and Z matrix now gives rotation around all three axes. And the same rotation matrix can be used for the vertex normals so the lighting model is correct as well now.I'm aware that rotation can be done all in one calculation and the code you posted is useful David. I can understand how it works, and I say I understand the principle of matrices and quaternions for rotation. I just tried to break the problem down into something simpler to solve it.Awesome guys. You made my Easter! I hope the easter bunny treats you both. :) 0 Share this post Link to post Share on other sites