Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Introduction to 3D Game Programming with DirectX.9.0 - F. D. Luna

.pdf
Скачиваний:
240
Добавлен:
24.05.2014
Размер:
6.94 Mб
Скачать

84 Chapter 3

3.4.3 Begin/End Scene

The last bit of information to mention is that all drawing methods must be called inside an IDirect3DDevice9::BeginScene and

IDirect3DDevice9::EndScene pair. For example, we would write:

_device->BeginScene(); _device->DrawPrimitive(...);

_device->EndScene();

 

 

 

 

Y

3.5 D3DX Geometric Objects

 

 

 

L

Building 3D objects by constructing each triangle in code is tedious.

Fortunately, the D3DX library provides some methods to generate the

 

 

M

 

mesh data of simple 3D objects for us.

 

 

The D3DX library provides the following six mesh creation

functions:

 

F

 

D3DXCreateBox

 

 

 

 

T

 

 

 

D3DXCreateSphere

 

 

 

 

D3DXCreateCylinderA

 

 

D3DXCreateEeapot

 

 

 

D3DXCreatePolygon

 

 

 

D3DXCreateTorus

 

 

 

Figure 3.3: Objects created and rendered using the D3DXCreate* functions

All six are used similarly and use the D3DX mesh data structure ID3DXMesh as well as the ID3DXBuffer interface. These interfaces are covered in Chapters 10 and 11. For now, we ignore their details and concentrate on using them in the simplest way.

HRESULT D3DXCreateTeapot(

LPDIRECT3DDEVICE9 pDevice, // device associated with the mesh LPD3DXMESH* ppMesh, // pointer to receive mesh LPD3DXBUFFER* ppAdjacency // set to zero for now

);

Team-Fly®

Drawing in Direct3D 85

An example of using the D3DXCreateTeapot function:

ID3DXMesh* mesh = 0;

D3DXCreateTeapot(_device, &mesh, 0);

Once we have generated the mesh data, we can draw it using the ID3DXMesh::DrawSubset method. This method takes one parameter, which identifies a subset of the mesh. The meshes generated by the above D3DXCreate* functions create a mesh with only one subset, so zero can be specified for this parameter. An example of rendering the mesh:

_device->BeginScene(); mesh->DrawSubset(0);

_device->EndScene();

When you are done with the mesh, you must release it:

_mesh->Release();

_mesh = 0;

3.6 Sample Applications: Triangle,

Cube, Teapot, D3DXCreate*

P a r t I I

There are four samples located in this chapter’s directory in the book’s companion files, which can be downloaded from the book’s web site.

Triangle—This very simple application demonstrates how to create and render a triangle in the wireframe mode.

Cube—A little more advanced than the triangle sample, this application renders a spinning wireframe cube.

Teapot—This application uses the D3DXCreateTeapot function to create and render a spinning teapot.

D3DXCreate—This application creates and renders several different kinds of 3D objects that can be created with the D3DXCreate* functions.

Let’s briefly discuss the implementation of the Cube sample. You can study the others on your own.

The sample application draws and renders a cube, as shown in Figure 3.4. The project and complete source code for this sample can be found on this chapter’s web page.

86 Chapter 3

Figure 3.4 A screen shot from the CubeApp sample

First we instantiate the following two global variables to hold the vertex and index data of our cube:

IDirect3DVertexBuffer9* VB = 0;

IDirect3DIndexBuffer9* IB = 0;

In addition, we instantiate two constant global variables that define the resolution of our screen:

const int Width = 800;

const int Height = 600;

We then define our vertex structure and the flexible vertex format of that structure. The vertex structure in this sample holds only vertex position information:

struct Vertex

{

Vertex(){}

Vertex(float x, float y, float z)

{

_x = x; _y = y; _z = z;

}

float _x, _y, _z; static const DWORD FVF;

};

const DWORD Vertex::FVF = D3DFVF_XYZ;

Let’s move on to the framework functions. The Setup function creates the vertex and index buffers, locks them, writes the vertices that make up the cube to the vertex buffer, and writes the indices that define the triangles of the cube. It then moves the camera a few units back so that it can see the cube that will be rendered at the origin of the world. Then it sets the projection transform. Finally, it sets the fill mode render state to wireframe mode:

bool Setup()

{

Drawing in Direct3D 87

// create vertex and index buffers Device->CreateVertexBuffer(

8 * sizeof(Vertex), D3DUSAGE_WRITEONLY, Vertex::FVF, D3DPOOL_MANAGED, &VB,

0);

Device->CreateIndexBuffer( 36 * sizeof(WORD), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &IB,

0);

//fill the buffers with the cube data Vertex* vertices;

VB->Lock(0, 0, (void**)&vertices, 0);

//vertices of a unit cube

vertices[0] = Vertex(-1.0f, -1.0f, -1.0f); vertices[1] = Vertex(-1.0f, 1.0f, -1.0f); vertices[2] = Vertex( 1.0f, 1.0f, -1.0f); vertices[3] = Vertex( 1.0f, -1.0f, -1.0f); vertices[4] = Vertex(-1.0f, -1.0f, 1.0f); vertices[5] = Vertex(-1.0f, 1.0f, 1.0f); vertices[6] = Vertex( 1.0f, 1.0f, 1.0f); vertices[7] = Vertex( 1.0f, -1.0f, 1.0f);

VB->Unlock();

//define the triangles of the cube: WORD* indices = 0;

IB->Lock(0, 0, (void**)&indices, 0);

//front side

indices[0] = 0; indices[1] = 1; indices[2] = 2; indices[3] = 0; indices[4] = 2; indices[5] = 3;

// back side

indices[6] = 4; indices[7] = 6; indices[8] = 5; indices[9] = 4; indices[10] = 7; indices[11] = 6;

// left side

indices[12] = 4; indices[13] = 5; indices[14] = 1; indices[15] = 4; indices[16] = 1; indices[17] = 0;

// right side

indices[18] = 3; indices[19] = 2; indices[20] = 6; indices[21] = 3; indices[22] = 6; indices[23] = 7;

// top

indices[24] = 1; indices[25] = 5; indices[26] = 6; indices[27] = 1; indices[28] = 6; indices[29] = 2;

P a r t I I

88 Chapter 3

// bottom

indices[30] = 4; indices[31] = 0; indices[32] = 3; indices[33] = 4; indices[34] = 3; indices[35] = 7;

IB->Unlock();

// position and aim the camera D3DXVECTOR3 position(0.0f, 0.0f, -5.0f); D3DXVECTOR3 target(0.0f, 0.0f, 0.0f); D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); D3DXMATRIX V;

D3DXMatrixLookAtLH(&V, &position, &target, &up);

Device->SetTransform(D3DTS_VIEW, &V);

//set projection matrix D3DXMATRIX proj; D3DXMatrixPerspectiveFovLH(

&proj,

D3DX_PI * 0.5f, // 90 - degree (float)Width / (float)Height, 1.0f,

1000.0f); Device->SetTransform(D3DTS_PROJECTION, &proj);

//set the render states

Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);

return true;

}

The Display method has two tasks; it must update the scene and then render it. Since we want to spin the cube, we are going to increment an angle every frame that specifies how much the cube is to rotate that frame. By incrementing it every frame, the cube will be slightly more rotated every frame, making it look like it is spinning. Notice that we use the world transformation to actually orient the cube. Then we draw the cube using the IDirect3DDevice9::DrawIndexedPrimitive method.

bool Display(float timeDelta)

{

if( Device )

{

//

//spin the cube:

D3DXMATRIX Rx, Ry;

//rotate 45 degrees on x-axis D3DXMatrixRotationX(&Rx, 3.14f / 4.0f);

//incremement y-rotation angle each frame static float y = 0.0f; D3DXMatrixRotationY(&Ry, y);

Drawing in Direct3D 89

y += timeDelta;

//reset angle to zero when angle reaches 2*PI if( y >= 6.28f )

y = 0.0f;

//combine rotations

D3DXMATRIX p = Rx * Ry;

Device->SetTransform(D3DTS_WORLD, &p);

//

// draw the scene:

//

Device->Clear(0, 0,

D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);

Device->BeginScene();

Device->SetStreamSource(0, VB, 0, sizeof(Vertex)); Device->SetIndices(IB); Device->SetFVF(Vertex::FVF); Device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,

0, 0, 8, 0, 12);

Device->EndScene(); Device->Present(0, 0, 0, 0);

}

return true;

}

Finally, we clean up any memory that we have allocated. This means releasing the vertex and index buffer interfaces:

void Cleanup()

{

d3d::Release<IDirect3DVertexBuffer9*>(VB);

d3d::Release<IDirect3DIndexBuffer9*>(IB);

}

3.7Summary

Vertex data is stored in the IDirect3DVertexBuffer9 interface. Similarly, index data is stored in the IDirect3DIndexBuffer9 interface. The reason for using vertex/index buffers is that the data can be stored in video memory.

Geometry that is static (that is, does not need to be updated every frame) should be stored in a static vertex/index buffer. On the other hand, geometry that is dynamic (that is, does need to get updated every frame) should be stored in a dynamic vertex/index buffer.

P a r t I I

90Chapter 3

Render states are states that the device maintains that affect how geometry is rendered. Render states remain in effect until changed, and the current values are applied to the geometry of any subsequent drawing operations. All render states have initial default values.

To draw the contents of a vertex buffer and an index buffer you must:

Call IDirect3DDevice9::SetStreamSource and hook the vertex buffer that you wish to draw from to a stream.

Call IDirect3DDevice9::SetFVF to set the vertex format of the vertices to render.

If you are using an index buffer, call IDirect3DDevice9:: SetIndices to set the index buffer.

Call either IDirect3DDevice9::DrawPrimitive or

IDirect3DDevice9::DrawIndexedPrimitive in between an IDirect3DDevice9::BeginScene and

IDirect3DDevice9::EndScene pair.

Using the D3DXCreate* functions, we can create the geometry of more complex 3D objects, such as spheres, cylinders, and teapots.

Chapter 4

Color

In the last chapter we rendered the objects in the scene using lines to form wireframe meshes. In this chapter we learn how to render solid objects with color.

Objectives

To learn how color is described in Direct3D

To understand how colors are shaded across a triangle

4.1Color Representation

In Direct3D, colors are described with an RGB triplet. That is, we specify the amount of red, green, and blue color. The additive mixing of these three components determines the final color. By using a combination of red, green, and blue, we can represent millions of colors.

We use two different types of structures to hold the RGB data. The first is the D3DCOLOR type, which is actually typedefed as a DWORD and is 32 bits. The bits in the D3DCOLOR type are divided into four 8-bit sections, where each section stores the intensity of a color component. Figure 4.1 shows the distribution.

Figure 4.1: A 32-bit color, where a byte is allocated for each primary color component red, green, and blue. A fourth byte is allocated for the alpha component.

Since each color component gets a byte of memory, the intensity of the color can range from 0-255. Values near 0 specify a low intensity, and values near 255 specify a strong intensity.

91

92 Chapter 4

Note: Do not worry about the alpha component now; it is used for alpha blending—the topic of Chapter 7.

Specifying each component and then inserting it into the proper position in the D3DCOLOR type will require some bit operations. Direct3D provides a macro that performs this for us called D3DCOLOR_ARGB.

There is one parameter for each color component and the alpha component. Each parameter must be in the range 0-255 and is used like so:

D3DCOLOR brightRed = D3DCOLOR_ARGB(255, 255, 0, 0);

D3DCOLOR someColor = D3DCOLOR_ARGB(255, 144, 87, 201);

Alternatively, we can use the D3DCOLOR_XRGB macro, which is similar but does not take the alpha parameter; rather, it sets the alpha to 0xff (255).

#define D3DCOLOR_XRGB(r,g,b) D3DCOLOR_ARGB(0xff,r,g,b)

Another way to store a color in Direct3D is with the D3DCOLORVALUE structure. With this structure we use a floating-point value to measure the intensity of each component. The range measures from 0 to 1—0 being no intensity and 1 being full intensity.

typedef struct _D3DCOLORVALUE {

float r; // the red component, range 0.0-1.0 float g; // the green component, range 0.0-1.0

float b; // the blue component, range 0.0-1.0 float a; // the alpha component, range 0.0-1.0

} D3DCOLORVALUE;

Alternatively, we can use the D3DXCOLOR structure, which contains the same data members as D3DCOLORVALUE but provides useful constructors and overloaded operators, making color manipulations easy. In addition, since they can contain the same data members, we can cast back and fourth between the two. D3DXCOLOR is defined as:

typedef struct D3DXCOLOR

{

#ifdef __cplusplus public:

D3DXCOLOR() {} D3DXCOLOR( DWORD argb );

D3DXCOLOR( CONST FLOAT * ); D3DXCOLOR( CONST D3DXFLOAT16 * ); D3DXCOLOR( CONST D3DCOLORVALUE& );

D3DXCOLOR( FLOAT r, FLOAT g, FLOAT b, FLOAT a );

// casting

operator DWORD () const;

operator FLOAT* ();

operator CONST FLOAT* () const;

Color 93

operator D3DCOLORVALUE* ();

operator CONST D3DCOLORVALUE* () const;

operator D3DCOLORVALUE& ();

operator CONST D3DCOLORVALUE& () const;

// assignment operators

D3DXCOLOR& operator += ( CONST D3DXCOLOR& ); D3DXCOLOR& operator -= ( CONST D3DXCOLOR& ); D3DXCOLOR& operator *= ( FLOAT ); D3DXCOLOR& operator /= ( FLOAT );

// unary operators

D3DXCOLOR operator + () const; D3DXCOLOR operator - () const;

// binary operators

D3DXCOLOR operator + ( CONST D3DXCOLOR& ) const; D3DXCOLOR operator - ( CONST D3DXCOLOR& ) const; D3DXCOLOR operator * ( FLOAT ) const;

D3DXCOLOR operator / ( FLOAT ) const;

friend D3DXCOLOR operator * (FLOAT, CONST D3DXCOLOR& );

BOOL operator == ( CONST D3DXCOLOR& ) const;

BOOL operator != ( CONST D3DXCOLOR& ) const;

#endif //__cplusplus FLOAT r, g, b, a;

} D3DXCOLOR, *LPD3DXCOLOR;

Note: Observe that the D3DCOLORVALUE and the D3DXCOLOR structure both have four floating-point components. This leads to the common notation of treating a color as a 4D vector (r, g, b, a). Color vectors are added, subtracted, and scaled just like regular vectors. On the other hand, dot and cross products do not make sense for color vectors, but component-wise multiplication does make sense for colors. Thus, the color-color multiplication operator in the D3DXCOLOR class performs component-wise multiplication. The symbol denotes component-wise multiplication, and it is defined as:

(c1, c2, c3, c4) (k1, k2, k3, k4) = (c1k1, c2k2, c3k3, c4k4).

We now update our d3dUtility.h file with the following global color constants:

namespace d3d

{

.

.

.

const D3DXCOLOR

WHITE(

D3DCOLOR_XRGB(255,

255,

255) );

const D3DXCOLOR

BLACK(

D3DCOLOR_XRGB(

0,

0,

0)

);

const D3DXCOLOR

RED(

D3DCOLOR_XRGB(255,

0,

0)

);

const D3DXCOLOR

GREEN(

D3DCOLOR_XRGB(

0,

255,

0)

);

const D3DXCOLOR

BLUE(

D3DCOLOR_XRGB(

0,

0,

255)

);

P a r t I I