Genesis-3D: Porting an old game engine to Linux
Today I will show you how I ported the Genesis-3D game engine to Linux. You can find the code here: https://codeberg.org/matiaslavik/Genesis-3D
WARNING: Do not try this at home. (if you want to make a game, don’t pick a 10 years old abandoned game engine and try to upgrade it).
Genesis-3D is an open source game engine made by Chinese ChangYou (畅游). It was started as a fork of NebulaDevice (which I have written about here), the game engine used for the Drakensang games, and they added various features to it such as C# scripting. It was released as beta in 2013, but was abandoned shortly after, and has not received any updates since then. The editor source code was never released.
I decided that I wanted to try to port this engine to Linux. Why? Mostly because it’s a fun challenge. The engine is quite old and maybe not the most relevant alternative to make a game in today (the renderer only supports GLES2 and DX9, and some of the 3rd party libraries are no longer maintained). However, I find the engine source code quite easy to work with, and I often used this engine as a reference and inspiration when I started to learn about game engine development.
By the way, the engine was already cross platform, with support for Windows, iOS and Android. So a lot of the work had already been done for me, but there was still a lot of porting that had to be done. First of all, the engine would have to be upgraded to support x64 and newer versions of C++ (C++17), and the third-party libraries as well. Most of them could just be built with the desired compiler, but some of them required some modifications to be able to build.
Genesis-3D and its dependencies
Genesis-3D depends quite a few third-party libraries, that had to be built for x64 and Linux. These include:
- OpenAL: For audio
- NVIDIA PhysX: For physics
- NVIDIA Cg Toolkit: For compiling Cg shaders.
- hlsl2glslfork: This is a fork of hlsl2glsls, and is used for converting HLSL shaders to GLSL. It was made by Aras Pranckevičius at Unity, and was used in earlier versions of Unity. It is no longer maintained, and I had to do a few changes to get it to build.
- Mono: For C# scripting.
- FreeImage: For dealing with images (loading, converting, resizing, etc.)
- FreeType: For dealing with fonts/text.
To support multiple rendering APIs (GLES and DX9), Cg/HLSL is used as the shader language, and the shaders are converted to GLSL on demand (using hlsl2glslfork).
Building the original Genesis-3D engine
The original engine source code can be found here (my unmodified fork), and there’s a link to the third-party package in the README. To build the engine I had to boot up a Windows machine, install Visual Studio 2010 and CMake, clone the Genesis-3D repository, unzip the third-party package in the root directory, configure with CMake and build. After doing that, I was able to test my custom built Genesis-3D engine by creating a project in the (not open source!) Genesis-3D editor and run the project with the following command:
./Genesis --gamedir "YOUR_PATH/GenesisTest" --scenename "asset:Test.scene" --sysdir "YOUR_PATH/DefaultAssets/System" --shddir "YOUR_PATH/DefaultAssets/Shader"
This is described in the official documentation, which is written in Chinese.
Upgrading the engine: 64 bit support and C++17
The first step was making sure the engine could be built with newer compilers and for x64 on Windows. I decided to start with Visual Studio 2019, using C++11 - and then later switched to C++17.
To be able to build the engine with C++17 I had to do a few changes:
- There were several source files that #defined
nullptrwas introduced in C++11, so there’s no need to define it ourselves.
- They used an old library called Opcode for some collision detection related code. This library had a lot of inline assembly and would not build in an x64 environment. Luckily it was only used by the
ProjectorComponent, so I just removed the dependency and disabled the ProjectorComponent alltogether (I’ve renamed the preprocessor definition since then).
- The PhysX library would have to be built for x64. Building and maintaining the library files for all configurations and compilers and manually linking them is a pain, and I’d rather add PhysX as a gitmodule and built it with the engine, so I decided to disable it for now. This could easily be done by defining the
__PHYSX_COMMIT__preprocessor definition. See my commit here.
- Genesis-3D uses Mono for C# scripting. Unfortunately the version they were using does not have x64 support, so I decided to disable that as well. In other words: Scripting no longer works, but I’m hoping to find time to update to a newer version of Mono soon.
- Some source files used the now deprecated and removed
std::binary_functionclass template. Since it has been removed since C++17, I had to replace it with my own implementation.
- All other 3rd-party libraries had to be re-built for x64.
Now, after fixing a few more minor issues (missing includes, conflicting preprocessor definitions, etc.) I was finally able to build and run a 64 bit version of the engine, built with C++11. Next step: Linux!
Porting the engine to Linux
This was obviously the most challenging part. There were a lot of source files and 3rd party libraries that had to be modified to build on Linux, and my first thught was: Where do I start? I decided to approeach this simply by trying to build the engine project-by-project (starting with the RenderSystem and GraphicsSystem projects), and fix the errors as they occured. After fixing a few CMake configuration issues I got my first compilation errors. Some of these errors were simple to fix, such as these missing includes. Others were slightly more time consuming, such as adding Linux-implementations for threads, interlocked, memory, etc. There are still some TODOs here:
- I only implemented the LinuxInterlocked class for GCC (this class handles atomic operations).
- Some of these platform-specific classes can possibly be removed, and replaced with modern C++ standard library.
I also had to build all third party libraries for Linux. In most cases this was quite simple, but one of them required quite a few changes: the hlsl2glslfork library. So I decided to fork it (“hlsl2glslforkfork”?) and add it as a git submodule and CMake subproject, so that it is built with the engine. My fork can be found here: https://codeberg.org/matiaslavik/hlsl2glslfork
Finally, I had to modify the Genesis loader project (which is responsible for loading the game project and starting the engine) to make it cross-platform, and create a window using GLFW.
After fixing the last build errors I finally managed to produce a build! It did of course not run properly, and I only saw a blue background. After some debugging I noticed that the
SkinnedMeshRenderComponentSerialization class failed to deserialise when loading the scene, so the test actor didn’t load properly. This turned out to be caused by the
ByteOrder class using the wrong endianness, because of a missing
__LINUX__ definition. After fixing that, I still got an empty blue scene - but now with lots of OpenGL errors! The shaders failed to parse, because other preprocessor definitions were automatically added to the beginning of the shader - before the
#version directive (the GLSL language specification requires that “The #version directive must occur in a shader before anything else, except for comments and white space.”). After fixing that it finally worked, and I was able to load and remder a test scene!
What’s left to do
While the engine now runs on Linux, there are still a few things left to do:
- Update Mono to a newer version, and re-enable C# scripting on Linux
- Add back PhysX (should be simple)
And here are some other things that would be nice to do later:
- Replace Cg library and hlsl2glslfork (especially if we want to use more modern rendering APIs)
- Make an editor (the original editor source code was never released)