Tyr: Debug Rendering
This second post this week, is to make up for the very very high likely-hood that there will be no post next week due to work and such.
It’s gotten to the stage where the editor could do with drawing some stuff, stuff like markers to show the position/size of the currently selected node, and this raises an interesting problem. It would also be good for game-nodes to be able to draw debug graphics, from anywhere in their update loop (or functions called from that).
So, I have need of a way to store rendering commands for later use, my idea was to have some form of buffer that is cleared every frame, and methods to add common shapes to that buffer (rectangles, circles, etc.). Due to the way that scenes work in Tyr (there can be more than one scene active at the same time) I need a buffer per-scene, and a global buffer, for things like the editor who will want to draw out-side of a nodes update loop.
Now, the simplest method of drawing shapes with OpenGL is with the glBegin and glEnd methods, basically, you call glBegin, passing it a mode (To draw lines, quads, triangles, etc.) and then repeatedly call glVertex, glTexCoord, glColour, describing the geometry you’d like to draw. So I decided to create a class that holds to std::vectors, the fist one is a list of commands (to be passed to glBegin) and an index into the second buffer, the second buffer will hold only floats, used to express the colour and vertex positions of the items I wish to draw. I made the decision that any primitive drawn by this debug-drawing code will have all vertecies drawn in the same colour, to save on the amount of information I’m saving (and to stress that this is for debug-only drawing, since it will be rather slow).
Primitives are added to this buffer by calling one of several methods (DrawRect, DrawCircle, etc.) all of which take a RGBA colour, and the relevant per-primitive information, this calls internal methods to add commands to the command list and float to the float list, this is all done so that the drawing code can be as simple as possible.
void Renderer::Render( DebugBuffer* pBuffer )
{
glDisable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, 0);
g_ShaderManager.Bind(INVALID_SHADER);
size_t commandIndex = 0;
size_t floatIndex = 0;
while(floatIndex < pBuffer->m_FloatList.size())
{
GLuint startIndex = pBuffer->m_CommandList[commandIndex++];
TYR_ASSERT_MSG(floatIndex == startIndex, "Start index does not match current float index");
GLuint command = pBuffer->m_CommandList[commandIndex++];
// Grab the colours
GLfloat red = pBuffer->m_FloatList[floatIndex++];
GLfloat green = pBuffer->m_FloatList[floatIndex++];
GLfloat blue = pBuffer->m_FloatList[floatIndex++];
GLfloat alpha = pBuffer->m_FloatList[floatIndex++];
// We should end before the next set
GLuint nextStartIndex = commandIndex < pBuffer->m_CommandList.size() ?
pBuffer->m_CommandList[commandIndex] :
pBuffer->m_FloatList.size();
// Draw it
glBegin(command);
for(;floatIndex < nextStartIndex;)
{
float x = pBuffer->m_FloatList[floatIndex++];
float y = pBuffer->m_FloatList[floatIndex++];
float z = pBuffer->m_FloatList[floatIndex++];
glColor4f(red, green, blue, alpha);
glVertex3f(x, y, z);
}
glEnd();
}
}
This is the code that renders a DebugBuffer, it simply loops through the commands, grabbing the needed floats from the float buffer that are then used to render the primitive. Each command knows the index of the first float that it needs to access (the red value for the colour) and by looking ahead to the next command, we can get the total number of floats that command uses.
The system as it stands seems to work quite well, as I mentioned earlier, each scene has it’s own debug buffer and there is a global one too, the rendering order is scene (and child nodes), the scene’s DebugBuffer and finally the global DebugBuffer, so that the debug information is always available (I may have to alter this when I add support for post-process effects, as those will want to be applied before the debug rendering. I also need to extent this method to allow the rendering of text, but I’m not entirely sure how to go about doing that one yet. This system is rather new (it was implemented mostly on Saturday night) so it may change over time when it is used more extensively.
Tags: C++, OpenGL, Tyr