OgreDotNet HelloWorld Part II: bypassing the configDialog and manually specifying the resources to use.

Verbage by DigitalCyborg. Code ported from OGRE Intermediate Tutorial 6

Introduction

In the first OgreDotNet "hello world", you should have noticed (and maybe felt jipped) that we did not print Hello World to the screen. We'll remedy that in this tutorial. The first hello world tutorial used the Config Dialog and resources script. In this version of the tutorial, we'll bypass the config script by explicitly setting our video mode and we'll explicitly state which resources we want to use.

You'll notice the app is still larger than any other hello world applications you've seen before, but OGRE is complex.

NOTE: I make use of anonymous functions for the EventHandler so if you're not using .NET 2.0, you'll have to forgo/remove/comment out the EventHandler section of code here.

Getting Started

Unlike the last hello world tutorial, I'm not going to provide a large code base to get started with. In this version of the application we'll do everything in main() so this is all you need to get started:

using System;
 using System.Drawing;
 using Math3D;
 using OgreDotNet;
 
 namespace OgreDotNetTutorial
 {
    class HelloWorldPartII
    {
        static void Main(string[] args)
        {
        }
    }
 }

Declaring variables



I've chosen to declare all variables up front. I also initialize all the references to null. This is an old habit of mine, even though its completely unneccesary. I think that they are listed in roughly the order that they get used in the code.

Root mRoot = null;
            LogManager mLogManager = null;
            
            //Variables used to choose the render system
            RenderSystemList mRenderSystemList = null;
            RenderSystemList.RenderSystemListEnumerator mRSListItr = null;
            RenderSystem mRenderSystem = null;
            String mRenderSystemName;
            
            //Variables used to configure the Render System.
            ConfigOptionMap mConfigOptionMap = null;
            ConfigOption mConfigOption = null;
            StringVector mStringVector = null;
            StringVector.StringVectorEnumerator mStringVectorEnumerator = null;
            int mNumConfigOptions;
 
            //Ogre "must haves"
            RenderWindow mRenderWindow = null;
            SceneManager mSceneManager = null;
            Camera mCamera = null;
            Viewport mViewport = null;
  
            //variables for quick, simple event handling.
            OgreDotNet.EventHandler mEventHandler;
            bool mDone = false;
 
            //Variables needed to print "hello world"
            OverlayContainer        mOverlayContainer=null;
            Overlay                mOverlay=null;
            TextAreaOverlayElement    mHelloTextOverlay=null;

Got root ?



Since we've declared all the variables, let's go ahead and instantiate mRoot and get a hold of the logmanager Singleton so that we can log stuff.

The first argument to the Root constructor allows us to specify the name of the plugin file we want to use. This is nice since we don't need to load plugins we won't use. You'll need to make a copy of plugins.cfg (helloplugins.cfg) which only contains the directives for PluginFolder, RenderSystem of your choice, and BSPSceneManager.
The second argument to the Root constructor allows us to specify the name of the file to load/store display configuration settings. We won't do either of those in this tutorial so I just left it with the default value.
The last argument to the Root constructor specifies the default logfile:

mRoot = new Root("helloplugins.cfg", "display.cfg", "HelloOgreDotNetv2.txt");

Getting the LogManager Singleton is trivial.

mLogManager = LogManager.GetSingleton();

Specifying the RenderSystem



In order to manually specify the rendering system, we'll iterate through the list of available renderers and compare thier names with the name of the render system we want to use. I'm using windows so I look for the DirectX subsystem. If you want the OpenGL subsystem, change the CompareTo (strcmp) line.
Once we find the Render system that we are looking for, we'll break out of the iteration loop and call Root.SetRenderSystem.

mRenderSystemList = mRoot.GetAvailableRenderers();
            mRSListItr = mRenderSystemList.GetEnumerator();
           
            while(mRSListItr.MoveNext())
            {
                mRenderSystem = mRSListItr.Current;
                mRenderSystemName = mRenderSystem.GetName();
                mLogManager.logMessage("FOUND RenderSystem: " + mRenderSystem.GetName());
                if(mRenderSystemName.CompareTo("Direct3D9 Rendering Subsystem") == 0)
                //if(mRenderSystemName.CompareTo("OpenGL Rendering Subsystem") == 0)
                {
                    //found the render system we were looking for.
                    break;
                }
            }
            //Set the RenderingSystem
            mRoot.SetRenderSystem(mRenderSystem);

Setting the Config Options for the RenderSystem



I didn't know what the config options were so I decided to print them to the log file then take a look and then decide which ones I wanted to use. The config options are stored in a map so first, we need to use mRenderSystem.GetConfigOptions to get it. Then we'll check to see how many of them there are. Once we know that, we can iterate over them and print of the list of possible values which is stored in a vector of strings.

//Print List of config Options to the logfile
            mConfigOptionMap = mRenderSystem.GetConfigOptions();
            mNumConfigOptions = (int) mConfigOptionMap.size();
            for(int i = 0; i<mNumConfigOptions; i++)
            {
                mLogManager.logMessage("RenderSystem Config Option: " + 
                    mConfigOptionMap.getitemKey(i));
                
                mConfigOption = mConfigOptionMap.getitemValue(i);
                mStringVector = mConfigOption.possibleValues;
                mStringVectorEnumerator = mStringVector.GetEnumerator();
                while(mStringVectorEnumerator.MoveNext())
                {
                    mLogManager.logMessage("\t " + mStringVectorEnumerator.Current);
                }
            }

Setting them up is quite easy. we just call set config option for all the options we found and set it one of the possible values which we just determined. Unless you have the same video card as the one in my laptop, you will definitely have to change the Rendering Device. Change any of the others as you see fit.

//Set the Config Opts for the RenderSystem.
            mRenderSystem.SetConfigOption("Allow NVPerfHUD", "No");//"Yes"
            mRenderSystem.SetConfigOption("Anti aliasing","None");//
            mRenderSystem.SetConfigOption("Floating-point mode","Fastest");//Consistant
            mRenderSystem.SetConfigOption("Full Screen","No");
            mRenderSystem.SetConfigOption("Rendering Device","NVIDIA GeForce FX Go5200");
            mRenderSystem.SetConfigOption("VSync","No");//"Yes"
            mRenderSystem.SetConfigOption("Video Mode","800 x 600 @ 32-bit colour");

Setup the Basics



If you need this code explained, please go back and do the earlier tutorials.

//Initialize Root
            mRenderWindow = mRoot.Initialise(true, "Hello OGRE.NET Part II");
        
            //Create SceneManager
            mSceneManager = mRoot.CreateSceneManager((ushort)SceneType.Generic);

            //Create & Setup Camera 
            mCamera = mSceneManager.CreateCamera("SceneCamera");
            mCamera.SetPosition(new Vector3(0, 0, 500));
            mCamera.LookAt = new Vector3(0, 0, 0);
            mCamera.SetNearClipDistance(5);

            // Create one Viewport, entire window
            mViewport = mRenderWindow.AddViewport(mCamera);
            mViewport.SetBackgroundColour(Color.Black);
            
            // Alter the camera aspect ratio to match the viewport
            mCamera.SetAspectRatio(mViewport.GetWidth() / mViewport.GetHeight());

Simple Event Handling



Adding this code is entirely optional. Personally, I think that having an application that we cannot close is really lame so I setup a simple event handler. If you are not using .NET 2.0, then you won't be able to compile this section since I've used anonymous methods. Other than that, the code is pretty basic and should look familiar.

//Setup a minimal eventhandler using anonymous methods.
            mEventHandler = new OgreDotNet.EventHandler(mRoot, mRenderWindow);
            mEventHandler.SubscribeEvents();
            mEventHandler.FrameStarted += delegate(FrameEvent e)
            {
                if (mRenderWindow.Closed || mDone) return false;
                return true;
            };
            mEventHandler.FrameEnded += delegate(FrameEvent e)
            {
                return true;
            };
            mEventHandler.KeyClicked += delegate(KeyEvent e)
            {
                if (e.KeyCode == KeyCode.Escape) mDone = true;
            };

Manually Specifying the resources



We need a font to actually print "Hello World" so we'll have to do some basic setup of the ResourceGroupManager Singleton. This isn't hard... we just need to tell the Resource group manager where, what type of resource and what group to all the resources to. After that just call the initialize function. Go take a look at sample.fontdef in the media/fonts directory and you'll see how OGRE describes fonts.

ResourceGroupManager.getSingleton().addResourceLocation(
                "../../media/fonts", "FileSystem", "General");

            ResourceGroupManager.getSingleton().initialiseAllResourceGroups();

Creating the text overlay



Most of this code came from ExampleApplication.cs. Perhaps in the future I'll understand more about Overlays and I can add a good description. Until then I think I'll let code speak for itself.

OverlayElement el = OverlayManager.Instance.CreateOverlayElement("Panel", "HelloOgreDotNet/Panel");
            mOverlayContainer = new OverlayContainer(OverlayElement.getCPtr(el).Handle, false);

            mOverlayContainer.setMetricsMode(GuiMetricsMode.GMM_PIXELS);
            mOverlayContainer.setPosition(10, 10);
            mOverlayContainer.setDimensions(500, 100);
            //mOverlayContainer->setMaterialName("MaterialName"); // Optional background material

            // Create a text area
            mHelloTextOverlay = OverlayManager.Instance.CreateTextAreaElement("HelloOgreDotNet/HelloWorldText");
            mOverlayContainer.addChild(mHelloTextOverlay);
            mHelloTextOverlay.setMetricsMode(GuiMetricsMode.GMM_PIXELS);
            mHelloTextOverlay.setPosition(0, 0);
            mHelloTextOverlay.setDimensions(100, 100);
            mHelloTextOverlay.setCaption("Hello World");
            mHelloTextOverlay.setCharHeight(48);
            mHelloTextOverlay.setFontName("IronMaiden");
            mHelloTextOverlay.setColourBottom(Converter.GetColor(0.3f, 0.5f, 0.3f));
            mHelloTextOverlay.setColourTop(Converter.GetColor(0.5f, 0.7f, 0.5f));

            // Create an overlay, and add the panel
            mOverlay = OverlayManager.Instance.create("Status/Overlay");
            mOverlay.add2D(mOverlayContainer);

            //Show the overlay
            mOverlay.show();

Ready? Set... Go!


//Start the rendering loop
            mRoot.StartRendering();

Clean up



We need to dispose of the objects which are local variables, but I think OGRE disposes of the "must have" stuff like Camera,Viewport,RenderWindow & SceneManager. If I'm wrong, then please update this code.

mHelloTextOverlay.Dispose();
            mOverlay.Dispose();
            mOverlayContainer.Dispose();
            if(mStringVector!=null) mStringVector.Dispose();
            if(mConfigOption!=null) mConfigOption.Dispose();
            if(mConfigOptionMap!=null) mConfigOptionMap.Dispose();
            if(mRenderSystemList!=null) mRenderSystemList.Dispose();
            mRoot.Dispose();