Introduction

This tutorial will show you how to integrate OgreSocks with Ogre. The current version of OgreSocks is 2.0.1. I am going to assume that you already have Ogre installed on your machine, and compiling and running properly. This tutorial extends Ogre's basic tutorial 5, so if you have not done this then you can do so here. In the basic tutorial 5, you learn how to use buffered input with OIS to move the camera around the screen. After working through this tutorial, you will be able to move the camera around by pressing keys on a remote PC. Please make sure that basic tutorial 5 is building properly, and that you can move the camera around the screen using the keys before you continue.

Before we get started, I should tell you that the current version of OgreSocks is built against version 1.4.8 of Ogre. OgreSocks does depend on Ogre, but it only uses Ogre's String and Exception classes, so if you have a different version of Ogre then chances are it will build against this too. But I have only tested with Ogre 1.4.8. You should also know that OgreSocks is built using Visual Studio.NET 2008. So if you want to use OgreSocks straight 'out of the box', then you will need to be building Ogre in Visual Studio.NET 2008 too. If you are using an earlier version of Visual Studio to build Ogre, then you will have to do a bit of project converting for OgreSocks, but I'm not going to talk about that here. Also, Visual Studio.NET express edition does not come with ATL, which you will need to run OgreSocks. And sorry Linux users, at the moment OgreSocks only runs on windows.

Getting Started

Before we start hammering away at the code, let's get Visual Studio set up correctly. I will also assume that you have already downloaded OgreSocks. If not, what are you waiting for ?! Get it from here. When you download the zip file, you will find a direcrory called OgreSocks_v2.0.1_vs2008 (assuming you have downloaded the latest version). The main project is contained in this folder. Also inside this folder is another folder called 'documentation'. This contains loads of html files, as generated by doxygen. The html files basically contain descriptions of all the public function in OgreSocks, and you can use these as reference should you feel the need. There are quite a few html files in the documentation folder, but all you really need is index.html.

Getting OgreSocks to build is mega easy. Open it up and hit build. Chances are you get a compile error saying:

Error 1 fatal error C1083: Cannot open include file: 'Ogre.h': No such file or directory

To fix this, you need to go to the property pages for the project, and in the C/C++ tab, under General, you need to ammend the Additional Include Directories so that it includes the path to your Ogre files (specifically Ogre.h (!)

Ok, hit build again. This time you've got a linker error:

Error 1 fatal error LNK1104: cannot open file 'OgreMain_d.lib' OgreSocks OgreSocks

To fix this, you need to go back to the property pages for the project, and in the Linker tab, under General, you need to ammend the Additional Library Directories so that it includes the path to the OgreMain_d.lib file (or OgreMain.lib file if you are in release mode).

Hit build again...hurrah ! You should be able to go to to your Debug (or Release) folder in your OgreSocks directory and use the .lib and .dll files you have just built !

Adding OgreSocks to your project

Now that you have built the relevant files, you need to add them to the basic tutorial 5 project. Go to the project's property pages and do the following:

1) Add the path to OgreSocks.h in your Additional Include Directories.
2) Add the path to OgreSocks_d.lib in your Additional Library Directories.
3) Add OgreSocks_d.lib in your Additional Dependencies.
4) Put the OgreSocks_d.dll in the same directory as the .exe for the basic tutorial 5.
5) Finally, add the following line of code at the top of the source file.

#Include "OgreSocks.h"


Make sure this still builds before you continue. OK, let's start adding some code to basic tutorial 5...

Initialising OgreSocks

Before you can use any OgreSocks function, you need to initialise OgreSocks. If you try and call any function without doing this, an exception will be raised. Similarly, you need to un-initialise OgreSocks when your application ends. You initialise OgreSocks via the SocketManager. You never need to create the SocketManager because it is a singleton. All you need to do is include OgreSocks.h in your application and you are good to go (which we have already done).

Let's add a constructor and a destructor to the TutorialApplication class, and we will do the initialising/unitialising of OgreSocks there. Add the following code to the TutorialApplication class:

TutorialApplication()
{
    OgreSocks::OgreSocksManager::GetSingleton()->Initialise();
}

~TutorialApplication()
{
    OgreSocks::OgreSocksManager::GetSingleton()->CleanUp();
}

Creating OgreSockets

Now we have initialised OgreSocks, we can create OgreSockets. OgreSocks allows you to create client and server TCP sockets via the SocketManager. You will never create or delete OgreSockets directly (ie by using 'new' and 'delete') - again you will use the SocketManager to do this.

We will create a server socket. This will allow a client to connect to us and control our camera remotely. Add the following code to the TutorialApplication class:

private:
    OgreSocks::OgreSocksTCPServer* m_ServerSock;


Then ammend the TutorialApplication constructor so that it NULLs the pointer:

TutorialApplication()
{
    OgreSocks::OgreSocksManager::GetSingleton()->Initialise();
    m_ServerSock = NULL;
}


Now we can create the socket. Add the following code to the top of CreateScene:

void createScene(void)
{
    try
    {
        OgreSocks::OgreSocksManager::GetSingleton()->CreateSocket(m_ServerSock, 6789);
    }
    catch(OgreSocks::OgreSocksException& e)
    {
        MessageBox(NULL, "Caught OgreSocks Exeption!", e.getFullDescription().c_str(), MB_OK);
    }


Note that NULL-ing the pointer (as we did in the constructor) is very important because if you do not pass a NULL pointer to the CreateSocket functions an exception will be raised.

The above code creates a server socket listening on port 6789. Notice that we have put a try/catch around the CreateSocket function. Functions in OgreSocks follow the error handling structure of Ogre in that they never return a value. Instead, it uses exceptions (inherited from the Ogre::Exception class) to signify errors. I won't go into to much detail with this just yet, but if you want to know which functions throw which exceptions, take a look at the documentation.

Listening for Events

OgreSocks uses callbacks to signify when a certain event has taken place. For example, you will probably want to know when the socket has just connected, or when it receives data. The callback facility in OgreSocks is quite powerful because usually if you want to use class member functions as callbacks, you either need to store a global pointer to your class (not very OO), and/or create template classes and on top of that write 5 or 6 lines or code per callback. With OgreSocks, you don't need to worry about any of this.

Firstly, we want to know when a client has connected. We will also want to know when the socket has received data. We will probably also want to know when the client has disconnected. Add the following lines of code just below the CreateSocket call:

try
{
    OgreSocks::OgreSocksManager::GetSingleton()->CreateSocket(m_ServerSock, 6789);

    m_ServerSock->SetConnectListener(OgreSocks::MakeConnectCallback(TutorialApplication, ConnectListener));
    m_ServerSock->SetReceiveListener(OgreSocks::MakeReceiveCallback(TutorialApplication, ReceiveListener));
    m_ServerSock->SetDisconnectListener(OgreSocks::MakeDisconnectCallback(TutorialApplication, DisconnectListener));


The SetXXXXXListener functions take the name of the class, and the name of the function within that class, that is to be fired when the relevant event happens. You must wrap the class name and the function name in the MakeXXXXXXCallback functions. Actually, the MakeXXXXXXCallback 'functions' are actually macros that hide alot of messiness.

So, when the OgreSocksTCPServer socket accepts a connection, the ConnectListener function of the TutorialApplication class will be fired. When the OgreSocksTCPServer socket receives data from the client, the ReceiveListener function of the TutorialApplication class will be fired. And, yes you've guessed it, when the client socket disconnects from the OgreSocksTCPServer socket, the DisconnectListener function of the TutorialApplication class will be fired.

But where are these functions? Let's create them ! Add the following code to the TutorialApplication class:

void ConnectListener(Ogre::String addr, unsigned long port, int i)
{
}

void ReceiveListener(std::list<char> data, Ogre::String addr, unsigned long port)
{
}

void DisconnectListener(Ogre::String addr, unsigned long port)
{
}

Make sure this builds at this point. Congratulations, your application is now a server !

Responding to Events

Now that our application is configured to listen for events, we can respond to them. Let's modify the ConnectListener and DisconnectListener functions so that when a client connects and disconnects we know about it. We will create a box that turns a different colour when we are connected. Add the following line of code to the TutorialApplication class to declare our box:

private:
    .
        .
        .
    Entity *m_boxent;


Now, add the following lines of code to the CreateScene function, just above the code that creates the ninja:

//add a box !
m_boxent = mSceneMgr->createEntity("Box", "cube.mesh");
SceneNode *boxnode = mSceneMgr->getRootSceneNode()->createChildSceneNode("BoxNode");
boxnode->attachObject(m_boxent);
boxnode->setScale(Ogre::Vector3(1, 0.2, 1));

// add the ninja
.
.
.


If you run the application now, the box should be white by default, because it has no material assigned to it. So, when a client connects, let's turn the box a different colour. I've chosen yellow, but feel free to change this should you wish. Also, when the client disconnects, we will turn the box back to white. Add the follwing code in bold to the ConnectListener and DisconnectListener functions:

void ConnectListener(Ogre::String addr, unsigned long port, int i)
{
    m_boxent->setMaterialName("Examples/Hilite/Yellow");
}

void ReceiveListener(std::list<char> data, Ogre::String addr, unsigned long port)
{
}

void DisconnectListener(Ogre::String addr, unsigned long port)
{
    m_boxent->setMaterialName("");
}

Sending and Receiving Data

Now were are connected, we can start sending and receiving data. Let's start by receiving data. We have already got our ReceiveListener hooked up, so now we can wait for the client to send us some data, and move our camera. The problem at the moment, though, is that the camera is currently controlled by the mDirection variable in the TutorialFrameListener, and our OgreSocket is in the TutorialApplication class. So we will need to add one or two lines of code so that our TutorialApplication class can access the mDirection in the TutorialFrameListener class. Add the following public functions to the TutorialFrameListener class:

void SetDirection(Vector3 dir)
{
    mDirection = dir;
}

Vector3 GetDirection()
{
    return mDirection;
}


Now we can access the direction of the camera, we will move it in response to whatever the client sends us. To make things easy, we will just use the current controls. so if the client sends a 'W', then we will move the camera forwards, if we receive an 'S' from the client, we will move our camera backwards etc etc. If we receive a string of text, we will process each character in turn. So, add the following code to the ReceiveListener function:

void ReceiveListener(std::list<char> data, Ogre::String addr, unsigned long port)
{
    for(std::list<char>::iterator iter=data.begin(); iter!=data.end(); iter++)
    {
        Vector3 direction = ((TutorialFrameListener*)mFrameListener)->GetDirection();
               
                char c = (*iter);
        switch(c)
        {
        case 'W':
            direction.z = -50;
            break;
        case 'S':
            direction.z = 50;
            break;
        case 'A':
            direction.x = -50;
            break;
        case 'D':
            direction.x = 50;
            break;
        }

        ((TutorialFrameListener*)mFrameListener)->SetDirection(direction);
    }
}


So as you can see, when our OgreSocket receives data from the client, we get the current direction from the TutorialFrameListener, process the characters to determine the new direction, and then re-set the direction on the TutorialFrameListener. We are only moving slowly compared to processing the keys on the local PC, and there is a reason for this that I will explain in a minute.

So we can now receive and process data from the client. How about sending data back ? We will send a simple string of text back to the client to inform it that we have received the data. Modify the ReceiveListener function so that it looks like the following:

void ReceiveListener(std::list<char> data, Ogre::String addr, unsigned long port)
{
    Ogre::String strReply = "";

    for(std::list<char>::iterator iter=data.begin(); iter!=data.end(); iter++)
    {
        Vector3 direction = ((TutorialFrameListener*)mFrameListener)->GetDirection();

        char c = (*iter);
                switch(c)
        {
        case 'W':
            direction.z = -50;
            strReply = "camera moved down";
            break;
        case 'S':
            direction.z = 50;
            strReply = "camera moved up";
            break;
        case 'A':
            direction.x = -50;
            strReply = "camera moved left";
            break;
        case 'D':
            direction.x = 50;
            strReply = "camera moved right";
            break;
        }

        ((TutorialFrameListener*)mFrameListener)->SetDirection(direction);
    }

        std::list<char> replyData;
        for(int i=0;i<strReply.Length(); i++)
        {
           replyData.push_back(strReply[i]);
        } 
    m_ServerSock->Send(replyData);
}


Almost there ! There is just one more thing we need to do and that is stop the camera when the client disconnects. Add the following code in bold to the DisconnectListener:

void DisconnectListener(Ogre::String addr, unsigned long port)
{
    m_boxent->setMaterialName("");

    ((TutorialFrameListener*)mFrameListener)->SetDirection(Vector3(0, 0, 0));
}


And that's it ! You will now be able to get a client and connect to the application. Of course, you can write a client application using OgreSocks, but I will not cover that in this tutorial. For the time being, use something like hyperterminal on a remote PC (which comes with Windows and can be found at Start/All Programs/Accessories/Communications). Connect to the IP address:port of the machine that is running your new application and watch the box turn yellow. Then press a few WSAD keys (note they need to be in CAPS) and watch the camera move around.

Done !

Future Work

Incidently, the reason why I slowed down the camera so much when controlling remotely is because we do not have the 'key up' event to stop the camera, like we do when we are controlling the camera using the local keyboard. Hyperterminal will only send the characters that you type, so you could try implementing a simple client yourself that mimics firing the key up and key down characters.

You could also modify this tutorial so that it is a server AND a client. To do this you will need to create an OgreSockTCPClient. I haven't covered client OgreSockets yet but they are no different in terms of how you create them, set their callbacks, send/receive data etc.

any problems with this tutorial, or the OgreSocks documentation, or if you encounter any bugs, feel free to send me a PM (unclepauly).


Alias: Using_OgreSocks_with_Ogre

<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.