Camera & Quaternions

Hello,

I’m currently trying to develop a camera class using Quaternions.

This is how I’d like my camera to behave:

[ul][li] The camera moves along its local axes by using the keyboard.[*] The camera rotates on itself (changing orientation, lookAt) by dragging the mouse.[/ul][/li]
Basically, the first part works but the second one is quite messy: when I drag the mouse, my scene is rotating (not the camera…) chaotically ^^.

Here’s a sample of my code:


void Camera::rotate( float32 angle, float32 axisX, float32 axisY, float32 axisZ )
{
	Quaternion q;
	Vec3 v (axisX,axisY,axisZ);
	
	q.FromAxis(v,angle);
	q.normalise();

	m_quat *= q ;
}

void Camera::buildTransformationMatrix() 
{
	m_quat.buildRotationMatrix();

	for(int i=0;i<12;i++)
	{
		m_TransformationMatrix[i]= m_quat.m_RotationMatrix[i];
	}

	m_TransformationMatrix[12]= m_Position.m_x;
	m_TransformationMatrix[13]= m_Position.m_y;
	m_TransformationMatrix[14]= m_Position.m_z;
	m_TransformationMatrix[15]= 1;
}

void Camera::glPut() 
{
	buildTransformationMatrix();

	glMatrixMode(GL_MODELVIEW);
	
	float32 mat [16];
	for(int i=0;i<16;i++) mat[i]=m_TransformationMatrix[i];
	
	glLoadMatrixf(mat);
}

void mapToSphere(const int x, const int y, Vec3* NewVec)
{
    Vec3 TempPt;
    float32 length;

    //Copy paramter into temp point
    TempPt = Vec3(x,y,0.f);

    //Adjust point coords and scale down to range of [-1 ... 1]
	TempPt.m_x  =        (TempPt.m_x * 2 / WindowWidth)  - 1.0f;
    TempPt.m_y  = 1.0f - (TempPt.m_y * 2 / WindowHeight);

    //Compute the square of the length of the vector to the point from the center
    length      = (TempPt.m_x * TempPt.m_x) + (TempPt.m_y * TempPt.m_y);

    //If the point is mapped outside of the sphere... (length > radius squared)
    if (length > 1.0f)
    {
        //Compute a normalizing factor (radius / sqrt(length))
        float32 norm = 1.0f / sqrt(length);

        //Return the "normalized" vector, a point on the sphere
        NewVec->m_x = TempPt.m_x * norm;
        NewVec->m_y = TempPt.m_y * norm;
        NewVec->m_z = 0.0f;
    }
    else    //Else it's on the inside
    {
        //Return a vector to a point mapped inside the sphere sqrt(radius squared - length)
        NewVec->m_x = TempPt.m_x;
        NewVec->m_y = TempPt.m_y;
        NewVec->m_z = sqrt(1.0f - length);
    }
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_CLOSE:
            PostQuitMessage(0);	break;

		case WM_PAINT:
			drawScene();
			SwapBuffers(hDC);
			break;

		case WM_SIZE:
			resize( LOWORD(lParam), HIWORD(lParam) ); break;

        case WM_DESTROY:
            return 0;

        case WM_KEYDOWN:
        {
           // [...] Translations
        }
        break;

		case WM_LBUTTONDOWN:
			lbtn_clicked = true ;
			break;

		case WM_LBUTTONUP:
			lbtn_clicked = false ;
			break;

        case WM_MOUSEMOVE:
			xMouseRel = LOWORD(lParam) - xMousePos;
			yMouseRel = HIWORD(lParam) - yMousePos;
			xMousePos = LOWORD(lParam);
			yMousePos = HIWORD(lParam);

			if(lbtn_clicked)
			{
				mapToSphere(xMousePos,yMousePos,&stVec);
				stVec.normalise();

				camera.rotate(atan(stVec.m_x/stVec.m_y), stVec.m_x, stVec.m_y, 0.f);
				camera.glPut();

			}
			break;

        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    return 0;
}

I tried different stuff I found on several websites (Nehe,gpwiki,…), but I can’t make it work correctly…
I’ll take any piece of advice I can get. I can, of course, post more extracts from my code if needed.

I doubt it’ll help much in this case, but I’m coding with Visual Studio 2008 Express, on a virtual Windows Xp, on a macbook 13"3.