xygine Feature: 3D Model Component

xygine is, at its heart, a 2D framework. While working on Lunar Mooner, however, it occurred to me that attaching 3D models as components may look better than creating 2D sprite sheets by pre-rendering 3D models. A lot of work is needed to create a sprite sheet, particularly when updating one after tweaking a model, so it seemed to make sense to provide the ability to render the 3D models directly in a scene. Don't get me wrong, xygine isn't going to become xyzgine at any point (geddit?) – the scene itself remains entirely 2D and any model attached to an entity remains firmly rooted in two dimensional space. It does mean, however, that 3D rendered models reap the benefits of using raw OpenGL, such as real time lighting and dynamic shadow casting. xygine already contained a lighting system which implemented point lights and a single directional light so building on this was an obvious choice. It means that 3D rendered objects are lit in exactly the same way as any of the traditional drawables, making everything blend together nicely. Rendering 3D models directly also means that once a model is animated it can be used right away, without the laborious task of stitching together sprite sheets.

Actually implementing what became the MeshRenderer wasn't trivial, however. Thankfully the task was made much easier by the existing SFML Texture and Shader classes which provide the ability to obtain the underlying OpenGL handle - this gives access to all the flexibility of raw OpenGL calls, while still being able to use the SFML interface. One of the biggest problems was the fact that the MeshRenderer relies on OpenGL 3.2 as a minimum, while SFML works with GL 2.0. This can cause some compatibility problems, particularly on OS X and macs with integrated Intel GPUs. The MeshRenderer class is a completely independent part of xygine though, and if it is not needed then never instantiating it means compatibility problems aren't an issue. The MeshRenderer works by watching a scene and maintaining its own view based on the current active camera. It then makes sure any model components, created via the MeshRenderer's factory function, are correctly aligned with the two dimensional scene. This does mean that the 3D world units are measured in SFML units, approximately in pixels, which can be a bit confusing at first. Often 3D models have units which are much larger such as inches, feet or metres. This can make models appear very small when initially loaded into xygine. The easiest solution to this is to set a scale on the model's parent entity, however the currently the supported model format of choice is *.iqm – an open binary model format developed by one of the Sauerbraten guys, Lee Salzman. There is an exporter written for Blender which, while not totally intuitive to use right off the bat (for me at least), provides a good clean and feature rich output of IQM model files. It has the benefit of not needing an intermediate format (although it also provides IQE, an ascii based output) and one of the features is the ability to scale models on export, so you can make sure they'll fit xygine perfectly. Currently xygine will load an IQM mesh file and any animations which are compiled within it. It also reads material data, but as yet does not parse it and materials have to be loaded by hand. I also plan on supporting animation only files which can be loaded on to a mesh separately.

The MeshRenderer takes a deferred rendering approach, so currently does not support transparent materials, but it does mean lighting calculations are efficient and even with multiple shadow maps a good (~200fps) frame rate can be maintained at 1080p on my old 2.1 GHz Intel machine with an nvidia GTS450 GPU. Performance can vary of course, mostly due to the number of active shadow maps which can be a maximum of nine, eight of which are point lights. Point lights can have shadow casting disabled, however, allowing some fine tuning per application. As the MeshRenderer is drawn over the top of a xygine scene the scene itself generally appears behind everything rendered in 3D. It is entirely possible, on the other hand, to render the scene to its own texture and use that with a quad which can be placed in the MeshRenderer. This gives proper z-ordering results, as well as meaning that 2D objects will receive shadows, for some interesting effects. Quad and cube meshes are readily available because the interface for model loading is designed to be flexible enough that loaders can be created for other model formats with minimal effort. For example as long as the MeshBuilder class is inherited, a loader for *.obj, *.mdl or any other type of model can easily be implemented. This also means it is trivial for xygine to provide such MeshBuilders for cubes and quads. I've completed as much of the doxygen documentation as possible, although I've yet to fully update the wiki as some aforementioned features are yet to be implemented or finalised. The xygine example application includes a MeshRenderer in the 'Platform Demo', a video of which can be seen here:

The menu on the side appears in debug mode and allows switching the output between the various deferred renderer stages as well as the active shadow maps (black shadow maps are inactive or non-existent shadow casters). The release build of xygine completely omits this menu for the sake of performance. There are also a series of console commands available for the MeshRenderer, which can be found by typing 'list_all' into the console while a MeshRenderer instance is active. These mostly mirror the function of the debug menu.

As always the full source is on Github, released under the zlib license. I'm currently looking for OS X testers, so contributions via the Githuib page are gratefully accepted.


Batcat model supplied by my talented friend Josh

Popular Posts