EAE6320 – Assignment 3 – Meshes and Abstraction

This week’s assignment was about vertex and index buffers and meshes.

The first part of the assignment was to convert our triangle drawing code to use a vertex buffer and an index buffer. Instead of having 6 vertices stored in the vertex buffer (to draw 2 triangles (square) ), we now have 4 vertices and 6 indices. The obvious benefit here is that we save memory by reusing the same vertices. The index buffer determines which vertices are used to draw triangles. We also had to add color to our square. This is done by assigning a color to each vertex and let the shader interpolate.  JP gave us OpenGL and DirectX implementation for this. For this first part we just had the trivial task of filling in some data. Result:

Capture
Beautiful Square

In the second part of this assignment we have to encapsulate our vertex/index buffer data into a “mesh”. A mesh can be thought of as geometric data (which provides the form of an object). OpenGL and DirectX have different ways of creating , storing and drawing the mesh data (vertices and indices). The overall goal is to design a system which provides a single interface through which users (us?) can create meshes and be able to draw them.

To keep things within scope, I assume  that a mesh consists only of triangles. In this assignment I have actively tried to keep things as simple and separate as I can.

The Mesh interface (Mesh.h)

namespace eae6320
{
    namespace Graphics
    {
        struct Mesh;
        struct sVertex
        {
            float x, y;
            uint8_t r, g, b, a;	// 8 bits [0,255] per RGBA channel
        };

        Mesh* createMesh(sVertex* vertexData, const uint32_t vertexCount, uint32_t* indexData, const uint32_t indexCount);
    }
}

Mesh.h provides the interface for working with meshes.  sVertex is the vertex format that we will be using everywhere. It is defined here so that it is available and consistent irrespective of the platform. The interesting thing here is the createMesh function. This is a static function, that can be called to create a mesh and returns a pointer to the mesh. Both Mesh and createMesh have platform specific implementations. The Mesh struct is forward declared to avoid including implementation specific headers (like GL.h) and propagating them to any other projects that want to use Meshes. The actual Mesh struct is defined in Mesh.d3d.h and Mesh.gl.h.

Mesh.d3d.h

struct Mesh
{
    IDirect3DVertexDeclaration9* vertexDeclaration = NULL;
    IDirect3DVertexBuffer9* vertexBuffer = NULL;
    IDirect3DIndexBuffer9* indexBuffer = NULL;

    uint32_t indexCount;
    uint32_t vertexCount;

    ...

Mesh.gl.h

struct Mesh
{
    GLuint vertexArrayId;
    GLenum mode;
    GLenum indexType;
    GLsizei indexCount;
   ...

Graphics.h

I have added two more functions to Graphics.h.

 void AddMeshToScene(Mesh* i_mesh);
 void RemoveMeshFromScene(Mesh* i_mesh);

AddMeshToScene adds the given mesh pointer to a list. The render loop then automatically picks up all the meshes in the list and draws them. Similarly RemoveMeshFromScene takes a pointer to a mesh, destroys it and removes the pointer from the list. Graphics also purges the mesh list same when ShutDown is called.

DrawMesh

The DrawMesh function(s) in Graphics.d3d.cpp and Graphics.gl.cpp encapsulates the draw calls. It is called the same way inside the Render loops for both APIs. This is a first step towards abstracting the Render loop.

	for (auto it = meshList.begin(); it != meshList.end(); ++it)
	{
		eae6320::Graphics::Mesh* m = *it;
		// Set the vertex and fragment shaders
		{
			glUseProgram( s_programId );
			assert( glGetError() == GL_NO_ERROR );

            bool result = DrawMesh(m);
		}
		
	}

There are clear advantages of separating platform-independent Graphics interface from the platform-specific implementation.  This makes it easier for us in the future to do game related graphics programming without worrying about specific implementation details.  The separation also makes it easier to debug. For example, if I notice a bug when running DirectX but not in OpenGL I know that the issue lies somewhere in the d3d implementation and not in my data or game logic.

Download the executable here.

Controls: Press Esc to exit.

Leave a Reply