0
\$\begingroup\$

i'm trying to develope a simple 3d cube in directX that can be rotated in any direction by dragging the mouse.

It works in the following way: i drag the mouse to the right or left, and the cube rotates along the Y axis accordingly. The same goes if i drag the mouse up or down (in this case the rotation is along the X axis).

The problem occurs if i rotate my cube 180 degree on X-axis. After this, every rotation along the Y-axis is in the opposite direction, so that if i try to move it towards positive Y-axis it moves in negative Y-axis.

I understand it has something to do with the way i do the transformations, but i can't fix the problem on my own.

D3DXMatrixRotationY(&MatRotY, rotation.y);
D3DXMatrixRotationX(&MatRotX, rotation.x);
D3DXMatrixTranslation(&MatTran, position.x, position.y, position.z);

D3DXMatrixMultiply(&MatRotXY, &MatRotY, &MatRotX);
D3DXMatrixMultiply(&MatRotXY, &MatRotXY, &MatTran);
D3DXMatrixMultiply(&worldMatrix, &worldMatrix, &MatRotXY);

PS: i know i shouldnt be using D3DX at all, but i'd like to solve this problem before having to switch to a different library.

thanks

\$\endgroup\$
  • \$\begingroup\$ Likely you are hitting an issue of gimbal lock with Euler angles. Quaternions are the usual solution here. Since you are using a mouse to do the rotation, an arc ball is the traditional way to do it. \$\endgroup\$ – Chuck Walbourn Feb 20 '16 at 7:23
  • \$\begingroup\$ thanks for pointing me in the direction of arc balls, i'll investigate the subject. \$\endgroup\$ – rekotc Feb 20 '16 at 15:56
0
\$\begingroup\$

For every matrix rotation, there is an axis and an angle. You are trying to marry your rotations to your 3d world's basis axes and applying Euler angles about them. That can invite issues when one of your cube's local axis is not aligned with the world axis and you want to rotate your cube around it or a 2nd world axis.

So the root of your problem is that you are storing your cube's rotation as Euler angles to access and modify the rotation next frame. There are various ways to store a rotation; Euler angles, matrix, quaternion, axis/angle... etc. For what you are doing, Euler angles does not lend itself as the best way. If your were to choose a Matrix to store your cube's orientation/position, you could simply store the rotation in the worldMatrix and do not use rotation anymore.


In your process, you are taking two identity matrices and applying an x rotation to one and a y rotation to the other then trying to combine them.

But what if you didn't start with two identify matrices? Why not simply apply the current frame's mouse x movement to last frame's worldMatrix directly and right after that take the current frame's mouse y movement and apply that to the "just modified" worldMatrix directly. All rotations will be as expected. The only thing is that you now have no idea what the overall rotation.X, rotation.Y is because you aren't modifying rotation.X & Y directly, your modifying worldMatrix directly. Hopefully the rest of your code can be OK with that. Trying to hang onto those rotation values is leading you down an unnecessarily complex path.

All you need to determine to do this is what axis to rotate the cube/worldMatrix around for each rotation and how much.

Since your rotations are tied to your mouse movements and the 3d world involvement of the mouse has to do with camera orientation and position, the rotational axes that you want your cube/worldMatrix to rotate about are actually stored in the view matrix.

//pseudo code

//extract mouse movement for this frame
float mouseX = input.GetMouseXMovementSinceLastFrame();
float moustY = input.GetMouseYMovementSinceLastFrame();
//scale mouseX & Y if necessary for your application

//get rotation axis
Matrix camWorld;
d3dxMatrixTranspose(&camWorld, &view);
d3dxVector3 xAxis;
d3dxVector3 yAxis;

xAxis.X = camWorld._11;
xAxis.Y = camWorld._12;
xAxis.Z = camWorld._13;

yAxis.X = camWorld._21;
yAxis.Y = camWorld._22;
yAxis.Z = camWorld._23;


//set worldMatrix to origin for rotation purposes but save position info
d3dxVector3 cubePosition;
cubePosition.X = worldMatrix._41;
cubePosition.Y = worldMatrix._42;
cubePosition.Z = worldMatrix._43;
worldMatrix._41 = 0;
worldMatrix._42 = 0;
worldMatrix._43 = 0;

//apply rotations

d3dxMatrixRotationAxis(&worldMatrix, yAxis, mouseX);
d3dxMatrixRotationAxis(&worldMatrix, xAxis, mouseY);
d3dxMatrixTranslation(&worldMatrix, cubePosition);

Now the worldMatrix has had rotations applied to it that are relative to the camera's vertical and horizontal axes and no matter what position the camera is at or what rotational orientation it has while looking at your cube, the cube will always rotate as expected. You just lost the overall rotation of your cube is relative to Euler angles. But that does not matter if the only reason you kept Euler angles anyway was to get worldMatrix orientated correctly, because this does that in a simpler fashion for this particular application.

If your camera's position and orientation were locked and never moved, you could calculate the rotation axes once and reuse them each frame simplifying this even more.

\$\endgroup\$
  • \$\begingroup\$ thanks for this perfect explanation! I was able to fix my problem. You were right, i was storing the cube's rotation as Euler angles to access and modify the rotation every frame. What i'm still trying to understand is why it didn't work by using Euler angles, i guess i need to read a bit more about it. Thank you very much again \$\endgroup\$ – rekotc Feb 20 '16 at 15:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.