Ogre 2: from zero to textured model

By Macoy Madson. Published on .

The entire setup is available here. I recommend going through this guide if you want to know the significance of each part.

I found it difficult to get set up with Ogre 2 in a way I was comfortable with. I think most users still use v1, so 2 is lacking in documentation.

This guide is my contribution to improving ramp-up for Ogre 2. Notably, I cover everything from initial project set up to getting your own textured meshes rendering. The manual seemed lacking in instructions on how to actually create the data, so I provide some details on that here.

This guide assumes a Linux operating system. Windows users may still find some use in this guide.

Cloning the source

The first step is getting a local copy of the Ogre 2 source code. You could get prebuilt binaries via your distrubution's package manager, but I'm generally suspicious of the longetivity of such an approach. If you clone an exact version to your repository, you can ensure compatibility much easier.

The following commands initialize a git repository (skip if you already have a repository):

mkdir ogre-start-project
cd ogre-start-project/
git init

From now own, I mostly assume you are running commands in the repository's root. In this case, that's ogre-start-project.

Next, I like to create a folder for 3rd-party dependencies. The following commands add Ogre as a submodule so that the main repository keeps track of our Ogre version for us (among other things, see git submodules):

mkdir Dependencies
git submodule add https://github.com/OGRECave/ogre-next Dependencies/ogre-next
git submodule add https://github.com/OGRECave/ogre-next-deps Dependencies/ogre-next-deps

# Make sure the dependencies are recursively added:
cd Dependencies/ogre-next-deps
git submodule update --init --recursive

Note that we also clone Ogre's 3rd-party dependencies. Again, this is to assist in portability for long-term development.

If you're looking for the most up-to-date way to clone Ogre, check the Build scripts.

Cloning this repository

From now on, if you are cloning this (your) repository, you can automatically clone the correct Ogre version as well:

git clone --recurse-submodules <your remote URL>

# Or, if you've already cloned without --recurse-submodules:
git submodule update --init --recursive

Building Ogre

I typically like to have a single .sh file, BuildDependencies_Debug.sh, in my project root which will build all 3rd-party dependencies for my project. You should never just run the build commands, because you're guaranteed to forget them after a while, especially if you don't do it very often.

The most up-to-date, cross-platform instructions for building Ogre are available in the manual. Refer to it if you encounter problems with the following instructions.

If you're looking for the most up-to-date way to build Ogre, check the Build scripts. My version is mostly copied from them. Copy the following into a .sh script:

#!/bin/sh
cd Dependencies/

# See the official script at
#  https://raw.githubusercontent.com/OGRECave/ogre-next/master/Scripts/BuildScripts/output/build_ogre_linux_c%2B%2Blatest.sh
echo "Building Ogre dependencies..."
cd ogre-next-deps && mkdir -p build && cd build && cmake  -G Ninja .. || exit $?
ninja || exit $?
ninja install || exit $?

echo "Building Ogre..."
cd ../../ogre-next
if test ! -f Dependencies; then
    ln -s ../ogre-next-deps/build/ogredeps Dependencies
fi

# -p = don't error if it already exists
mkdir -p build/Debug
mkdir -p build/Release

cd build/Debug
echo "--- Building Ogre (Debug) ---"
cmake -D OGRE_USE_BOOST=0 -D OGRE_CONFIG_THREAD_PROVIDER=0 -D OGRE_CONFIG_THREADS=0 \
  -D OGRE_BUILD_COMPONENT_SCENE_FORMAT=1 \
  -D OGRE_BUILD_SAMPLES2=1 -D OGRE_BUILD_TESTS=1 \
  -D CMAKE_BUILD_TYPE="Debug"  -G Ninja ../.. || exit $?
ninja || exit $?

# While we're at it, build the optimized version
cd ../Release
echo "--- Building Ogre (Release) ---"
cmake -D OGRE_USE_BOOST=0 -D OGRE_CONFIG_THREAD_PROVIDER=0 -D OGRE_CONFIG_THREADS=0 \
  -D OGRE_BUILD_COMPONENT_SCENE_FORMAT=1 \
  -D OGRE_BUILD_SAMPLES2=1 -D OGRE_BUILD_TESTS=1 \
  -D CMAKE_BUILD_TYPE="Release"  -G Ninja ../.. || exit $?
ninja || exit $?

Once you get everything set up, you can come back to this script and configure Ogre to your project.

Finally, run the script (this will take a while!):

# Only need to run this once, to give executable permission
chmod +x BuildDependencies_Debug.sh

./BuildDependencies_Debug.sh

If you get an error early on when building Ogre's dependencies, make sure you ran git submodule update --init --recursive in Dependencies/ogre-next-deps (this bit me while I was writing this tutorial).

I prefer not to "install" libraries, again, because each project should keep track of the known-compatible version of the dependency. What we built with that script exists in Dependencies/ogre-next/build/Debug (or Release, you get the idea).

Testing

Let's try the samples in Dependencies/ogre-next/build/Debug/bin, to make sure everything is set up:

cd Dependencies/ogre-next/build/Debug/bin
./Sample_PbsMaterials

You can explore the other samples if you'd like. This gives you some motivation to continue with the integration!

Building your code

I'm a big fan of having as complete an understanding of your code as possible. The sample/starter Ogre 2 projects consist of several files in Dependencies/ogre-next/Samples/2.0/Common/src. The manual recommends following the tutorials in Dependencies/ogre-next/Samples/2.0/Tutorials. However, I prefer a single-file approach. Once you've gotten that set up, you can refer to the tutorials for your desired features (e.g. multi-threading).

Before we get into the file, let's make sure we can compile and link the file successfully. The Ogre manual encourages using CMake, but I prefer the more hands-on approach, which is manually adding the necessary search directories and link arguments. By following this approach, you should be able to use any build system you like for your project.

If you are familiar with C/C++ dynamically-linked project setup, you'll find Ogre is very straightforward. If you're new to this sort of thing, you may want to do some research on more basic setups first.

Here are the arguments you will need to add in order to build an Ogre-based project:

Compile arguments

GCC and clang should work fine with the same arguments.

These arguments ensure your compiler can find the Ogre header files when you #include them:

-IDependencies/ogre-next/OgreMain/include
-IDependencies/ogre-next/Components/Hlms/Common/include
-IDependencies/ogre-next/Components/Hlms/Pbs/include
-IDependencies/ogre-next/Components/Hlms/Unlit/include
-IDependencies/ogre-next/build/Debug/include
-IDependencies/ogre-next/Components/Overlay/include

In Debug, you should make sure that the headers you #include generate the same code by adding the preprocessor debug #define:

-D_DEBUG

There are two main parts to the link arguments: which dynamic libraries to link, and where to look for them when starting up.

Note: This article assumes you are dynamically linking Ogre. Consult Ogre's manual if you want to statically link.

Here are the arguments requesting dynamic libraries we will use, as well as an argument to tell the linker where to find them:

# Compiled libraries we need
-lOgreHlmsPbs_d -lOgreHlmsUnlit_d -lOgreMain_d -lOgreOverlay_d
# Where to find them
-LDependencies/ogre-next/build/Debug/lib

(for Release, drop the _d).

If you attempted to link at this point, everything would be fine, but running the program would fail, because the operating system doesn't know where the actual .so libraries are. We need to add our lib directory to the dynamic library search paths:

-Wl,-rpath,.:Dependencies/ogre-next/build/Debug/lib

An explanation of this command:

Note that when you ship your project to end users, you should move the .so files into e.g. a lib folder near the executable, and add lib to the rpath instead of that long Dependencies path.

If you have trouble linking, check out my article, Know what your linker knows. It provides some tools for debugging linking issues.

Initial code

Let's create main.cpp and start filling it with the code necessary to initialize Ogre.

The entirety of the file is available here (and in raw form for direct saving/copy paste).

There is a good amount of it because Ogre is a powerful, complex rendering engine. Don't worry too much about the details for now; you can come back to this code once you've gotten more familiar with the engine, and have a working example to tweak and debug on.

Because there is so much, I recommend just copy-pasting it and learning it later. I wanted one big file rather than a complicated OOP problem to decode (complicated like Samples/Common, which is admittedly trying to solve a different problem). You should feel free to rearrange, simplify, abstract, etc. this code to suit your fancy.

Do a quick skim of this code, then give it a build:

clang++ -c main.cpp -IDependencies/ogre-next/OgreMain/include \
  -IDependencies/ogre-next/Components/Hlms/Common/include \
  -IDependencies/ogre-next/Components/Hlms/Pbs/include \
  -IDependencies/ogre-next/Components/Hlms/Unlit/include \
  -IDependencies/ogre-next/build/Debug/include \
  -IDependencies/ogre-next/Components/Overlay/include

Finally, link it into an executable:

clang++ -o ogreApp main.o -LDependencies/ogre-next/build/Debug/lib \
  -lOgreHlmsPbs_d -lOgreHlmsUnlit_d -lOgreMain_d -lOgreOverlay_d \
  -Wl,-rpath,.:Dependencies/ogre-next/build/Debug/lib

(These build commands are in BuildDebug.sh for your convenience, but you should integrate them with a proper build system to save time on rebuilds).

If you do ./ogreApp now, you'll get an error like this one:

Render system not found!
Mesh: Loading Suzanne.mesh.
terminate called after throwing an instance of 'Ogre::FileNotFoundException'
  what():  OGRE EXCEPTION(6:FileNotFoundException): Cannot locate resource Suzanne.mesh
   in resource group General or any other group. in ResourceGroupManager::openResource at
    ../../OgreMain/src/OgreResourceGroupManager.cpp (line 790)
Aborted (core dumped)

Not to worry! Ogre is working, but it cannot find the data we requested to be loaded.

Initial data

If you've gotten this far, you're on the home stretch. The final things we need to do involve copying the data we are going to load. This includes two major types of data:

Plug-ins

Plug-ins are e.g. rendering backends. These can be statically linked, but for now we'll use the default dynamic linking setup.

Create a file data/plugins_d.cfg with the following contents (assumes debug build):

# Defines plugins to load

# Define plugin folder
PluginFolder=Dependencies/ogre-next/build/Debug/lib

# Define plugins
# Plugin=RenderSystem_Direct3D11
 Plugin=RenderSystem_GL3Plus_d
# Plugin=RenderSystem_GLES
# Plugin=RenderSystem_GLES2
# Plugin=RenderSystem_Metal
 Plugin=Plugin_ParticleFX_d
# Plugin=Plugin_CgProgramManager

You can comment/uncomment plugins which make sense for your platform.

Fundamental materials/shaders

The manual recommends copying some data from the samples in order for Hlms to function:

The following commands will perform this for you:

mkdir data
mkdir data/Hlms
mkdir data/CommonMaterials
rsync -av Dependencies/ogre-next/Samples/Media/Hlms/Common data/Hlms/
rsync -av Dependencies/ogre-next/Samples/Media/Hlms/Pbs data/Hlms/
rsync -av Dependencies/ogre-next/Samples/Media/Hlms/Unlit data/Hlms/
rsync -av Dependencies/ogre-next/Samples/Media/2.0/scripts/materials/Common \
    data/CommonMaterials

Once you are well advanced in your project, you can come back to these folders and delete unused materials.

Resources

A file resources2.cfg is used by registerHlms() to specify where the materials files are. I would like to remove the need to make this file by specifying it all in code, but I have not yet.

Create data/resources2.cfg and add the following contents:

[General]
FileSystem=CommonMaterials
FileSystem=CommonMaterials/GLSL
FileSystem=CommonMaterials/HLSL
FileSystem=CommonMaterials/Metal

# Do not load this as a resource. It's here merely to tell the code where
# the Hlms templates are located
[Hlms]
DoNotUseAsResource=./

Mesh

Finally, we need the "Suzanne.mesh" asset. You can follow the Asset pipeline section below or copy the mesh from here (do right click save as on the Raw). You should copy it to data/Models.

Try it out!

Run it:

./ogreApp

If it works, you should see a Suzanne monkey moving across the screen. Huzzah!

If you have troubles, there are usually a couple things that could be going wrong:

Ogre can be a bit ornery. Stick with it though, and remember how it's one of the largest libraries you'll likely have to set up in your entire project!

What's next?

Read over the code and try to get an understanding of what is happening. You can start playing with the constants, then making your own lights, changing where resources get loaded from, and setting up your own Hlms materials.

There is a lot of hard-coded stuff in there that makes assumptions about the user's platform. You will have to do more work to make it platform-independent. Refer to Ogre's official samples for help with that.

I commented root->showConfigDialog() because I find it very frustrating during development to click through. The renderer is hard-coded to GL3 and the resolution is set to 1920x1080. Check ogreInitializeInternal() to change these.

ogreCreatePbsSpheres() is an example of the High-level material system. Read the manual to get an idea of what's going on with it. Copy SaintPetersBasilica.dds into data/Materials/Textures and uncomment the "Reflection texture" block to see what reflections look like with an actual cubemap. (I did not include this because I wanted to avoid including non-essential assets).

Once you get a feel for Ogre, you should restructure/split the file as desired to be the right level of modularity/data-driven/whatever you need.

Follow the Asset pipeline instructions and get your own models into your project!

Asset pipeline

It was quite hard to find documentation on actually getting my assets into Ogre. Most resources refer to v1. Here, I'll use Blender to create an asset and export it to my Ogre project.

Clone the exporter

There are different exporters for the various 3D asset creation programs out there. I'm only going to cover Blender.

Download the exporter:

git submodule add https://github.com/OGRECave/blender2ogre Dependencies/blender2ogre

We'll put it in our Dependencies folder, because being able to build assets is about as important as building the project!

Blender setup

cp -r Dependencies/blender2ogre/io_ogre/ ~/.config/blender/2.91/

You are now ready to export. Open your model or create one, then do File -> Export -> Ogre3D. See the following section for settings. Note: I will be making command-line driven auto-exporting to reduce these manual steps.

OGRE Export Settings

Materials and Textures

The blender2ogre plugin doesn't do much to help with Ogre v2 materials.

The following steps require competence in using Blender. See Blender's support page if you have difficulty creating your asset.

To create a textured mesh:

Converting .mesh.xml to .mesh

You shouldn't need to do this step if you set OGRETOOLS_XML_CONVERTER, but in case blender2ogre didn't recognize it, here's how I worked around it:

cd Dependencies/ogre-next/build/Debug/bin
./OgreMeshTool_d -e -O puqs ../../../../../test/data/Models/Suzanne.mesh.xml

Run this after you've made an attempted export from the Blender OGRE plugin.

Bonus: Using SDL to create the window

I had to switch rendering engines on my last project, so tying my input to the renderer is something I'm cautious of.

Instead, I want to create my own window and only use Ogre for rendering. That way, if I had to switch, I wouldn't have to rewrite my input code, nor manage an input abstraction layer.

I chose SDL because it handles many other things I want besides input.

Here is code to create an SDL window suitable for Ogre output:

bool sdlInitialize(SDL_Window** windowOut)
{
    if (SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        printf("SDL_Error: %s\n", SDL_GetError());
        return false;
    }

    // This is necessary to make sure the context is created using a newer version. I mainly did
    // this because RenderDoc said it needed it. This version comes from my current machine's
    // version, and isn't a requirement to be this high
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);

    (*windowOut) = SDL_CreateWindow("Ogre Window",
                                    SDL_WINDOWPOS_UNDEFINED,
                                    SDL_WINDOWPOS_UNDEFINED,
                                    1920, 1080,
                                    (SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL));
    if (!(*windowOut))
    {
        printf("SDL_Error: %s\n", SDL_GetError());
        return false;
    }

    // Must explicitly create the GL context for Ogre
    if (!SDL_GL_CreateContext((*windowOut)))
    {
        printf("SDL_Error: %s\n", SDL_GetError());
        return false;
    }
    return true;
}

To use your window instead of Ogre's, pass true to ogreInitializeInternal() (from our reference file). Refer to what useCurrentWindow does in order to see the necessary changes.