(For more resources related to this topic, see here.)
Before we get started, please note the folder structure that we'll be using. This will help you quickly find the files referred to in each recipe.
Executables for every sample project will be output in the bin/debug or bin/release folders depending on the project's build configuration. These folders also contain the following required DLLs and configuration files:
File name
Description
OgreMain.dll
Main Ogre DLL.
RenderSystem_Direct3D9.dll
DirectX 9 Ogre render system DLL. This is necessary only if you want Ogre to use the DirectX 9 graphics library.
RenderSystem_GL.dll
OpenGL Ogre render system DLL. This is necessary only if you want Ogre to use the OpenGL graphics library.
Plugin_OctreeSceneManager.dll
Octree scene manager Ogre plugin DLL.
Plugin_ParticleFX.dll
Particle effects Ogre plugin DLL.
ogre.cfg
Ogre main configuration file that includes render system settings.
resources.cfg
Ogre resource configuration file that contains paths to all resource locations. Resources include graphics files, shaders, material files, mesh files, and so on.
plugins.cfg
Ogre plugin configuration file that contains a list of all the plugins we want Ogre to use. Typical plugins include the Plugin_OctreeSceneManager, RenderSystem_Direct3D9, RenderSystem_ GL, and so on.
In the bin/debug folder, you'll notice that the debug versions of the Ogre plugin DLLs all have a _d appended to the filename. For example, the debug version of OgreMain.dll is OgreMain_d.dll. This is the standard method for naming debug versions of Ogre DLLs.
The media folder contains all the Ogre resource files, and the OgreSDK_vc10_v1-7-1 folder contains the Ogre header and library files.
Creating a Win32 Ogre application
The Win32 application is the leanest and meanest of windowed applications, which makes it a good candidate for graphics. In this recipe, we will create a simple Win32 application that displays a 3D robot model that comes with Ogre, in a window. Because these steps are identical for all Win32 Ogre applications, you can use the completed project as a starting point for new Win32 applications.
Getting ready
To follow along with this recipe, open the solution located in the Recipes/Chapter01/OgreInWin32 folder in the code bundle available on the Packt website.
How to do it...
We'll start off by creating a new Win32 application using the Visual C++ Win32 application wizard.
Create a new project by clicking on File | New | Project. In the New Project dialog-box, expand Visual C++, and click on Win32 Project. Name the project OgreInWin32. For Location, browse to the Recipes folder and append Chapter_01_Examples, then click on OK.
In the Win32 Application Wizard that appears, click on Next. For Application type, select Windows application, and then click on Finish to create the project. At this point, we have everything we need for a bare-bones Win32 application without Ogre.
Next, we need to adjust our project properties, so that the compiler and linker know where to put our executable and find the Ogre header and library files.
Open the Property Pages dialog-box, by selecting the Project menu and clicking on Properties.
Expand Configuration Properties and click on General. Set Character Set to Not Set.
Next, click on Debugging. Select the Local Windows Debugger as the Debugger to launch, then specify the Command for starting the application as ......bindebug$(TargetName)$(TargetExt).
Each project property setting is automatically written to a per-user file with the extension .vcxproj.user, whenever you save the solution.
Next we'll specify our VC++ Directories, so they match our Cookbook folder structure.
Select VC++ Directories to bring up the property page where we'll specify general Include Directories and Library Directories. Click on Include Directories, then click on the down arrow button that appears on the right of the property value, and click on <edit>.
In the Include Directories dialog-box that appears, click on the first line of the text area, and enter the relative path to the Boost header files: ......OgreSDK_vc10_v1-7-1boost_1_42.
Click on the second line, and enter the relative path to the Ogre header files ......OgreSDK_vc10_v1-7-1includeOGRE, and click OK.
Edit the Library Directories property in the same way. Add the library directory ......OgreSDK_vc10_v1-7-1boost_1_42lib for Boost, and ......OgreSDK_vc10_v1-7-1libdebug for Ogre, then click OK.
Next, expand the Linker section, and select General. Change the Output File property to ......bindebug$(TargetName)$(TargetExt).
Then, change the Additional Library Directories property to ......OgreOgreSDK_vc10_v1-7-1libdebug.
Finally, provide the linker with the location of the main Ogre code library. Select the Input properties section, and prepend OgreMain_d.lib; at the beginning of the line.
Note that if we were setting properties for the release configuration, we would use OgreMain.lib instead of OgreMain_d.lib.
Now that the project properties are set, let's add the code necessary to integrate Ogre in our Win32 application.
Copy the Engine.cpp and Engine.h files from the Cookbook sample files to your new project folder, and add them to the project. These files contain the CEngine wrapper class that we'll be using to interface with Ogre.
Open the OgreInWin32.cpp file, and include Engine.h, then declare a global instance of the CEngine class, and a forward declaration of our InitEngine() function with the other globals at the top of the file.
CEngine *m_Engine = NULL;
void InitEngine(HWND hWnd);
Next, create a utility function to instantiate our CEngine class, called
void InitEngine(HWND hWnd){
m_Engine = new CEngine(hWnd);
}
Then, call InitEngine() from inside the InitInstance() function, just after the window handle has been created successfully, as follows:
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance,
NULL);
if (!hWnd){
return FALSE;
}
InitEngine(hWnd);
Our last task is to render the 3D scene and display it in the window when we receive a WM_PAINT message. Add a call to renderOneFrame() to the WndProc() function, as follows:
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
m_Engine->m_Root->renderOneFrame();
EndPaint(hWnd, &ps);
break;
And that's it!
How it works...
Let's look at the CEngine class to see how we create and initialize an instance of the Ogre engine, and add a camera and robot model to the scene.
Open Engine.cpp, and look at the constructor for CEngine. In the constructor, we create an instance of the Ogre engine, and store it in the m_Root class member variable.
m_Root = new Ogre::Root("", "", Ogre::String(ApplicationPath +
Ogre::String("OgreInWin32.log")));
An instance of Ogre::Root must exist before any other Ogre functions are called. The first parameter to the constructor is the plugins configuration filename, which defaults to plugins.cfg, but we pass it an empty string because we are going to load that file manually later. The second parameter is the main configuration filename, which defaults to ogre.cfg, but we pass it an empty string, also because we'll be loading that file manually as well. The third parameter is the name of the log file where Ogre will write the debugging and the hardware information.
Once the Ogre::Root instance has been created, it can be globally accessed by Root::getSingleton(), which returns a reference or Root::getSingletonPtr(), which returns a pointer.
Next, we manually load the configuration file ogre.cfg, which resides in the same directory as our application executable.
OgreConfigFile.load(Ogre::String(ApplicationPath +
Ogre::String("ogre.cfg")), "t:=", false);
The ogre.cfg configuration file contains Ogre 3D engine graphics settings and typically looks as follows:
# Render System indicates which of the render systems
# in this configuration file we'll be using.
Render System=Direct3D9 Rendering Subsystem
[Direct3D9 Rendering Subsystem]
Allow NVPerfHUD=No
Anti aliasing=None
Floating-point mode=Fastest
Full Screen=Yes
Rendering Device=NVIDIA GeForce 7600 GS (Microsoft Corporation - WDDM)
VSync=No
Video Mode=800 x 600 @ 32-bit colour
[OpenGL Rendering Subsystem]
Colour Depth=32
Display Frequency=60
FSAA=0
Full Screen=Yes
RTT Preferred Mode=FBO
VSync=No
Video Mode=1024 x 768
Once the main configuration file is loaded, we manually load the correct render system plugin and tell Ogre which render system to use.
Ogre::String RenderSystemName;
RenderSystemName = OgreConfigFile.getSetting("Render System");
m_Root->loadPlugin("RenderSystem_Direct3D9_d);
Ogre::RenderSystemList RendersList = m_Root->getAvailableRenderers();
m_Root->setRenderSystem(RendersList[0]);
There's actually a little more code in Engine.cpp for selecting the correct render system plugin to load, but for our render system settings the RenderSystem_Direct3D9_d plugin is all we need.
Next, we load the resources.cfg configuration file.
Ogre::ConfigFile cf;
Ogre::String ResourcePath = ApplicationPath + Ogre::String("resources.
cfg");
cf.load(ResourcePath);
The resources.cfg file contains a list of all the paths where Ogre should search for graphic resources.
Then, we go through all the sections and settings in the resource configuration file, and add every location to the Ogre resource manager.
Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
Ogre::String secName, typeName, archName;
while (seci.hasMoreElements()){
secName = seci.peekNextKey();
Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
Ogre::ConfigFile::SettingsMultiMap::iterator i;
for(i = settings->begin(); i != settings->end(); ++i){
typeName = i->first;
archName = i->second;
archName = ApplicationPath + archName;
Ogre::ResourceGroupManager::getSingleton().
addResourceLocation(archName, typeName, secName);
}
}
Now, we are ready to initialize the engine.
m_Root->initialise(false);
We pass in false to the initialize() function, to indicate that we don't want Ogre to create a render window for us. We'll be manually creating a render window later, using the hWnd window handle from our Win32 Application.
Every graphics object in the scene including all meshes, lights, and cameras are managed by the Ogre scene manager. There are several scene managers to choose from, and each specializes in managing certain types of scenes of varying sizes. Some scene managers support rendering vast landscapes, while others are best for enclosed spaces. We'll use the generic scene manager for this recipe, because we don't need any extra features.
m_SceneManager = m_Root->createSceneManager(Ogre::ST_GENERIC,
"Win32Ogre");
Remember when we initialized Ogre::Root, and specifically told it not to auto-create a render window? We did that because we create a render window manually using the externalWindowHandle parameter.
Ogre::NameValuePairList params;
params["externalWindowHandle"] =
Ogre::StringConverter::toString((long)hWnd);
params["vsync"] = "true";
RECT rect;
GetClientRect(hWnd, &rect);
Ogre::RenderTarget *RenderWindow = NULL;
try{
m_RenderWindow = m_Root->createRenderWindow("Ogre in Win32", rect.
right
- rect.left, rect.bottom - rect.top, false, ¶ms);
}
catch(...){
MessageBox(hWnd, "Failed to create the Ogre::RenderWindownCheck that
your graphics card driver is up-to-date", "Initialize Render
System",
MB_OK | MB_ICONSTOP);
exit(EXIT_SUCCESS);
}
As you have probably guessed, the createRenderWindow() method creates a new RenderWindow instance. The first parameter is the name of the window. The second and third parameters are the width and height of the window, respectively. The fourth parameter is set to false to indicate that we don't want to run in full-screen mode. The last parameter is our NameValuePair list, in which we provide the external window handle for embedding the Ogre renderer in our application window.
If we want to see anything, we need to create a camera, and add it to our scene. The next bit of code does just that.
m_Camera = m_SceneManager->createCamera("Camera");
m_Camera->setNearClipDistance(0.5);
m_Camera->setFarClipDistance(5000);
m_Camera->setCastShadows(false);
m_Camera->setUseRenderingDistance(true);
m_Camera->setPosition(Ogre::Vector3(200.0, 50.0, 100.0));
Ogre::SceneNode *CameraNode = NULL;
CameraNode = m_SceneManager->getRootSceneNode()-
>createChildSceneNode("CameraNode");
First, we tell the scene manager to create a camera, and give it the highly controversial name Camera. Next, we set some basic camera properties, such as the near and far clip distances, whether to cast shadows or not, and where to put the camera in the scene. Now that the camera is created and configured, we still have to attach it to a scene node for Ogre to consider it a part of the scene graph, so we create a new child scene node named CameraNode, and attach our camera to that node.
The last bit of the camera-related code involves us telling Ogre that we want the content for our camera to end up in our render window. We do this by defining a viewport that gets its content from the camera, and displays it in the render window.
Ogre::Viewport* Viewport = NULL;
if (0 == m_RenderWindow->getNumViewports()){
Viewport = m_RenderWindow->addViewport(m_Camera);
Viewport->setBackgroundColour(Ogre::ColourValue(0.8f, 1.0f, 0.8f));
}
m_Camera->setAspectRatio(Ogre::Real(rect.right - rect.left) /
Ogre::Real(rect.bottom - rect.top));
The first line of code checks whether we have already created a viewport for our render window or not; if not, it creates one with a greenish background color.
We also set the aspect ratio of the camera to match the aspect ratio of our viewport. Without setting the aspect ratio, we could end up with some really squashed or stretched-looking scenes.
You may wonder why you might want to have multiple viewports for a single render window. Consider a car racing game where you want to display the rear view mirror in the top portion of your render window. One way to accomplish, this would be to define a viewport that draws to the entire render window, and gets its content from a camera facing out the front windshield of the car, and another viewport that draws to a small subsection of the render window and gets its content from a camera facing out the back windshield.
The last lines of code in the CEngine constructor are for loading and creating the 3D robot model that comes with the Ogre SDK.
Ogre::Entity *RobotEntity = m_SceneManager->createEntity("Robot",
"robot.mesh");
Ogre::SceneNode *RobotNode = m_SceneManager->getRootSceneNode()-
>createChildSceneNode();
RobotNode->attachObject(RobotEntity);
Ogre::AxisAlignedBox RobotBox = RobotEntity->getBoundingBox();
Ogre::Vector3 RobotCenter = RobotBox.getCenter();
m_Camera->lookAt(RobotCenter);
We tell the scene manager to create a new entity named Robot, and to load the robot.mesh resource file for this new entity. The robot.mesh file is a model file in the Ogre .mesh format that describes the triangles, textures, and texture mappings for the robot model. We then create a new scene node just like we did for the camera, and attach our robot entity to this new scene node, making our killer robot visible in our scene graph. Finally, we tell the camera to look at the center of our robot's bounding box.
Finally, we tell Ogre to render the scene.
m_Root->renderOneFrame();
We also tell Ogre to render the scene in OgreInWin32.cpp whenever our application receives a WM_PAINT message. The WM_PAINT message is sent when the operating system, or another application, makes a request that our application paints a portion of its window. Let's take a look at the WM_PAINT specific code in the WndProc() function again.
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
m_Engine->m_Root->renderOneFrame();
EndPaint(hWnd, &ps);
break;
The BeginPaint() function prepares the window for painting, and the corresponding EndPaint() function denotes the end of painting. In between those two calls is the Ogre function call to renderOneFrame(), which will draw the contents of our viewport in our application window.
During the renderOneFrame() function call, Ogre gathers all the objects, lights, and materials that are to be drawn from the scene manager based on the camera's frustum or visible bounds. It then passes that information to the render system, which executes the 3D library function calls that run on your system's graphics hardware, to do the actual drawing on a render surface. In our case, the 3D library is Direct X and the render surface is the hdc, or Handle to the device context, of our application window.
The result of all our hard work can be seen in the following screenshot:
Flee in terror earthling!
There's more...
If you want to use the release configuration instead of debug, change the Configuration type to Release in the project properties, substitute the word release where you see the word debug in this recipe, and link the OgreMain.lib instead of OgreMain_d.lib in the linker settings.
It is likely that at some point you will want to use a newer version of the Ogre SDK. If you download a newer version and extract it to the Recipes folder, you will need to change the paths in the project settings so that they match the paths for the version of the SDK you downloaded.
Read more