Refactoring the engine: the rendering subsystem
Since I began writing my small XNA demo, I knew that, because of my obsessive-compulsive mania for software architecture, I'll have to rewrite the whole thing several times, eventually ending up with a full scale engine... Well, here goes chapter 2 of "XNA engine architecture". ![]()
As the rewrite I did during this week is pretty big and complex, I will split the "game engine" topic in a series of 3 posts. Let's start with the most important subsystem: the renderer.
Preliminary analysis
As I already wrote down in the first article about my engine's architecture, I'll create a simple hierarchy of objects (so called "Actors") which will encapsulate all the game logic of the in-game entities (space ships, planets, projectiles, killer monkeys...) and will be able to render themselves when needed.
This solution is not very clean in terms of separation of concerns: we are mixing what should be "game" logic with what should be exclusively "rendering" logic, but for the scope of my litte demo game this should work out fine. If the engine will be adapted to a bigger game, I'd most likely separate my Actor hierarchy in "Entities" (game logic) and "Renderable objects".
One of the things the engine must be capable of, is to render the same scene multiple times, providing some way for the user to determine which objects render and to change them on the fly each time. This may seem useless, but it will be absolutely necessary for the very nice "sun overcasting" effect I'll try to implement later.
Actors
All game actors will implement one of two interfaces: IActor (base interface for all renderable/updatable actors) and IPhysical (interface deriving from IActor, implemented by all actors that will have some sort of physical presence in the scene, that cause collision detection and have velocity and acceleration vectors).

As you can see, the only physical elements of the game will be the player's space ship, the "prize" (a special object the player must collect in order to win the game) and the planets. The PlayerShip class also implements the IControllable interface, meaning that we will be able to register the ship to our input handlers and let them influence the ship's behavior (i.e. it's acceleration).
We do not put input handling code in the PlayerShip directly in order to keep the code clean and to allow us to create multiple input handlers (keyboard, gamepad and so on) without having to glue everything together in the same class. Keeping the input handlers in a separate class also yields another benefit: we can implement the IControllable interface somewhere else and therefore "listen" to what the user does without having to replicate the input logic.
The rendering subsystem
Now to the real interesting stuff! ![]()
Most of the renderer's architecture has been influenced and inspired by the Ogre3d architecure (a wonderful open-source rendering engine for DirectX 9 and OpenGL). Of course my humble renderer is a far cry from the complete Ogre3d engine, but some elements are similarly organized.
First of all, our renderer will be written as a DrawableGameComponent, implementing the IRendererService interface. This way we should be able to change renderer very easily (and we'll keep the code uncluttered, which is always my top priority
).
The renderer keeps a list of render ports: these ports are rectangles of screen on which the scene (or whathever) will be renderer at each frame. In the final game we'll use a single renderport covering the whole screen, but in theory we could define a second RenderPort and create a splitscreen game (multiplayer?).
Each render port has an active camera that will be used to render the scene, a Viewport (an XNA class defining a screen region), a shared render queue and a list of render queue invocations. Let's take a look at these elements one at a time:
Render Queue
The render queue is a simple list of RenderOperation objects (more about that later). Each render port has its own list, which will be shared between all RenderQueueInvocation objects. The user can register to an event on the render queue in order to listen to all render operations added and eventually change them on the fly.
One of the most important capabilities of the RenderQueue class is the reordering or render operations: it's a complex topic, but usually all render calls should be ordered by distance from the viewer, alpha blending and so on, to minimize conflicts, z-fighting and problems with alpha blending state changes (check out the Cornflower Blue blog for a nice article about this kind of problems). This is automatically handled by this class before the render is executed.
Render Queue Invocations
A render queue invocation is created on a specific render port and will render the whole scene, as seen through its render port, to a RenderTarget object (usually the backbuffer, but could as well be a texture or else). The user can register to an event on the invocation as well, in order to listen to specific render operations of that invocation. This way the user may change all render opertaions of a specific invocation, leaving all the others untouched. All render operations added to a render queue invocation will be scheduled and reordered on the same shared RenderQueue instance.
Render Operations
Each RenderOperation instance keeps a reference to a renderable mesh (a class that abstracts a specific rendering method: could be a simple XNA Model or a custom VertexBuffer) and a reference to a Material (an abstraction of an XNA Effect, i.e. a shader and its parameters and textures). The RenderOperation class will prepare the render device and then execute the drawing call correctly, using the provided model and material.

How it works
OK, now all elements are in place: at each Draw() call in our Renderer class we loop through all registered render ports, set up the correct Viewport and loop through all RenderQueueInvocation instances. Each invocation calls the Scene manager (next article
), that will go through all renderable actors of the scene and queue them as RenderOperation instances in the queue. The queue automatically sorts all operations based on the alpha blending, distance and other parameters, and finally the scene is rendered on the RenderPort's active RenderTarget.
The series continues with a post about scene organization...



