Initializing Direct3D

  CU_InitializeD3D.zip (12.9 KiB, 3,963 hits)

Now that we know how to create a window, we need to know how to give it DirectX capabilities. To get access to DirectX Graphics, we need to include its header file, d3d9.h. Also, the d3dx9.h header file contains a lot of helpful functions and macros that will make our lives easier. These headers are included in the precompiled header file, stdafx.h, as shown in the previous tutorial.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
  // Clip…window creation code here
  // Create Direct3D9 Object 
  LPDIRECT3D9 pD3D9 = Direct3DCreate9( D3D_SDK_VERSION ); 
  if (!pD3D9) {
    MessageBox( 0, “Direct3DCreate9() – Failed”, 0, 0 ); 
    return 0;
  }

  // Fill out the presentation parameters 
  D3DPRESENT_PARAMETERS D3Dpp; 
  ZeroMemory(&D3Dpp, sizeof(D3Dpp)); 
  D3Dpp.Windowed = TRUE; 
  D3Dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

  // Create the Direct3D Device 
  LPDIRECT3DDEVICE9 pD3DDevice = NULL; 
  if (FAILED(pD3D9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                 D3DCREATE_SOFTWARE_VERTEXPROCESSING, &D3Dpp,
                                 &pD3DDevice))) {
    MessageBox(0, “CreateDevice() – Failed”, 0, 0); 
    SAFE_RELEASE(pD3D9); 
    return 0;
  }

  // Clip…message loop here
}

To initialize DirectX Graphics, we first create the Direct3D object with Direct3DCreate9. The Direct3D object lets us configure the application based on the capabilities of the user’s video card. Since this tutorial just covers basic initialization, we’re not going to cover all that junk just yet. We’re just using the object to create the Direct3D Device, which is used to render all of our pretty graphics.

After we create the object, we need to fill up a D3DPRESENT_PARAMETERS structure. This structure is used to specify how DirectX is going to behave. Members include pixel formats, how backbuffers are displayed, how many backbuffers are used, etc. In this tutorial, we are doing the bare minimum, so I just fill out a couple of the members of the structure.

With the D3DPRESENT_PARAMETERS structure now filled up, we can proceed to create the Direct3D device. We can do this by simply calling IDirect3D9::CreateDevice. If the creation was a success, DirectX Graphics is now initialized and there is much rejoicing. If device creation fails, we alert the user and quit the program.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
  // Clip…DirectX initialization here

  // Main loop 
  MSG msg; 
  while (1) {
    // Only render when there are no messages 
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
      if (msg.message == WM_QUIT) {
        break;
      } 
      TranslateMessage(&msg); 
      DispatchMessage (&msg);
    } else {
      pD3DDevice->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 100), 1.0f, 0); 
      pD3DDevice->BeginScene();
      
      // Render the frame here

      pD3DDevice->EndScene(); 
      pD3DDevice->Present( 0, 0, 0, 0 );
    }
  }

  // Give back resources 
  SAFE_RELEASE(pD3DDevice); 
  SAFE_RELEASE(pD3D9);

  return 0;
}

With DirectX Graphics now initialized, we can update our message loop to begin rendering geometry. As mentioned in the first tutorial, we will only render our scene when there are no messages in the message queue. To render our scene, we first clear the surface we are drawing on, called the back buffer, with IDirect3DDevice9::Clear. This function clears the window with a specified color with the help of the D3DCOLOR_XRGB macro. Before we can actually render any geometry, we need to call IDirect3DDevice9::BeginScene. And when we’re done rendering, we need to call IDirect3DDevice9::EndScene. These two function calls basically just tell DirectX to listen up because we are going to start rendering some graphics. At this point we still cannot view the newly cleared window because we actually cleared the back buffer. To switch the back and frame (front) buffers, we call IDirect3DDevice9::Present, which displays what we just rendered to the screen.

When we quit the message loop, we need to give resources back to Windows by releasing the COM interfaces. The Direct3D object and the Direct3D Device are both COM objects. I’m no COM guru, but you just need to know a few COM basics to be able to use DirectX. When a COM object is created, an internal counter is incremented. This counter is used to keep track of how many interface pointers are currently active. When we release a COM interface with Release, the internal counter gets decremented. When the counter reaches 0, the object destroys itself. So, to destroy the COM instances, we’ll release them in the reverse order that they are created, like a stack, by calling Release.

And just like that, DirectX is initialized.