Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
93 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Introduction

For those of you just joining the discussion, in my previous article I began a conversation about creating a small engine design which would allow our scene to be rendered in both Direct3D and OpenGL using an abstract DLL system. We had a good start, and now that we've had some time to digest the information, we should proceed with the next level.

Oh Primitive Where Art Thou?

We left our previous discussion with the obvious cliffhanger, "Great. I can create a renderer DLL system. Now how do I render my primitives?". That's what we'll discuss right now. Just to remind the reader, again this is just one way of solving the cross-graphics API problem.

There are a plethora of primitive types that both OpenGL (1.1) and Direct3D support. Triangles, triangle strips, line lists, etc. I think for ease of use (and to keep this article less than 20 pages in length), we'll focus on just using the Triangle primitive type. Our goal is to create a system where we can just keep lobbing triangles at our engine and then let the engine take care of the rendering.

Within OpenGL, this is a pretty much easy and straight forward thing to do. We simply just keep adding GL_TRIANGLE data, and let the OpenGL pipeline take care of the rest.

With Direct3D8, however, we aren't QUITE as lucky (or maybe we are, depends on how you look at it). The Direct3DDevice8 interface doesn't just allow us to lob data at it, no matter how primitive it is. We first have to pass our triangle data into what's known as a "Vertex Buffer", which is then passed over to our Direct3D pipeline.

The SDK documentation explains the concept of a Vertex Buffer in great detail well enough, so we won't bother going over ground that's been repeated already. Suffice it to say, it's what you would expect it to be with a name like Vertex Buffer.

To proceed from where we are now, to our goal of rendering triangles to the screen, we need to form an action plan that we can follow for our rendering DLL system. Remember, "If you fail to plan, you plan to fail.":

  1. Create a VertexBuffer interface that we can instantiate with our DLL system
  2. Implement the OpenGL code for said interface
  3. Implement Direct3D8 code for said interface
  4. Test by sending triangles to the interface
  5. Get happy!

Step 1: A VertexBuffer interface

Similar to our Renderer interface for our DLL system, we need a common interface that we can use to abstract our VertexBuffer object. The way I say it, we only need a few methods for this interface:

  • Create the object
  • Lock down a section of video hardware memory
  • Add a primitive to the buffer
  • Unlock the section of video hardware memory
  • Flush the buffer (ie. send the buffer over to our graphics pipeline)
//vbInterface.h
//This class is to provide us with an interface object to our vertex buffer object,
//to free us from having specific graphic API calls inside our static library.
class vbInterface
{
  protected:
    //some base level objects can go here

  public:
    vbInterface(){};
    virtual ~vbInterface(){};

    //method to create and initialize our object
    virtual HRESULT createVB(int, VOID* = NULL) = 0;

    //method to destroy it
    virtual void destroyVB() = 0;

    //method to lock down video hardware memory
    virtual HRESULT lockVB() = 0;

    //method to unlock video hardware memory
    virtual void unlockVB() = 0;

    //method to add triangle to the buffer
    virtual void addTriToVB(D3DXVECTOR3 pos, FLOAT r, FLOAT g, FLOAT b, FLOAT a, D3DXVECTOR2 tex) = 0;

    //method to flush the buffer
    virtual void flushBuffer() = 0;

};

//Note that for now we're going to create this object
//within our existing rendererInterface object, so we'll add it to that now..

class rendererInterface
{
   protected:
      //keep the existing data members the same

   public:
      vbInterface* m_pVB;//declare a base pointer to our object
   
   //... the rest of the object is the same

};

This interface definition is straight forward enough I think. We pretty much explained everything before coding this interface.

Because we're stuffing our vbInterface object inside the rendererInterface, we won't be able to instantiate our rendererInterface object until we properly define derivations of the vbInterface object.



Conclusion

Contents
  Introduction
  Conclusion

  Source code
  Printable version
  Discuss this article

The Series
  Part I
  Part II
  Part III
  Part IV