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
101 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

When I first began building a DirectX graphics engine, I ran into an almost ironic problem. I couldn't find out how to implement the features I wanted to write in Direct3D. It was particularly bad with certain features such as BSP rendering; all the examples I found were in OpenGL. I've ported OpenGL code to Direct3D before, but problems arose when people decided to use the glVertex* family of commands. There just isn't anything like them in Direct3D. It got even worse when I needed to write some sort of culling or polygon reduction algorithm and the easiest way to do it was vertex by vertex, face by face. I never found any examples that had used OpenGL's vertex array commands, which have Direct3D counterparts. Recently, I came up with a rather clever idea. I figured that it should be possible to write the OpenGL vertex commands in Direct3D so that the vertices could be specified one by one, but they would still be sent down the graphics pipeline in a vertex buffer. The overhead incurred would be minimal, and it would be extremely useful. Porting code from OpenGL to Direct3D would be simplified greatly; it would be easier to write multi-API code.

The Concept

It's important to realize that I am not going to develop a Direct3D wrapper using OpenGL commands. I am essentially implementing a new set of commands for the Direct3D API. We will follow the OpenGL naming conventions for our new commands and approximately the same argument format. In this particular article, I will show how to implement the following commands:

d3dBegin( D3DPRIMITIVETYPE Type, IDirect3DDevice8* pDevice );
d3dEnd();
d3dVertex3f( float x, float y, float z );
d3dNormal3f( float x, float y, float z );
d3dColor4f( float a, float r, float g, float b );  
d3dTexCoord2f( float tu, float tv );

I decided that these were the core set of commands necessary for the implementation. Once you see the functions, it will be very easy to add more versions (e.g. d3dColor4ub) of these functions, as well as whole new functions. The next step, though, is to determine exactly what each of these functions should do.

The Interface

I considered whether or not to write these as regular C functions, or to build a C++ class out of them. While a C++ class would have some advantages, primarily that it would allow multiple sets of vertices, it's not really all that useful. In addition, I'm trying to follow OpenGL's model as closely as possible, so we'll write them in regular C style. (NOTE: This code is C++, not C! We'll be using C++ to avoid the painfully long calls involved with coding DirectX in C.) In order to write these functions in C style, we will need a number of static variables in the CPP file along with the function implementations.

The Functions

For the definitions of these functions, I've stayed as close as reasonably possible to the definitions for their OpenGL counterparts. There are only two changes. First of all, we need to get the device pointer somewhere; I decided to get it in the call to d3dBegin(). Second, OpenGL usually takes its colors in RGBA, but Direct3D likes them in ARGB format. I changed d3dColor4f() to use ARGB color order. It's not so important for that particular call, but it will become important if you ever write d3dColor4fv, which takes a pointer to the color locations.

d3dBegin( D3DPRIMITIVETYPE Type, IDirect3DDevice8* pDevice );
In d3dBegin, we want to set up the data structures we will be using and prepare to store the vertices. We'll also store a pointer to the device. It's important to realize that this function will not call pDevice->BeginScene(). I've chosen not to call it for flexibility; it shouldn't be a problem to call BeginScene() just before the call to d3dBegin() if you really want to. Since no actual rendering or vertex buffer creation will occur at this stage, we don't really need the device pointer. We could have passed pDevice into d3dEnd. You can move it if you like; it's a semantic decision on my part. If you do leave it here, you must not forget to put pDevice->AddRef()! (You will see the implementation later; I'm just reminding you about it.) Lastly, it would be really helpful to remember whether or not d3dBegin has been called. If d3dEnd gets called first, all sorts of wacky stuff could happen, so we need to make sure things work out correctly.

d3dEnd();
This is the function with the most code. When d3dEnd is called, we will have a list of vertices with their associated normals, diffuse colors, and texture coordinates. We will create a vertex buffer for these vertices, transfer the vertices to the vertex buffer, and render the vertices. There will also be a couple of calculations to be made, like the number of faces to be drawn. As with d3dBegin(), this function will not call pDevice->EndScene()! You can do it just after the call if you need to.

d3dVertex( float x, float y, float z );
The Vertex function is, of course, the one that adds vertices to our list of vertices. Also, it will be assigned the duty of resizing the list if it has run out of space. This brings us to the problem of the best way to represent our list of vertices. I will discuss it in a few paragraphs; let's first define what our functions are supposed to do.

d3dNormal3f( float x, float y, float z );
Here, we define the normals for the vertex. This might seem a bit tricky because the normal is not actually applied to a vertex until the d3dVertex3f call. Furthermore, subsequent calls to d3dVertex3f should use the same normal, unless d3dNormal3f is called again. We will therefore need some sort of variable to keep track of the current normals.

d3dColor4f( float a, float r, float g, float b );
This function will define the diffuse color for the vertices. As with d3dNormal3f, we will need to store these values between calls. Also notice that I rearranged the parameters from RGBA (used in the OpenGL standard) to ARGB. This is to conform to the Direct3D standard, which in this case opposes the OpenGL standards.

d3dTexCoord2f( float tu, float tv );
This defines the texture coordinates for the vertices. Once again, we will need to store these values between calls.



Page 2

Contents
  Page 1
  Page 2
  Page 3

  Source code
  Printable version
  Discuss this article