Hito
Hito

Reputation: 803

Moving in 3D world - OpenGL

So I am currently working on some game elements including some engine-like stuff in OpenGL and I have come to this problem. (dealing with 3D world) I am getting the angles of rotation in the world by mouse, like this:

int prevX = mouse.x;
int prevY = mouse.y;

GetCursorPos(&mouse);

cam.xRot -= atan(prevX-mouse.x);
cam.yRot -= atan(prevY-mouse.y);

Seems working good. (the xRot is in fact meant like rotation around Y axis and yRot around x axis) Camera rotation is good. Then I started to work on movement relative to the camera. After some drawings and desperate attempts I came to this:

//W
if (keys[57])
       {
           cam.z += cos(angleY)*cos(angleX)*cam.speed;
           cam.x -= cos(angleY)*sin(angleX)*cam.speed;
           cam.y += sin(angleY);
       }
//S
if (keys[53])
       {
          cam.z -= cos(angleY)*cos(angleX)*cam.speed;
          cam.x += cos(angleY)*sin(angleX)*cam.speed;
          cam.y -= sin(angleY);
       }
//A
if (keys[41])
       {
          cam.z += sin(angleX)*cam.speed;
          cam.x += cos(angleX)*cam.speed;
       }
//D
if (keys[44])
       {
          cam.z -= cos(angleY)*sin(angleX)*cam.speed;
          cam.x -= cos(angleY)*cos(angleX)*cam.speed;
       }

It works too but the problem is when I rotate the camera around z axis (the depth one). I tried some combinations etc but nothing seems to be working. What am I missing? I made the equations above like this: When I take it as a 2D just x and z and I want to go forward with camera rotated by zero angle I need to move only in the z axis direction, in a positive way so let's use a function with it's maximum there => cos. When I rotate by 90 and need to move forward I need to move on x axis so let's use sin cause we need the opposite. That handle's 2D well. Now I needed to add z axis so I though that maybe just restrict the values of x and y by cos of the second angle (yRot or angle of rotation around x if you want) and make sin of it the actual movement on y axis. Yea working good but as I said problems when I rotate around z and basically switch the y and x axis. What to do with it? :D

Oh by the way I have read some other tutorials and I noticed that some people use a different ways with vectors etc. What is a common way in today's games? I don't really like using goniometric functions so often. Maybe my approach is not the best one huh?

Upvotes: 1

Views: 2091

Answers (1)

Nico Schertler
Nico Schertler

Reputation: 32647

When it comes to camera management, most people seem to make it far more difficult than it had to be. The key point is matrix math.

Consider an object with world matrix W, which consists of a rotation and a translation part. If we want to move this object to its right, we can just multiply an according translation matrix to W (note that all further explanations assume the OpenGL notation with column vectors and column-major matrices):

W* = W * T(x, 0, 0)

The important thing is the multiplication order. If you multiply the translation matrix to the right side, it will move the object in the object's local coordinate system. If you multiply it to the left side, it will move it in the global coordinate system. Similarly, we can multiply a rotation matrix to rotate the object in its local coordinate system:

W** = W* * Rx(phi)

Now, the view matrix of camera's is a bit trickier because it is actually an inverted matrix. So if you have a view matrix V, you have to multiply the inverse matrices to the left side:

V*  = T(-x, 0, 0) * V
V** = Rx(-phi) * V*
//...

One problem of this, though, is the fixture of the up-vector. If you perform rotations about the local y axis, the up-direction might get changed. We could calculate an adapted matrix, which avoids this, but just re-calculating the entire rotation matrix from the two angles is probably much easier. After re-calculating the matrix, you must re-position it at the old position (global position, so multiply a translation matrix to the left). Calculating the old position requires a bit of math (because the matrix is inverted):

pos = -tx * first row - ty * second row - tz * third row
where tx is the entry in the first row, last column
      ty is the entry in the second row, last column
      tz is the entry in the third row, last column

Summary

If you want to maintain a flexible camera representation, store the view matrix and the two rotation angles. If you want to move the camera, just multiply a translation matrix. If you want to rotate the camera about the local x-axis, change the angle and multiply the rotation matrix. If you want to rotate about the local y-axis, change the angle and re-calculate the entire matrix, including a repositioning.

Your current solution basically does this because the trigonometric results are exactly the entries of the matrix at the relevant positions. But as you see, it is quite cumbersome to maintain and if you want to allow a third rotation, you would have to change the entire code.

Embrace matrix maths!

Upvotes: 1

Related Questions