top of page
Writer's pictureDan Hatch

IS IT HARD TO WRITE A GAME ENGINE? (PT 3)

(Part 3: “So let’s go”)


Let’s get this party started

In the last post, I found a few nice libraries to do the heavy lifting of my game engine. I kept to the “Good enough” philosophy and just found the libraries that I’ll need to do the absolute basics to get something running for now. I was now ready to (and desperately wanted to) jump in to some coding.


But where to start?

My starting point, as with the library searching, was to get something to see. This means starting out with a really basic implementation of a rendering system and main loop.

For now, the main loop will be super simple, just good enough to run the rendering system.

while (true) {
    // update
}

That’ll do it for now.

As the engine will be made up of subsystems, I figured it would be sensible to knock together the start of a quick interface that all subsystems will implement. Again, nothing elaborate, just enough to do what needs to be done right now. Something along the lines of:

#ifndef _BFDE_ISUBSYSTEM_HPP
#define _BFDE_ISUBSYSTEM_HPP

namespace bfde {
    class ISubSystem {
    public:
        virtual ~ISubSystem() {};
        virtual void Initialise() = 0;
        virtual void Terminate() = 0;
        virtual void Update() = 0;
    };
}

#endif

It’s probably worth a quick aside at this point. My business is called Blueflame Digital. I’ve really pushed the limits of creativity with the name of my engine and called it Blueflame Digital Engine, hence all the BFDE namespaces and whatnot.


Render Subsystem

The next step was to head on over to the Ogre3D tutorials, and start to dissect the minimum required to get a window running. I’m not teaching Ogre3D here, but what I have found is that a lot of the tutorials use a basic example framework to build on. This framework was actually a bit too extensive for my purposes, so I’ve boiled it down a bit to something more simple.

For the moment, my render subsystem doesn’t need to do a great deal of work. One thing I will say though is that it’s going to be taking care of window duties. To do this, I will make my render subsystem extend Ogre::WindowEventListener. To find out the whys and hows of this, go check out the Ogre website, but in short it means we get told when window events like resize etc happens.

At some point, I’ll most likely be adding my own configuration system, so I went with just hard coding window settings for the moment. This example is just a snippet to give the general jist of getting Ogre to create a window, I threw this into my implementation of the Initialise method:

root_ = new Ogre::Root(pluginsCfg_);

SetupResources();

// set render system
Ogre::RenderSystemList renderSystemList =
    root_->getAvailableRenderers();

foreach_(Ogre::RenderSystem* renderSystem, renderSystemList) {
    if (renderSystem->getName().find(“OpenGL”) !=
            std::string::npos) {
        root_->setRenderSystem(renderSystem);
        break;
    }
}

// initialise ogre
root_->initialise(false);

// create a render window
std::string windowName = “BFDE”;
int width = 800;
int height = 600;
bool fullScreen = false;

window_ = root_->createRenderWindow(windowName, width, height, fullScreen, 0);

// Get the SceneManager, in this case a generic one
sceneMgr_ = root_->createSceneManager(Ogre::ST_GENERIC);

// Set default mipmap level (NB some APIs ignore this)
Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);

// Load resources
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

A couple of notes about this snippet…

Firstly there is a SetupResources method used, the contents of this method is lifted straight from the tutorials and it basically loads a bunch of paths from the standard Ogre config files into the system.

Secondly there is foreach_. This is a macro to BOOST_FOREACH, I use it all the time, and it’s just a personal preference to have it LESS SHOUTY.

#define foreach_ BOOST_FOREACH

I dropped the Ogre initialisation code into a class that that also implements the ISubSystem interface, and called is something really imaginative — OgreRenderSubSystem. Then I hooked it back into our main loop. Something like this:

OgreRenderSubSystem* renderSubSystem = new OgreRenderSubSystem();

renderSubSystem->Initialise();

// main loop
while (true) {
    renderSubSystem->Update();
}

renderSubSystem->Terminate();

delete renderSubSystem;

Once that was built and run, I was rewarded with an empty window.


There’s nothing to see as there is nothing actually to see. There’s not even a camera or viewport to see anything with. In fact, even if there were a camera, viewport and something to see, that still wouldn’t show anything as I’m never telling Ogre to render anything. Lets quickly resolve that now or there will be all sorts of “fun” later trying to figure out why nothing is rendered. It’s a simple job, in the Update method of OgreRenderSubSystem, I just needed to tell Ogre to deal with it’s window events and then render a frame, like this:

Ogre::WindowEventUtilities::messagePump();
root_->renderOneFrame();

Nothing much will look different when run, but it will make all the difference in the next step.

I thought at this point that I’d better check that it actually renders something . Bearing in mind that as this is meant to be a game engine, the creating and configuring of the objects in the render system will likely be controlled from an external configuration (maybe a config file? or from scripts?), I may as well add some public methods to do this stuff. For now I can just call these methods to test the system, but the framework will be in place ready for something else to call them later.

In order to see something, I’ll be needing:

  • Some light.

  • A camera (and viewport in Ogre).

  • An entity.

How to do all of these things is very well documented, so I’ll just quickly list the implementations that I went with to get things running.

void OgreRenderSubSystem::SetAmbientLight(
        float r, 
        float g, 
        float b) {
    sceneMgr_->setAmbientLight(Ogre::ColourValue(r, g, b));
}



ILightRenderNode* OgreRenderSubSystem::CreateLightRenderNode(
        float x_, 
        float y_, 
        float z_) {
    Ogre::Light* light = sceneMgr_->createLight(“MainLight”);
    light->setPosition(x_, y_, z_);

    ILightRenderNode* renderNode = 
        new OgreLightRenderNode(light);

    return renderNode;
}



ICameraRenderNode* OgreRenderSubSystem::CreateCameraRenderNode(
        float x, 
        float y, 
        float z) {
    // Create the camera
    camera_ = sceneMgr_->createCamera(“PlayerCam”);

    // position it
    camera_->setPosition(Ogre::Vector3(x, y, z));

    CreateViewports();

    ICameraRenderNode* renderNode = 
        new OgreCameraRenderNode(camera_);

    return renderNode;
}



void OgreRenderSubSystem::CreateViewports() {
    // Create one viewport, entire window
    Ogre::Viewport* vp = window_->addViewport(camera_);
    vp->setBackgroundColour(Ogre::ColourValue(0, 0, 0));

    // Alter the camera aspect ratio to match the viewport
    camera_->setAspectRatio(
        Ogre::Real(vp->getActualWidth())  
        / 
        Ogre::Real(vp->getActualHeight()));
}

IEntityRenderNode* OgreRenderSubSystem::CreateEntityRenderNode(
        const std::string& nodeName, 
        const std::string& meshName) const {
    Ogre::Entity* entity = sceneMgr_->createEntity(meshName);

    Ogre::SceneNode* node = sceneMgr_
        ->getRootSceneNode()
        ->createChildSceneNode(nodeName);

    node->attachObject(entity);

    IEntityRenderNode* renderNode = 
        new OgreEntityRenderNode(node);

    return renderNode;
}

I created simple interfaces and implementations for the return objects for these methods. They’re just bare bones, but my plan is that I’ll be able to use them later to be able to interact with the objects when the time comes. I was also lazy and created a single full window viewport when you create the camera rather than a separate CreateViewport method. I just thought for now that it was reasonable that anything I’d be doing in my engine will have a single viewport per camera.

I then hacked in a call to these methods in main, right after I’ve created and initialised the render subsystem to see if they work. After the test, I will remove the test code.


FINALLY, a 3D thing on screen! Now we’re cooking!

The 3D thing that’s showing in all it’s glory is actually a low poly space ship that Rich created (by hand in a text editor I believe) for his side project — SpaceCruiser2020. He’s kindly let me use a few of his hand cranked models as they suit my purposes well. You get a whole bunch of free assets with Ogre though, so it’s probably worth working with them to get things going. I tweaked the test code to render Sinbad’s head — ogrehead.mesh (with poor lighting).


An annoyance with this little sample is that there is no way to cleanly exit the application (as I’m sure you’ve found out if you tried it). As I’d extended the WindowEventListener, I can tell when the window is closed by implementing the windowClosed method, so that’s a good starting point to get the engine exiting cleanly.

void OgreRenderSubSystem::windowClosed(Ogre::RenderWindow* rw) {
}

It’s pretty obvious that what needs to be done when the window is closed is to be breaking out of the main loop, which will then cleanly shut down the subsystems and exit the application. The question is *how* to do that. We’re way down in the render subsystem here, how do we tell the main loop — or other subsystems that something has happened?


Next time…

I show you my solution to getting the subsystems communicating with one another…

5 views0 comments

Recent Posts

See All

Comments


bottom of page