Architecture, Data Structures, Graphics
Josh Cooper
 Pub. Date: 2016.03.23


An Introduction


When I was in college, I received the source code for a game engine by the name “Blit3D”. It was a simple engine, nothing fancy, that one of my instructors had put together. If you’re interested you can find it here; it is available under an MIT license. The intention was to grease the wheels on our assignments, since many required a GUI or other graphical components; my classmates and I, we all found Blit3D incredibly valuable to our course work.

After about the second assignment I finished using Blit, I came to the conclusion I didn’t like it very much. It was still quite helpful to have however, since I didn’t know how to develop an engine from scratch. Way back then, there were only a few things I really wanted to change: input latency; threading; and the interfaces. I started with the interfaces, replacing the texture manager and then moving onto the input which I fixed with the equivalent of a thumbtack. I appropriately dealt with input latency, to some extent, when I changed the threading.

Eventually I had finished replacing Blit3D with Cheryl-Engine, and I could consider v1 complete. Eventually a lot of time passed by, and then I began work on a version 2.

Description


This project was about making a version 2 of my Cheryl-Engine. Up to this point the engine was merely Blit3D with a couple optimizations and numerous component rewrites. So I wanted to try my hand at designing an engine from the ground up.

The engine is designed with modularity in mind. I decided to separate it into three distinct libraries: glEngine, AssetFaculties, and GameFramework. The glEngine is entirely standalone, whereas the other two are dependent on it or a similar interface; in the case of the GameFramework it is also dependent on the AssetFaculties.

glEngine provides input callbacks for the keyboard, and window resizing. It provides an interface for registering update, and draw functions or methods. It initializes GLFW, and creates a Window and rendering context. After initialization when it goes to run, it spawns two threads for Update and Draw, and then begins polling input in the main thread. Input is triggered as callbacks which is another function or method to be registered.


AssetFaculties provides a number of faculties regarding assets, hence the name. It starts with centralized memory management, meaning all assets managed by this library are allocated and deleted in the same location as one another – regardless of type. The next layer is the pooling of asset objects, each type has its own pool or pools; multiple pools may be allocated if multiple requests are given to the pooler and it can’t fulfil the request without allocating a new pool. Then you’ve got the Asset Loader which requests arrays from the pooler, and after having finished loading an individual asset records that asset with the Asset Manager. The Asset Manager basically performs retrievals; this is because Assets are expected to load automatically, and retrieved later on. Just two things remaining which act as interfaces to the aforementioned interfaces: Asset Factories, and the Asset Faculties object itself. (Additionally, asset definitions are included into this library because their loading methods are coupled with this section of the engine – something due for a change)


GameFramework was a matter of convenience. It seemed a waste to include the same source files for every project I worked on when the architecture remained unchanged. So I decided to abstract that architecture into the GameFramework library part of Cheryl. It links everything together, thus giving me a quick way to create a new project with an opengl render context. There is also an input management component included with this library which is, by default, registered for input callbacks. I didn’t want it to be limiting, so I opened up my imagination a little bit and planned for some future scenarios. The result was that I implemented something called a “GameModule” which can be attached to the “Game” (ie. the GameFramework core component). This module class is nothing more than a C++ interface, it provides pure virtual methods. (Init; Deinit; Process; Update; PostProcess; Buffer; Draw)

Objectives


Early on I created a list of features I wanted the v2.0 to include. Implementing those features was my objective.
Modularized Engine:

  • Input Thread, Render Thread, Update Thread
  • Asset Faculties
    • Asset Definitions
      • Shaders
      • Textures
      • Game Asset (Abstraction)
      • VBO Data (Abstraction)
        • One class fits all (2D & 3D)
      • Sprites
      • Meshes
    • Asset Creation
      • Asset Directory Registrations
      • Automatic Asset Loading
    • Generic Asset Management
      • Allocation
      • Pooling
      • Loading
      • Retrieval
  • Generic Game Framework

Highlights


Successes

  • Created a functional, entirely generic, asset management system. This was my main goal, and the second reason I came up with for upgrading Cheryl to v2.0. The first was compilation issues caused by including an entire engine’s source code with every project.
  • Learned about the allocator design, and that I should compare using it with my current memory management practices in Cheryl.
  • Found reasons to decouple Asset Management from Asset Definitions. I found I couldn’t easily make a separate library for the definitions because they required the AssetFaculties library in order to assign textures and similarly shared assets.

Challenges

  • Sorting through headers. Given the interconnectedness many of the features possessed, I had a particularly challenging time sorting what needed inclusion where, and resolving the circular dependencies that came with that. I found running Doxygen on the source tree was amazingly helpful in this process, it kept me from running around in circles(myself~). (This process was mainly removing included headers which were being implicitly included through another header.)
  • Debugging Asset Creation. It turned out I had been initializing my asset pools incorrectly, I was initializing all objects as if they were base object types and not derived types. So this was the moment I was destined to learn, fully, about pointer arithmetic.

Results


It worked out well.

I no longer have projects rebuilding the entire engine because of a failed file output. My projects had been suffering issues with outputting to .exe and .pdb files, which was resulting in time consuming rebuilds of all source files when testing small changes – frequently.

New clean projects, with opengl render contexts, can be made in a matter of minutes; just register a couple directories housing asset files, and then I’m good to go. No need for copying any files around from place to place.

Don’t get the wrong idea, Cheryl isn’t perfect. She does get the job done though. A version 2.1 or even 3.0 is definitely in order when I have the time, so sometime after getting this site up to date with my work. I will probably also be waiting until after I’ve worked more with procedural generation, rpg mechanics, build a game using the Steamworks API, and worked on some 3D projects with animated models. In other words, it’ll be a while – probably years.