Recently, I've been having some trouble with a first person camera for a game I'm working on. The code for it came from the book
DirectX 9 Graphics: The Definitive Guide to Direct 3D by Alan Thorn.
Here's a rotating cube when the game first starts and the mouse hasn't been moved:
The problem is, when the mouse is moved in a circle... This happens.
It doesn't just break with circles, but
anything that isn't a straight line.
The following are used in the game itself to rotate the camera, calling functions from the included header file. MouseX is a float for the distance the cursor moved from the coordinates of the last calculation to the next on the x-axis, and MouseY is the same but for the y-axis.
TheCamera.RotateDown(InputData->MouseY / 200.0f);
TheCamera.RotateRight(InputData->MouseX / 200.0f);
I guess what I'd like to know is if I've implemented the code right, and if so, how do I fix the problem with it, and if I'm
not using the code right, I'd like to know how to use it correctly. Again, I didn't create the code for the camera, it was taken from a book.
Any help would be greatly appreciated. Here's the header file for the camera:
extern LPDIRECT3DDEVICE9 d3ddev; // Rendering device/video card.
D3DXMATRIX matTranslate;
D3DXMATRIX matRotateY;
D3DXMATRIX matProjection; // Bunch of selfexplanatory matrices.
class CXCamera // Just note our camera is called "TheCamera".
{
protected:
D3DXVECTOR3 m_Position;
D3DXVECTOR3 m_LookAt;
D3DXVECTOR3 m_Right;
D3DXVECTOR3 m_Up;
float m_fRotAboutUp;
float m_fRotAboutRight;
float m_fRotAboutFacing;
D3DXMATRIX m_ViewTransform; // Matrix for view transform.
LPDIRECT3DDEVICE9 m_pDevice;
bool m_UpdateRequired;
// Function prototypes:
HRESULT UpdateCameraMatrices();
public:
void LookAtPos(D3DXVECTOR3 *Position, D3DXVECTOR3 *LookAt, D3DXVECTOR3 *Up);
void SetPosition(FLOAT X, FLOAT Y, FLOAT Z);
D3DXVECTOR3* GetPosition() {return &m_Position;}
D3DXVECTOR3* GetLookAt() {return &m_LookAt;}
D3DXVECTOR3* GetRight() {return &m_Right;}
D3DXVECTOR3* GetUp() {return &m_Up;}
D3DXMATRIX* GetViewMatrix() {return &m_ViewTransform;}
CXCamera(LPDIRECT3DDEVICE9 pDevice);
HRESULT Update();
void RotateDown(float fAngle);
void RotateRight(float fAngle);
void Roll(float fAngle);
void MoveForward(float fDist);
void MoveRight(float fDist);
void MoveUp(float fDist);
void MoveInDirection(float fDist, D3DXVECTOR3* Dir);
};
//---------------------------------------------------------------------------------
CXCamera::CXCamera(LPDIRECT3DDEVICE9 pDevice)
{
// Set camera to starting positions, etc.
m_Position = D3DXVECTOR3(0.0f,0.0f,0.0f);
m_LookAt = D3DXVECTOR3(0.0f,0.0f,1.0f);
m_Right = D3DXVECTOR3(1.0f,0.0f,0.0f);
m_Up = D3DXVECTOR3(0.0f,1.0f,0.0f);
m_UpdateRequired = true;
m_fRotAboutUp = m_fRotAboutRight = m_fRotAboutFacing = 0.0f;
D3DXMatrixIdentity(&m_ViewTransform);//Make view transform an identity matrix/initialze it
m_pDevice = pDevice; // Class gets its own address to d3ddev to use.
}
HRESULT CXCamera::Update()
{
if(m_pDevice) // If the device/its address is valid...
{
return UpdateCameraMatrices(); // See function.
}
return E_FAIL; // Or else it fails. This shouldn't happen.
}
HRESULT CXCamera::UpdateCameraMatrices()
{
D3DXMATRIX matTotal;
D3DXMATRIX matRotAboutUp, matRotAboutRight, matRotAboutFacing;
// Build matrices for rotations around axis:
D3DXMatrixRotationAxis(&matRotAboutRight,&m_Right,m_fRotAboutRight);
D3DXMatrixRotationAxis(&matRotAboutUp,&m_Up,m_fRotAboutUp);
D3DXMatrixRotationAxis(&matRotAboutFacing,&m_LookAt,m_fRotAboutFacing);
// Multiply the matrices into matrix matTotal.
D3DXMatrixMultiply(&matTotal,&matRotAboutUp,&matRotAboutRight);
D3DXMatrixMultiply(&matTotal,&matRotAboutFacing,&matTotal);
// Transform the vectors by a matrix.
D3DXVec3TransformCoord(&m_Right,&m_Right,&matTotal);
D3DXVec3TransformCoord(&m_Up,&m_Up,&matTotal);
D3DXVec3Cross(&m_LookAt, &m_Right,&m_Up);
if(fabs(D3DXVec3Dot(&m_Up,&m_Right)) > 0.01) // If the dot product of the two vectors is
// greater than 0.01... (?)
{
D3DXVec3Cross(&m_Up, &m_LookAt,&m_Right); // Get the cross product of LookAt and Right.
}
// Turn vectors into normal vectors.
D3DXVec3Normalize(&m_Right,&m_Right);
D3DXVec3Normalize(&m_Up,&m_Up);
D3DXVec3Normalize(&m_LookAt,&m_LookAt);
float fView41,fView42,fView43;
// Set part of the view transform matrix values to the negative dot product of a vector.
fView41 = -D3DXVec3Dot(&m_Right,&m_Position);
fView42 = -D3DXVec3Dot(&m_Up,&m_Position);
fView43 = -D3DXVec3Dot(&m_LookAt,&m_Position);
// Make the view transform matrix.
m_ViewTransform = D3DXMATRIX(m_Right.x, m_Up.x, m_LookAt.x, 0.0f,
m_Right.y, m_Up.y, m_LookAt.y, 0.0f,
m_Right.z, m_Up.z, m_LookAt.z, 0.0f,
fView41, fView42, fView43, 1.0f);
m_fRotAboutUp = m_fRotAboutRight = m_fRotAboutFacing = 0.0f;
m_UpdateRequired = false;
static float index = 0.0f; index+=0.05f;
D3DXMatrixRotationY(&matRotateY, index);
D3DXMatrixTranslation(&matTranslate, 20.0f, 4.0f, 75.0f);
D3DXMatrixPerspectiveFovLH(&matProjection,
D3DXToRadian(45), // The horizontal field of view
(FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, // Aspect ratio
1.0f, // The near view-plane
100000.0f); // The far view-plane
m_pDevice->SetTransform(D3DTS_PROJECTION, &matProjection);
m_pDevice->SetTransform(D3DTS_WORLD, &(matRotateY * matTranslate));
return m_pDevice->SetTransform(D3DTS_VIEW, &m_ViewTransform);
}
void CXCamera::LookAtPos(D3DXVECTOR3 *Position, D3DXVECTOR3 *LookAt, D3DXVECTOR3 *Up)
// Not for playing, more for cutscenes.
{
D3DXMatrixLookAtLH(&m_ViewTransform, Position, LookAt, Up);
m_Position = *(Position);
m_Right.x = m_ViewTransform._11;
m_Right.y = m_ViewTransform._21;
m_Right.z = m_ViewTransform._31;
m_Up.x = m_ViewTransform._12;
m_Up.y = m_ViewTransform._22;
m_Up.z = m_ViewTransform._32;
m_LookAt.x = m_ViewTransform._13;
m_LookAt.y = m_ViewTransform._23;
m_LookAt.z = m_ViewTransform._33;
m_fRotAboutUp = m_fRotAboutRight = m_fRotAboutFacing = 0.0f;
}
void CXCamera::SetPosition(FLOAT X, FLOAT Y, FLOAT Z) //Self-explanatory.
//For cutscenes probably.
{
m_Position = D3DXVECTOR3(X, Y, Z);
m_UpdateRequired = true;
}
// All of these functions should be self explanatory:
void CXCamera::RotateDown(float fAngle) //Rotate camera up/down.
{
m_fRotAboutRight += fAngle;
m_UpdateRequired = true;
}
void CXCamera::RotateRight(float fAngle) //Rotate camera left/right.
{
m_fRotAboutUp += fAngle;
m_UpdateRequired = true;
}
void CXCamera::Roll(float fAngle) //Roll the camera.
{
m_fRotAboutFacing += fAngle;
m_UpdateRequired = true;
}
void CXCamera::MoveForward(float fDist) //Move camera forward/back.
{
m_Position += fDist * m_LookAt;
m_UpdateRequired = true;
}
void CXCamera::MoveRight(float fDist) //Move camera left/right.
{
m_Position += fDist * m_Right;
m_UpdateRequired = true;
}
void CXCamera::MoveUp(float fDist) //Move camera up/down.
{
m_Position += fDist * m_Up;
m_UpdateRequired = true;
}
void CXCamera::MoveInDirection(float fDist, D3DXVECTOR3* Dir) //Useless?
{
D3DXVECTOR3 DirToMove(0,0,0);
D3DXVec3Normalize(&DirToMove, Dir);
m_Position += fDist*DirToMove;
m_UpdateRequired = true;
}
void DebugCamera() //This works for making a camera. Fall back on this if you must ensure
//it's the camera causing the problem, not another part of the program.
{
static float index = 0.0f; index+=0.05f; // an ever-increasing float value
D3DXMatrixRotationY(&matRotateY, index);
D3DXMatrixTranslation(&matTranslate, 20.0f, 4.0f, 75.0f);
d3ddev->SetTransform(D3DTS_WORLD, &(matRotateY * matTranslate));
// the projection transform matrix
D3DXMatrixPerspectiveFovLH(&matProjection,
D3DXToRadian(45), // the horizontal field of view
(FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, // aspect ratio
1.0f, // the near view-plane
100000.0f); // the far view-plane
d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection);
}