Initial Mouse Position With OIS and CEGUI         How to initialize the mouse cursor to a starting position

The Problem: Setting mouse position

In Intermediate Tutorial 2, you learn how to display a mouse cursor using CEGUI and use it to trigger actions (e.g. left-clicking creates a Robot at the mouse cursor position). However, when starting, you can only set the mouse position to the left-upper corner. If you set the mouse cursor to start in the middle of the screen, for example, the robot appears some centimeters left and above the cursor when left-clicking. We want to fix that, so that the mouse can be properly initialized to any position we want.

First, some snippets of the non-working (!) code version:

'''In MouseListeners constructor:'''
 //Register this so that we get mouse events.
 mMouse->setEventCallback( this );
 
 '''In mousePressed(const OIS::MouseEvent &arg,OIS::MouseButtonID id):'''
 Ray mouseRay = mCamera->getCameraToViewportRay(  arg.state.X.abs/float(arg.state.width), arg.state.Y.abs/float(arg.state.height) );
 mRaySceneQuery->setRay( mouseRay );
 //...intersect and create robot
 
  '''In mouseMoved (const OIS::MouseEvent& arg):'''
 // Update CEGUI with the mouse motion
 CEGUI::System::getSingleton().injectMouseMove( arg.state.X.rel, arg.state.Y.rel );

The Explanation: Mouse states

The mouse position is saved in two positions: once in OIS and once in CEGUI. When initialized, OIS sets the absolute mouse position to (0,0) (top-left pixel of the screen). On the other hand, CEGUI initializes the mouse cursors absolute position to appear in the middle of the screen. When moving the mouse, we use OIS relative mouse position and inject this change into CEGUI, so the OIS mouse and CEGUI mouse move by the same amount, but to different positions. Now when we click, we construct a Ray based on the initial position of the OIS mouse cursor.

Solution 1: Change CEGUI's mouse position

Generally, OIS drives the position of CEGUI's displayed cursor. In this light, we then move CEGUI's cursor to the top left (0,0) in order to align it with the initial position of OIS's cursor location:

''' Some time during initialization: '''
 // Move CEGUI mouse to (0,0)                                        
 CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();  
 CEGUI::System::getSingleton().injectMouseMove(-mousePos.d_x,-mousePos.d_y);


Alternatively, if you're really worried that OIS will not initially be at (0,0), as this may be the case in future releases:

''' Some time during initialization: '''
 // Align CEGUI mouse with OIS mouse
 const OIS::MouseState state = mMouse->getMouseState();
 CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();  
 CEGUI::System::getSingleton().injectMouseMove(state.X.abs-mousePos.d_x,state.Y.abs-mousePos.d_y);

Note that you will need to replace mMouse with a pointer to your own constructed OIS::Mouse object. Also, do this before actually setting CEGUI's mouse cursor theme, that way the user doesn't see any cursor jumps.

Solution 2: Using CEGUIs mouse position

One possible solution is to simply use CEGUI's mouse position. Just change

'''In mouseMoved (const OIS::MouseEvent& arg):'''
 Ray mouseRay = mCamera->getCameraToViewportRay(  arg.state.X.abs/float(arg.state.width), arg.state.Y.abs/float(arg.state.height) );

to

CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();
 Ray mouseRay = mCamera->getCameraToViewportRay(  mousePos.d_x/float(arg.state.width), mousePos.d_y/float(arg.state.height) );

in order to use CEGUI's mouse position. This however has the disadvantage that OIS and CEGUI have different absolute mouse position until you moved the mouse to the top left corner. However, most people should be able to live with that.

Solution 3: Setting OIS mouse position

If you want CEGUI and OIS to have the same absolute mouse positions, so you don't have to think about which one you want to use. OIS has no interface to allow this, we can get a const reference to the absolute mouse position and cast away the "constness":

'''In MouseListeners constructor:'''
 CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();
 OIS::MouseState &mutableMouseState = const_cast<OIS::MouseState &>(mMouse->getMouseState());
 mutableMouseState.X.abs = mousePos.d_x;
 mutableMouseState.Y.abs = mousePos.d_y;

This is a bit hackish, as this is not the intended way to use the OIS API, but it works.

Alias: Initial_Mouse_Position_With_OIS_and_CEGUI