VC++ Tutorial: Using
DirectX, DirectDraw in MFC, Article, Example Codes
|
|
Hanbo Sun
Click here for a larger
image.
Environment: VC++
6, DirectX
7.0 or above, Windows 2000/XP
Preface
I know there are a bunch
of classes and related articles
written in this DirectX
section . So this article
is a simple tutorial
for people who just started programming
in DirectDraw. I hope
you guys will enjoy this tutorial.
I was working on some
design for Galloping Ghost Productions when I discovered
there is a way to integrate a DirectDraw
interface into Document/View
architecture. People might ucancode.net, why add an DirectDraw
interface into the view object when a full-screen DirectDraw
application can be done? Yes, doing a windowed DirectDraw
application is not as cool as doing a full-screen hardcore
DirectDraw
application. However, you have some advantages when
designing a windowed application:
- It is easier for
debugging.
- It can create an
interface for using DirectShow
interfaces.
- It can be used to
design windowed games using DirectDraw
and Direct3D.
KOEI's "Romance of Three Kingdoms" used
windowed applications.
- The concept of this tutorial
can integrate into other concepts. For example,
there is a small class that can be used for designing
screen savers. You can integrate the stuff in this tutorial
into that to create DirectDraw-based
screen savers.
- ......
Anyway, let's start the tutorial.
Setup
We all know the CView
class is a derivative of the CWnd class. Thus it has all
the physical attributes of a window. So it is really not
hard at all to add a DirectDraw
interface into the view
class derived from CView. I find it is much easier to
program than doing full-screen DirectDraw. I used to spend
hours correcting problems with SetDisplayMode() or
SetCooperationLevel(), or problems with off-screen
surfaces. All these problems are due to the problem of
debugging. It is really hard to debug under full-screen
mode. This problem can be eliminated by using windowed
mode DirectDraw and
with the debugging tools provided by MFC
classes. I am wasting time here; sorry.
- First, you have to
make some slight modifications to Visual
C++ 6.0. I am certain that when you
installed DirectX
7.0 SDK or above, the environmental variables in Visual
C++ are automatically set up correctly. If,
after you download
my code and
compile it and there are errors during linking or
compiling, you need to do some tuning:
- Go to Tools,
Options.
- Select the
Directories tab.
- Select
"Include files" in the "Show
directories for" list box.
- Add the INCLUDE
directory of DirectX
on top of all directories. For example,
if you installed DirectX
7.0 in C:\DXSDK, there is a directory inside
called "include", which is C:\DXSDK\INCLUDE;
put this directory on top of all other directories
shown in the list box. See the following figure:
- Do the same thing
with the LIB directory by selecting "Library
files" in "Show directories for";
then find the LIB directory in your DirectX
SDK directory,
For example, if you installed DirectX
7.0 in C:\DXSDK, then there is a directory inside
called "include", which is C:\DXSDK\LIB,
put this directory on top of all other directories
shown in the list box. See the following figure:
Then, click OK.
- If there still are
problems in linking, add some more things to the
project settings:
- Go to Project,
Settings.
- Select the Link
tab.
- In
"Object/library modules", add "ddraw.lib"
and "dxguid.lib".
See the following figure:
Then click OK.
- Now you can download
my code, compile
it, and run.
My Code
I wrote this CDDrawSystem
class (located in DDrawSystem.h and DDrawSystem.cpp files)
to display graphics using DirectDraw.
Basically it has:
- Constructor,
Destructor.
- Init() function
that creates the IDirectDraw7 interface, a primary
surface buffer, a back surface buffer, and a clipper
for the primary surface buffer. It is self
explanatory. I grabbed the code from DDUTIL.H and
DDUTIL.CPP, which are from Microsoft, along with the
rest of the DirectX
SDK.
- Terminate()
function that terminates all objects using the COM
method release().
- Clear()
function that clears the primary surface and the back
surface buffer by blit color 0 (black) to these two
surfaces.
- Display()
function will blit the back surface buffer to the
primary surface and thus the graphic will be
displayed. The problem with Display is that you cannot
blit using absolute coordinates. You have to know
exactly where your view (which is the client window)
is, and blit the graphics to this view
area. So this is how I designed the function:
void CDDrawSystem::Display()
{
HRESULT hRet;
RECT rt;
POINT p = {0, 0};
ClientToScreen(hWnd, &p);
rt.left = 0 + p.x;
rt.top = 0 + p.y;
rt.right = 800 + p.x;
rt.bottom = 600 + p.y;
while( 1 )
{
hRet = m_pddsFrontBuffer->Blt(&rt,
m_pddsStoreBuffer,
NULL,
DDBLT_WAIT,
NULL);
if (hRet == DD_OK)
break;
else if(hRet == DDERR_SURFACELOST)
{
m_pddsFrontBuffer->Restore();
m_pddsStoreBuffer->Restore();
}
else if(hRet != DDERR_WASSTILLDRAWING)
return;
}
}
- In addition to these
functions, you can add new functions such as load
bitmap files, bit blit bitmaps from off-screen
surfaces, and draw text, primitive geometric shapes,
and lock surface and do crazy stuff.
I wrote this TestDraw()
function just to show you how to do primitive drawings on
back screen surfaces and blit them to the primary surface.
It will display "This is a stinky App" at
coordinate (20, 20). Then, if you click anywhere in the
client area, it will draw
a big white circular dot (about 50 pixels in radius).
Now, the Complete Code
#if !defined(AFX_DDRAWSYSTEM_H__1E152EB4_ED1D_4079_BDD4_773383DD98C8
__INCLUDED_)
#define AFX_DDRAWSYSTEM_H__1E152EB4_ED1D_4079_BDD4_773383DD98C8
__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif
#include <ddraw.h>
#define _CHARACTORBUILDER_
#include "../GameLib/Image.h"
class CDDrawSystem
{
public:
CDDrawSystem();
virtual ~CDDrawSystem();
BOOL Init(HWND hWnd);
void Terminate();
void Clear();
void TestDraw(int x, int y);
void Display();
protected:
LPDIRECTDRAW7 m_pDD;
LPDIRECTDRAWSURFACE7 m_pddsFrontBuffer;
LPDIRECTDRAWSURFACE7 m_pddsStoreBuffer;
LPDIRECTDRAWCLIPPER pcClipper;
HWND hWnd;
};
#endif
773383DD98C8__INCLUDED_)
#include "stdafx.h"
#include "DDrawSystem.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
CDDrawSystem::CDDrawSystem()
{
m_pDD = NULL;
m_pddsFrontBuffer = NULL;
m_pddsStoreBuffer = NULL;
pcClipper = NULL;
}
CDDrawSystem::~CDDrawSystem()
{
Terminate();
}
BOOL CDDrawSystem::Init(HWND hWnd)
{
HRESULT hRet;
this->hWnd = hWnd;
hRet = DirectDrawCreateEx(NULL, (VOID**)&m_pDD,
IID_IDirectDraw7, NULL);
if(hRet != DD_OK)
{
AfxMessageBox("Failed to create directdraw object.");
return FALSE;
}
hRet = m_pDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);
if(hRet != DD_OK)
{
AfxMessageBox("Failed to set directdraw display behavior.");
return FALSE;
}
HRESULT hr;
DDSURFACEDESC2 ddsd;
ZeroMemory( &ddsd, sizeof( ddsd ) );
ddsd.dwSize = sizeof( ddsd );
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
if(FAILED(hr = m_pDD->CreateSurface(&ddsd,
&m_pddsFrontBuffer, NULL)))
{
AfxMessageBox("Failed to create primary surface.");
return FALSE;
}
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN |
DDSCAPS_3DDEVICE;
ddsd.dwWidth = 800;
ddsd.dwHeight = 600;
if(FAILED(hr = m_pDD->CreateSurface(&ddsd,
&m_pddsStoreBuffer, NULL)))
{
AfxMessageBox("Failed to create back buffer surface.");
return FALSE;
}
if(FAILED(hr = m_pDD->CreateClipper(0, &pcClipper, NULL)))
{
AfxMessageBox("Failed to create clipper.");
return FALSE;
}
if(FAILED(hr = pcClipper->SetHWnd(0, hWnd)))
{
pcClipper->Release();
AfxMessageBox("Failed to create primary surface.");
return FALSE;
}
if(FAILED(hr = m_pddsFrontBuffer->SetClipper(pcClipper)))
{
pcClipper->Release();
AfxMessageBox("Failed to create primary surface.");
return FALSE;
}
return TRUE;
}
void CDDrawSystem::Terminate()
{
if (m_pDD != NULL)
{
if (m_pddsFrontBuffer != NULL)
{
if (m_pddsStoreBuffer != NULL)
{
m_pddsStoreBuffer->Release();
m_pddsStoreBuffer = NULL;
}
if (pcClipper != NULL)
{
pcClipper->Release();
pcClipper = NULL;
}
m_pddsFrontBuffer->Release();
m_pddsFrontBuffer = NULL;
}
m_pDD->Release();
m_pDD = NULL;
}
}
void CDDrawSystem::Clear()
{
HRESULT hRet;
DDBLTFX fx;
fx.dwSize = sizeof(fx);
fx.dwFillColor = 0x000000;
while (1)
{
hRet = m_pddsFrontBuffer->Blt(NULL, NULL, NULL,
DDBLT_COLORFILL, &fx);
if (hRet == DD_OK)
break;
else if (hRet == DDERR_SURFACELOST)
{
m_pddsFrontBuffer->Restore();
}
else if (hRet != DDERR_WASSTILLDRAWING)
break;
}
while (1)
{
hRet = m_pddsStoreBuffer->Blt(NULL, NULL, NULL,
DDBLT_COLORFILL, &fx);
if (hRet == DD_OK)
break;
else if (hRet == DDERR_SURFACELOST)
{
m_pddsStoreBuffer->Restore();
}
else if (hRet != DDERR_WASSTILLDRAWING)
break;
}
}
void CDDrawSystem::TestDraw(int x, int y)
{
HRESULT hRet;
HDC dc;
hRet = m_pddsStoreBuffer->GetDC(&dc);
if (hRet != DD_OK)
return;
POINT p = {0 + x, 0 + y};
ClientToScreen(hWnd, &p);
SetTextColor(dc, RGB(255, 0, 0));
TextOut(dc, 20, 20, "This is a stinky App", lstrlen("This is
a stinky App"));
Ellipse(dc, x-50, y-50, x+50,y+50);
m_pddsStoreBuffer->ReleaseDC(dc);
}
void CDDrawSystem::Display()
{
HRESULT hRet;
RECT rt;
POINT p = {0, 0};
ClientToScreen(hWnd, &p);
rt.left = 0 + p.x;
rt.top = 0 + p.y;
rt.right = 800 + p.x;
rt.bottom = 600 + p.y;
while(1)
{
hRet = m_pddsFrontBuffer->Blt(&rt, m_pddsStoreBuffer,
NULL, DDBLT_WAIT, NULL);
if (hRet == DD_OK)
break;
else if(hRet == DDERR_SURFACELOST)
{
m_pddsFrontBuffer->Restore();
m_pddsStoreBuffer->Restore();
}
else if(hRet != DDERR_WASSTILLDRAWING)
return;
}
}
Downloads
Download
source - 39 Kb
|