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.
References:
Batcat model supplied by my talented friend Josh
Comments
Post a Comment