• No results found

Part Overview

Chapter 4: Direct3D Initialization Overview

4.6 Demo Application: Hello Direct3D

Finally, we can put everything we have learned thus far together and write our first Direct3D program. In this demo application, we initialize Direct3D and output text to the client area using Direct3D. To make the demo less boring, we draw the text using a new random color each frame. Figure 4.10 shows a screenshot of the demo:

Figure 4.10: A screenshot of the demo application we create in this chapter.

When beginning a new application, we derive a new class from D3DApp and override the framework functions:

class HelloD3DApp : public D3DApp {

public:

HelloD3DApp(HINSTANCE hInstance, std::string winCaption,

This child class has one data member, an ID3DXFont interface from which text output in Direct3D is done.

To get the application rolling, we instantiate an instance of our child class and enter the message loop:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd)

Note that the first few lines of WinMain are used for debugging and are discussed in the next section, so just ignore them for now. Also recall that gd3dApp is a global D3DApp pointer declared in d3dUtil.h; in this way, we have access to the Direct3D application instance at the global scope.

Now, let's examine the implementation of the constructor, destructor, and framework methods.

HelloD3DApp::HelloD3DApp(HINSTANCE hInstance, std::string winCaption,

D3DDEVTYPE devType, DWORD requestedVP) : D3DApp(hInstance, winCaption, devType, requestedVP)

{

fontDesc.OutputPrecision = OUT_DEFAULT_PRECIS;

fontDesc.Quality = DEFAULT_QUALITY;

fontDesc.PitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;

_tcscpy(fontDesc.FaceName,_T("Times New Roman"));

HR(D3DXCreateFontIndirect(gd3dDevice, &fontDesc, &mFont));

}

The constructor first constructs its parent part in the initialization list. Then it seeds the random number generator. Next it checks the device capabilities with checkDeviceCaps. It then fills out a D3DXFONT_DESC structure, which describes the attributes of the font we use for text drawing. Finally, it invokes the D3DXCreateFontIndirect function, which returns a pointer to an ID3DXFont interface (via the third parameter) based on the specified font description

(parameter two). Note that the function also requires that we pass in a copy of a valid Direct3D device pointer (parameter one), since the font will need the device for drawing the text (all drawing is done through the Direct3D device).

Because the constructor created an ID3DXFont object, the destructor must destroy it:

HelloD3DApp::~HelloD3DApp() {

ReleaseCOM(mFont);

}

The ReleaseCOM macro is defined in d3dUtil.h and simply calls the Release method and sets the pointer to null:

#define ReleaseCOM(x) { if(x){ x - >Release();x = 0; } }

For this simple demo, there are no device capabilities to check; thus our checkDeviceCaps implementation simply returns true:

bool Hel1oD3DApp::checkDeviceCaps() {

// Nothing to check.

return true;

}

Similarly, there is nothing to update in this demo, so our updateScene function does nothing:

void HelloD3DApp::updateScene(float dt) {

}

Recall that certain resources need to be released before resetting the device, and certain resources and device states need to be restored after resetting the device.

To handle these situations, our framework provides the onLostDevice and onResetDevice methods, which are called before and after a device is reset, respectively. The mFont object contains Direct3D resources internally and needs to do some work before a reset and after; thus we have:

void HelloD3DApp::onLostDevice()

Here mFont - >OnLostDevice invokes whatever code ID3DXFont needs to execute before a reset, and mFont - >OnResetDevice invokes whatever code ID3DXFont needs to execute after a reset. (We don't know what that code is since we didn't implement ID3DXFont, but the documentation says to use these functions like this, so we do it.)

Finally, we implement the drawScene method and output the text:

void HelloD3DApp::drawScene() {

HR(gd3dDevice - >Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255, 255, 255), 1.0f, 0));

RECT formatRect;

GetClientRect(mhMainWnd, &formatRect);

HR(gd3dDevice - >BeginScene());

Observe first that we call the IDirect3DDevice9::Clear method, which clears the back buffer (target) and depth buffer to D3DCOLOR_XRSB(255, 255, 255) (white) and 1.0, respectively. The declaration of IDirect3DDevice9::Clear is:

HRESULT IDirect3DDevice9::Clear(

Count: Number of rectangles in the pRects array.

pRects: An array of screen rectangles to clear. This allows us to only clear parts of a surface.

Flags: Specifies which surfaces to clear. We can clear one or more (combined by a bitwise OR) of the following surfaces:

D3DCLEAR_TARGET: The render target surface, usually the back buffer.

D3DCLEAR_ZBUFFER: The depth buffer.

D3DCLEAR_STENCIL: The stencil buffer.

Color: The color to which we wish to clear the render target.

Z: The value to which we wish to set the depth buffer (z-buffer).

Stencil: The value to which we wish to set the stencil buffer.

After we have cleared the back buffer and depth buffer, we get the dimensions of the client area rectangle, which will be used to format the text in the window.

Now observe that the text drawing method ID3DXFont::DrawText is between IDirect3DDevice9::BeginScene and

IDirect3DDevice9::EndScene calls; this is important, as all drawing by the Direct3D device must always take place between these two functions.

The ID3DXFont::DrawText function takes six parameters:

INT ID3DXFont::DrawText(

LPD3DXSPRITE pSprite, LPCTSTR pString, INT Count, LPRECT pRect, DWORD Format, D3DCOLOR Color);

pSprite: Pointer to an ID3DXSprite interface. Set this to null for now; we talk about the ID3DXSprite interface in Chapter 5.

pString: Pointer to the string to draw.

Count: Number of characters in the string. We can specify -1 if the string is null terminating.

pRect: Pointer to a RECT structure that defines the area on the screen to which the text is to be drawn and formatted.

Format: Optional flags that specify how the text should be formatted in the RECT specified by pRect. In drawScene, we use the combination DT_CENTER | DT_VCENTER, which means to center the text horizontally and vertically relative to the formatting rectangle.

Color: The text color. To obtain a D3DCOLOR type, we use the D3DCOLOR_XRGB macro, which returns a D3DCOLOR based on the combination of red (first parameter), green (second parameter), and blue (third parameter) intensity; note that color component intensities range from 0 (no intensity) to 255 (full intensity). In drawScene, we use a random color each frame and thus specify: D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256).

Finally, after drawing is done, we present the back buffer (§4.2.2) by calling the IDirect3DDevice9::Present method; problem 3 of the Exercises asks you to investigate the parameters of this method, but for now we just want to present the back buffer and can specify null for the four

parameters.

Note Before continuing, or at least before moving on to the next chapter, you should compile this first demo application and familiarize yourself with the code.

It is important to have a good handle on the basics before moving on to more difficult topics.

Note Remember to link the following DirectX library files into your project: d3d9.lib, d3dx9.lib, dxguid.lib, DxErr9.lib, and dinput8.lib.