Tutorial Introduction
Ogre Tutorial Head In this short tutorial you will be learning to use OIS's buffered input as opposed to the unbuffered input we used last tutorial. This tutorial differs from the last in that we will be handling keyboard and mouse events immediately, as they happen, instead of once per frame. Note that this is only meant as an introduction to buffered input, and not a complete tutorial on how to use OIS. For more information on that, be sure to look at the Using OIS article.

Any problems you encounter during working with this tutorial should be posted in the Help Forum(external link).

Prerequisites

  • This tutorial assumes you have knowledge of C++ programming and are able to set up and compile an Ogre application.
  • This tutorial also assumes that you have created a project using the Ogre Wiki Tutorial Framework, either manually, using CMake or the Ogre AppWizard - see Setting Up An Application for instructions.
  • This tutorial builds on the previous basic tutorials, and it assumes you have already worked through them.


You can find the code for this tutorial here. As you go through the tutorial you should be slowly adding code to your own project and watching the results as we build it.

Getting Started

This tutorial will be building on the last tutorial, but we are changing the way that we do input. Since the functionality will be basically the same, we will use the same TutorialApplication class from last time, but we will be starting over on the TutorialFrameListener. Create a project in the compiler of your choice for this project, and add a source file which contains this code:

//code here

Buffered Input in a Nutshell

Introduction

In the previous tutorial we used unbuffered input, that is, every frame we queried the state of OIS::Keyboard and OIS::Mouse instances to see what keys and mouse buttons were being held down.
Buffered input uses listener interfaces to inform your program that events have occurred.
For example, when a key is pressed, a KeyListener::keyPressed event is fired and when the button is released (no longer being pressed) a KeyListener::keyReleased event is fired to all registered KeyListener classes.
This takes care of having to keep track of toggle times or whether the key was unpressed the previous frame.

OIS also supports unbuffered Joystick events through the OIS::JoystickListener interface, though we will not cover how to use this in this tutorial.

One important thing to note about OIS's listener system is that you can only have one listener per Keyboard, Mouse, or Joystick object.
This is done for simplicity (and for speed).
Calling the setEventCallback function (which we will cover later) multiple times will result in only the last registered listener getting events.
If you need multiple objects to get Key, Mouse, or Joystick events, you will have to write a message dispatch yourself.
Also, be sure to note that we still call the Keyboard::capture and Mouse::capture in the frameStarted method.
OIS does not use threads (or magic) to determine the keyboard and mouse states, so you will have to specify when it should capture the input.

The KeyListener Interface

OIS's KeyListener interface provides two pure virtual functions.
The first is the keyPressed function, which is called every time a key is pressed.
The second is the keyReleased function, which is called every time a key is let up.
The parameter passed to these functions is a KeyEvent, which contains the key code of what is being pressed/released.

The MouseListener Interface

The MouseListener interface is only slightly more complex than the KeyListener interface.
It contains functions to see when a mouse button was pressed or released: MouseListener::mousePressed and MouseListener::mouseReleased.
It also contains a mouseMoved function, which is called when the mouse is moved.
Each of these functions receive a MouseEvent object, which contains the current state of the mouse in the "state" variable.

The most important thing to note about the MouseState object is that it contains not only the relative X and Y coordinates of the mouse move (that is, how far it has moved since the last time the MouseListener::mouseMoved function was called), but also the absolute X and Y coordinates (that is, where exactly on the screen they are).

The Code

class TutorialFrameListener : public ExampleFrameListener, public OIS::MouseListener, public OIS::KeyListener
    : ExampleFrameListener(win, cam, true, true)

We subclass the OIS MouseListener and KeyListener classes so that we can receive events from them. Note that the OIS MouseListener handles both mouse button events and mouse movement events. The third and forth parameters of ExampleFrameListener have been change to true, which specifies that we will be using buffered input for the keyboard and mouse. We will go into more detail on how to manually set up OIS as well as the rest of Ogre in the next tutorial.

Variables

A few variables have changed from the last tutorial. I have removed mToggle and mMouseDown (which are no longer needed). I have added a few as well:

Real mRotate;          // The rotate constant
Real mMove;            // The movement constant

SceneManager *mSceneMgr;   // The current SceneManager
SceneNode *mCamNode;   // The SceneNode the camera is currently attached to

bool mContinue;        // Whether to continue rendering or not
Vector3 mDirection;     // Value to move in the correct direction

The mRotate, mMove, mSceneMgr, and mCamNode are the same as the last tutorial (though we will be changing the value of mRotate since we are using it differently). The mContinue variable is returned from the frameStarted method. When we set mContinue to be false the program will exit. The mDirection variable contains information on how we are going to translate camera node every frame.

TutorialFrameListener Constructor

In the constructor, we will initialize some of the variables as we did in the previous tutorial, and we will set the mContinue rendering to be true. Add the following code to TutorialFrameListener's constructor:

// Populate the camera and scene manager containers
mCamNode = cam->getParentSceneNode();
mSceneMgr = sceneMgr;

// set the rotation and move speed
mRotate = 0.13;
mMove = 250;

// continue rendering
mContinue = true;

The OIS mMouse and mKeyboard objects are already obtained in the ExampleFrameListener constructor. We can register the TutorialFrameListener as the listener by calling the setEventCallback method on these input objects as follows:

mMouse->setEventCallback(this);
mKeyboard->setEventCallback(this);

Last, we need to initialise mDirection to be the zero vector (since we are initially not moving):

mDirection = Vector3::ZERO;

Key Bindings



Before we go any further, we should bind the Escape key to exiting the program so we can run it. Find the TutorialFrameListener::keyPressed method. This method is called with a KeyEvent object every time a button on the keyboard goes down. We can obtain the key code (KC_*) of the key that was pressed by checking the "key" variable on the object. We will build a switch for all of the key bindings we use in the application based on this value. Find the keyPressed method and replace it with the following code:

bool keyPressed(const OIS::KeyEvent &e)
{
    switch (e.key)
    {
    case OIS::KC_ESCAPE: 
        mContinue = false;
        break;
    default:
        break;
    }
    return mContinue;
}

Make sure you can compile and run the application before continuing.

We need to add bindings for other keys in that switch statement. The first thing we are going to do is allow the user to switch between the viewpoints by pressing 1 and 2. The code for this (needs to be included in the switch statement) is the same as it was in the previous tutorial, except we no longer have to deal with the mToggle variable:

case OIS::KC_1:
    mCamera->getParentSceneNode()->detachObject(mCamera);
    mCamNode = mSceneMgr->getSceneNode("CamNode1");
    mCamNode->attachObject(mCamera);
    break;
 
case OIS::KC_2:
    mCamera->getParentSceneNode()->detachObject(mCamera);
    mCamNode = mSceneMgr->getSceneNode("CamNode2");
    mCamNode->attachObject(mCamera);
    break;

As you can see, this is much cleaner than dealing with a temporary variable to keep track of toggle times.

The next thing we are going to add is keyboard movement. Every time the user presses a key that is bound for movement, we will add or subtract mMove (depending on direction) from the correct direction in the vector:

case OIS::KC_UP:
case OIS::KC_W:
    mDirection.z = -mMove;
    break;
  
case OIS::KC_DOWN:
case OIS::KC_S:
    mDirection.z = mMove;
    break;
  
case OIS::KC_LEFT:
case OIS::KC_A:
    mDirection.x = -mMove;
    break;

case OIS::KC_RIGHT:
case OIS::KC_D:
    mDirection.x = mMove;
    break;
 
case OIS::KC_PGDOWN:
case OIS::KC_E:
    mDirection.y = -mMove;
    break;
  
case OIS::KC_PGUP:
case OIS::KC_Q:
    mDirection.y = mMove;
    break;

Now we need to "undo" the change to the mDirection vector whenever the key is released to stop the movement. Find the keyReleased method and add this code to it:

switch (e.key)
{
case OIS::KC_UP:
case OIS::KC_W:
    mDirection.z = 0;
    break;

case OIS::KC_DOWN:
case OIS::KC_S:
    mDirection.z = 0;
    break;

case OIS::KC_LEFT:
case OIS::KC_A:
    mDirection.x = 0;
    break;

case OIS::KC_RIGHT:
case OIS::KC_D:
    mDirection.x = 0;
    break;

case OIS::KC_PGDOWN:
case OIS::KC_E:
    mDirection.y = 0;
    break;

case OIS::KC_PGUP:
case OIS::KC_Q:
    mDirection.y = 0;
    break;

default:
    break;
}
return true;

Now that we have mDirection updated based on key input, we need to actually make the translation happen. This code is the exact same as the last tutorial, so add this to the frameStarted function:

mCamNode->translate(mDirection * evt.timeSinceLastFrame, Node::TS_LOCAL);

Compile and run the application. We now have key-based movement using buffered input!

Mouse Bindings

Now that we have key bindings completed, we need to work on getting the mouse working. We'll start with toggling the light on and off based on a left mouse click. Find the mousePressed function and take a look at the parameters. With OIS, we have access to both a MouseEvent as well as a MouseButtonID. We can switch on the MouseButtonID to determine the button that was pressed. Replace the code in the mousePressed function with the following:

Light *light = mSceneMgr->getLight("Light1");
switch (id)
{
case OIS::MB_Left:
    light->setVisible(! light->isVisible());
    break;
default:
    break;
}
return true;

Compile and run the application. Voilà ! Now that this is working, the only thing we have left is to bind the right mouse button to a mouse look mode. Every time the mouse is moved, we will check to see if the right mouse button is down. If it is, we will rotate the camera based on the relative movement. We can access the relative movement of the mouse from the MouseEvent object passed into this function. It contains a variable called "state" which contains the MouseState (which is basically detailed information about the mouse). The MouseState::buttonDown will tell us whether or not a particular mouse button is held down, and the "X" and "Y" variables will tell us the relative mouse movement. Find the mouseMoved function and replace the code in it with the following:

if (e.state.buttonDown(OIS::MB_Right))
{
    mCamNode->yaw(Degree(-mRotate * e.state.X.rel), Node::TS_WORLD);
    mCamNode->pitch(Degree(-mRotate * e.state.Y.rel), Node::TS_LOCAL);
}
return true;

Compile and run the application and the camera will act in free-look mode while the right mouse button is held down.

Other Input Systems

OIS is generally very good, and should suit most purposes for your application. With that said, there are alternatives if you wish to use something different. Some windowing systems may be what you are looking for, such as wxWidgets, which people have successfully integrated with Ogre. You can use the standard Windows message system or one of the many Linux GUI toolkits for input, if you don't mind your application being platform specific.

You can also try SDL, which provides not only cross platform windowing/input systems, but also joystick/gamepad input. While I cannot give you guidance on setting up wx/gtk/qt/etc with Ogre (as I've never done it before), I have had a good amount of success getting SDL's joystick/gamepad input to work well with Ogre. To get SDL's joystick system started, wrap your application's intial startup with SDL_Init and SDL_Quit calls (this can be in your application's main function, or possibly in your application object):

SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE);
SDL_JoystickEventState(SDL_ENABLE);

app.go();

SDL_Quit();

To setup the Joystick, call SDL_JoystickOpen with the Joystick number (you can specify multiple joysticks by calling with 0, 1, 2...):

SDL_Joystick* mJoystick;
mJoystick = SDL_JoystickOpen(0);
 
if ( mJoystick == NULL )
    ; // error handling

If SDL_JoystickOpen returns NULL, then there was a problem opening the joystick. This almost always means that the joystick you requested doesn't exist. Use SDL_NumJoysticks to find out how many joysticks are attached to the system. You also need to close the joystick after you are done with it:

SDL_JoystickClose(mJoystick);

To use the joystick, call the SDL_JoystickGetButton and SDL_JoystickGetAxis buttons. I personally used this with a playstation2 controller, so I had four axes to play with and twelve buttons to play with. This is my movement code:

SDL_JoystickUpdate();
 
mTrans.z += evt.timeSinceLastFrame * mMoveAmount * SDL_JoystickGetAxis(mJoystick, 1) / 32767;
mTrans.x += evt.timeSinceLastFrame * mMoveAmount * SDL_JoystickGetAxis(mJoystick, 0) / 32767;

xRot -= evt.timeSinceLastFrame * mRotAmount * SDL_JoystickGetAxis(mJoystick, 3) / 32767;
yRot -= evt.timeSinceLastFrame * mRotAmount * SDL_JoystickGetAxis(mJoystick, 2) / 32767;

mTrans was later fed into the camera's SceneNode::translate method, xRot was fed into SceneNode::yaw, and yRot was fed into SceneNode::pitch. Note that the SDL_JoystickGetAxis returns a value between -32767 and 32767, so I have scaled it to be between -1 and 1. This should get you started using SDL joystick input. If you are looking for more information in this area, the best documentation comes from the SDL joystick header.

You should also refer to the standard SDL documentation if you start to seriously use this in your application.

Proceed to Basic Tutorial 6 The Ogre Startup Sequence

<HR>
Creative Commons Copyright -- Some rights reserved.


THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.

1. Definitions

  • "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
  • "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
  • "Licensor" means the individual or entity that offers the Work under the terms of this License.
  • "Original Author" means the individual or entity who created the Work.
  • "Work" means the copyrightable work of authorship offered under the terms of this License.
  • "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
  • "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.

2. Fair Use Rights

Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.

3. License Grant

Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:

  • to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
  • to create and reproduce Derivative Works;
  • to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
  • to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
  • For the avoidance of doubt, where the work is a musical composition:
    • Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
    • Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
    • Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).


The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.

4. Restrictions

The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:

  • You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(c), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(c), as requested.
  • You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.5 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
  • If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.

5. Representations, Warranties and Disclaimer

UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.

6. Limitation on Liability.

EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

7. Termination

  • This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
  • Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.

8. Miscellaneous

  • Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
  • Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
  • If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
  • No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
  • This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.