We'll need a few new data structures, here are the definitions for these global items
//Declare a structure to hold a vertex with all the information that we need
struct my_vertex{
FLOAT x, y, z, rhw; // The transformed position for the vertex.
DWORD color; // The vertex color.
};
//A handy little 'macro' for our definition of the vertex. When we use the vertex data
//we have to tell D3D what data we're passing it. D3DFVF_DIFFUSE specifies that the
//vertex will have a colour, the D3DFVF_XYZRHW specifies that the vertex will have
//coordinate given in screen space.
#define D3D8T_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
//The x & y values here are given in 'screen space'. Vertices in screen space
//are referred to as Transformed vertices. What this means is that the x and y
//coordinates are given as offsets from the top left of the screen. If you change
//these coordinates by adding 50 to each x value, you will move everything to the
//right by 50 pixels.
//With Transformed vertices, the z coordinate doesn't do much. If you have a Z Buffer
//it determines which objects block other objects, but changing the z coordinate will
//have no other effect. The visible size (given by the x,y coordinates) is static, so
//even though you can effectively move the object deeper into the screen, it will remain
//the same size.
my_vertex g_triangle_vertices[] ={
{ 125.0f, 50.0f, 0.5f, 1.0f, 0xFFFFFFFF }, // x, y, z, rhw, color
{ 200.0f, 200.0f, 0.5f, 1.0f, 0xFFFFFFFF },
{ 50.0f, 200.0f, 0.5f, 1.0f, 0xFFFFFFFF }
};
//Direct3D does not support a Quad rendering primitive like OpenGL does. It does have
//Triangle Strips. In a Triangle Strip, the first 3 vertices form a triangle, and
//then each additional vertex adds a triangle formed by itself and the previous 2
//vertices. Thus to draw a square we need only 4 vertices.
my_vertex g_square_vertices[] ={
{ 250.0f, 200.0f, 0.5f, 1.0f, 0xFFFFFFFF }, // x, y, z, rhw, color
{ 250.0f, 50.0f, 0.5f, 1.0f, 0xFFFFFFFF },
{ 400.0f, 200.0f, 0.5f, 1.0f, 0xFFFFFFFF },
{ 400.0f, 50.0f, 0.5f, 1.0f, 0xFFFFFFFF }
};
//Vertex buffers are a method of storing vertices to be rendered in an optimized manner.
IDirect3DVertexBuffer8 *g_triangle=NULL;
IDirect3DVertexBuffer8 *g_square=NULL;
We have to add some clean up to our kill_scene function.
// Function:kill_scene
// Whazzit:Clean up any objects we required for rendering.
void kill_scene(void){
if(g_triangle){
g_triangle->Release();
g_triangle=NULL;
}
if(g_square){
g_square->Release();
g_square=NULL;
}
}
Our init_scene() now has quite a bit of code in it
// Function:init_scene
// Whazzit:One-time preparation of objects required for rendering. In this tutorial we prepare
// 2 objects:a triangle & a square.
void init_scene(void){
HRESULT hr;
unsigned char *vb_vertices;
//As mentioned above, a Vertex Buffer is an optimized storage medium for vertices.
//Here we create a vertex buffer large enough to hold 3 vertices. We specify that
//it can only be written to and we allow Direct3D to determine where in memory
//it should be placed.
hr=g_d3d_device->CreateVertexBuffer(3*sizeof(my_vertex),//Size of memory to be allocated
// Number of vertices * size of a vertex
D3DUSAGE_WRITEONLY, // We never need to read from it so
// we specify write only, it's faster
D3D8T_CUSTOMVERTEX, //Our custom vertex specifier (coordinates & a colour)
D3DPOOL_MANAGED, //Tell DirectX to manage the memory of this resource
&g_triangle); //Pointer to our triangle, after this call
//It will point to a valid vertex buffer
if(FAILED(hr)){
FatalError("Error creating triangle vertex buffer");
}
//The only difference between this and the above call is that we're allocating
//enough space for 4 vertices instead of 3.
hr=g_d3d_device->CreateVertexBuffer(4*sizeof(my_vertex),//Size of memory to be allocated
// Number of vertices * size of a vertex
D3DUSAGE_WRITEONLY, // We never need to read from it so
// we specify write only, it's faster
D3D8T_CUSTOMVERTEX, //Our custom vertex specifier (coordinates & a colour)
D3DPOOL_MANAGED, //Tell DirectX to manage the memory of this resource
&g_square); //Pointer to our triangle, after this call
//It will point to a valid vertex buffer
if(FAILED(hr)){
FatalError("Error creating square vertex buffer");
}
//Now we have our Vertex Buffers, but they're empty. To put our data into them
//we Lock the Vertex Buffer so Direct3D knows we're modifying it, then we copy
//our data in and Unlock it so Direct3D knows we're done.
hr=g_triangle->Lock(0, //Offset, we want to start at the beginning
0, //SizeToLock, 0 means lock the whole thing
&vb_vertices, //If successful, this will point to the data in the VB
0); //Flags, nothing special
if(FAILED(hr)){
FatalError("Error Locking triangle buffer");
}
//vb_vertices now points to our vertices inside the Vertex buffer, so
//to fill in our VB, we copy to vb_vertices.
memcpy(vb_vertices, g_triangle_vertices, sizeof(g_triangle_vertices) );
//Unlock so Direct3D knows we're done and can do any behind-the-scenes magic required
g_triangle->Unlock();
//Now we go through the same process to fill in our VB for the square.
hr=g_square->Lock(0, //Offset, we want to start at the beginning
0, //SizeToLock, 0 means lock the whole thing
&vb_vertices, //If successful, this will point to the data in the VB
0); //Flags, nothing special
if(FAILED(hr)){
FatalError("Error Locking square buffer");
}
memcpy(vb_vertices, g_square_vertices, sizeof(g_square_vertices) );
g_square->Unlock();
}
Here's our new render() function
// Function: render
// Whazzit:Clears the screen to a pseudo-random colour, draws a triangle and a square
// and then presents the results.
void render(void){
static unsigned char red=0,green=0,blue=0;
//These will safely overflow when the values go over 255, wrapping back to 0.
red++;
green+=2;
blue+=3;
//Clear the buffer to our new colour.
g_d3d_device->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(red,green,blue), 1.0f, 0 );
//Notify the device that we're ready to render
if(SUCCEEDED(g_d3d_device->BeginScene())){
//Vertex shaders are a complex topic, but you can do some amazing things with them
//For this example we're not creating one, so we tell Direct3D that we're just
//using a plain vertex format.
g_d3d_device->SetVertexShader(D3D8T_CUSTOMVERTEX);
//D3D's rendering functions read from streams. Here we tell D3D that the
//VB we created for our triangle is the stream it should read from.
g_d3d_device->SetStreamSource(0,g_triangle,sizeof(my_vertex));
//After all that setup, actually drawing the triangle is pretty easy.
//We tell it what we're giving it (a Triangle List), where it should
//start reading (0, the beginning), and how many triangles we're drawing(1)
g_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST,0,1);
//Set the active stream to be our square.
//NOTE: Because of the offsets that DrawPrimitive takes, we could
//have built the triangle & square into a single VB and still
//drawn them seperately.
g_d3d_device->SetStreamSource(0,g_square,sizeof(my_vertex));
//Now we're drawing a Triangle Strip, 4 vertices to draw 2 triangles.
g_d3d_device->DrawPrimitive(D3DPT_TRIANGLESTRIP,0,2);
//Notify the device that we're finished rendering for this frame
g_d3d_device->EndScene();
}
//Show the results
g_d3d_device->Present( NULL, NULL, NULL, NULL );
}
|