Jump to content
  • Advertisement
Sign in to follow this  
slowmike

camera tutorial that allows full flips on x-axis?

This topic is 3563 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

C++ / DirectX Hi, I'm looking for a solid camera tutorial or class code that allows FULL rotation on all axis. I'm working on a game set in space and the only Up or Down direction is in relation to my ship(camera). I'm looking for answers to my problem below. By myself, I got it so I'm able to rotate left/right in complete circles. Or I can rotate up/down in complete circles. But for some reason, after rotating left/right, the up/down rotations become wacked. When the camera gets near straight up/down, it sort of spins 180 degrees. When the up/down rotations work properly (before rotating left or right), I'm starting from camPos=(0,0,0) camAim = (0,0,10).

Share this post


Link to post
Share on other sites
Advertisement
You need to do LOCAL rotations, not GLOBAL ones.
This means instead of making 3 rotation matricies as axis-angle around (1,0,0) (0,1,0) and (0,0,1), you need local rotations.
I assume you have:
camMat = rotMatX * rotMatY * rotMatZ;

What you need is(assuming you still want controll of YawPitchRoll speeds:

rot_camUp = RotationMatrix_FromAxisAngle(camMatPrev.up, yaw);
camMat = camMatPrev * rot_camUp;
rot_camLeft = RotationMatrix_FromAxisAngle(camMat.at, pitch);
camMat = camMat * rot_camLeft;
rot_camAt = RotationMatrix_FromAxisAngle(camMat.at, roll);
camMat = camMat * rot_camAt;

Now, you can simplify this to something like this if you just want FPS type mouse control:

newcamat = cam.at + cam.up * mouse_dy + cam.left * -mouse_dx;
newcamat = normalize(newcamat);
rotAxis = cross(cam.at, newcamat);
rotAngle = ACOS(dot(cam.at,newcamat));
camMat = camMatPrev * RotationMatrix_FromAxisAngle(rotAxis, rotAngle);


I'm sure SOMEONE is going to say "quaternions solve gimblelock" it always happens. A quaternion based camera doesn't solve your problem if you still use GLOBAL rotations. The key is using incremental LOCAL rotations (which can be represented as quaternions if you want to be able to use the SLERP function to smooth out your rotation).

Share this post


Link to post
Share on other sites
Quote:
After I read-up on quaternions, I'll try to get this to work in my program.
Just in case there's some confusion here, KulSeran was saying that you don't need to use quaternions in order to implement the kind of camera you have in mind. It can all be done directly with matrices (or even just vectors).

Share this post


Link to post
Share on other sites
I've implemented a camera class that allows full rotations along all axes, as well as movement. It has some accessor methods forgetting the angles so that you can determine how to move the camera. I'm only posting te main part, but if anyone needs/wants the rest, just let me know via this thread, email (cameron.alan.kelley@gmail.com), or a private message.


void Camera::SetViewport()
{
D3DXMATRIX d3dCameraRotation;
D3DXMatrixRotationYawPitchRoll( &d3dCameraRotation, yawAngle, pitchAngle, rollAngle );

D3DXVECTOR3 vWorldUp, vWorldAhead;
D3DXVECTOR3 vLocalUp = D3DXVECTOR3( 0, 1, 0 );
D3DXVECTOR3 vLocalAhead = D3DXVECTOR3( 0, 0, 1 );
D3DXVec3TransformCoord( &vWorldUp, &vLocalUp, &d3dCameraRotation );
D3DXVec3TransformCoord( &vWorldAhead, &vLocalAhead, &d3dCameraRotation );

D3DXVECTOR3 lookAt = eye + vWorldAhead;

D3DXMATRIX d3dViewportMatrix;
D3DXMatrixLookAtLH( &d3dViewportMatrix, &eye, &lookAt, &vWorldUp );
d3dDevice -> SetTransform( D3DTS_VIEW, &d3dViewportMatrix );
}



yawAngle is the angle between the z axis and x axis, pitchAngle is the angle between the y axis and z axis, and roll axis is the angle between the y axis and x axis.

The application should not call SetViewport directly: instead, any methods which update the camera's position or rotation should call it.

To maintain modularity, the camera class is in charge of all camera operations, so setting up the projection matrix is also included.

If anyone is interested in movement of the camera along the x-z-plane only, here is the code I am using.


float mag = 5.0f * seconds;

if( keys[DIK_UP] & 0x80 )
{
float dX = mag * sin( camera -> GetYaw() );
float dZ = mag * cos( camera -> GetYaw() );
camera -> Move(dX, 0.0f, dZ);
}
else if( keys[DIK_DOWN] & 0x80 )
{
float dX = mag * sin( camera -> GetYaw() + D3DX_PI );
float dZ = mag * cos( camera -> GetYaw() + D3DX_PI );
camera -> Move(dX, 0.0f, dZ);
}

if( keys[DIK_LEFT] & 0x80 )
{
float dX = mag * sin( camera -> GetYaw() - (D3DX_PI / 2) );
float dZ = mag * cos( camera -> GetYaw() - (D3DX_PI / 2) );
camera -> Move(dX, 0.0f, dZ);
}
else if( keys[DIK_RIGHT] & 0x80 )
{
float dX = mag * sin( camera -> GetYaw() + (D3DX_PI / 2) );
float dZ = mag * cos( camera -> GetYaw() + (D3DX_PI / 2) );
camera -> Move(dX, 0.0f, dZ);




Move updates the x, y, and z coordinates by the float supplied, and then calls SetViewport();

Share this post


Link to post
Share on other sites
Great. Thanks for the replies.

Ok. I've got everything working fine, except that I need a clean way to calculate an upVector for my camera to be used in CalcViewMatrix(). Simply using upVec(0,1,0) is no good for what I'm doing. So, given two points (camPos and camLookAt) is there a function for obtaining a vector at a 90 degree angle that I could use for my upVector?

Share this post


Link to post
Share on other sites
Quote:
Original post by slowmike
Great. Thanks for the replies.

Ok. I've got everything working fine, except that I need a clean way to calculate an upVector for my camera to be used in CalcViewMatrix(). Simply using upVec(0,1,0) is no good for what I'm doing. So, given two points (camPos and camLookAt) is there a function for obtaining a vector at a 90 degree angle that I could use for my upVector?
If you've already (correctly) implemented a 6DOF camera, you should already have the up vector available.

A position and 'look-at' point do not uniquely define an orientation, so if your camera is actually 6DOF, then you must be storing some information in addition to these two points. If you can tell us what that info is, we can tell you how to compute the up vector.

Share this post


Link to post
Share on other sites
Quote:
Original post by jyk
Quote:
Original post by slowmike
Great. Thanks for the replies.

Ok. I've got everything working fine, except that I need a clean way to calculate an upVector for my camera to be used in CalcViewMatrix(). Simply using upVec(0,1,0) is no good for what I'm doing. So, given two points (camPos and camLookAt) is there a function for obtaining a vector at a 90 degree angle that I could use for my upVector?
If you've already (correctly) implemented a 6DOF camera, you should already have the up vector available.

A position and 'look-at' point do not uniquely define an orientation, so if your camera is actually 6DOF, then you must be storing some information in addition to these two points. If you can tell us what that info is, we can tell you how to compute the up vector.


Good point. Ok, so now everything works. My camera's upVector is already what it should be. My real problem now is that when I want to rotate my camera left/right I alter the value of Yaw, but that only rotates around the Y axis. Either I still have a local/global problem that I'm not aware of, or I need to find out how much to alter Pitch and Roll in addition to the Yaw. Because right now, if I'm looking almost straight up, and try to rotate, I basically just spin around a point in the imaginary ceiling of my world.

I've basically borrowed Brain me's code snippet to try and get this working, so here's what I've got. (Thanks btw)


// CalcViewMatrix()
D3DXMATRIX camRot;
D3DXMatrixRotationYawPitchRoll(&camRot, m_camYawAngle, m_camPitchAngle, m_camRollAngle);

D3DXVECTOR3 worldUp;
D3DXVECTOR3 worldAhead;
D3DXVECTOR3 localUp = D3DXVECTOR3(0,1,0);
D3DXVECTOR3 localAhead = D3DXVECTOR3(0,0,1);

D3DXVec3TransformCoord(&worldUp, &localUp, &camRot);
D3DXVec3TransformCoord(&worldAhead, &localAhead, &camRot);

D3DXVECTOR3 lookAt = m_camPos + worldAhead;

D3DXMATRIX matView;
D3DXMatrixLookAtLH(&matView, &m_camPos, &lookAt, &worldUp);
m_D3DDevice->SetTransform(D3DTS_VIEW, &matView);


Currently, if I want to aim the camera up/down, I alter m_camPitchAngle. If I want to aim the camera left/right, I alter m_camYawAngle.

Share this post


Link to post
Share on other sites
Quote:

// CalcViewMatrix()
D3DXMATRIX camRot;
D3DXMatrixRotationYawPitchRoll(&camRot, m_camYawAngle, m_camPitchAngle, m_camRollAngle);

D3DXVECTOR3 worldUp;
D3DXVECTOR3 worldAhead;
D3DXVECTOR3 localUp = D3DXVECTOR3(0,1,0);
D3DXVECTOR3 localAhead = D3DXVECTOR3(0,0,1);

D3DXVec3TransformCoord(&worldUp, &localUp, &camRot);
D3DXVec3TransformCoord(&worldAhead, &localAhead, &camRot);

D3DXVECTOR3 lookAt = m_camPos + worldAhead;

D3DXMATRIX matView;
D3DXMatrixLookAtLH(&matView, &m_camPos, &lookAt, &worldUp);
m_D3DDevice->SetTransform(D3DTS_VIEW, &matView);


Currently, if I want to aim the camera up/down, I alter m_camPitchAngle. If I want to aim the camera left/right, I alter m_camYawAngle.
What you have there is not a 6DOF camera, but rather a typical 'yaw-pitch-roll' Euler-angle camera. If you want full 6DOF motion, you'll need to do something similar to what KulSeran described earlier (i.e., update the object's orientation incrementally using local rotations).

Share this post


Link to post
Share on other sites
WooHoo! I finally got what I wanted.

// CalcViewMatrix

// Yaw rotation
D3DXMATRIX yawMatrix;
D3DXMatrixRotationAxis(&yawMatrix, &m_camUp, DEGTORAD(m_camYaw));
D3DXVec3TransformCoord(&m_camAim, &m_camAim, &yawMatrix); // Rotate look vector
D3DXVec3TransformCoord(&m_camRight, &m_camRight, &yawMatrix); // Rotate right vector

// Pitch rotation
D3DXMATRIX pitchMatrix;
D3DXMatrixRotationAxis(&pitchMatrix, &m_camRight, DEGTORAD(m_camPitch));
D3DXVec3TransformCoord(&m_camAim, &m_camAim, &pitchMatrix); // Rotate look vector
D3DXVec3TransformCoord(&m_camUp, &m_camUp, &pitchMatrix); // Rotate up vector

// Roll rotation
D3DXMATRIX rollMatrix;
D3DXMatrixRotationAxis(&rollMatrix, &m_camAim, DEGTORAD(m_camRoll));
D3DXVec3TransformCoord(&m_camRight, &m_camRight, &rollMatrix); // Rotate right vector
D3DXVec3TransformCoord(&m_camUp, &m_camUp, &rollMatrix); // Rotate up vector

// Create view matrix
D3DXMATRIX viewMatrix;
D3DXMatrixIdentity(&viewMatrix);

viewMatrix._11 = m_camRight.x; viewMatrix._12 = m_camUp.x; viewMatrix._13 = m_camAim.x;
viewMatrix._21 = m_camRight.y; viewMatrix._22 = m_camUp.y; viewMatrix._23 = m_camAim.y;
viewMatrix._31 = m_camRight.z; viewMatrix._32 = m_camUp.z; viewMatrix._33 = m_camAim.z;

viewMatrix._41 = - D3DXVec3Dot(&m_camPos, &m_camRight);
viewMatrix._42 = - D3DXVec3Dot(&m_camPos, &m_camUp);
viewMatrix._43 = - D3DXVec3Dot(&m_camPos, &m_camAim);

m_D3DDevice->SetTransform(D3DTS_VIEW, &viewMatrix);

m_camYaw = 0.0f;
m_camPitch = 0.0f;
m_camRoll = 0.0f;


I'm resetting yaw, pitch, and roll at the end of each call to CalcViewMatrix. I've got member functions altering their values based on input. And I'm keeping track of all the camera vectors that make up the matrix(at/look/up/right).

Thanks to all that replied to this thread. You've been very helpful. (I'll be doing rating bumps in a minute.)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!