1: Basic Direct3D 8 Startup/Shutdown:

 

Over here, we’ll create a very simple skeleton for opening a Direct3D 8 window. You must have the DirectX 8 or later SDK installed, or Visual Studio .NET.

 

First, we include some additional header files from the Direct3D system:

 

// Direct3d header files

#include <d3d8.h>

#include <d3dx8.h>

#include <dxerr8.h>

 

 

Now, we use the #pragma command to link to the library files, instead of messing up with the compiler options:

 

// Include library files

#pragma comment( lib, "DXGUID.lib" )

#pragma comment( lib, "DXERR8.LIB" )

#pragma comment( lib, "D3D8.LIB" )

#pragma comment( lib, "D3DX8.LIB" )

#pragma comment( lib, "D3DXOF.lib" )

 

We then define an abstract  Renderer class, which will be used in the future for changing drivers(OpenGL/DirectX). Subclasses should implement this interface:

 

// Define a Renderer Base Interface

struct Renderer

{

      virtual void Init( HWND& hwnd ) = 0;

      virtual void Close() = 0;

      virtual bool IsRunning() = 0;

      virtual void BeginScene() = 0;

      virtual void EndScene() = 0;

};

 

Now, we define the RendererD3d class. This class tries to encapsulate the Direct3d functionality and to provide a simple interface. The main API encapsulator methods are: Close(), Init(), BeginScene(), EndScene(). The method Get() is used as a singleton instance, providing only one instance of the class at a time. The method IsRunning() is used to know if the device was created ok. The method DoFrame() is located here temporarily as an example; its purpose is to show how the BeginScene()/EndScene() should be called.

 

// Direct3d implementation

class RendererD3d : public Renderer

{

      public:

            virtual ~RendererD3d()

            {

                  Close();

            }

 

            virtual void Init( HWND& hwnd )

            {

                  // If already initialized, skip

                  if( m_pd3d || is_running )

                        return;

 

                  // Create the main Direct3d Object

                  if( !( m_pd3d = Direct3DCreate8( D3D_SDK_VERSION ) ) )

                  {

                        DXTRACE_ERR( "Direct3DCreate8", E_FAIL );

                        return;

                  }

 

                  // Get the current display mode of the adapter

                  HRESULT hr;

                  D3DDISPLAYMODE d3ddm;

                  if( FAILED( hr = m_pd3d->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) )

                  {

                        DXTRACE_ERR( "GetAdapterDisplayMode", hr );

                        return;

                  }

 

                  // Set how is the data going to be presented on screen and create the output device

                  D3DPRESENT_PARAMETERS d3dpp;

                  ZeroMemory( &d3dpp, sizeof(d3dpp) );

                  d3dpp.Windowed = TRUE;

                  d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

                  d3dpp.BackBufferFormat = d3ddm.Format;

                  if( FAILED( hr = m_pd3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,

                        D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &m_pd3dDevice ) ) )

                  {

                        DXTRACE_ERR( "CreateDevice", hr );

                        return;

                  }

 

                  // No lighting, and no culling

                  m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

                  m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

                  is_running = true;

            }

 

            // Close Direct3d device and objects

            virtual void Close()

            {

                  if( m_pd3dDevice )

                  {

                        m_pd3dDevice->Release();

                        m_pd3dDevice = NULL;

                  }

 

                  if( m_pd3d )

                  {

                        m_pd3d->Release();

                        m_pd3d = NULL;

                  }

 

                  is_running = false;

            }

 

            // Is the renderer running?

            virtual bool IsRunning() { return is_running; }

 

            // Prepare Direct3d for drawing a new frame/scene

            virtual void BeginScene()

            {

                  if( m_pd3dDevice )

                  {

                        // Clear the background to blue ( 0xAARRGGBB )

                        m_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, 0x000000FF, 1.0f, 0 );

                        m_pd3dDevice->BeginScene();

                  }

            }

 

            // Flush and draw the data on-screen

            virtual void EndScene()

            {

                  if( m_pd3dDevice )

                  {

                        m_pd3dDevice->EndScene();

                        m_pd3dDevice->Present( NULL, NULL, NULL, NULL );

                  }

            }

 

            // Called each drawing frame

            void DoFrame()

            {

                  if( is_running )

                  {

                        this->BeginScene();

 

                        // Perform any rendering here...

 

                        this->EndScene();

                  }

            }

 

            // Gets the instance of the renderer

            static RendererD3d& Get()

            {

                  static RendererD3d r3d;

                  return r3d;

            }

 

      protected:

            bool is_running;

            LPDIRECT3D8 m_pd3d;

            LPDIRECT3DDEVICE8 m_pd3dDevice;

 

            // Constructor hidden here to avoid duplicity

            RendererD3d() : is_running( false ), m_pd3d( NULL ), m_pd3dDevice( NULL )

            {

            }

};

 

Now, on the Window Procedure, we have 2 changes: While handling the WM_PAINT message, we check if the renderer is running in order to avoid drawing over the Direct3D device. On the WM_DESTROY message, we call the Close() method to free the resources, since the window is being closed.

 

// Windows Message Procedure

LRESULT WINAPI MsgProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )

{

    switch( msg )

    {

 

            case WM_PAINT:

                  {

                        // Do not draw over the Direct3d area if it is initialized

                        if( !RendererD3d::Get().IsRunning() )

                        {

                              // Simply clear the window to white, and display a message centered

                              RECT rect;

                              PAINTSTRUCT ps;

                              HDC hdc = BeginPaint( hwnd, &ps );

                              GetClientRect( hwnd, &rect );

                              FillRect( hdc, &rect, CreateSolidBrush( 0x00ffffff ) );

                              SetTextColor( hdc, 0 );

                              ::DrawText( hdc, "Basic Window", -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE );

                              EndPaint( hwnd, &ps );

                        }

                        return 0;

                  }

 

            case WM_DESTROY:

                  {

                        // Close the Direct3d stuff since the window is closing/destroying

                        RendererD3d::Get().Close();

                        PostQuitMessage( 0 );

                        return 0;

                  }

    }

 

    return DefWindowProc( hwnd, msg, wparam, lparam );

}

 

Finally, on the WinMain function, we add the Init() method after creating the window. The last thing added is that if no Windows message is received, the message loop executes a DoFrame() call, thereby rendering the scene.

 

// Main Win32 Entry Point

int WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )

{

      // Register the window class

      WNDCLASSEX wc = { sizeof( WNDCLASSEX ), CS_OWNDC, MsgProc, 0, 0, GetModuleHandle( NULL ), NULL, NULL,

                                    NULL, NULL, WINDOWS_CLASS, NULL };

      RegisterClassEx( &wc );

 

      // Create the application's window

      HWND hwnd = CreateWindow( WINDOWS_CLASS, WINDOWS_CAPTION, WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, CW_USEDEFAULT,

                        CW_USEDEFAULT, SCREEN_X, SCREEN_Y, GetDesktopWindow(), NULL, wc.hInstance, NULL );

 

      // Show the window

      ShowWindow( hwnd, SW_SHOWDEFAULT );

      UpdateWindow( hwnd );

 

      // After the window is created, initialize Direct3d

      RendererD3d::Get().Init( hwnd );

 

      // Enter the message loop

      MSG msg;

      ZeroMemory( &msg, sizeof( msg ) );

 

      // Process all messages and draw while idle

      while( msg.message != WM_QUIT )

      {

            if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )

            {

                  TranslateMessage( &msg );

                  DispatchMessage( &msg );

            }

            else

            {

                  // When Windows doesn't send a message to our application, execute a frame

                  RendererD3d::Get().DoFrame();

            }

      }

 

      // Unregister the class

      UnregisterClass( WINDOWS_CLASS, wc.hInstance );

 

      return 0;

}