matrix multiplication - non-commutativity (solved)

Started by
21 comments, last by Staffan E 19 years, 5 months ago
thanks to Mushu my ships are now translated (moved) properly, and moving as well. However, either way i multiply the matrices, some ships are moving sideways... Which is not my intention (well, yet anyway - i do know about strafing :)). All ships should simply move forward, and while they do move, it's not forward. It's something i have to look into, but first i need to know if i'm doing matrix multiplication right, but just wanted to give a little background info. As for the order, all i know is to rotate first while the object's on the origin, then translate (move) it to the proper position. An example of matrix multiplication (that i use) follows.

void CFlightShip::GetMatrix(D3DXMATRIX* mat){
	// Advanced 3D GAME Programming with DirectX 9.0
	// page 166 - 167 'black box'
	//... except, NOT!
// Again, not sure what i meant with above comments...

	D3DXMATRIX matRot1;
	D3DXMATRIX matRot2;
	D3DXMATRIX matMove;
	D3DXMatrixIdentity(mat);
	D3DXMatrixTranslation(&matMove, m_Pos.x, m_Pos.y, m_Pos.z);

	D3DXMatrixRotationX(&matRot1, atan2f(m_Dir.y, m_Dir.z));
	D3DXMatrixRotationY(&matRot2, atan2f(m_Dir.z, m_Dir.x));

	D3DXMatrixMultiply(mat, &matRot1, &matRot2);

	D3DXMatrixRotationAxis(&matRot1, &m_Dir, atan2f(m_Dir.y, m_Dir.x));

//	D3DXMatrixMultiply(mat, &matRot1, mat);
//	D3DXMatrixMultiply(mat, &matMove, mat);
	D3DXMatrixMultiply(mat, mat, &matRot1);
	D3DXMatrixMultiply(mat, mat, &matMove);
}


As you can see in the last 4 lines i'm not sure which i'm supposed to do. First, mat = rotX * rotY. Next i rotate around the direction axis (for rotZ) because the book (in the comments) said something about losing a degree of freedom in some bizarre cases. I admit i don't fully understand it but i did manage to find some examples, as well as find this function which no doubt was made to fix that. So next, i do mat = oldMat * rotZ = rotX * rotY * rotZ. I'm not sure but i don't think order matters in rotations. Finally: mat = oldmat * move = rotX * rotY * rotZ * move. I guess i'm asking, is that the right order? -- Different from above example -- Also, wouldn't D3DXMatrixIdentity(mat); D3DXMatrixMultiply(mat, mat, &matRot1); D3DXMatrixMultiply(mat, mat, &matMove); be the same as D3DXMatrixIdentity(mat); D3DXMatrixMultiply(mat, &matMove, mat); D3DXMatrixMultiply(mat, &matRot1, mat); (except of course the first being more clear - or at least i think so). As a sidenote (as i said above i woulnd't mind looking into it myself first, but) Mushu said my CFlightShip::UpdatePos function was 'borked' :) So, i wouldn't mind having it double-checked... (it looks fine to me but if the problem's not in the matrix multiplications above this would be my next guess).

void CFlightShip::UpdatePos(DWORD dwTimeElapsed){
//	New position = old position + (direction * distance moved)
	m_Pos = m_Pos + m_Dir * ((float)dwTimeElapsed/1000.0f * m_fVel);
}


[Edited by - kVandaele on November 6, 2004 5:20:29 PM]
Advertisement
it's been 3 weeks... did i perhaps ask the wrong question?
Umm... in the last two lines, you're storing the result of the multiplication into one of the arguments. The documentation doesn't say explicitly that you can't do this, but it's something I'd be wary of.

Try using another temporary matrix instead of the D3DXMultiply(mat, mat, ***) calls?
well i've never seen it done otherwise, but for good measure let me try it out...

void CFlightShip::GetMatrix(D3DXMATRIX* mat){	// Advanced 3D GAME Programming with DirectX 9.0	// page 166 - 167 'black box'	//... except, NOT!	D3DXMATRIX temp1;	D3DXMATRIX temp2;	D3DXMATRIX matRot1;	D3DXMATRIX matRot2;	D3DXMATRIX matMove;	D3DXMatrixIdentity(mat);	D3DXMatrixTranslation(&matMove, m_Pos.x, m_Pos.y, m_Pos.z);	D3DXMatrixRotationX(&matRot1, atan2f(m_Dir.y, m_Dir.z));	D3DXMatrixRotationY(&matRot2, atan2f(m_Dir.z, m_Dir.x));//	D3DXMatrixMultiply(mat, &matRot1, &matRot2);	D3DXMatrixMultiply(&temp1, &matRot1, &matRot2);	D3DXMatrixRotationAxis(&matRot1, &m_Dir, atan2f(m_Dir.y, m_Dir.x));//	D3DXMatrixMultiply(mat, &matRot1, mat);//	D3DXMatrixMultiply(mat, &matMove, mat);//	D3DXMatrixMultiply(mat, mat, &matRot1);	D3DXMatrixMultiply(&temp2, &temp1, &matRot1);//	D3DXMatrixMultiply(mat, mat, &matMove);	D3DXMatrixMultiply(mat, &temp2, &matMove);}


I can't positively say it's the exact same effect, but it didn't fix the problem... Ships are most certainly still not moving forward...
Order _does_ matter in rotation matrix concatenation. Think of the case that you have a ship that you want to rotate pi/4 around Y and also pi/4 around X. If you did it in the order Y then X you would first turn the ship pi/4 and then you would drive a needle through it where the X-axis currently is (which is diagonally through the ship if looking from above) and then rotating around that. The ship would be tilting in a way that probably wasn't the intention.
If you did it the other way around you would first pitch the ship around the X-axis pi/4 so it would be facing slightly downward, then drive a needle from above along with the Y-axis and spin it pi/4 around that one. Think about it and try to visualise it in front of you with your hand. The results are clearly not the same. Maybe none of them are what you intended.

The big problem with complex matrix rotation is that when you make the first rotation you rotate the local coordinate system along with the model. Then when doing the next rotation you still rotate along the global coordinate system.

Most times when using complex rotation, try to use the D3DMatrixRotationAxis() as much as possible, it can even be useful to have a set of three vectors that go along with each model that make up the local coordinate system base. Just remember to rotate them along with the model all the time.

Sorry, guess it didn't answer your question about ships moving errantly though...
Hack my projects! Oh Yeah! Use an SVN client to check them out.BlockStacker
Um... have you tried reversing the order of all multiplications alltogether...?
If D3D express vectors as one-row matrices or one-column matrices (dont remember which), it has effect on how all matrices are finally applied to the vector. I dont know, but it might be worth a shot.
Hack my projects! Oh Yeah! Use an SVN client to check them out.BlockStacker
reversing the multiplications doesn't work... but i do see now that order matters in rotations, and why... I suspect that's where (perhaps only part) of the problem lays...

It took me a while to figure out how exactly to do these rotation with the axis thing so i'll try and figure it out but it will take a while. Any help is much appreciated...

... so basically rather than
D3DXMatrixRotationY(&matRot2, atan2f(m_Dir.z, m_Dir.x));

i need something along the lines of
D3DXMatrixRotationAxis(&matRot1, &XDIR, atan2f(m_Dir.z, m_Dir.x));
where XDIR means the axis created by the xrotation...
Hmm... maybe I'm just complicating your problem further...

But there is something fishy about the two rotation matrices you create, though...

D3DXMatrixRotationX(&matRot1, atan2f(m_Dir.y, m_Dir.z));
D3DXMatrixRotationY(&matRot2, atan2f(m_Dir.z, m_Dir.x));

First you rotate the mesh around the X-axis by

angle = atan(m_Dir.y / m_Dir.z)

This can't be right since, if m_Dir viewed from above is perpendicular to the ship (points left or right) then m_Dir.z = 0 and a division by zero will occur. Even if you handle that by an if (m_Dir.z == 0) the calculation will only end up correct if m_Dir.x = 0. Otherwise it will only rotate to match a projection of m_Dir on the Y-Z-plane, not the actual m_Dir vector.

To cover for this you could begin rotate around the Y-axis by the angle:

angle = -atan(m_Dir.x / m_Dir.z)
(minus since D3D is left handed)

and cover explicitly for the case if m_Dir.z = 0.
Then you have got a mesh that, viewed from above, is aligned to m_Dir. Rotating around X now should align it to m_Dir completely. But Just rotating around the X now would be an error since global X doesn't match local X after the Y rotation. To find the local X-axis:

LocalXVector = D3DXVec3Cross(&m_Dir, &D3DXVECTOR(0.0f, 1.0f, 0.0f));

The angle to rotate is:

angle = atan(m_Dir.y / sqrt(m_Dir.x*m_Dir.x + m_Dir.z*m_Dir.z));

Then rotate:

D3DMatrixRotationAxis(&matRot2, &LocalXVector, angle);

(Maybe it's -angle in the last parameter... try it out...)

This should by my knowledge of linear algebra cover for all cases of m_Dir except if it points straight up or down along the global Y-axis... I guess youll have to test for that too... or just keep it in mind.

Dont know if this solves your movement problem still... but...
Hack my projects! Oh Yeah! Use an SVN client to check them out.BlockStacker
before answering your post, i have attempted (and failed) to fix the problem according to what i think your explanation was:

I have marked the changes with comments
void CFlightShip::GetMatrix(D3DXMATRIX* mat){	// Advanced 3D GAME Programming with DirectX 9.0	// page 166 - 167 'black box'	//... except, NOT!	D3DXMATRIX matRot1;	D3DXMATRIX matRot2;	D3DXMATRIX matMove;	D3DXMatrixIdentity(mat);	D3DXMatrixTranslation(&matMove, m_Pos.x, m_Pos.y, m_Pos.z);	D3DXMatrixRotationX(&matRot1, atan2f(m_Dir.y, m_Dir.z));//	D3DXMatrixRotationY(&matRot2, atan2f(m_Dir.z, m_Dir.x));// above has been removed and the 2lines below replaces that functionality	D3DXVECTOR3 tempVec3 = D3DXVECTOR3(0, m_Dir.y, m_Dir.z);	D3DXMatrixRotationAxis(&matRot2, &tempVec3, atan2f(m_Dir.z, m_Dir.x));// end replacement, below is unchanged	D3DXMatrixMultiply(mat, &matRot1, &matRot2);	D3DXMatrixRotationAxis(&matRot1, &m_Dir, atan2f(m_Dir.y, m_Dir.x));//	D3DXMatrixMultiply(mat, &matRot1, mat);//	D3DXMatrixMultiply(mat, &matMove, mat);	D3DXMatrixMultiply(mat, mat, &matMove);	D3DXMatrixMultiply(mat, mat, &matRot1);}

note that this is probably different, but it does not fix anything.


Now for your reply. Indeed y/x is undefined if x = 0. I am however always cautious about things like these... In the documentation of the atan2f function, under remarks, this is listed:
Quote:atan2 calculates the arctangent of y/x. atan2 is well defined for every point other than the origin, even if x equals 0 and y does not equal 0.

The m_Dir vector is normalised... but since it uses only 2 points i suppose it could at some point be 'the origin'?

I think for now i will try out your equations and wonder later how they work.

This is what i came up with:
void CFlightShip::GetMatrix(D3DXMATRIX* mat){	D3DXMATRIX matRot1;	D3DXMATRIX matRot2;	D3DXMATRIX matMove;	D3DXMatrixIdentity(mat);	D3DXMatrixTranslation(&matMove, m_Pos.x, m_Pos.y, m_Pos.z);/*	D3DXMatrixRotationX(&matRot1, atan2f(m_Dir.y, m_Dir.z));//	D3DXMatrixRotationY(&matRot2, atan2f(m_Dir.z, m_Dir.x));				// OPTION ONE RotY	D3DXVECTOR3 tempVec3 = D3DXVECTOR3(0, m_Dir.y, m_Dir.z);				// OPTION TWO RotY	D3DXMatrixRotationAxis(&matRot2, &tempVec3, atan2f(m_Dir.z, m_Dir.x));	// OPTION TWO RotY*/ // Replaced code, with original Y Rotation and my second attempt// Following is your solution	D3DXMatrixRotationY(&matRot1, -atan(m_Dir.x / m_Dir.z));	D3DXVECTOR3 tempVec3;	D3DXVec3Cross(&tempVec3, &m_Dir, &D3DXVECTOR3(0.0f, 1.0f, 0.0f));	D3DXMatrixRotationAxis(&matRot2, &tempVec3, atan(m_Dir.y / sqrt(m_Dir.x*m_Dir.x + m_Dir.z*m_Dir.z)));// code below is unmodified	D3DXMatrixMultiply(mat, &matRot1, &matRot2);	D3DXMatrixRotationAxis(&matRot1, &m_Dir, atan2f(m_Dir.y, m_Dir.x));	D3DXMatrixMultiply(mat, mat, &matRot1);	D3DXMatrixMultiply(mat, mat, &matMove);}


I'll now investigate your code/solution, but it did not work (no matter the signs of both angles).
The atan2f() function obvoisly seems superior to atan(). I didn't use it since I'm not familiar with it. I actually looked it up in the Win32SDK and D3DSDK but I didn't find it so I couldn't know.

If you replace all my ugly atan()s with atan2f()s will it generate a better result?

What kind of result do you get. Is it random transformation or does the object show at all?


I've gone through the calcs a few more times and apart from the atan issue it should work. I don't have a project set up to test it myself.

I guess it's a bit hard to follow without the aid of illustrations. I have some at my desk here beside me but I have no means to publish them. Sorry.

If you want I could try to explain it by word in some more detail...
Hack my projects! Oh Yeah! Use an SVN client to check them out.BlockStacker

This topic is closed to new replies.

Advertisement