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;
}