views:

764

answers:

2

I have a camera object that I have put together from reading on the net that handles moving forward and backward, strafe left and right and even look around with the mouse. But when I move in any direction plus try to look around it jumps all over the place, but when I don't move and look around its fine.

I'm hoping someone can help me work out why I can move and look around at the same time?

main.h

#include "SDL/SDL.h"
#include "SDL/SDL_opengl.h"

#include <cmath>


#define CAMERASPEED 0.03f    // The Camera Speed



struct tVector3 // Extended 3D Vector Struct

{     

tVector3() {} // Struct Constructor

tVector3 (float new_x, float new_y, float new_z) // Init Constructor  

{ x = new_x; y = new_y; z = new_z; }

// overload + operator

tVector3 operator+(tVector3 vVector) {return tVector3(vVector.x+x, vVector.y+y, vVector.z+z);}

// overload - operator

tVector3 operator-(tVector3 vVector) {return tVector3(x-vVector.x, y-vVector.y, z-vVector.z);}

// overload * operator

tVector3 operator*(float number)  {return tVector3(x*number, y*number, z*number);}

// overload / operator

tVector3 operator/(float number)  {return tVector3(x/number, y/number, z/number);}



float x, y, z;      // 3D vector coordinates

};



class CCamera 

{

public:



 tVector3 mPos; 

 tVector3 mView;  

 tVector3 mUp;   



 void Strafe_Camera(float speed);



 void Move_Camera(float speed);

 void Rotate_View(float speed);
 void Position_Camera(float pos_x, float pos_y,float pos_z,

        float view_x, float view_y, float view_z,

       float up_x,   float up_y,   float up_z);

};



void Draw_Grid();

camera.cpp

#include "main.h"

void CCamera::Position_Camera(float pos_x, float pos_y, float pos_z,
    float view_x, float view_y, float view_z, 
    float up_x, float up_y, float up_z)
{
mPos = tVector3(pos_x, pos_y, pos_z);
mView = tVector3(view_x, view_y, view_z);
mUp = tVector3(up_x, up_y, up_z);
}

void CCamera::Move_Camera(float speed)
{
tVector3 vVector = mView - mPos;

mPos.x  = mPos.x  + vVector.x * speed;

mPos.z  = mPos.z  + vVector.z * speed;

mView.x = mView.x + vVector.x * speed;

mView.z = mView.z + vVector.z * speed;
}

void CCamera::Strafe_Camera(float speed)
{
tVector3 vVector = mView - mPos;

tVector3 vOrthoVector;



vOrthoVector.x = -vVector.z;

vOrthoVector.z =  vVector.x;



mPos.x  = mPos.x  + vOrthoVector.x * speed;

mPos.z  = mPos.z  + vOrthoVector.z * speed;

mView.x = mView.x + vOrthoVector.x * speed;

mView.z = mView.z + vOrthoVector.z * speed;
}

void CCamera::Rotate_View(float speed)
{
tVector3 vVector = mView - mPos;
tVector3 vOrthoVector;

vOrthoVector.x = -vVector.z;

vOrthoVector.z =  vVector.x;


mView.z = (float)(mPos.z + sin(speed)*vVector.x + cos(speed)*vVector.z);

mView.x = (float)(mPos.x + cos(speed)*vVector.x - sin(speed)*vVector.z);

}

and the mousemotion code

void processEvents()
{
int mid_x = screen_width  >> 1;

int mid_y = screen_height >> 1;
int mpx = event.motion.x;
int mpy = event.motion.y;

float angle_y  = 0.0f;

float angle_z  = 0.0f;

while(SDL_PollEvent(&event))
{
 switch(event.type)
 {
  case SDL_MOUSEMOTION:
   if( (mpx == mid_x) && (mpy == mid_y) ) return;



   // Get the direction from the mouse cursor, set a resonable maneuvering speed

   angle_y = (float)( (mid_x - mpx) ) / 1000; //1000

   angle_z = (float)( (mid_y - mpy) ) / 1000; //1000


   // The higher the value is the faster the camera looks around.

   objCamera.mView.y += angle_z * 2;



   // limit the rotation around the x-axis

   if((objCamera.mView.y - objCamera.mPos.y) > 8)  objCamera.mView.y = objCamera.mPos.y + 8;

   if((objCamera.mView.y - objCamera.mPos.y) <-8)  objCamera.mView.y = objCamera.mPos.y - 8;


   objCamera.Rotate_View(-angle_y);
   SDL_WarpMouse(mid_x, mid_y);
   break;

  case SDL_KEYUP:
   objKeyb.handleKeyboardEvent(event,true);
   break;
  case SDL_KEYDOWN:
   objKeyb.handleKeyboardEvent(event,false);
   break;

  case SDL_QUIT:
   quit = true;
   break;

  case SDL_VIDEORESIZE:
   screen = SDL_SetVideoMode( event.resize.w, event.resize.h, screen_bpp, SDL_OPENGL | SDL_HWSURFACE | SDL_RESIZABLE | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE );
   screen_width = event.resize.w;
   screen_height = event.resize.h;
   init_opengl();
   std::cout << "Resized to width: " << event.resize.w << " height: " << event.resize.h << std::endl;
   break;

  default:
   break;
 }
}
}
+2  A: 

I'm not entirely sure what you are doing above.

Personally I would just allow a simple 4x4 matrix. Any implementation will do. To rotate you, simply, need to rotate using the change of mouse x and y as euler inputs for rotation around the y and x axes. There is lots of code available all over the internet that will do this for you.

Some of those matrix libraries won't provide you with a "MoveForward()" function. If this is the case its ok, moving forward is pretty easy. The third column (or row if you are using row major matrices) is your forward vector. Extract it. Normalise it (It really should be normalised anyway so this step may not be needed). Multiply it by how much you wish to move forward and then add it to the position (the 4th column/row).

Now here is the odd part. A view matrix is a special type of matrix. The matrix above defines the view space. If you multiply your current model matrix by this matrix you will not get the answer you expect. Because you wish to transform it such that the camera is at the origin. As such you need to, effectively, undo the camera transformation to re-orient things to the view defined above. To do this you multiply your model matrix by the inverse of the view matrix.

You now have an object defined in the correct view space.

This is my very simple camera class. It does not handle the functionality you describe but hopefully will give you a few ideas on how to set up the class (Be warned, I use row major, ie DirectX style, matrices).

BaseCamera.h:

#ifndef BASE_CAMERA_H_
#define BASE_CAMERA_H_

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

#include "Maths/Vector4.h"
#include "Maths/Matrix4x4.h"

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

class BaseCamera
{
protected:
    bool     mDirty;
    MathsLib::Matrix4x4  mCameraMat;
    MathsLib::Matrix4x4  mViewMat;
public:
    BaseCamera();
    BaseCamera( const BaseCamera& camera );
    BaseCamera( const MathsLib::Vector4& vPos, const MathsLib::Vector4& vLookAt );
    BaseCamera( const MathsLib::Matrix4x4& matCamera );

    bool IsDirty() const;
    void SetDirty();

    MathsLib::Matrix4x4&  GetOrientationMatrix();
    const MathsLib::Matrix4x4& GetOrientationMatrix() const;

    MathsLib::Matrix4x4&  GetViewMatrix();
};

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

inline MathsLib::Matrix4x4& BaseCamera::GetOrientationMatrix()
{
    return mCameraMat;
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

inline const MathsLib::Matrix4x4& BaseCamera::GetOrientationMatrix() const
{
    return mCameraMat;
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

inline bool BaseCamera::IsDirty() const
{
    return mDirty;
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

inline void BaseCamera::SetDirty()
{
    mDirty = true;
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

#endif

BaseCamera.cpp:

#include "Render/stdafx.h"

#include "BaseCamera.h"

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

BaseCamera::BaseCamera() :
    mDirty( true )
{
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

BaseCamera::BaseCamera( const BaseCamera& camera ) :
    mDirty( camera.mDirty ),
    mCameraMat( camera.mCameraMat ),
    mViewMat( camera.mViewMat )
{
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

BaseCamera::BaseCamera( const MathsLib::Vector4& vPos, const MathsLib::Vector4& vLookAt ) :
    mDirty( true )
{
    MathsLib::Vector4 vDir = (vLookAt - vPos).Normalise();
    MathsLib::Vector4 vLat = MathsLib::CrossProduct( MathsLib::Vector4( 0.0f, 1.0f, 0.0f ), vDir ).Normalise();
    MathsLib::Vector4 vUp = MathsLib::CrossProduct( vDir, vLat );//.Normalise();

    mCameraMat.Set( vLat, vUp, vDir, vPos ); 
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

BaseCamera::BaseCamera( const MathsLib::Matrix4x4& matCamera ) :
    mDirty( true ),
    mCameraMat( matCamera )
{
}

    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

MathsLib::Matrix4x4& BaseCamera::GetViewMatrix()
{
    if ( IsDirty() )
    {
     mViewMat = mCameraMat.Inverse();
     mDirty  = false;
    }
    return mViewMat;
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
Goz
+1  A: 

I agree with Goz. You need to use homegenous 4x4 matrices if you want to represent affine transformations such as rotate + translate

Assuming row major representation then if there is no scaling or shearing, your 4x4 matrix represents the following:
Rows 0 to 2 : The three basis vectors of your local co-ordinate system ( i.e x,y,z )
Row 3 : the current translation from the origin

So to move along your local x vector, as Goz says, because you can assume it's a unit vector if there is no scale/shear you just multiply it by the move step ( +ve or -ve ) then add the resultant vector onto Row 4 in the matrix So taking a simple example of starting at the origin with your local frame set to world frame then your matrix would look something like this

1 0 0 0 <--- x unit vector
0 1 0 0 <--- y unit vector
0 0 1 0 <--- z unit vector
0 0 0 1 <--- translation vector

In terms of a way most game cameras work then the axes map like this:
x axis <=> Camera Pan Left/Right
y axis <=> Camera Pan Up/Down
z axis <=> Camera Zoom In/Out

So if I rotate my entire frame of reference to say look at a new point LookAt then as Goz puts in his BaseCamera overloaded constructor code, you then construct a new local co-ordinate system and set this into your matrix ( all mCameraMat.Set( vLat, vUp, vDir, vPos ) does typically is set those four rows of the matrix i.e VLat would be row 0, vUp row 1, vDir row 2 and vPos row 3 )

Then to zoom in/out would just become row 3 = row 2 * stepval

Again as Goz, rightly points out, you then need to transform this back into world-space and this is done by multiplying by the inverse of the view matrix

zebrabox
Do bear in mind that the above code is for a row major matrix. You will need to transpose the matrix for a column major matrix :)
Goz
Yep absolutely true
zebrabox