Quaternion trackball rotation problem

Hi,

I’m trying to implement a basic trackball navigation based on quaternions, i’ve found the new orientation but im not sure how to plug it in, I would like to set it to my basic camera, which I have as vectors cameraPos, cameraView and cameraUp that I use with the glulookat() function.

If I assign the quaternion rotation to the vectors I get a trackball rotation, but it gets wrong after a couple of rotations. I guess I need to keep track of the rotation in some way, by setting up a own lookat view matrix and get the rotation matrix from that or save the old orientation? Is it even possible to use the glulookat for this, or need I glMultMatrix(…)/glLoadMatrix(…)?

This is how I setup the rendering


glViewport(0, 0, width, height);	
glClearColor(0.f, 0.f, 0.f, 0.f);

// Enable Z-buffer read and write
glEnable(GL_DEPTH_TEST);   

    // Setup a perspective projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.f, (GLfloat)width/(GLfloat)height, 1.0f, 100.0f );
	
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
	
// Camera position, view target, up vector.
gluLookAt( m_cameraPos.x() , m_cameraPos.y() , m_cameraPos.z() ,
               m_cameraView.x(), m_cameraView.y(), m_cameraView.z(),
               m_cameraUp.x()  , m_cameraUp.y()  , m_cameraUp.z()  );


Here I get the point coords and assign the rotation


Vec3<double> p1 = MapToSphere(oldPoint);
Vec3<double> p2 = MapToSphere(currPoint);

Vec3<double> axis = p2.cross(p1);	
double angle = asin(axis.length());

Quaternion<double> rot(axis,angle);
rot.normalize();

m_cameraPos = rotate(m_cameraPos, rot)*5;
m_cameraView = rotate(m_cameraView, rot);
m_cameraUp = rotate(m_cameraUp, rot);

Here is the rotate() function

	
Quaternion<double> rotconj = rot;
rotconj.conjugate();

Quaternion<double> qTarget(vec.x(),
                               vec.y(),
                               vec.z(),
                               0);

Quaternion<double> qResult = (rot * qTarget) * rotconj;
Vec3<double> vecRes(qResult.x(),qResult.y(),qResult.z());
vecRes.normalize();
return vecRes;

Thanks for your help!

Vec3<double> axis = p2.cross(p1);
double angle = asin(axis.length());

Quaternion<double> rot(axis,angle);
rot.normalize();

Is there a reason why you felt the need to normalize the results? I find that disconcerting.

In general, quaternion constructors that take an angle/axis rotation will generate normalized quaternions, so long as the axis is a unit vector. Your axis here is almost certainly not a unit vector. So one of two things will happen:

1: The quaternion constructor will normalize the axis vector for you, in which case the “normalize” call is meaningless.

2: The quaternion constructor will not normalize the axis vector, in which case you will not get a valid quaternion. And normalizing it will not make it into the quaternion you want. You must normalize the axis vector before constructing the quaternion.

Best normalize the axis either way, since #1 is generally unlikely.

Here is the rotate() function

Please provide the prototype for functions. That way, we don’t have to ask, “Are ‘vec’ and ‘rot’ parameters to the function or global variables?”

Second, are ‘vec’ and ‘rot’ parameters to the function or global variables? :wink:

Third, what exactly is this function supposed to do? Are you trying to rotate a vector by a quaternion? If so, I don’t remember the math for that off-hand, but I’m pretty sure that normalizing the returned vector isn’t part of the process.

If you pass a world-space position into this function, you’ll get a unit vector back. Which is probably not helpful.

I removed the glulookat function and made a rotation matrix of the quaternion instead and it seems to work better.

@alfonse
Without the normalization of the quaternion the object is stretched on the screen and with normalization of the axis the object disappears when rotating it. So I think it should be correct.

Here is how I now did it


Quaternion<double> rot(axis,angle);
m_orientation = rot * m_orientation;
m_orientation.normalize();

m_rotMatrix = m_orientation.toMatrix4();

In the render function:


glMultMatrixd(&m_rotMatrix.m[0][0]);
drawCube();

Here is the axis-angle function of the quaternion


template<class T>
Quaternion<T>::Quaternion<T>(const Vec3<T> &axis, T angle)
{
	float sinAngleOver2 = sin(angle/2);
	_w = cos(angle/2);
	_x = reinterpret_cast<const T*>(&axis)[0]*sinAngleOver2;
	_y = reinterpret_cast<const T*>(&axis)[1]*sinAngleOver2;
	_z = reinterpret_cast<const T*>(&axis)[2]*sinAngleOver2;
}

But now I wonder how I could make rotation on the camera instead? Because now i’m only adding rotation to the cube object.

I have no problems to use quaternions with gluLookAt. You must keep your 3 axis, to know what parameters to give to the function.

But now I wonder how I could make rotation on the camera instead? Because now i’m only adding rotation to the cube object.

Can you explicit your question ?

I think I got it to work with a camera now. I created my own lookat function and save 3 axes for it. Then I update the view matrix for each frame with the new orientation.

createLookAt()


	m_zAxis = eye-focus;
	m_zAxis.normalize();
	    
	m_xAxis = u.cross(m_zAxis);
	m_xAxis.normalize();

	m_yAxis = m_zAxis.cross(m_xAxis);
	m_yAxis.normalize();

	m_viewMatrix.m[0][0] = m_xAxis.x();
    m_viewMatrix.m[1][0] = m_xAxis.y();
    m_viewMatrix.m[2][0] = m_xAxis.z();
    m_viewMatrix.m[3][0] = -m_xAxis.dot(eye);

    m_viewMatrix.m[0][1] = m_yAxis.x();
    m_viewMatrix.m[1][1] = m_yAxis.y();
    m_viewMatrix.m[2][1] = m_yAxis.z();
    m_viewMatrix.m[3][1] = -m_yAxis.dot(eye);

    m_viewMatrix.m[0][2] = m_zAxis.x();
    m_viewMatrix.m[1][2] = m_zAxis.y();
    m_viewMatrix.m[2][2] = m_zAxis.z();    
    m_viewMatrix.m[3][2] = -m_zAxis.dot(eye);

    m_orientation.fromMatrix(m_viewMatrix);

Update the view matrix


	m_viewMatrix = m_orientation.toMatrix4();

	m_xAxis.setX(m_viewMatrix.m[0][0]);
	m_xAxis.setY(m_viewMatrix.m[1][0]);
	m_xAxis.setZ(m_viewMatrix.m[2][0]);

	m_yAxis.setX(m_viewMatrix.m[0][1]);
	m_yAxis.setY(m_viewMatrix.m[1][1]);
	m_yAxis.setZ(m_viewMatrix.m[2][1]);

	m_zAxis.setX(m_viewMatrix.m[0][2]);
	m_zAxis.setY(m_viewMatrix.m[1][2]);
	m_zAxis.setZ(m_viewMatrix.m[2][2]);

	m_cameraPos = m_cameraView + m_zAxis * (5);
	m_viewMatrix.m[3][0] = -m_xAxis.dot(m_cameraPos);
    m_viewMatrix.m[3][1] = -m_yAxis.dot(m_cameraPos);
    m_viewMatrix.m[3][2] = -m_zAxis.dot(m_cameraPos);