EIE 360 Lab Group A Lab 1 by cuiliqing



                  Department of Electronic and Information Engineering

                                 EIE360 Integrated Project
                              Group A Lab 1: Motion Controller

1. Objective
In this experiment, we shall develop computer program to use the i-too motion tracking controller in
Ogre. More specifically, students are asked to control the movement of a 3D object using the i-too
motion tracking controller in Ogre.

2. Preparation
      A PC with Windows Vista SP2 32-bit / 64-bit Version
      Microsoft Visual Studio 2008 SP1
      OGRE SDK v1.7.2 or later
      Ogre AppWizard_v1.7.2
      DirectX End-User Runtimes (June 2010)
      Basic knowledge in C++ programming, object-oriented programming and 3D graphics
      i-too motion tracking controller

3. Developing 3D Graphics with OGRE
The following procedures will guide you to develop a 3D graphics program using OGRE.

3.1 3D Graphic Engine OGRE
In this experiment, an OGRE (Object-Oriented Graphics Rendering Engine) is used for displaying 3D
graphics. The OGRE SDK and wizard have been installed in the lab machines to help developing
program running in OGRE. You need to set up a path for the access of the OGRE SDK. It can be done
by setting the environment variables.

For Windows Vista,

      Control Panel  User Accounts  Change my environment variables (at the left)  User

For Windows XP,

      Control Panel  System  Advanced (at the top right)  Environment Variables  User

Add the OGRE_HOME environment variable for the location of OGRE SDK as shown below (Press
New if there is no OGRE_HOME variable defined in User Variable).

                                                                                             Page 1

        Variable name: OGRE_HOME
        Variable value: C:\OgreSDK_vc9_v1-7-2

Close all windows and re-open them to activate the new environment setting.

3.2 Create an OGRE Project
Now, you should create a working directory for this lab. Use Window Explorer or similar tool to
create a directory of your own. All the project files in this lab will be saved in this directory.

Then Start the Visual Studio 2008. In Visual Studio C++ IDE,

       Click File → New  Project… in menu bar.
       Click Visual C++ in the Project types and you will see a list of Visual Studio installed templates
        on the right.
       Select OGRE Application.
       Type the project name as “EIE360Project” and the location as the full path of your working
        directory you created earlier. Then click OK.
       The Ogre Application Wizard will be shown. Click Application Settings.
       Select Standard Application.
       Uncheck Postbuild Copy if checked.
       Then click Finish.


       Click Project  Properties… in the menu bar.
       Click Configuration Properties. Check in the top right box that the Configuration is
       Click Configuration Properties  Debugging, change the Command to
        “Bin\$(ConfigurationName)\$(ProjectName).exe”        and   the Working     Directory
        to “Bin\$(ConfigurationName)”.
       Change the Configuration to Release, and repeat the above.
       Change the Configuration back to Active(Debug).
       Click OK when finish.

In addition, use the Windows Explorer or any equivalent tool to do the following:

       Copy folders bin and media from C:\OgreSDK_vc9_v1-7-2 to your project directory, i.e.
        (working directory name)\EIE360Project\EIE360Project.
       Download       the       file Resource_A1.zip      from     the      subject    web       page
        (http://www.eie.polyu.edu.hk/~enpklun/EIE360/EIE360.html) and unzip it to your working
       In the unzipped folder, look for the sub-folder media. Copy all the above sub-folders to your
        project directory. Since the name of these sub-folders is the same as the folders in the
        project directory, the system will generate a warning window. Click Yes to All such that if the
        files in these sub-folders have the same name as those in the project directory, they will
        replace the original files.

                                                                                                  Page 2

Then in Visual Studio, open the file EIE360Project.h. Replace the codes of EIE360Project.h with the
ones below:

#ifndef __EIE360Project_h_
#define __EIE360Project_h_

#include "BaseApplication.h"
#include "../res/resource.h"

class EIE360Project : public BaseApplication
       virtual ~EIE360Project(void);

       virtual void createScene(void);
       virtual bool frameRenderingQueued(const Ogre::FrameEvent &evt);
       void processCalculation(const Ogre::FrameEvent& evt);

       Ogre::SceneNode *mSceneNode;
       Ogre::SceneNode *mHead[2];
       Ogre::OverlayElement *mTextarea;

#endif // #ifndef __EIE360Project_h_

And modify EIE360Project.cpp by adding the codes that are bolded as shown in the following box.

#include "EIE360Project.h"


void EIE360Project::createScene(void)
       // Set ambient light
       mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5));

       // Create a light
       Ogre::Light* l = mSceneMgr->createLight("MainLight");

       // Create ferris wheel and 2 heads
       Ogre::Entity *ferrisWheel = mSceneMgr->createEntity( "FerrisWheel",
"FerrisWheel.mesh" );
       mSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
       mSceneNode->attachObject( ferrisWheel );
       mSceneNode->setPosition(20, 40, 30);

                                                                                            Page 3


       Ogre::Entity *ogrehead1 = mSceneMgr->createEntity( "OgreHead1",
"ogrehead.mesh" );
       mHead[0] = mSceneMgr->getRootSceneNode()->createChildSceneNode();
       mHead[0]->attachObject( ogrehead1 );

       Ogre::Entity *ogrehead2 = mSceneMgr->createEntity( "OgreHead2",
"ogrehead.mesh" );
       mHead[1] = mSceneMgr->getRootSceneNode()->createChildSceneNode();
       mHead[1]->attachObject( ogrehead2 );
       mHead[1]->setPosition(20+28*Ogre::Math::Cos(Ogre::Degree(30)), 40,30-

       Ogre::Entity *ground = mSceneMgr->createEntity( "Ground", "Ground.mesh" );
       Ogre::SceneNode *groundNode = mSceneMgr->getRootSceneNode()-
       groundNode->attachObject( ground );

      // Set skydome
      mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8);

       // Create an overlay layer (for drawing text) with a textarea
       Ogre::Overlay *myOverlay =
       mTextarea =

      // Create a directional light
      Ogre::Light *light = mSceneMgr->createLight("MainLight2");
      light->setDirection(Ogre::Vector3( 0, -5, 1 ));

      // Set camera
      mCamera->setPosition(Ogre::Vector3(0, 30, 130));

bool EIE360Project::frameRenderingQueued(const Ogre::FrameEvent& evt)
       bool ret = BaseApplication::frameRenderingQueued(evt);


      // Slow down refresh rate

      return ret;

void EIE360Project::processCalculation(const Ogre::FrameEvent& evt)
       std::wostringstream os;
       os << L"This is a Ferris Wheel." << std::endl;



                                                                               Page 4

#include "windows.h"

//Do not remove the codes after this line
// :
// :

Open bin\debug\resources_d.cfg and bin\release\resources.cfg
with Visual Studio 2008 and add the following statement:
FileSystem=../../media/EIE360. Remember to save
the changes.

Build the project. Make sure there is no compilation error up to
this point. Run the project and select the 1024 x 768 @ 32-bit
color video mode.

Write down in the box below the average number of frames
(FPS) of graphics that can be shown by OGRE per second (it is shown in the box at the lower left
hand corner of the screen).

You can stop the program and return to Windows by pressing the Escape key.

Modify the program to change the screen output as required below:

        Modify the function EIE360Project::processCalculation() such that one
         more statement “Lab 1 Experiment.” will be shown on the output screen.
        Modify the position (x, y, z) of the two heads to (-30, 30, -30) and (-50, 50, -50), respectively,
         by adding codes in the function EIE360Project::processCalculation(). You can
         use the function setPosition() to set the position of the heads mHead[0] and
         mHead[1]. For example,

        mHead[0]->setPosition(x, y, z); // x, y, z are float type numbers.

        Grab the screen output resulted and paste it below:

                                                                                                   Page 5

3.3 Further Analysis of the Program
The program execution basically follows the sequence below:

      EIE360Project()                              Update screen:
     Create all core objects:

                                                 User's defined functions:

                                                 System function:

                                                   update screen

              Fig.3.1 The basic execution flow of the Ogre program developed in this lab work

Every time OGRE updates the screen (at a frame rate you have put down above), the function
frameRenderingQueued() will be called and then the user's defined functions
processCalculation() will be called in sequence. Finally the system function update() will
be called to update the screen.

                                                                                                Page 6

Now try to modify the program to give the two Ogre heads some motion. In EIE360Project.h, add
two private variables of type float with name zpos0 and zpos1. In the constructor of
EIE360Project class, initialize the two variables to 0. Replace the codes in
processCalculation() with the following ones so that the two heads are moving along the z-

void EIE360Project::processCalculation(void)
       //Add your codes here
       std::wostringstream os;
       os << L"This is a Ferris Wheel." << std::endl;
       os << L"Lab 1 Experiment." << std::endl;

       zpos0 = ((int)zpos0)%30;
       zpos1 = ((int)zpos1)%50;
       mHead[0]->setPosition(-30, 30, zpos0); // x, y, z are float type numbers.
       mHead[1]->setPosition(-50, 50, zpos1); // x, y, z are float type numbers.

Build and execute the resulting program. Do you notice the two heads are moving along the z-axis?
Do you notice that the two heads move away from you until a point that they suddenly jump back to
the original positions and move backward again? Modify the above program such that the two heads
will smoothly move away from you and then smoothly move towards you repeatedly. Paste your
codes in the box below:

                                                                                          Page 7

3.4 Adding Buttons
Now we are going to modify the program to add two buttons on the screen.

In EIE360Project.cpp, add the following codes to EIE360Project::createScene().

       // Create buttons
       OgreBites::Button *button1 = mTrayMgr->createButton(OgreBites::TL_BOTTOM,
"Button1", "Start",150);
       OgreBites::Button *button2 = mTrayMgr->createButton(OgreBites::TL_BOTTOM,
"Button2", "Stop",150);


We would like to fix the camera orientation,                 add   the    following   codes   in   the


Then, add the following three functions to the Class EIE360Project.

void EIE360Project::buttonHit(OgreBites::Button* button)
       if (button->getCaption() == "Start")
       else if (button->getCaption() == "Stop")

void EIE360Project::startGame(void)
       //Add your codes here

void EIE360Project::finishGame(void)
       //Add your codes here

Modify the file EIE360Project.h to define the above functions.

Build and run the project. Make sure there is no compilation error up to this point. You will see two
buttons are added to the display. When the buttons are pressed, the functions startGame(...)
and finishGame(...) will be called. At the moment, nothing will be performed. We shall add
more codes to these two functions later. Grab the screen output when the program is executed and
paste it below.

                                                                                               Page 8

4. Motion Controller
Before start, remove the Ferris Wheels, all Ogre heads by deleting the related codes in
EIE360Project::createScene() in EIE360Project.cpp. Remove also all codes in

Then, add the following codes to EIE360Project::createScene() for showing an Ogre head
with appropriate size. We will later on move and rotate the Ogre head using the controller.

       // Create a Ogre head in the middle of the screen
       Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");
       Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()-

4.1 Using i-too Motion Tracking Controller
To receive the signals from i-too in OGRE, we need to make use of the Object Oriented Input System
(OIS). OIS is a cross-platform library supports many different human interface devices, in three basic
device categories (keyboards, mice, and joysticks). It is an open source library, so you can make
changes and alterations as you see them to fit. OIS is used in OGRE since version 1.4. To use OIS, we
need to follow the 3 steps:

        Initialization
        Object creation
        Capture data from object

Since i-too has been configured as game controller (or joystick), you just need to modify your
program following the steps below to receive its signals.

a) Initialization and create the joystick device:
    i. In EIE360Project.h, include "OISJoyStick.h".
   ii. In EIE360Project.h, modify the class definition to add OIS::JoyStickListener as
            another base class for EIE360Project as follows:

class EIE360Project : public BaseApplication, OIS::JoyStickListener

                                                                                               Page 9

    iii.   In EIE360Project.h, add a member variable for joystick input to the EIE360Project class:

OIS::JoyStick* mJoyStick; // initialize as "0"

    iv.    Overriding the createFrameListener(...) of BaseApplication by adding
             another one in EIE360Project. Add the following codes to EIE360Project.cpp.

void EIE360Project::createFrameListener(void)

           mJoyStick = static_cast<OIS::JoyStick*>(
                  mInputManager->createInputObject(OIS::OISJoyStick, true));

             Remember to modify the header file to add its declaration.

b) Capture the joystick input: The signals of the i-too are captured in real time with the following


     Put this statement in EIE360Project::frameRenderingQueued(...) so that the
     signals from i-too can be received once per frame update.

c) There are five basic callback functions for joystick:

     bool     buttonPressed(const OIS::JoyStickEvent &arg, int button);
     bool     buttonReleased(const OIS::JoyStickEvent &arg, int button);
     bool     axisMoved(const OIS::JoyStickEvent &arg, int axis);
     bool     sliderMoved(const OIS::JoyStickEvent &arg, int slider);
     bool     povMoved(const OIS::JoyStickEvent &arg, int pov);

     It means that one of these five functions will be called when the joystick is involved. For example,
     buttonPressed(…) is invoked when a button of the joystick is pressed, and the argument
     button holds the number corresponding to the button being pressed. Add these 5 functions to
     the class EIE360ProjectApp. For each function, there is only one statement:

return true;

     We shall detail some of these functions next.

d) Now let's try to modify the buttonPressed(...) and buttonReleased(...)
   functions so that you can show a message “Joystick button 0 is pressed” on the screen by
   pressing the button 0 of i-too (one of the white buttons) and it will be removed when releasing
   the button. To test if joystick button 0 is pressed, you can just add the following if statement to
   the functions (remember to put in your codes inside the function):

                                                                                                Page 10

if (button == 0)

Compile the project and see what you get. Note that you must have your joystick attached to the PC
for the program to run properly. Grab the screen outputs when you press and release button 0 of i-

When button 0 is pressed,

When button 0 is released,

                                                                                          Page 11

4.2 Develop the Motion Analyzer Class
In this section, a C++ class called MotionAnalyzer will be developed to analyze the 6-axis signals
received from i-too. The procedure is listed as follows:

       Click Project  Add Class…, and select C++ Class, then click Add.
       Enter “MotionAnalyzer” as the class name. In the boxes .h file: and .cpp file:, enter
        “include\MotionAnalyzer.h” and “src\MotionAnalyzer.cpp”, then click Finish.

In the file MotionAnalyzer.h, enter the following codes.

#pragma once

#include "BaseApplication.h"
#include "OISJoyStick.h"

class MotionAnalyzer

        void Initialize(void);
        void SetJoyStick(OIS::JoyStick *joyStick);
        OIS::JoyStick *GetJoyStick(void) { return mJoyStick; };
        void Capture(void);

       OIS::JoyStick *mJoyStick; // initialize as "0"

In the file MotionAnalyzer.cpp, enter the following codes.

#include "..\include\MotionAnalyzer.h"



void MotionAnalyzer::Initialize(void)
       mJoyStick = 0;

void MotionAnalyzer::SetJoyStick(OIS::JoyStick *joyStick)
       mJoyStick = joyStick;

void MotionAnalyzer::Capture(void)

Now, we modify our EIE360ProjectApp class to add a MotionAnalyzer class.

                                                                                          Page 12

       Include MotionAnalyzer.h in the file EIE360Project.h (MotionAnalyzer.h is in the current
        directory), and
       Add a MotionAnalyzer pointer namely mMotionAnalyzer as a private member variable
        of the EIE360Project class, i.e.

MotionAnalyzer *mMotionAnalyzer;

In the constructor of the class EIE360ProjectApp, add the following codes:

mMotionAnalyzer = 0;
mMotionAnalyzer = new MotionAnalyzer();
The above codes will instantiate an object of the class MotionAnalyzer and perform initialization.

Complete the destructor by adding the following codes:

if (mMotionAnalyzer)
       delete mMotionAnalyzer;
       mMotionAnalyzer = 0;
As can be seen in the codes above, whenever we want to delete a pointer, we should check first if
the pointer is still valid. Otherwise a runtime error will be generated.

Now let the motion analyzer knows which joystick is to be accessed. In
EIE360Project::createFrameListener, append at the end the following statement:


Then, replace the statement mJoyStick->capture() in EIE360Project::
frameRenderingQueued()by calling mMotionAnalyzer->Capture() (note the change
in the case of the first character of capture). In this case, we shall ask the MotionAnalyzer to
help us capture i-too signals rather than directly do it in the EIE360Project class.

Now you can build the project. Make sure there is no compilation error up to this point. Do you find
the program is just the same as before when executing?

4.3 Create a Separated Thread to Obtain i-too Hammer data
For the program above, the signals of i-too will be captured once per frame update. It can be too
slow particularly if there are many 3D objects to be rendered. Now we are going to modify the
MotionAnalyzer class to execute a separated thread such that i-too signals will be captured
irrespective to the frame update rate.

In addition to the original codes in MotionAnalyzer.h, add the following to the public part of the class

                                                                                               Page 13

        static DWORD WINAPI RunningUpdateThread(LPVOID parameter);
        bool StartRunningThread(void);
        void StopRunningThread(void);

        void Analysis(void);            //To analysis joystick data

       enum Motion { Motion_None = 0, Motion_Forward, Motion_Backward, Motion_Up,
Motion_Down };
       Motion GetMotion(void) { retun mMotion };

In addition to the original codes in MotionAnalyzer.h, add the following to the private part of the
class MotionAnalyzer:

        HANDLE mThread;
        DWORD mThreadId;
        bool mUpdateThreadExit;
        Motion mMotion;

In addition to the original codes in MotionAnalyzer.cpp, add the following:

       mThread = 0;
       mThreadId = 0;
       mUpdateThreadExit = false;
       mMotion = Motion_None;

bool MotionAnalyzer::StartRunningThread(void)
       if (mThread)
              return false;

        mUpdateThreadExit = false;
        mThread = CreateThread(NULL, 0, RunningUpdateThread, this, 0, &mThreadId);

        if (mThread == NULL)
               return false;

        return true;

void MotionAnalyzer::StopRunningThread(void)
       mUpdateThreadExit = true;

        Sleep(500);             // Give time for the thread to exit

        if (mThread)
               DWORD exitCode;
               GetExitCodeThread(mThread, (LPDWORD)&exitCode);
               if (exitCode == STILL_ACTIVE)
                      TerminateThread(mThread, exitCode);
               mThread = 0;

void MotionAnalyzer::Analysis(void)
       //Add your codes here

                                                                                           Page 14

DWORD WINAPI MotionAnalyzer::RunningUpdateThread(LPVOID parameter)
       //Add your codes here

        return 0;

With the new MotionAnalyzer class, a thread will be created whenever the function
MotionAnalyzer::StartRunningThread() is called. The two threads will execute the
thread functions


A shared variable, namely, mUpdateThreadExit is also added to the MotionAnalyzer class
to facilitate the termination of the thread. When the thread is created, the variable will be set to
false. When the function MotionAnalyzer::StopRunningThread() is called and if the
thread are active, the variable will be set to true.

Modify your program to do the following:

       Whenever the “Start” button is pressed, your program will start the two threads by calling
        MotionAnalyzer::StartRunningThread(); and whenever the “Stop” button is
        pressed, your program will destroy the two threads by calling MotionAnalyzer::
        StopRunningThread().                    You    may          want         to        modify
        EIE360Project::startGame(...)and EIE360Project::finishGame(...)
        to achieve this.
       Implement the thread function MotionAnalyzer::RunningUpdateThread() such
        that it will execute MotionAnalyzer::Capture() for every 10ms (approximately) and
        the thread will terminate if mUpdateThreadExit is set to true by
        MotionAnalyzer::StopRunningThread() (See your course notes for details).
       Call    MotionAnalyzer::StopRunningThread()                  in    the    destructor    of
        EIE360Project class so that the created thread will be terminated when the user quits
        the program.
       Display the i-too six-axes data on the screen.

Since Capture() will be called in MotionAnalyzer::RunningUpdateThread(), you can
remove the original statement for calling Capture() in the EIE360Project::

Build and run your project to see if it works.

Give your codes below of the functions that you have modified to achieve the above requirements.

                                                                                            Page 15

4.4 Analysis and Action
Up to now, we still have not done anything meaningful using i-too. It is because we have not
implemented the functions MotionAnalyzer::Analysis().

The function MotionAnalyzer::Analysis() should analyze the i-too data to determine what
kind of actions should be performed. It should be executed whenever Capture() is called. The
variable mMotion of type Motion should carry out the motions determined by
MotionAnalyzer::Analysis()               and     it    can   be     retrieved by      calling
MotionAnalyzer::GetMotion() in the EIE360Project class.

You are free to redefine the type Motion or add other motion types to suit your needs.

The following are the tasks that you are required to finish in this part:

       Modify the function MotionAnalyzer::RunningUpdateThread() such that the
        function MotionAnalyzer::Analysis() will be called whenever the function
        MotionAnalyzer::Capture() is called.
       Implement the function MotionAnalyzer::Analysis().                              The function
        MotionAnalyzer::Analysis() should check if the Y-Rotation data is positive and is
        larger than a threshold (with value determined by you). If so, it should fill in the mMotion
        with the value that defines the action is going forward. However, if the Y-Rotation data is
        negative and is smaller than a threshold (again with value determined by you), it should fill
        in the mMotion with the value that defines the action is going backward. Otherwise reset
        the mMotion.
       Modify EIE360Project::processCalculation()such that the Ogre head will
        move forward or backward based on the command in the mMotion. The function
        MotionAnalyzer::GetMotion() is used to retrieve the mMotion. The speed of Ogre
        head should be even in different computers.

                                                                                              Page 16

Give your codes for the functions MotionAnalyzer::RunningUpdateThread(),
MotionAnalyzer::Analysis() and EIE360Project::processCalculation()

4.5 Motion Detection
The above MotionAnalyzer::Analysis() function only analyzes one axis of the i-too
controller at a time. However, very often we need to analyze a sequence of data of different axes
before we can make the decision of which action should be taken. To do so, we need to modify our
program to introduce a first-in-first-out buffer which keeps the latest 10 data of different axes of the
i-too controller.

Modify your program as follow (refer to the course notes for the explanation of the codes):

    1. Declare std::vector<int> mRecordAxes[6];
    2. Modify Capture() function by adding the following codes:
        for (int i = 0; i < mJoyStick->getJoyStickState().mAxes.size(); i++)

                                                                                               Page 17

                if (mRecordAxes[i].size() > 10)

With the above modification, a 10 elements vector namely mRecordAxes is formed. Each element
of the vector allows us to store the data of all 6 axes of the i-too controller.

5. Exercise
Modify your program so that the Ogre head will go backward when the Z-Acceleration data is
changing quickly from positive to negative (e.g. when you swing your i-too controller quickly along
the Z-axis from pointing-up to pointing-down); and return to the original position (z = 0) when the Y-
Rotation data is negative (e.g. when you hold your i-too controller up and point to the sky).

Show the result to your tutor.

Write down your codes below.

                                                                                              Page 18

6. References
[1] http://www.sengital.com/

[2] VRMS Game http://www.vrmsgame.com/

[3] PC Motion Games User Guide (Ver. 1.1)

[4] OGRE 3D, http://www.ogre3d.org/

[5] OGRE Tutorials – OGRE Wiki http://www.ogre3d.org/wiki/index.php/Ogre_Tutorials

[6] Ogre AppWizards http://code.google.com/p/ogreappwizards/

[7] MSDN

[8] SD3983 Computer Game Development II Lab sheet



                                                                                     Page 19

To top