Hi, I'm doing a university project that consists of a 3D visor (that is, an application that plots in a 3D environment a certain kind of objects (like lines, points, ...) and allows navigation through them by using mouse + keyboard to see how the scene is composed from different positions).
I'm using wxWidgets with an OpenGL canvas and I work with C++. Following the suggestions provided in one of Nehe's tutorials I've created two quaternions, one for the pitch and one for the heading (snippets of the code are below, I can provide the full source code to anyone willing to read it).
My problem is that when I perform a rotation, the keys seems to be "locked" to the old xyz-axes, I mean, if I perform a 180° rotation around myself, the "w" key goes backward in the scene instead of forward.
How can I "lock" the keys to operate on the new reference system?
Thanks in advance to anyone willing to help me.
Roberto
<< header file >>
Quaternion heading_;
Quaternion pitch_;
GLdouble headingDeg_;
GLdouble pitchDeg_;
GLPoint position_;
<< initialization in the constructor >>
pitchDeg_ = 0.0;
headingDeg_ = 0.0;
position_.x_ = 0.0;
position_.y_ = 0.0;
position_.z_ = -15.0;
<< rotations are performed using the mouse >>
void
GLCanvas::OnMouse(wxMouseEvent& event)
{
GLdouble delta;
mouseX_ = event.GetX();
mouseY_ = event.GetY();
if(event.Dragging()) {
if(mouseX_ < centerMouseX_) {
delta = GLdouble(centerMouseX_ - mouseX_);
ChangeHeading(-0.01 * delta);
}
if(mouseX_ > centerMouseX_) {
delta = GLdouble(mouseX_ - centerMouseX_);
ChangeHeading(0.01 * delta);
}
if(mouseY_ < centerMouseY_) {
delta = GLdouble(centerMouseY_ - mouseY_);
ChangePitch(-0.01 * delta);
}
if(mouseY_ > centerMouseY_) {
delta = GLdouble(mouseY_ - centerMouseY_);
ChangePitch(0.01 * delta);
}
Refresh(false);
}
}
<< keyboard is used for translations/strafe >>
void
GLCanvas::OnKeyboard(wxKeyEvent& event)
{
switch(event.GetKeyCode()) {
case WXK_UP:
ChangePitch(3.0);
break;
case WXK_DOWN:
ChangePitch(-3.0);
break;
case WXK_LEFT:
ChangeHeading(-3.0);
break;
case WXK_RIGHT:
ChangeHeading(3.0);
break;
case (int)'w':
position_.z_ += 0.3;
break;
case (int)'s':
position_.z_ -= 0.3;
break;
case (int)'a':
position_.x_ += 0.3;
break;
case (int)'d':
position_.x_ -= 0.3;
break;
case (int)'z':
position_.y_ -= 0.3;
break;
case (int)'x':
position_.y_ += 0.3;
break;
case (int)'b':
ChangeHeading(-180.0);
break;
default:
event.Skip();
break;
}
Refresh(false);
}
<< perform rotation + translation and plot two teacups >>
void
GLCanvas::Render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/**
Roto-translation matrix
**/
GLdouble rotMatrix[16] = { 1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0 };
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
RotateCamera(rotMatrix);
glMultMatrixd(rotMatrix);
TranslateCamera(rotMatrix);
glMultMatrixd(rotMatrix);
glColor3d(1.0, 0.0, 0.0);
glutWireTeapot(3.0);
glPushMatrix();
glColor3d(0.0, 1.0, 0.0);
glTranslated(4.0, 4.0, 4.0);
glutWireTeapot(3.0);
glPopMatrix();
glFlush();
}
<< camera rotation obtained through quaternions >>
void
GLCanvas::RotateCamera(GLdouble* rotMatrix)
{
Quaternion tmp;
pitch_.CreateFromAxisAngle(1.0, 0.0, 0.0, pitchDeg_);
heading_.CreateFromAxisAngle(0.0, 1.0, 0.0, headingDeg_);
tmp = pitch_ * heading_;
tmp.CreateMatrix(rotMatrix);
}
<< camera translation -- I chose to perform it by hand >>
void
GLCanvas::TranslateCamera(GLdouble* rotMatrix)
{
rotMatrix[12] = position_.x_;
rotMatrix[13] = position_.y_;
rotMatrix[14] = position_.z_;
}
<< pitch movement >>
void
GLCanvas::ChangePitch(GLdouble degrees)
{
pitchDeg_ += degrees;
if(pitchDeg_ > 360.0)
pitchDeg_ -= 360.0;
if(pitchDeg_ < -360.0)
pitchDeg_ += 360.0;
}
<< heading movement >>
void
GLCanvas::ChangeHeading(GLdouble degrees)
{
if((pitchDeg_ > 90.0 && pitchDeg_ < 270.0)
|| (pitchDeg_ < -90.0 && pitchDeg_ > -270))
headingDeg_ -= degrees;
else
headingDeg_ += degrees;
if(headingDeg_ > 360.0)
headingDeg_ -= 360.0;
if(headingDeg_ < -360.0)
headingDeg_ += 360.0;
}
[Edited by - roby1984 on July 24, 2008 1:27:59 PM]