Skip to main content
gamedev.net
🔒 Locked

Rotation question

Started by outlawhacker 34 replies 3.6k views
Original Post
outlawhacker
outlawhacker
Hey I'm trying out some simple rotations in Direct3D. I have a small problem when rotating my cube. I have a camera looking at the front of a cube. I want the cube to always rotate in the "same" directions. If I press up - I want the cube to rotate "into" the view. The problem is that when I perform a rotation along the y-axis and then rotate on the x-axis (or vice versa) my cube rotates wrong. This might illustrate it better: 1. ORIGINAL POSITION: (y-axis) .........Up........... Left....Front....Right (x-axis) ........Down.......... 2. ROTATING ON Y-AXIS FIRST: .........Up........... Front....Right....Back ........Down.......... 3. Error. THEN ON X-AXIS (this is what happens): .........Front..... Down....Right....Up ........Down....... 3. Correct. WHAT I WANT INSTEAD: .........Right....... Front....Down....Back ........Left......... I guess you understand my problem :). Any suggestions on how to solve this would be appreciated. Thanks
DesignerX
DesignerX
It really depends on how you create your matrices and how you set the world transformation.

Can you please tell me how you implement the rotation (especially how you set the world transformation)
There is nothing that can't be solved. Just people that can't solve it. :)
Calin
Calin
Hi there!
At first glance I would say you are not using matrix multiplication or use it in a wrong way.
A correct code for that operation should look like this:
                float rotX,rotY;        if ( button 'W' is being pressed)            rotX += 0.05;        if ( button 'D' is being pressed)            rotY += 0.05;        D3DXMATRIX matXRotation, matYRotation, matWorld;	D3DXMatrixRotationX(&matXRotation, rotX );	D3DXMatrixRotationY(&matYRotation, rotY);		D3DXMatrixMultiply(&matWorld, &matXRotation, &matYRotation);// !!!Matrix Multiplication	        g_pDevice->SetTransform(D3DTS_WORLD, &matWorld);


If you don`t understand something feel free to ask
My project`s facebook page is “DreamLand Page”
outlawhacker
outlawhacker
Hey

Thanks for the replies! Sorry for the long time not answering, I'm blaiming the weekend :P

Ok. I'm doing my rotation as Calin wrote. But the problem is when I first rotate on the y-axis and then the x-axis, the cube doesn't rotate "into" the view because the x-axis is transformed after the first rotation.

Is there someway to always perform rotation from the "original" y and x-axis (with the y-axis pointing upwards and the x-axis pointing to the right in the view), no mather how many rotations I've done before? I want the cube to rotate to the right, left, up and down an infinite amount of times (it's up to the user, of course how he/she decides to rotate). But up should always mean that the cube rotates into the view, and down should always mean that the cube rotate "towards" the viewer. Take another look at my illustration if I explain like kaka :P.

Thanks
DesignerX
DesignerX
try changing calin's code like this :

set rotX and rotY to zero.
change += to =
change the rotation functions with :

D3DXMatirxRotationYawPitchRoll(&matWorld, rotY, rotX, 0);

and change SetTransform to MultiplyTransform
There is nothing that can't be solved. Just people that can't solve it. :)
outlawhacker
outlawhacker
Sorry dude, that didn't do anything for me. MultiplyTransform sounded really nice but somehow it didn't work.

Thanks for the reply though.
wasd
wasd
K, this is just off the top of my head, so it might not work, but then again, it might.

Keep track of the Total transformation matrix.
Initialise Total to the identity matrix.

When Up / Down is pressed, initialise temporary rotation value around X to 0.
While Up / Down is still pressed, increase / decrease rotation around X.
When Up / Down is released, calculate the matrix that represents the rotation around X and combine it with the Total matrix.

Do a similar thing for Left / Right and rotation around Y.

When rendering, your matrix should be the combination of the temporary rotations and the Total matrix.
DesignerX
DesignerX
Ok, I have found a bit of an ugly way to do it.
The problem with the rotation was because of the matrix multiplication.

Try the following code :

	D3DXMATRIX matRotX, matRotY;	D3DXMatrixIdentity(&matRotX);	D3DXMatrixIdentity(&matRotY);	if (up key has been pressed)	{		D3DXMatrixRotationY(&matRotY, D3DXToRadian(0.05f));	}	if (right key has been pressed)	{		D3DXMatrixRotationX(&matRotX, D3DXToRadian(0.05f));	}        // Add code here for other keys       	YourVertexStruct* pData; 	// Get the first vertex data from vertex buffer	m_pVB->Lock(0, 0, (void**)&pData, 0); 	// Iterate through each vertex and...	for (int i = 0; i < vertices_number; i++)	{		// Copy current vertex position to vector		D3DXVECTOR3 v(pData.fX, pData.fY, pData.fZ);		// Transform vector using the created matrices (This is the key for the correct rotation you desire)		D3DXVec3TransformCoord(&v, &v, &matRotX);		D3DXVec3TransformCoord(&v, &v, &matRotY);		// Store new position to current vertex		pData.fX = v.x;		pData.fY = v.y;		pData.fZ = v.z;	}	// Unlock vertex buffer	m_pVB->Unlock();	


Make sure you set the world transformation to an identity matrix before rendering.

If something here is not making sense to you, tell me and i'll explain.
There is nothing that can't be solved. Just people that can't solve it. :)
wasd
wasd
I've just implemented it using the following code (C# code but pretty self explanatory)

private void Form1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e){        // Reset temporary values	m_TempXRotation = 0;	m_TempYRotation = 0;}private void Form1_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e){	switch(e.KeyChar)	{		case 'w':			m_TempXRotation += 0.05f;			break;		case 'a':			m_TempXRotation -= 0.05f;			break;		case 's':			m_TempYRotation += 0.05f;			break;		case 'd':			m_TempYRotation -= 0.05f;			break;	}	this.OnPaint(null);}private void Form1_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e){	// Recreate total matrix	Microsoft.DirectX.Matrix Total = m_Transform;	Total.Multiply(Matrix.RotationY(m_TempYRotation));	Total.Multiply(Matrix.RotationX(m_TempXRotation));	m_Transform = Total;	// Reset temporary values	m_TempXRotation = 0;	m_TempYRotation = 0;}// In your render function, calculate transformation like this:// Calculate total transformation matrixMicrosoft.DirectX.Matrix Total = m_Transform;Total.Multiply(Matrix.RotationY(m_TempYRotation));Total.Multiply(Matrix.RotationX(m_TempXRotation));
Kest
Kest
Take a look at D3DXMatrixRotationAxis. Use its current matrix to get the axis:
const D3DXMATRIX &cm = ObjectStateMatrix;D3DXMATRIX new_state;//To spin on y, do this:D3DXMatrixRotationAxis( &new_state, D3DXVECTOR(cm._21, cm._22, cm._23), + / - angle );//To spin on x, do this:D3DXMatrixRotationAxis( &new_state, D3DXVECTOR(cm._11, cm._12, cm._13), + / - angle );//To spin on z, do this:D3DXMatrixRotationAxis( &new_state, D3DXVECTOR(cm._31, cm._32, cm._33), + / - angle );// Add this state to the old stateObjectStateMatrix *= new_state;

Going by what you wrote, and me being half asleep, I'm not positive this is what you're trying to accomplish, but I think so. If it doesn't do what you want, try using the camera's state matrix as the direction axis instead of the object's state (cm matrix). Or rather, change this line:
const D3DXMATRIX &cm = CameraStateMatrix;
outlawhacker
outlawhacker
Ok so I'm back.

I've tried some of your solutions and I'm almost there :)

This is how I do it. I press a button: Up, Down, Right or Left. When doing so the cube rotates 90 degrees in wanted direction and stops.

A rotation function, called until rotation ends:

void CCube::RotateUp(float passedTime){	m_rotating = true;	m_rotationX += passedTime * m_speed;	        // I'm rotating 90 degrees then stop the cube	if (m_rotationX > (m_rotationXPrev + (D3DX_PI / 2.0f)))	{                // If we've rotated 360 degrees - zero the rotation		m_rotationX = (m_rotationXPrev + (D3DX_PI / 2.0f));		if (m_rotationX >= (D3DX_PI * 2.0f))			m_rotationX = 0.0f;		m_rotationXPrev = m_rotationX;		m_rotating = false;		m_animation = ROTATION_NONE;	}}



Updating the rotation every frame:

if (m_animation == ROTATION_UP || m_animation == ROTATION_DOWN){        // When doing up and down rotation - do y axis rotation first        D3DXMatrixRotationY(&m_matRotationY, m_rotationY);        D3DXMatrixRotationX(&m_matRotationX, m_rotationX);        D3DXMatrixMultiply(&m_matWorld, &m_matRotationY, &m_matRotationX );}else if (m_animation == ROTATION_LEFT || m_animation == ROTATION_RIGHT){        // When doing left and down rotation - do x axis rotation first        D3DXMatrixRotationX(&m_matRotationX, m_rotationX);        D3DXMatrixRotationY(&m_matRotationY, m_rotationY);        D3DXMatrixMultiply(&m_matWorld, &m_matRotationX, &m_matRotationY );}


The rotation is going in the right directions, but after doing some animations, the cube flips like crazy. Some values that I believe are f*cked up:

m_matWorld._11 = -4.3711388e-008
m_matWorld._22 = -4.3711388e-008

and such...Does that seem like valid values? Some values has 15 decimals, and a float can only hold 12 if I'm not mistaken.

Anybody who sees what I'm doing wrong? I've also been hinted to look up quaternions, anybody used those? I've heard they are the thing when doing 3D rotaions.

Thanks for replies



daVinci1980
daVinci1980
Hello OutlawHacker-

The easiest solution to your problem is actually to stop messing with the WORLD matrix, and start manipulating the VIEW matrix instead.

If I understand correctly, what you'd like to do is make it so that when you press UP, the cube always rolls over a horizontal axis running perpendicular to the current VIEW, regardless of what the cube's orientation in the world is.

So, the easiest way to do this is to manipulate the view matrix in this case, instead of messing with the world matrix (you can restore the original VIEW matrix after you've rendered this one object, in case you'd like to render the rest of the world without rotation).

The original issue you were having came from the fact that the axes in your world and view stopped lining up once you had rotated the cube to the left or right. Then you would continue to apply rotations against your WORLD matrix, which whould cause the object to spin around it's local axis, rather than spinning around in the camera's view.

Basically, the code you want should rotate around the world, with the cube at it's central location. The psuedocode for this would look something like this:

// Store off current view matrix
// translate view matrix by -cube.position.x, -cube.position.y, -cube.position.z
// Apply rotation in X and in Y (use D3DXMatrixRotationYawPitchRoll)
// translate view matrix by cube.position.x, cube.position.y, cube.position.z
// Render Cube
// Restore Original View Matrix


A couple of notes:
- You should store your rotation angles (in X and Y) as radians, and you should return them back to -2pi..2pi whenever they go outside that range. This will prevent the ugly popping you've seen.

- The call to D3DXMatrixRotationYawPitchRoll is hiding some complexity. Basically, the issue is that when you rotated by the first angle (let's say rotX) about the axis Y, you then could no longer rotate the object using the second angle without adjusting the axis of rotation. You would've done so by rotating the axis of rotation (if you're rotating towards Y, you would've probably been rotating around the axis X, or possibly -X). The new rotation angle would've become X'. If you're interested in determining what's going on here, try to look up and understand what's going on in cases of gimbal lock.

I hope this helps. :-)
-daVinci
Kest
Kest
I explained how to accomplish what you're trying to do. Is there any particular reason you didn't try it? If some part of it is confusing, just ask away. Switching to quaternions will not make things easier. It will just take away some of the options you have. In the end, you'll end up doing exactly what I'm telling you to do: axis rotations.
outlawhacker
outlawhacker
Hey, thanks for the replies.

I haven't had time to go through my project for a couple of days, but I did try your solution earlier Kest, and I'm sorry to tell you it gave me the exact same result. I'll try it again tomorrow I believe, or whenever I can get away from this new assignment we've been given :P

Thanks
DesignerX
DesignerX
Did you tried my solution ?

Although it requires a direct change of the vertex buffer, I think it works.
There is nothing that can't be solved. Just people that can't solve it. :)
outlawhacker
outlawhacker
Hey again.

Yes, DesignerX I've tried your solution and again I got the same result. I think this is somewhat mindblowing, since I was convinced your solution would work too. I resisted against using it because it's an ugly solution.

I must be doing something crazy I believe. I can rotate one time on the y-axis and one time on the x-axis, but then the rotation values gets crazy. It can't be that matrix rotation using Euler angles or axis rotation is so limited, can it?

Any way, this is only giving me a head ache, so I'm gonna dive into quaternions for a while and see what that'll give me.

If I find a solution to my rotation problem, I'll post it.

Thanks
Swapik
Swapik
I did not look at your question too much, but maybe is a problem with so-called gimbal lock. This may occur when you are using Euler transformations. For example, if you rotate your object 90 degrees around x-axis, then we want to rotate it around z-axis BUT this rotation will be as you are rotating around y-axis because of previous rotation. So you cannot rotate around z-axis.

The solution is: QUATERNIONS!
DesignerX
DesignerX
Well, with my approach (Ugly indeed, I agree :)) I succeeded in rotating 45 degrees around the x axis and then rotate around the y axis 45 degrees and it worked perfectly (this was automatic degree increment and I haven't tried it with control like you did)
There is nothing that can't be solved. Just people that can't solve it. :)
outlawhacker
outlawhacker
Hey.

DesignerX - are you doing rotation INTO the view? That is - rotating on the x-axis as I want it should always make the cube rotate into (or towards the player (up/down)) the view. And when rotating on the y-axis it should always make the cube rotate left/right, no mather how many rotations I've done before.

I don't know what I'm doing wrong, but obviously something is bad in my implementation. I've had several people looking at my different solutions, but noone can see what I'm doing wrong. :(

Now I've tried quaternions but with no luck. As long as I'm doing rotation only on one axis, the cube rotates nice and smooth. But when switching direction, my world matrix somehow gets, well...like cleared or something, and the cube flips to some crazy position, and then continues rotating nice on the same axis (as long as I don't change direction, of course). My rotation values looks totally fine otherwise.

This is how I do it:

void CCube::Init(){               D3DXQuaternionIdentity(&m_qRotation);}void CCube::RotateUp(float passedTime){	m_rotating = true;	D3DXQuaternionRotationAxis(&m_qRotation, &D3DXVECTOR3(1.0f, 0.0f, 0.0f), m_rotationX);	// Get resulting matrix        // This is where my world matrix is screwed after changing direction	D3DXMatrixRotationQuaternion(&m_matWorld, &m_qRotation);	m_rotationX += passedTime * m_speed;		if (m_rotationX > (m_rotationXPrev + (D3DX_PI / 2.0f)))	{		m_rotationX = (m_rotationXPrev + (D3DX_PI / 2.0f));		m_rotationXPrev = m_rotationX;		m_rotating = false;		m_animation = ROTATION_NONE;	}}


Then I'm doing SetTransform on my device as usual, with the m_matWorld matrix.

Any idea what I'm doing wrong?

Thanks
DesignerX
DesignerX
Since this thing got me very curios on how to implement it , I'll try to write a code for rotating a cube as you desire, if I'll succeed, I'll post it here.
There is nothing that can't be solved. Just people that can't solve it. :)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.