Sunday, 28 April 2013

Moving Around a 3D cube with Mouse and Keyboard (Part 1)

I never got the hang of XBox controllers

So moving on from a painfully coloured 3D spinning cube created in OpenGL and Qt (see here), I now want to add user input via a mouse and keyboard. The first part of this will be fairy easy and just result in being able to rotate the cube when ever the middle mouse button is pressed - 'mouse look' for those in the know.

So, first up, we need to put an additional function in for checking mouse movement. Qt makes this very easy (not surprisingly) as all widgets can catch mouse events by overriding the mouseMoveEvent. So after adding the following:

header file: 

protected:
   void mouseMoveEvent(QMouseEvent *event);

cpp file:

void OGLWidget::mouseMoveEvent(QMouseEvent *event)
{

}

We now have a function that will catch any mouse movement. Or mostly, anyway. This will only work with movement when a button is pressed (dragging basically) which won't quite work for this. You therefore should add the following to the constructor of the widget:

setMouseTracking(true);

This will set the widget to track ALL mouse movements.

So now, how do we ensure the cube moves around with the mouse and stops when we release the button? We need to keep two 'temporary' (but member) variables that record the rotation of the cube and mouse coords. The new rotation is then calculated based on the difference between this mouse position and the current one, which is then added to the stored rotation when the mouse button was pressed. As with pictures, code is often worth a thousand words so all of that can probably more easily be understood by viewing the following:

void OGLWidget::mouseMoveEvent(QMouseEvent *event)
{
    // is the middle mouse button down?
    if (event->buttons() == Qt::MidButton)
    {
        // was it already down? If not, store the current coords
        if (!mouseLook_)
        {
            tmpMousePos_ = event->pos();
            tmpRotValue_ = rotValue_;
            mouseLook_ = true;
        }

        // update the rotation values depending on the relative mouse position
        rotValue_.setX( tmpRotValue_.x() + (tmpMousePos_.x() - event->pos().x()) * 0.2 );
        rotValue_.setY( tmpRotValue_.y() + (tmpMousePos_.y() - event->pos().y()) * -0.2 );
    }
    else
    {
        // turn off mouse look
        mouseLook_ = false;
    }
}

Things to note:
  • I've got an additional flag showing whether mouse look is on or not - I could have set one of the other tmp variables to a special value but this is almost never a good idea and variables are (generally) cheap.
  • I've applied a factor of 0.2 to each mouse movement. This is basically the mouse speed and should be configurable an ideal world
  • To incorporate two axis rotation, I've changed rotValue_ to a QPoint type where x stores the y-axis rotation and y stores the x axis rotation.

So you can now rotate the cube when holding down the middle mouse button. Next time, we try to actually move the camera using the WASD keys. This will require a few more changes to the rendering code.

Find the code at:

https://github.com/doc-sparks/Interface/tree/v0.2

No comments:

Post a Comment