Skip to main content
gamedev.net
🔒 Locked

[Solved]Basic Camera Problems

Started by FoxMulder900 Oct 1, 2008 at 3:49 PM 6 replies 1.4k views
Original Post
FoxMulder900
FoxMulder900
Hello, I have the following code:

void Camera::translate(float x, float y, float z)
{
	D3DXMATRIX tmp;
	D3DXMatrixTranslation(&tmp, x, y, z);
	camMatrix = camMatrix*tmp;
}
void Camera::rotateX(float x)
{
	D3DXMATRIX tmp;
	D3DXMatrixRotationX(&tmp, D3DXToRadian(x));
	camMatrix = camMatrix*tmp;
}
void Camera::rotateY(float y)
{
	D3DXMATRIX tmp;
	D3DXMatrixRotationY(&tmp, D3DXToRadian(y));
	camMatrix = camMatrix*tmp;
}
void Camera::rotateZ(float z)
{
	D3DXMATRIX tmp;
	D3DXMatrixRotationZ(&tmp, D3DXToRadian(z));
	camMatrix = camMatrix*tmp;
}
void Camera::mouseMoved(int dx, int dy)
{
	rotateY(-dx);
	rotateX(-dy);
}

It seems to work great at first, when i move the mouse up and down, and left and right the camera responds correctly, Even moving the mouse diagonally gives the correct results, however when i move the mouse in a circular motion - the world itself starts rotating. I understand that this is because I need to ensure that when I rotate around the Y axis it is always (0,1,0) - but i have no idea how to do that. I thought that is what D3DXMatrixRotationY was supposed to do and D3DXMatrixRotationAxis rotates around an arbitrary axis. Any suggestions? [Edited by - FoxMulder900 on October 6, 2008 3:30:58 PM]
ET3D
ET3D
I think it's because your axes have changed once you've rotated. In any case, accumulating changes leads to inaccuracies.

I'd suggest that you keep the full x/y offset and calculate the rotations from these angles, instead of multiplying by small rotations.
FoxMulder900
FoxMulder900
Ok, i have completely restructured my camera class based on your suggestion and a few examples i found online however i am getting completely wrong results, Sorry for posting so much code but im really stumped on this one, any ideas what i have wrong?

Camera::Camera(){	D3DXMatrixIdentity(&camMatrix);	speed = 0.0001;	pos = D3DXVECTOR3(0,0,0);  	forward = D3DXVECTOR3(0,0,-1);  	right = D3DXVECTOR3(1,0,0);  	up = D3DXVECTOR3(0,1,0);}void Camera::translate(D3DXVECTOR3 tVector){	pos = pos+tVector;}void Camera::rotateX(float x){	D3DXMATRIX tmp;	D3DXMatrixRotationAxis(&tmp, &right, D3DXToRadian(x));	D3DXVec3TransformCoord(&forward, &forward, &tmp);	//D3DXVec3TransformCoord(&up, &up, &tmp);}void Camera::rotateY(float y){	D3DXMATRIX tmp;	D3DXMatrixRotationAxis(&tmp, &up, D3DXToRadian(y));	D3DXVec3TransformCoord(&forward, &forward, &tmp);	D3DXVec3TransformCoord(&right, &right, &tmp);}void Camera::rotateZ(float z){	D3DXMATRIX tmp;	D3DXMatrixRotationAxis(&tmp, &forward, D3DXToRadian(z));	D3DXVec3TransformCoord(&up, &up, &tmp);	D3DXVec3TransformCoord(&right, &right, &tmp);}D3DXMATRIX Camera::getMatrix(){	D3DXMATRIX tmp = D3DXMATRIX(right.x,up.x,forward.x,0,								 right.y,up.y,forward.y,0,								 right.z,up.z,forward.z,0,								 -D3DXVec3Dot(&pos, &right), -D3DXVec3Dot(&pos, &up), -D3DXVec3Dot(&pos, &forward), 1);	return tmp;}
Ashkan
Ashkan
Your basis vectors must form an orthonormal basis at any point in time. A set of vectors form an orthonormal basis iff they are mutually orthogonal and of unit length each. You must regularly orthonormalize your basis vectors to make for the floating-point round-off errors that are gradually introduced as time passes by.

Here's how you orthonormalize three vectors:
void Vector3 Orthonormalize( Vector3& U, Vector3& V, Vector3& W ) {	U.Normalize();	float dot0 = U.Dot( V ); 	V -= dot0 * U;	V.Normalize();	const float dot1 = V.Dot( W );	dot0 = U.Dot( W );	W -= dot0 * U + dot1 * V;	W.Normalize();}


Further reading for the mathematically astute reader.
ET3D
ET3D
My suggestion was more like your first code, but keep the angle. I think it's simpler than keeping vectors. Keep the x, y, z angles, and use them in rotations when you need the matrix. There are a lot of methods to get a working camera, but storing angles is good because it allows you to debug more easily, since you can understand what's stored.
Ashkan
Ashkan
Quote:
Original post by ET3D
My suggestion was more like your first code, but keep the angle. I think it's simpler than keeping vectors. Keep the x, y, z angles, and use them in rotations when you need the matrix. There are a lot of methods to get a working camera, but storing angles is good because it allows you to debug more easily, since you can understand what's stored.


Be aware that storing rotations with Euler Angles, as you seem to suggest, results in the infamous Gimbal Lock. His second approach, in which he generates a rotation matrix is more preferable.
Ashkan
Ashkan
I unfortunately don't have enough time to thoroughly debug the code you posted, but I quickly digged through some of my old archives, and I came by a DirectX Camera class from Frank Luna, the author of Introduction to 3D Game Programming series. You might learn a thing or two from it:

Camera.h:
//////////////////////////////////////////////////////////////////////////////////////////////////// // File: Camera.h// // Author: Frank Luna (C) All Rights Reserved//// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0 //// Desc: Defines a camera's position and orientation.//         //////////////////////////////////////////////////////////////////////////////////////////////////#ifndef __cameraH__#define __cameraH__#include <d3dx9.h>class Camera{public:	enum CameraType { LANDOBJECT, AIRCRAFT };	Camera();	Camera(CameraType cameraType);	~Camera();	void strafe(float units); // left/right	void fly(float units);    // up/down	void walk(float units);   // forward/backward		void pitch(float angle); // rotate on right vector	void yaw(float angle);   // rotate on up vector	void roll(float angle);  // rotate on look vector	void getViewMatrix(D3DXMATRIX* V); 	void setCameraType(CameraType cameraType); 	void getPosition(D3DXVECTOR3* pos); 	void setPosition(D3DXVECTOR3* pos); 	void getRight(D3DXVECTOR3* right);	void getUp(D3DXVECTOR3* up);	void getLook(D3DXVECTOR3* look);private:	CameraType  _cameraType;	D3DXVECTOR3 _right;	D3DXVECTOR3 _up;	D3DXVECTOR3 _look;	D3DXVECTOR3 _pos;};#endif // __cameraH__


Camera.cpp
//////////////////////////////////////////////////////////////////////////////////////////////////// // File: Camera.cpp// // Author: Frank Luna (C) All Rights Reserved//// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0 //// Desc: Defines a camera's position and orientation.//         //////////////////////////////////////////////////////////////////////////////////////////////////#include "Camera.h"Camera::Camera(){	_cameraType = AIRCRAFT;	_pos   = D3DXVECTOR3(0.0f, 0.0f, 0.0f);	_right = D3DXVECTOR3(1.0f, 0.0f, 0.0f);	_up    = D3DXVECTOR3(0.0f, 1.0f, 0.0f);	_look  = D3DXVECTOR3(0.0f, 0.0f, 1.0f);}Camera::Camera(CameraType cameraType){	_cameraType = cameraType;	_pos   = D3DXVECTOR3(0.0f, 0.0f, 0.0f);	_right = D3DXVECTOR3(1.0f, 0.0f, 0.0f);	_up    = D3DXVECTOR3(0.0f, 1.0f, 0.0f);	_look  = D3DXVECTOR3(0.0f, 0.0f, 1.0f);}Camera::~Camera(){}void Camera::getPosition(D3DXVECTOR3* pos){	*pos = _pos;}void Camera::setPosition(D3DXVECTOR3* pos){	_pos = *pos;}void Camera::getRight(D3DXVECTOR3* right){	*right = _right;}void Camera::getUp(D3DXVECTOR3* up){	*up = _up;}void Camera::getLook(D3DXVECTOR3* look){	*look = _look;}void Camera::walk(float units){	// move only on xz plane for land object	if( _cameraType == LANDOBJECT )		_pos += D3DXVECTOR3(_look.x, 0.0f, _look.z) * units;	if( _cameraType == AIRCRAFT )		_pos += _look * units;}void Camera::strafe(float units){	// move only on xz plane for land object	if( _cameraType == LANDOBJECT )		_pos += D3DXVECTOR3(_right.x, 0.0f, _right.z) * units;	if( _cameraType == AIRCRAFT )		_pos += _right * units;}void Camera::fly(float units){	// move only on y-axis for land object	if( _cameraType == LANDOBJECT )		_pos.y += units;	if( _cameraType == AIRCRAFT )		_pos += _up * units;}void Camera::pitch(float angle){	D3DXMATRIX T;	D3DXMatrixRotationAxis(&T, &_right,	angle);	// rotate _up and _look around _right vector	D3DXVec3TransformCoord(&_up,&_up, &T);	D3DXVec3TransformCoord(&_look,&_look, &T);}void Camera::yaw(float angle){	D3DXMATRIX T;	// rotate around world y (0, 1, 0) always for land object	if( _cameraType == LANDOBJECT )		D3DXMatrixRotationY(&T, angle);	// rotate around own up vector for aircraft	if( _cameraType == AIRCRAFT )		D3DXMatrixRotationAxis(&T, &_up, angle);	// rotate _right and _look around _up or y-axis	D3DXVec3TransformCoord(&_right,&_right, &T);	D3DXVec3TransformCoord(&_look,&_look, &T);}void Camera::roll(float angle){	// only roll for aircraft type	if( _cameraType == AIRCRAFT )	{		D3DXMATRIX T;		D3DXMatrixRotationAxis(&T, &_look,	angle);		// rotate _up and _right around _look vector		D3DXVec3TransformCoord(&_right,&_right, &T);		D3DXVec3TransformCoord(&_up,&_up, &T);	}}void Camera::getViewMatrix(D3DXMATRIX* V){	// Keep camera's axes orthogonal to eachother	D3DXVec3Normalize(&_look, &_look);	D3DXVec3Cross(&_up, &_look, &_right);	D3DXVec3Normalize(&_up, &_up);	D3DXVec3Cross(&_right, &_up, &_look);	D3DXVec3Normalize(&_right, &_right);	// Build the view matrix:	float x = -D3DXVec3Dot(&_right, &_pos);	float y = -D3DXVec3Dot(&_up, &_pos);	float z = -D3DXVec3Dot(&_look, &_pos);	(*V)(0,0) = _right.x; (*V)(0, 1) = _up.x; (*V)(0, 2) = _look.x; (*V)(0, 3) = 0.0f;	(*V)(1,0) = _right.y; (*V)(1, 1) = _up.y; (*V)(1, 2) = _look.y; (*V)(1, 3) = 0.0f;	(*V)(2,0) = _right.z; (*V)(2, 1) = _up.z; (*V)(2, 2) = _look.z; (*V)(2, 3) = 0.0f;	(*V)(3,0) = x;        (*V)(3, 1) = y;     (*V)(3, 2) = z;       (*V)(3, 3) = 1.0f;}void Camera::setCameraType(CameraType cameraType){	_cameraType = cameraType;}


Especially note how the camera axes are kept orthogonal by constantly orthogonalizing the basis vectors in Camera::getViewMatrix. This was the source of your problem.

Hope it helps.
FoxMulder900
FoxMulder900
Thank you greatly for your help everyone! My camera class is now completed and it works wonderfully!

Topic Locked

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

Sign in to reply to this topic.