|
Features

2D
Programming in a 3D World:
Developing a 2D Game Engine Using DirectX 8 Direct3D
Listing
1
// Listing 1 - Sample
Simplified Code snippet for DX7/DirectDraw screen draw
#include <ddraw.h>
// Pointer to Backbuffer and FrontBuffer surfaces
extern LPDIRECTDRAWSURFACE7 pddsBackBuffer, pddsFrontBuffer;
void DirectDrawRender()
{
DDBLTFX ddbltfx;
ZeroMemory( &ddbltfx, sizeof(DDBLTFX));
ddbltfx.dwSize = sizeof(DDBLTFX);
// First we clear the backbuffer, unless we draw over it all
ddbltfx.dwFillColor = RGB(0,0,0);
pddsBackBuffer->Blt( NULL, NULL, NULL,
DDBLT_COLORFILL | DDBLT_WAIT , &ddbltfx
);
// In real code, check for DDERR_SURFACELOST
here and restore surfaces if needed
//
Now, draw sprites images etc etc
ZeroMemory( &ddbltfx, sizeof(DDBLTFX));
ddbltfx.dwSize = sizeof(DDBLTFX);
RECT RectDest = ...
// Destination location
RECT RectSource = ...
// Source rect co-ordinates
LPDIRECTDRAWSURFACE7 pddsSourceSurface =
...
// Image Surface pointer
DWORD flags = DDBLT_KEYSRC | DDBLT_WAIT
| ...
// Any relavant Blt() flags
pddsBackBuffer->Blt( &RectDest, pddsSourceSurface,
&RectSource, flags, &ddbltfx );
// Keep blitting for each image. Should
check for DDERR_SURFACELOST after each
//
Finally, Flip the screen
pddsFrontBuffer->Flip( NULL, DDFLIP_WAIT
);
// Don't forget to check return for DDERR_SURFACELOST
or DDERR_SURFACEBUSY etc
}
Back
to Feature
Listing
2
//Listing 2
//-----------------------------------------------------------------------------
// Direct3D8 Application Skeleton
//
// A bare-bones skeleton based on the Microsoft DirectX SDK Tutorial 1
//
//-----------------------------------------------------------------------------
#include <d3d8.h>
#include <d3dx8.h>
//-----------------------------------------------------------------------------
// DX8 Information Class - a convenient, single place to store global
DX vars
//-----------------------------------------------------------------------------
class DX8VARS {
public:
LPDIRECT3D8 pD3D; // Used to create the
D3DDevice
LPDIRECT3DDEVICE8
pd3dDevice; // Our rendering device
LPD3DXSPRITE pd3dxSprite; // Interface to
Sprite routines
D3DCAPS8 *pd3dcaps; // Caps for the rendering
device
//
Constructor/Destructor:
DX8VARS() { pD3D = NULL; pd3dDevice = NULL;
pd3dxSprite = NULL;
pd3dcaps =
new D3DCAPS8; }
~DX8VARS() { if (pd3dcaps) delete pd3dcaps;
}
// If you feel very object-oriented, you
could make InitD3D(), Cleanup()
// and Render() all methods of the DX8VARS
class.
};
//----------------------------------------------------------------------------
// Global variables
//----------------------------------------------------------------------------
DX8VARS dx8;
//----------------------------------------------------------------------------
// InitD3D() : Initializes Direct3D
//----------------------------------------------------------------------------
HRESULT InitD3D( HWND hWnd )
{
// Create the D3D object, which is needed
to create the D3DDevice.
if( ( dx8.pD3D = Direct3DCreate8( D3D_SDK_VERSION
) ) == NULL )
return E_FAIL;
// Get the current desktop display mode
D3DDISPLAYMODE d3ddm;
if( FAILED( dx8.pD3D->GetAdapterDisplayMode(
D3DADAPTER_DEFAULT,
&d3ddm
)))
return E_FAIL;
//
Parameters for the D3DDevice. Most parameters are zero'd out.
// This sets the video format to match the
current desktop display.
// Check docs for other options you can
set here, such as
// 'Windowed' which would create a window-based
app (not full screen)
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferWidth = d3ddm.Width;
d3dpp.BackBufferHeight = d3ddm.Height;
d3dpp.BackBufferFormat = d3ddm.Format;
//
Create the Direct3D device, using the default adapter (most systems
// only have one, unless they have multiple
graphics hardware cards
// installed). See SDK for more details
on other options here.
if( FAILED( dx8.pD3D->CreateDevice( D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL, hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp,
&dx8.pd3dDevice ) ) )
return E_FAIL;
//
Device state would normally be set here
return
S_OK;
}
//----------------------------------------------------------------------------
// Cleanup() : Releases all previously initialized objects
//----------------------------------------------------------------------------
void Cleanup()
{
if( dx8.pd3dDevice )
dx8.pd3dDevice->Release();
if(
dx8.pD3D )
dx8.pD3D->Release();
}
//----------------------------------------------------------------------------
// DrawScene() : Draws the contents of the scene
//----------------------------------------------------------------------------
void DrawScene()
{
// Drawing code goes here...
return;
}
//----------------------------------------------------------------------------
// Render() : Prepares for drawing the scene
//----------------------------------------------------------------------------
void Render()
{
if( dx8.pd3dDevice == NULL)
return;
//
Clear the backbuffer to a black color
dx8.pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET,
D3DCOLOR_XRGB(0,0,255),
1.0f, 0 );
// Begin the scene
dx8.pd3dDevice->BeginScene();
// Rendering of scene objects
DrawScene();
// End the scene
dx8.pd3dDevice->EndScene();
// Present the backbuffer contents to the
display
dx8.pd3dDevice->Present( NULL, NULL,
NULL, NULL );
}
//----------------------------------------------------------------------------
// MsgProc() : The window's message handler
//----------------------------------------------------------------------------
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam
)
{
switch( msg )
{
 case WM_CHAR:
PostQuitMessage(
0 ); // Exit Sample on any keypress. Sample only.
return 0;
 case
WM_DESTROY: // If you use Windowed mode process loss of it
PostQuitMessage(
0 );
return 0;
 case
WM_PAINT:
// NOTE: In
a full-screen app, you may choose to draw the screen at
// a different
point, such as using a timer message or outside of
// the windows
message loop
Render();
ValidateRect( hWnd, NULL
);
return 0;
}
return
DefWindowProc( hWnd, msg, wParam, lParam );
}
//----------------------------------------------------------------------------
// WinMain() : The application's entry point
//----------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
// Register the window class
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC,
MsgProc, 0L, 0L,
GetModuleHandle(NULL),
NULL, NULL, NULL, NULL,
"D3D
Skeleton", NULL };
RegisterClassEx( &wc );
//
Create the application's window
HWND hWnd = CreateWindow( "D3D Skeleton",
"D3D Skeleton: CreateDevice",
WS_OVERLAPPEDWINDOW,
100, 100, 300, 300,
GetDesktopWindow(), NULL, wc.hInstance,
NULL );
//
Initialize Direct3D
if( SUCCEEDED( InitD3D( hWnd ) ) )
{
// Show the
window
ShowWindow(
hWnd, SW_SHOWDEFAULT );
UpdateWindow(
hWnd ); // Forces a WM_PAINT
//
Enter the message loop
MSG msg;
while( GetMessage(
&msg, NULL, 0, 0 ) )
{
TranslateMessage(
&msg );
DispatchMessage(
&msg );
}
}
//
Clean up everything and exit the app
Cleanup();
UnregisterClass( "D3D Skeleton",
wc.hInstance );
return 0;
}
Back
to Feature
Listing
3
// LISTING 3
//----------------------------------------------------------------------------
// LoadTexture() : Load a texture from a file, returns ptr to texture.
// D3DX8
supports BMP, PPM, DDS, JPG, PNG, TGA & DIB.
//----------------------------------------------------------------------------
LPDIRECT3DTEXTURE8 LoadTexture(char *filename)
{
LPDIRECT3DTEXTURE8 pd3dTexture;
// Set black as our source color key. Use
0xFFFF00FF for magenta instead.
// Use 0x00000000 for no 'color key' substitution
D3DCOLOR colorkey = 0xFF000000;
// The D3DX function will fill in the following
image info for us
D3DXIMAGE_INFO SrcInfo; // Optional
//
Load image from file - maintain size of original file.
// It is also possible to specify a new
height/width and ask D3DX to filter
// the image to the new size (stretch/shrink).
See SDK docs for details.
D3DXCreateTextureFromFileEx( dx8.pd3dDevice,
filename, 0, 0, 1, 0,
D3DFMT_A8R8G8B8,
D3DPOOL_MANAGED, D3DX_FILTER_NONE, D3DX_DEFAULT,
colorkey,
&SrcInfo , NULL, &pd3dTexture);
// Check the return value here, etc
//
Remember, textures sizes may be larger or smaller than your image,
// to allow for legal texture sizes &
maximums on user's hardware
return pd3dTexture;
}
//----------------------------------------------------------------------------
// BltSprite() : A replacement of the DirectDraw Blt() function for Direct3D8
// Supports DDBLTFX_MIRRORLEFTRIGHT and DDBLTFX_MIRRORUPDOWN
// If pDestRect is NULL, copies it to the same co-ord as src
// If pSrcRect is NULL, copies the entire texture
//----------------------------------------------------------------------------
HRESULT BltSprite( RECT *pDestRect, LPDIRECT3DTEXTURE8 pSrcTexture,
RECT *pSrcRect,
DWORD dwFlags )
{
D3DXVECTOR2 scaling(1,1), rcenter(0,0),
trans(0,0);
//
Set the parameters - translation (screen location)
if (pDestRect) {
trans.x =
(float) pDestRect->left;
trans.y =
(float) pDestRect->top;
}
// Scaling - If Source & Destination
are different size rects, then scale
if (pDestRect && pSrcRect) {
scaling.x
= ((float) (pDestRect->right - pDestRect->left)) /
((float)
(pSrcRect->right - pSrcRect->left));
scaling.y
= ((float) (pDestRect->bottom - pDestRect->top)) /
((float)
(pSrcRect->bottom - pSrcRect->top));
}
// Handle the flags - need to change scaling
& adjust translation
if (pSrcRect && dwFlags) {
if (dwFlags&DDBLTFX_MIRRORLEFTRIGHT)
{
scaling.x
= -scaling.x;
trans.x
+= (float) (pDestRect->right - pDestRect->left);
}
if (dwFlags&DDBLTFX_MIRRORUPDOWN)
{
scaling.y
= -scaling.y;
trans.y
+= (float) (pDestRect->bottom - pDestRect->top);
}
}
//
And here we go:
return dx8.pd3dxSprite->Draw( pSrcTexture,
pSrcRect, &scaling,
&rcenter,
0, &trans, 0xFFFFFFFF );
}
Back
to Feature
Listing
4
// LISTING 4
//----------------------------------------------------------------------------
// BltSpriteEx() : A replacement of the DirectDraw Blt() function for
Direct3D8
// Supports DDBLTFX_MIRRORLEFTRIGHT and DDBLTFX_MIRRORUPDOWN,
// modulate is color/alpha modifier
// rotation is value of rotation in RADIANS, counter-clockwise
// prcenter is the rotation centre point of the obj, or NULL
// If pDestRect is NULL, copies it to the same co-ord as src
// If pSrcRect is NULL, copies the entire texture
//----------------------------------------------------------------------------
HRESULT BltSpriteEx( RECT *pDestRect, LPDIRECT3DTEXTURE8 pSrcTexture,
RECT
*pSrcRect, DWORD dwFlags, D3DCOLOR modulate = 0xFFFFFFFF,
float
rotation = 0, POINT *prcenter = NULL )
{
D3DXVECTOR2 scaling(1,1), rcenter(0,0),
trans(0,0);
//
Set the parameters - translation (screen location)
if (pDestRect) {
trans.x =
(float) pDestRect->left;
trans.y =
(float) pDestRect->top;
}
// Rotation Center
if (prcenter) {
rcenter.x
= (float) prcenter->x;
rcenter.y
= (float) prcenter->y;
} else if (pSrcRect) {
// Set Rotation
Center to be object center if none provided
rcenter.x
= (float) (pSrcRect->right - pSrcRect->left) / 2;
rcenter.y
= (float) (pSrcRect->bottom - pSrcRect->top) / 2;
}
// Scaling - If Source & Destination
are different size rects, then scale
if (pDestRect && pSrcRect) {
scaling.x
= ((float) (pDestRect->right - pDestRect->left)) /
((float)
(pSrcRect->right - pSrcRect->left));
scaling.y
= ((float) (pDestRect->bottom - pDestRect->top)) /
((float)
(pSrcRect->bottom - pSrcRect->top));
}
// Handle the flags - need to change scaling
& adjust translation
if (pSrcRect && dwFlags) {
if (dwFlags&DDBLTFX_MIRRORLEFTRIGHT)
{
scaling.x
= -scaling.x;
trans.x
+= (float) (pDestRect->right - pDestRect->left);
}
if (dwFlags&DDBLTFX_MIRRORUPDOWN)
{
scaling.y
= -scaling.y;
trans.y
+= (float) (pDestRect->bottom - pDestRect->top);
}
}
//
And here we go:
return dx8.pd3dxSprite->Draw( pSrcTexture,
pSrcRect, &scaling,
&rcenter,
rotation, &trans, modulate );
}
Back
to Feature
Listing
5
// LISTING 5
// Some D3DCOLOR shortcut macros:
#define DRGBA(r,g,b,a) ((D3DCOLOR)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))
#define DRGB(r,g,b) ((D3DCOLOR)(((0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))
#define GetB(rgb) ((BYTE)(rgb))
#define GetG(rgb) ((BYTE)(((WORD)(rgb)) >> 8))
#define GetR(rgb) ((BYTE)((rgb)>>16))
#define GetA(rgb) ((BYTE)((rgb)>>24))
// Flags Used by DrawRect()
#define DR3DLOOK 0x01
#define DR3DLOOK2 0x02
//-----------------------------------------------------------------------------
// DrawRect() : Draws a rect on 'the viewport' using Clear, with some
options
// This is not a Microsoft-endorsed use
of the Clear command!
// Note that the Alpha values of colors
are significant - 0xFF for opaque
// fillstyle: GDI values of BS_SOLID or
BS_NULL (hollow)
// bordersize is -ve for internal to rectangle
// drflags are DR3DLOOK or DR3DLOOK2 for
embossed shadow button effects
//-----------------------------------------------------------------------------
HRESULT DrawRect(RECT rc, DWORD fillstyle, D3DCOLOR fillcolor, int bordersize,
D3DCOLOR bordercolor,
DWORD drflags)
{
// Error if not a normal rectangle
if (rc.bottom-rc.top < 1) return DDERR_INVALIDPARAMS;
if (rc.right-rc.left < 1) return DDERR_INVALIDPARAMS;
//
First, we do the inside (fill) part
switch (fillstyle) {
case BS_SOLID:
// Fill area
is full size of rectangle
dx8.pd3dDevice->Clear(
1, (D3DRECT *)&rc, D3DCLEAR_TARGET,
fillcolor,
1.0f, 0);
break;
case BS_NULL:
default:
// No fill
or unknown, no action
break;
}
//
This handles drawing the borders (four sides)
if (bordersize != 0) {
D3DCOLOR topbord=bordercolor,
botbord=bordercolor;
RECT brc;
// Adjust
for border outside of rect if +ve border size:
if (bordersize
> 0) {
rc.left
-= bordersize;
rc.right
+= bordersize;
rc.top
-= bordersize;
rc.bottom
+= bordersize;
bordersize
= -bordersize;
}
//
Embossed effect flags:
if (drflags
& DR3DLOOK) {
topbord
= DRGB( (GetR(bordercolor)+255)/2,
(GetG(bordercolor)+255)/2,
(GetB(bordercolor)+255)/2 );
botbord
= DRGB( (GetR(bordercolor))/2,
(GetG(bordercolor))/2,
(GetB(bordercolor))/2 );
} else if
(drflags & DR3DLOOK2) {
//
Reversed shadow, for 'selected' look
topbord
= DRGB( (GetR(bordercolor))/2,
(GetG(bordercolor))/2,
(GetB(bordercolor))/2 );
botbord
= DRGB( (GetR(bordercolor)+255)/2,
(GetG(bordercolor)+255)/2,
(GetB(bordercolor)+255)/2 );
}
//
Draw top and left
SetRect(&brc,
rc.left, rc.top, rc.right, rc.top - bordersize);
dx8.pd3dDevice->Clear(
1, (D3DRECT *)&brc, D3DCLEAR_TARGET,
topbord,
1.0f, 0);
SetRect(&brc,
rc.left, rc.top, rc.left-bordersize, rc.bottom);
dx8.pd3dDevice->Clear(
1, (D3DRECT *)&brc, D3DCLEAR_TARGET,
topbord,
1.0f, 0);
//
Draw bottom and right
SetRect(&brc,
rc.left, rc.bottom+bordersize, rc.right, rc.bottom);
dx8.pd3dDevice->Clear(
1, (D3DRECT *)&brc, D3DCLEAR_TARGET,
botbord,
1.0f, 0);
SetRect(&brc,
rc.right+bordersize, rc.top, rc.right, rc.bottom);
dx8.pd3dDevice->Clear(
1, (D3DRECT *)&brc, D3DCLEAR_TARGET,
botbord,
1.0f, 0);
}
return
S_OK;
}
Back
to Feature
|