It's free to join Gamasutra!|Have a question? Want to know who runs this site? Here you go.|Targeting the game development market with your product or service? Get info on advertising here.||For altering your contact information or changing email subscription preferences.
Registered members can log in here.Back to the home page.

Search articles, jobs, buyers guide, and more.

By George Geczy
Gamasutra
[Author's Bio]
June 29, 2001

2D Programming in a 3D World

Implementing the Fundamentals

Special Effects: Alpha Blending, Scaling, and Rotation

Code Listing

Printer Friendly Version
 
Discuss this Article

Letters to the Editor:
Write a letter
View all letters


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


join | contact us | advertise | write | my profile
news | features | companies | jobs | resumes | education | product guide | projects | store



Copyright © 2003 CMP Media LLC

privacy policy
| terms of service