OgreDotNet Basic Tutorial 4        

OgreDotNet Beginner Tutorial 4: Event Handling

Original version by Clay Culver

C# changes by DigitalCyborg

Note: This was written for Ogre 1.2 and is known to compile against it. Any problems you encounter while working with this tutorial should be posted to the Help Forum.

Prerequisites

This tutorial assumes you have basic knowledge of C# programming and are able to setup and compile an OgreDotNet application in C# (if you have trouble setting up your application, see this guide for specific compiler setups). This tutorial builds on the previous C# beginner tutorials, and it assumes you have already worked through them.

Introduction

In this tutorial we will be introducing one of the most useful OgreDotNet constructs: the Eventhandler. By the end of this tutorial you will understand EventHandler and Event Delegates, how to use them to do things that require updates every frame, and how to use them.

As you go work tutorial you should be slowly adding code to your own project and watching the results as we build it.

Getting Started

As with the previous tutorials, we will be using a pre-constructed code base as our starting point. Create a project in the compiler of your choice (VS200X assumed) for this project, and add a source file which contains this code:

using System;
 using System.Drawing;
 using OgreDotNet;
 using Math3D;
 
 namespace TutorialApplication4
 {
   class TutorialApplication4 : ExampleApplication
   {
       protected bool MouseDown;       // Whether or not the left mouse button was down last frame
       protected float mToggle;        // The time left until next toggle
       protected float mRotate;        // The rotate constant
       protected float mMove;          // The movement constant
       protected SceneNode mCamNode = null;   // The SceneNode the camera is currently attached to
       
 
       protected override void CreateEventHandler()
       {
       }
 
       protected override bool FrameEnded(FrameEvent e)
       {
       }
 
       protected override bool FrameStarted(FrameEvent e)
       {
       }
 
       protected override void MouseMotion(MouseMotionEvent e)
       {
       }
 
       protected override void MouseDragged(MouseMotionEvent e)
       {
       }
 
       protected override void MouseClick(MouseEvent e)
       {
       }
 
       protected override void KeyClicked(KeyEvent e)
       {
       }
 
       protected override void  KeyPressed(KeyEvent e)
       {
       }
 
       protected override void KeyReleased(KeyEvent e)
       {
       }
 
       protected override void MousePressed(MouseEvent e)
       {
       }
 
       protected override void MouseReleased(MouseEvent e)
       {
       }
 
       protected override void  CreateCamera()
       {
       }
 
       protected override void CreateScene()
       {
       }
 
       //[STAThread]
       static void Main(string[] args)
       {
           using(TutorialApplication4 app = new TutorialApplication4())
           {
               app.Start();
           }
       }
    }
 }

We will be defining the program controls during this tutorial.

EventHandling

Introduction

In the previous tutorials we only looked at what we could do when we add code to the createScene method. In OgreDotNet, we use Event Delegates to receive notification when an input event happens. The EventHandler interface uses Event Delegates to handle Mouse and Keyboard events. In C#, We define Event Delegates to handle the events which occure When a mouse or keyboard event occurs. If you are familar with "callbacks", these are the same thing: Functions(Methods) which get called when something happens.

Since the ExampleApplication already defines them. we'll need to use the override keyword.

protected override bool FrameEnded(FrameEvent e) { return true; }
       protected override bool FrameStarted(FrameEvent e) { return true; }
       protected override void MouseMotion(MouseMotionEvent e) {}
       protected override void MouseDragged(MouseMotionEvent e) {} 
       protected override void MouseClick(MouseEvent e) {}
       protected override void KeyClicked(KeyEvent e) {}
       protected override void KeyPressed(KeyEvent e) {}
       protected override void KeyReleased(KeyEvent e) {}
       protected override void MousePressed(MouseEvent e) {}  
       protected override void MouseReleased(MouseEvent e) {}

Ogre's (and OgreDotNet's) main loop (Root::startRendering) looks like this:

  1. The Root object calls the frameStarted method on all registered FrameListeners.
  2. The Root object renders one frame.
  3. The Root object calls the frameEnded method on all registered FrameListeners.


In the ExampleApplication in C#, this loops until mDone is set to true or (I think) until FrameStarted() or FrameEnded returns false. If you set mDone to true or return false from either Frame* function, the program will exit.

Registering an EventHandler

The FrameEvent object contains two variables, but only the timeSinceLastFrame is useful to us right now. This variable keeps track of how long it's been since the FrameStarted or FrameEnded last fired. Note that in the FrameStarted method, TimeSinceLastFrame will contain how long it has been since the last FrameStarted event was last fired (not the last time a FrameEnded method was fired). This tutorial uses the mDeltaTime variable inherited from the ExampleApplication.

mDeltaTime = e.TimeSinceLastFrame;

One important concept to realize about OgreDotNet's Event Handlers is that they are called asynchronously when each event happens. Each one actually runs as its own thread. For the all the dirty details read this http://msdn.microsoft.com/msdnmag/issues/02/12/BasicInstincts/

This is why we define event delegates for each event. The ExampleApplication already sets up a nice, neat clean framework in the OgreDotNet.EventHandler class, so we'll use it in our version of CreateEventHandler():

protected override void CreateEventHandler()
       {
           mEventHandler = new OgreDotNet.EventHandler(mRoot, mRenderWindow);
           mEventHandler.SubscribeEvents();
           mEventHandler.FrameStarted += new FrameEventDelegate(FrameStarted);
           mEventHandler.FrameEnded += new FrameEventDelegate(FrameEnded);
       }

See how easy it is to use? We just call the SubscribeEvents function to register the EventHandler with the framework and then tell the EventHandler instance which functions to use when events happen. :-)

You might also notice that the main loop really only does three things, and since nothing happens in between the frameEnded and FrameStarted methods being called, you can use them almost interchangably. Where you decide to put all of your code is entirely up to you. You can put it all in one big FrameStarted or FrameEnded method, or you could divide it up between the two.

Currently the above code will compile, but since we have overridden the CreateEventHandler method of ExampleApplication and CreateCamera, if you run the application you will not be able to kill it. We'll fix this problem soon. But first, lets create the camera.

Find the TutorialApplication4::createCamera method and add the following code to it:

// create camera, but leave at default position
           mCamera = mSceneManager.CreateCamera("PlayerCam");
           mCamera.SetNearClipDistance(5);

We have not done anything out of the ordinary with this. The only reason we need to overide ExampleApplication's createCamera method is because the createCamera method moves the camera and changes its orientation, which we do not want for this tutorial.

Since the Root class is what renders frames, it also is in charge of keeping track the EventHandler. The first thing we need to do is create an instance of our EventHandler. Find the TutorialApplication::createEventHandler method, and notice this code:

mEventHandler = new OgreDotNet.EventHandler(mRoot, mRenderWindow);
          mEventHandler.SubscribeEvents();

The mRoot and mRenderWindow variables are defined in the ExampleApplication class. We call the SubscribeEvents() function to tell the framework what to do when an event happens. As I already mentioned, this is not the whole story though, we'll define and setup callbacks for each event later in the tutorial.

Be sure you can compile the application before continuing. It's not useful to run it though. we haven't told the event handler how to handle any mouse or key events, and hence, there is really no way to exit cleanly.

Setting up the Scene

Introduction

Before we dive directly into the code, I would like to briefly outline what we will be doing so that you understand where I am going when we create and add things to the scene.

We will be placing one object (a ninja) in the scene, and one point light in the scene. If you left click the mouse, the light will toggle on and off. Holding down the right mouse button turns on "mouse look" mode (that is, you look around with the Camera). We will also be placing SceneNodes around the scene which we will be attaching the Camera to for different viewports. Pressing the 1 and 2 buttons chooses which Camera viewpoint to view the scene from. We also add some functionality to move the camera around with the keyboard.

The Code

Find the TutorialApplication::createScene method. The first thing we will be doing is setting the ambient light of the scene very low. We want scene objects to still be visible when the light is off, but we also want the light going on/off to be noticable. I also prefer a black background: (maybe you like Orange or something? .. change it if you want)

mSceneManager.SetAmbientLight(Color.FromArgb(63,63,63));
         mRenderWindow.GetViewport(0).SetBackgroundColour(Color.Black);

Note that the arguments to FromArgb are in the range 0,255, so 63 is the same as .25 in the non DotNet version of SetAmbientLight

Now, add a Ninja entity to the scene at the origin:

Entity ent = mSceneManager.CreateEntity("Ninja","ninja.mesh");
         SceneNode node = mSceneManager.GetRootSceneNode().CreateChildSceneNode("NinjaNode");
         node.AttachObject(ent);

Now we will create a white point light and place it in the Scene, a small distance (relatively) away from the Ninja:

Light light = mSceneManager.CreateLight("Light1");
         light.SetLightType(Light.LightTypes.Point);
         light.SetPosition(new Vector3(250,150,250));
         light.SetDiffuseColour(Color.White);
         light.SetSpecularColour(Color.White);

Now we need to create the SceneNodes which the Camera will be attached to. It is important to note that when using this system for Cameras we need to have a seperate SceneNode to handle the pitch (up and down rotation) of the Camera. I will go into full detail why we need that when we actually move the Camera later on. For now, lets create the first SceneNode and have it face the ninja:

// Create the scene node
         node = mSceneManager.GetRootSceneNode().CreateChildSceneNode("CamNode1",new Vector3(-400,200,400));
           
         // Make it look towards the ninja
         node.Yaw(-45.0f);

Now, we need to create a child SceneNode that will control the pitch of the camera (and it will have the Camera itself attached to it). At this point, we'll also save which node the camera is attached to.:

// Create the pitch nod
         node.CreateChildSceneNode("PitchNode1");
         node.AttachObject(mCamera);
         mCamNode = node;

Now, we have a heirarchy of nodes. The Camera is attached to "PitchNode1", which is attached to "CamNode1", which is attached to the SceneManager's root SceneNode. When we want to move the camera or yaw/roll it, we do that to "CamNode1". If we want to change the pitch of the Camera, we do that to "PitchNode1".

Now, add a second CamNode and PitchNode. We will use these later as a second viewing position for our Camera:

// create the second camera node/pitch node
         node = mSceneManager.GetRootSceneNode().CreateChildSceneNode("CamNode2",new Vector3(0,200,400));
         node = node.CreateChildSceneNode("PitchNode2");

Now we are done with the CreateScene(). Onto the Event Handling!

Event Handling Part II, adding code to our callbacks

Variables which we'll use:

We have defined a few variables in the TutorialFrameListener class which I'd like to go over before we get any further:

bool mMouseDown;       // Whether or not the left mouse button was down last frame
     float mToggle;          // The time left until next toggle
     float mRotate;          // The rotate constant
     float mMove;            // The movement constant
     SceneNode *mCamNode;   // The SceneNode the camera is currently attached to

The mCamNode holds the current SceneNode that the Camera is attached to (that would be the "CamNodeX", not the "PitchNodeX"). The mRotate and mMove are our constants of rotation and movement. If you want the movement or rotation to be faster or slower, tweak those variables to be higher or lower.

The other two variables (mToggle and mMouseDown) control our input.
We will be using callbacks for mouse and key input in this tutorial. This means that we will be setting up methods that are called methods by our EventHandlerr to use the input provided by the keyboard and mouse. We run into an interesting problem when we try to use the keyboard to change the state of some object on the screen. If we see that a key is down, we can act on this information, but what happens the next frame? Do we see that the same key is down and do the same thing again? In some cases (like movement with the arrow keys) this is what we want to do. However, lets say we want the "T" key to toggle between a light being on or off. The first frame the T key is down, the light gets toggled, the next frame the T key is still down, so it's toggled again...and again and again until the key is released. We have to keep track of the key's state between frames to avoid this problem.

The mMouseDown keeps track of whether or not the mouse was also down the previous frame (so if mMouse down is true, we do not perform the same action again until the mouse is released). The mToggle button specifies the time until we are allowed to perform an action again. That is, when a button is pressed, mToggle is set to some length of time where no other actions can occur.

Construction

The first thing to notice about the class variables is that we initialize them to values which are useful (at least to us) ExampleFrameListener's constructor:

We will set default values for all variables:

protected bool MouseDown = false;       // Whether or not the left mouse button was down last frame
       protected float mToggle = 0.0f;          // The time left until next toggle
       protected float mRotate = 0.13f;          // The rotate constant
       protected float mMove = 250.0f;            // The movement constant
       protected SceneManager SceneMgr = null;   // The current SceneManager
       protected SceneNode mCamNode = null;   // The SceneNode the camera is currently attached to

The FrameStarted Method



Now we are going to get into the real meat of the tutorial: performing actions every event. Currently our FrameStarted method has the following code in it:

if (mRenderWindow.Closed || mDone) return false;
 
         mDeltaTime = e.TimeSinceLastFrame;
 
         UpdateDebugOverlay();

This is almost enough to get us running in windowed mode (since closing the window will set mRenderWindow.Closed. Once we add some code to CreateEventHandler we can run our application in windowed mode:

mEventHandler.FrameStarted += new FrameEventDelegate(FrameStarted);
           mEventHandler.FrameEnded += new FrameEventDelegate(FrameEnded);

The KeyClicked Method



The next callback that we'll setup is the KeyClicked function. In This function, we are passed a KeyEvent to handle. In this part of the tutorial, we'll handle 3 keys. First, the Escape key. The code for this is fairly straightforward. Since the ExampleApplication already contains a member variable mDone, which tells it when to exit, all we have to do is set it.

protected override void KeyClicked(KeyEvent e)
       {
           switch (e.KeyCode)
           {
               case KeyCode.Escape:
                   mDone = true;
                   break;
            }
       }

Ok,now that we've handled that (ESC), lets add some functionality to switch the camera between our two cam nodes. We'll do this by adding code for handling a user pressing '1' and a user pressing '2'.

Now that we have a way to kill the application in FullScreen mode (pressing ESC) we need to tell our Event Handler (mEventHandler) which function to use when a key is pressed. Update CreateEventHandler() with this:

mEventHandler.KeyClicked += new KeyEventDelegate(KeyClicked);

Next, we'll be adding functionality to change which node the camera is attached to:

When a user presses 1, we'll detach the camera from its current node and attach it camnode1.

case KeyCode.One:
                   if (mToggle < 0.0f)
                   {
                       mToggle = 1.0f;
 
                       mCamera.GetParentSceneNode().DetachObject(mCamera);
                       mSceneManager.GetSceneNode("PitchNode1").AttachObject(mCamera);
                       mCamNode = mSceneManager.GetSceneNode("CamNode1");
                   }
                   break;

In order to disallow rapid clicking between '1' & '2' we use the mToggle variable. We check it here to make sure that its less than zero and reset it to 1.0 after we toggle. We'll decrement it in FrameStarted:

if (mToggle >= 0.0f)
               mToggle -= mDeltaTime;

Similarly, When a user presses '2' we'll detach it and add it to CamNode2

case KeyCode.Two:
                 if (mToggle < 0.0f)
                   {
                       mToggle = 1.0f;
                       
                       mCamera.GetParentSceneNode().DetachObject(mCamera);
                       mSceneManager.GetSceneNode("PitchNode2").AttachObject(mCamera);
                       mCamNode = mSceneManager.GetSceneNode("CamNode2");
                   }
                   break;

At this point, the KeyClicked function is completed and looks like this:

protected override void KeyClicked(KeyEvent e)
       {
           switch (e.KeyCode)
           {
               case KeyCode.Escape:
                   mDone = true;
                   break;
 
               case KeyCode.One:
                   if (mToggle < 0.0f)
                   {
                       mToggle = 1.0f;
 
                       mCamera.GetParentSceneNode().DetachObject(mCamera);
                       mSceneManager.GetSceneNode("PitchNode1").AttachObject(mCamera);
                       mCamNode = mSceneManager.GetSceneNode("CamNode1");
                   }
                   break;
 
               case KeyCode.Two:
                  if (mToggle < 0.0f)
                   {
                       mToggle = 1.0f;
                       
                       mCamera.GetParentSceneNode().DetachObject(mCamera);
                       mSceneManager.GetSceneNode("PitchNode2").AttachObject(mCamera);
                       mCamNode = mSceneManager.GetSceneNode("CamNode2");
                   }
                   break;
           }
       }

The MousePressed Method



The next tidbit of functionality we'll add handles what happens when a mouse key is pressed. In this tutorial, we want to toggle the light when the left mouse button is pressed. We'll do this by updating the code in the MousePressed fxn.

The mouse button event handler uses a bitvector (e.ButtonID) to specify which button is pressed. The binary value 5'b 10000 (16) is the left button. The code to toggle the light is also pretty simple. All we need to do is check the value returned by the light's IsVisible() method and use the light's SetVisible() method to set it to the opposite value:

switch(e.ButtonID)
           {
               case 16:
                   Light light = mSceneManager.GetLight("Light1");
                   light.SetVisible(!light.IsVisible());
                   break;
           }

So, at this point MousePressed looks like this:

protected override void MousePressed(MouseEvent e)
       {
           switch(e.ButtonID)
           {
               case 16:
                   Light light = mSceneManager.GetLight("Light1");
                   light.SetVisible(!light.IsVisible());
                   break;
           } 
       }

Now we can update CreateEventHandler to tell the framework to use our MousePressed function.

mEventHandler.MousePressed += new MouseEventDelegate(MousePressed);

The KeyPressed method



Now that we can toggle the light and switch the camera viewpoints, let's add some functionality to move the camera around using the A S D W Q E and Up, Down, Left, Right, PageUp, & PageDown keys.

The ExampleApplication already contains a variable which we can use to determine how we should move the camera (mMoveCam) so we'll use it. In this function all we need to do is use mMoveCam as a bitvector to determine which way to move. We'll use each of the bits in mMoveCam to determine which way to move: bit 0 (decimal #1) to move forward, bit 1 (decimal #2) to move backwards, bit 2 (decimal #4) to move left, bit 3 (decimal #8) to move right, bit 4 (decimal #16) to move up, and bit 5 (decimal #32) to move right.

We'll use the information in this bitvector (mMoveCam) in FrameStarted (which we'll update a little later in the tutorial)

The KeyPressed function is basically a big switch which is called whenever a key is pressed. If you don't like my keybindings (or maybe are doing something wierd, like using a Dvorak keyboard), change them to something that suits your style better.

protected override void  KeyPressed(KeyEvent e)
       {
           switch(e.KeyCode)
           {
               case KeyCode.W:
               case KeyCode.Up:
                   mMoveCam |= 1;
                   break;
 
               case KeyCode.S:
               case KeyCode.Down:
                   mMoveCam |= 2; 
                   break;
 
               case KeyCode.A:
               case KeyCode.Left:
                   mMoveCam |= 4;
                   break;
 
               case KeyCode.D:
               case KeyCode.Right:
                   mMoveCam |= 8;
                   break;
 
               case KeyCode.PageUp:
               case KeyCode.Q:
                   mMoveCam |= 16;
                   break;
 
               case KeyCode.PageDown:
               case KeyCode.E:
                   mMoveCam |= 32;
                   break;
               default:
                   break;
           }
       }

And, yep.. you probably guessed it. Update CreateEventHandler to use our KeyPressed function

mEventHandler.KeyPressed += new KeyEventDelegate(KeyPressed);

The KeyReleased method



Similarly, the KeyReleased method will be called whenever a key is released. In this tutorial, all we need to do is unset the bit of mMoveCam that we set in KeyPressed:

protected override void KeyReleased(KeyEvent e)
       {
           switch (e.KeyCode)
           {
               case KeyCode.W:
               case KeyCode.Up:
                   mMoveCam &= ~1;
                   break;
 
               case KeyCode.S:
               case KeyCode.Down:
                   mMoveCam &= ~2;
                   break;
 
               case KeyCode.A:
               case KeyCode.Left:
                   mMoveCam &= ~4;
                   break;
 
               case KeyCode.D:
               case KeyCode.Right:
                   mMoveCam &= ~8;
                   break;
 
               case KeyCode.PageUp:
               case KeyCode.Q:
                   mMoveCam &= ~16;
                   break;
 
               case KeyCode.PageDown:
               case KeyCode.E:
                   mMoveCam &= ~32;
                   break;
               default:
                   break;
           }
       }

Don't forget to update CreateEventHandler() so that it uses our KeyReleased method

mEventHandler.KeyReleased += new KeyEventDelegate(KeyReleased);

Using the variables set by KeyPressed in FrameStarted



Now that we have a bitvector (mMoveCam) set when a user presses a key, we need to update FrameStarted to actually use it.
The method we use here is also fairly straightforward. All we need to do is check which bit is set and use it. In order to do that we'll use a Vector3 which contains the direction to move. We'll also use the mMoveScale variable in the ExampleApplication (override it if you want) and the time since the last frame to determine how far to move:

if (mMoveCam > 0)
           {//mMoveCam bits: 1=forward, 2=backward, 4=left, 8=right, 16=up, 32=down
                   Vector3 vCamMove = Vector3.Zero;
                   float mvscale = mMoveScale * e.TimeSinceLastFrame;
 
                   if ((mMoveCam & 1) > 0)
                       vCamMove += Vector3.NegativeUnitZ;
                   if ((mMoveCam & 2) > 0)
                       vCamMove += Vector3.UnitZ;
                   if ((mMoveCam & 4) > 0)
                       vCamMove += Vector3.NegativeUnitX;
                   if ((mMoveCam & 8) > 0)
                       vCamMove += Vector3.UnitX;
                   if ((mMoveCam & 16) > 0)
                       vCamMove += Vector3.UnitY;
                   if ((mMoveCam & 32) > 0)
                       vCamMove += Vector3.NegativeUnitY;
                   if((mMoveCam & 64) > 0) 
 
                   vCamMove *= mvscale;
                   mCamera.MoveRelative(vCamMove);
           }

At this point we are done with FrameStarted.. Here it is:

protected override bool FrameStarted(FrameEvent e)
       {
           if (mRenderWindow.Closed || mDone) return false;
 
           mDeltaTime = e.TimeSinceLastFrame;
 
           UpdateDebugOverlay();
 
           if (mToggle >= 0.0f)
               mToggle -= mDeltaTime;
 
           if (mMoveCam > 0)
           {//mMoveCam bits: 1=forward, 2=backward, 4=left, 8=right, 16=up, 32=down
                   Vector3 vCamMove = Vector3.Zero;
                   float mvscale = mMoveScale * e.TimeSinceLastFrame;
 
                   if ((mMoveCam & 1) > 0)
                       vCamMove += Vector3.NegativeUnitZ;
                   if ((mMoveCam & 2) > 0)
                       vCamMove += Vector3.UnitZ;
                   if ((mMoveCam & 4) > 0)
                       vCamMove += Vector3.NegativeUnitX;
                   if ((mMoveCam & 8) > 0)
                       vCamMove += Vector3.UnitX;
                   if ((mMoveCam & 16) > 0)
                       vCamMove += Vector3.UnitY;
                   if ((mMoveCam & 32) > 0)
                       vCamMove += Vector3.NegativeUnitY;
                   if((mMoveCam & 64) > 0) 
 
                   vCamMove *= mvscale;
                   mCamera.MoveRelative(vCamMove);
           }
           return true;
       }

Looking Around with the camera



At this point in the tutorial, we can toggle the light using the Left mouse button, we can change camera position using the '1' and '2' keys and we can move the the camera around using WASDQE or Left,Right,Up,Down,PgUp,PdDown keys.

Let's add some 'mousen ook' functionality next. In this tutorial, we only want mouse look when the RIGHT mouse button is pressed. This slightly complicates things. In order to do this we'll use the mMoveCam bitvector similar to how we used it earlier. The next available, unused bit in our mMoveCam bit vector is 64.

We already setup an event delegate for the mouse pressed function to toggle the light, so we'll use it to update the mMoveCam bitvector when the RIGHT mouse button is pressed. Update the switch statement in MousePressed and it should look like this after you are done.

protected override void MousePressed(MouseEvent e)
       {
           switch(e.ButtonID)
           {
               case 16:
                   Light light = mSceneManager.GetLight("Light1");
                   light.SetVisible(!light.IsVisible());
                   break;
               case 32:
                   mMoveCam |= 64;
                   break;
           } 
       }

Since we only want to use mouselook when the the right button is pressed we need to define another event delegate for the mouse button released event. All we want to do here is clear the button we set in MousePressed.

protected override void MouseReleased(MouseEvent e)
       {
           switch (e.ButtonID)
           {
               case 32:
                   mMoveCam &= ~64;
                   break;
           }
       }

Now, what happens when the mouse is moved?... The answer is nothing until you tell the code how to handle the MouseMotion & MouseDragged events. The mouseDragged event is called when a button is held while the mouse is moved and the MouseMotion event is called when the mouse is moved. since we already have a variable (well, actually a bit in a bitvector) to determine if we should mouselook, we dont need to do anything in MouseDragged except call MouseMotion

protected override void MouseDragged(MouseMotionEvent e)
       {
           this.MouseMotion(e);
       }

In the MouseMotion method, we'll check for the mouselook bit and use the Pitch and Yaw functions provided by the Ogre Framework:

protected override void MouseMotion(MouseMotionEvent e)
       {
           if ((mMoveCam & 64) > 0)
           {
               mCamera.Pitch(new Radian(-e.DeltaY * mDeltaTime * 500.0f));
               mCamera.Yaw(new Radian(-e.DeltaX * mDeltaTime * 500.0f));
           }
       }

Well, the only thing that is left is to setup our event handler to call our event delegates for mouse. Update CreateEventListener with:

mEventHandler.MouseMoved += new MouseMotionEventDelegate(MouseMotion);
           mEventHandler.MouseClicked += new MouseEventDelegate(MouseClick);
           mEventHandler.MousePressed += new MouseEventDelegate(MousePressed);
           mEventHandler.MouseReleased += new MouseEventDelegate(MouseReleased);
           mEventHandler.MouseDragged += new MouseMotionEventDelegate(MouseDragged);

End



If you didn't understand this, ask questions in the OrgeDotNet Forum.

Credits

Original tutorial by Clay Culver.

C# Update by DigitalCyborg