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
97 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:

  Contents

 DirectX Graphics
 Basic Application
 Drawing Triangles
 Indexed Triangles
 Adding Texture
 Full Screen
 Graphics


 Get the source
 Printable version

 


Drawing Triangles (d3d2.cpp)

Triangles have a few interesting properties that make them attractive to 3D programming. They are always planar. A combination of triangles can make up any shape. In the upcoming examples, we will use triangles to build a cube. I used a rotating cube as the base for my first 3D engine. If its good enough for one programmer, its good enough for another.

In its simplest form, a triangle consists of three vertices. How these vertices are defined is up to the programmer. A 2D triangle may be as plain as x and y coordinates for each point. A beefy 3D program may specify coordinates for position, transformed coordinates, color, several texture coordinates, and possibly other information.

The exact semantics of how to use this information is slightly different between OpenGL and Direct3D. Drawing discrete triangles would use this information raw and define each triangle separately. However, when drawing a model, vertices are shared between triangles, so storing all three vertices for each triangle would be inefficient. For both OpenGL and Direct3D, you can specify all the vertices of a model in a huge array. Triangles are defined as a triple of indices into this array. You can take this approach to the extreme by specifying many indices in another array, and passing the index array to a function, such as DrawIndexedPrimitive, which will draw a large part of the model at once.

We will get this far eventually, but for the next example we will just set up the foundations for this approach. To define your vertex format, Direct3D introduces the concept of a flexible vertex format (FVF). In FVF, you define a structure that includes just the components of the vertex that we need. This structure will change as your program changes, but you will initially define it as:

struct MYVERTEX { FLOAT x, y, z; // The transformed position FLOAT rhw; // 1.0 (reciprocal of homogeneous w) DWORD color; // The vertex color };

Go ahead and instantiate an array of this structure, named vertices, defining each of the three vertices for a triangle. In your InitDirect3D function, you have to create a vertex buffer:

int num_elems = sizeof(vertices) / sizeof(vertices[0]); pID3DDevice->CreateVertexBuffer(sizeof(MYVERTEX) * num_elems, D3DUSAGE_WRITEONLY, D3DFVF_XYZRHW|D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &pStreamData);

Here, the size of the vertex array in bytes is the first parameter. Since the app won’t read from these vertices, you pass in D3DUSAGE_WRITEONLY. There are various other flags that you could pass here to specify how your vertex array would be used, but you can go ahead and trust Direct3D to do the right thing for now.

Next you specify the FVF that we are using. Since you are using pre-transformed coordinates (meaning that you won’t be doing matrix operations), you first specify D3DFVF_XYZRHW. Later when you do your own matrix transformations, this will change to D3DFVF_XYZ. D3DFVF_DIFFUSE tells Direct3D that you will specify a color for each of the vertices.

The next parameter represents the type of memory management you require. You can trust Direct3D again, and pass D3DPOOL_DEFAULT.

Lastly, you pass a pointer to your vertex buffer. You may recall that this was defined in our first example, but it went unused.

Your vertex buffer is useless without filling it with meaningful data:

MYVERTEX *v; pStreamData->Lock(0, 0, (BYTE**)&v, 0); for(int ii = 0; ii < num_elems; ii++) { v[ii].x = vertices[ii].x; v[ii].y = vertices[ii].y; v[ii].z = vertices[ii].z; v[ii].rhw = vertices[ii].rhw; v[ii].color = vertices[ii].color; } pStreamData->Unlock();

This isn’t that complicated. Lock returns a pointer to where you can write your vertex data. Next, you copy your vertex data verbatim from your vertices array. Then you give the pointer back.

A pair of calls will tell Direct3D about your FVF and set your vertex array as your active vertex array (you can have multiple vertex arrays).

pID3DDevice->SetVertexShader(D3DFVF_XYZRHW | D3DFVF_DIFFUSE);

pID3DDevice->SetStreamSource(0, pStreamData, sizeof(MYVERTEX));

These parameters should be obvious. SetVertexShader tells Direct3D to use the same format that was specified in the CreateVertexBuffer call above. Since SetVertexShader and CreateVertexBuffer will use the parameter for the FVF, you can, and should, use a macro to make sure these stay the same.

SetStreamSource tells Direct3D to use pStreamData as the active vertex array, and gives the size of each element.

That was easy. You can now add the code to draw a triangle. In between the BeginScene and EndScene calls in the DrawScene function, insert this:

int num_elems = sizeof(vertices) / sizeof(vertices[0]); pID3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, num_elems / 3);

D3DPT_TRIANGLELIST will command Direct3D to draw discrete triangles, with each vertex specified individually. You start at index zero, and specify the number of triangles to draw as the last parameter.

If everything has been done correctly, you should have a triangle drawn on your previous green background.




Next : Indexed Triangles