DirectX-tasy
by André LaMothe

[Editor's Note: This article refers to DirectX 5, which was released in 1997; much of the information presented is not applicable to more recent releases.]

X-Genesis

A few years ago, Bill Gates put on a party at Microsoft called "Judgment Day". I was one of the lucky people that partook in the experience. I have to say one thing, "Microsoft sure can throw a party!". However, the truth of the matter is that MS knew (knows) that the game community is really the last market segment that still had a mind of its own. MS knew if they could get the game programmers to jump on the Windows 95/DirectX bandwagon then they would have us all...

Well, maybe back then the hordes of game programmers with "DOS is Dead" on their tee-shirts didn't mean much more than free tee-shirts, and we know game programmers like free tee-shirts. But today that little slogan is a hard fact. DOS is DEAD. Let me repeat, DOS is DEAD, DEAD, DEAD! Professional game programmers can no longer write DOS games and expect any publisher to work with them. Sure you might be able to get a shareware publisher to distribute your game, and DOS is a great learning platform, and if you have a Quake killer in DOS 32 you could hand it over to Electronic Arts and I don't think they would say no. Nevertheless, the point is that we can't start any more projects for the MS-DOS platform, we must target Windows 95/NT. This means that we have to embrace what Microsoft calls DirectX.

DirectX has made its way into our lives and is sitting with a grin next to you (actually it's probably levitating). I think at first nobody took DirectX seriously, but after 3 revisions with number 5 in beta (which is called DirectX 6.0) DirectX has matured to the point that it actually works! In this article we are going to cover some philosophical ground about DirectX and its origins, some actual experiences with DirectX, and then finish up with some technical stuff and a working DirectX program that will allow you to simulate a DOS 32 game console with very little knowledge of DirectX or Windows 95.

DirectX is a way of thinking more than anything else. Technically, it is a set of COM (Component Object Model) objects that implement a number of interfaces that allow you to communicate with video hardware, sound hardware, networking, input devices, and some system services. But more than that, it's a way of letting go of what game programmers hold so dear, and that is; game programming. Game programmers are artists, they are explorers, and for the past 25 years (give or take) they have been coming up with the most cleaver tricks on Earth to create animated universes for us to play in. DirectX takes a lot of the fun out of game programming. No longer, do we need to figure out the video card, write assembly language drivers for the joystick, create compiled sprite blitters, etc. It's all their for us. We just have to use it.

DirectX was created to solve a set of problems that were growing every day. The problems were that hardware manufactures were popping up all over the place creating radical new technologies such as 2D/3D accelerators, advanced sound synthesis hardware, networking hardware, and truly bizarre input devices. MS saw this as an opportunity; to in one swift blow, make a zillion dollars, unify the PC's hardware and software together, and finally to get every game programmer on Earth using their tools and APIs for PC based games. Not a bad idea. So DirectX was born to solve these problems.

The first version of DirectX was without a doubt a joke. It was nothing more than a glimpse of the future. It had a terribly flawed API that was full of bugs. The documentation was a joke and I still to this day don't understand why MS didn't just pay a couple awesome game programmers to create some good demos, I still have nightmares about that clown demo! Anyway, DirectX 1.0 was a first draft by MS to tackle an area that they had no clue about. MS is not in the business of making games, so they approached it like a word processor. A ton of features, and a bunch of overhead that only 5 people on Earth would use. Luckily, they got smart and really started asking the game programming community what they thought should be changed, improved etc. The game programming community responded with the largest onslaught of critical feedback in history (no doubt) and DirectX 2.0 was born.

DirectX 2.0 was getting there. It had more DirectX components and it actually worked. There were even a few games released that used it last year. Wow! However, during this period 99% of all games released were still DOS 32, but the game community knew that DirectX was here to stay and MS was committed to making it work. After two revisions MS knew what everyone wanted (I think) and could give it to them. Direct X 5.0 is here and it is pretty good. It has clean support for Direct3D, the DirectInput module supports mice, and keyboards now. The 3D component of DirectSound works and the overall API is stable as far as I'm concerned. DirectX 6.0 is in beta and when released will work on both Windows 95/98 and NT.

So if your are asking yourself the question, "should I try Direct X". The answer is yes! You must, otherwise you will be assimilated anyway. Admitingly, one of the biggest issues to a game programmer is Windows programming. Windows programming is completely the opposite way game programmers want to program. Who wants the OS telling us what to do? And worst yet, who wants to share resources? Well, these are issues for newbee Windows converts and you just have to live through the conversion. However, once you get the basics of Windows programming under DirectX, you can almost forget about the Windows environment or the API. It is possible to create a virtual machine that resembles a DOS 32 machine from the programmers point of view. We will actually cover a basic model of this tactic at the end of the article.

X-treme Functionality

Let's assume for a moment that you are buying my pitch and you are going to use DirectX. What is in it for you? Well, first your code will work on every Windows 95 machine alive. This is very important. No longer do you have to worry about device drivers, TSRs, conventional memory etc. Windows 95 is a clean OS that DirectX works well with, so in a way, it's like programming for a generic PC. Next, just about every video card these days has 2D acceleration and most new computers come with 3D accelerators. However, I bet if you just did a DOS game, you may of thought about using acceleration to do things such as blitting, scrolling, lighting, or even 3D, but didn't even want to think about writing drivers for 1000 different video cards. DirectX and the hardware manufactures handle this for us under DirectX. This is very important.

When we create a sprite and blit it to the screen with DirectX, if there is acceleration then it is utilized, otherwise, no penalty. So our games instantly look better and the guy who spends $500 on his new 128 bit video card gets his moneys worth and it makes your game look better and run faster. The next big win is sound. Out of all the game programmers in the world there are about 2 that write their own sound drivers. The rest use a sound library from a 3rd party such as Miles, or Diamondware, etc. Sound cards are similar to video cards, but game programmers let go of them long ago since sound is boring compared to graphics. Anyway, the point is that under DirectSound there is no more need for licensing sound libraries, it's free and Microsoft will keep upgrading it with the latest abilities to take advantage of 3D sound cards, waveguide synthesizers, and whatever else comes out.

The next plus for DirectX is networking. This is probably the most underestimated or talked about aspect of DirectX. DirectPlay is the networking component of DirectX and although it is complex, it allows the programmer to create network connections, send and receive packets, and write multiplayer games very easy compared to doing it yourself. There is also a DirectInput component of DirectX that allows the interfacing of all the new input devices. This is another area that has suffered. Most game programmers don't support 34 buttons and 6 degrees of freedom. They write the basic joystick driver and if you have the latest joystick with laser optical positioning then too bad, its just going to act like a basic joystick. With DirectInput the programmers can query the input system if he want's to know the exact device connected and take advantage or simply use the basics. In either case, the programmer doesn't have to write drivers, or program registers, he just makes some calls and DirectInput returns the results.

In addition to DirectDraw, DirectSound, DirectInput, DirectPlay, there is also DirectSetup, and Autoplay. DirectSetup is a simple software API that facilitates the installation of the run-time components of DirectX needed on a users machine. Autoplay is a Win32 component that allows CDs to be put in the drive and automatically run. Last, but least is Direct3D. This is definitely not worth using. I have written nearly 50,000 lines of DirectX code and I actually like it. I tried and tried to work with Direct3D and like it, but I keep getting nauseated. The API is complex, broken, and just takes the wrong angle at the whole thing. It's a shame since so many hardware vendors have created Direct3D drivers for their cards. However, in my opinion Direct3D will not last. We all know that John Carmack has denounced it, no one is really using it, and OpenGL is getting a lot of media play as the saving API to make 3D work.

I'm not sure what it going to happen with the 3D API war. I worked with OpenGL years ago in scientific computing and VR before it was in vogue, and I have to admit I really didn't like it. However, I strongly hate Direct3D, so my advice is, for now take a look at Direct3D and OpenGL, see what you think. Then while you making up your mind call up the half dozen leading 3D accelerator manufacturers and obtain their native API. That is the best way to go right now in my opinion.

X-tending the Creative Limits

All right, we have talked shop a bit from a technical point of view, but what about everyone else that has a life, such as artists and musicians? Good news. No more VOC and PCX files, palette problems, aspect ratio distortion, and whatever else goes along with MS-DOS game programming. DirectX allows the programmers to very easily communicate with the video and sound hardware, so that they don't have to do a lot of the work anymore to set things up. Although, mode 13h, and mode X were great for games yesterday, today people want to see at least 640x480 in 256 colors and prefer 16 bit color. This wasn't the easiest thing to do. Now is nothing more than a function call. This means that artists and animators don't need to worry as much about working in a 320x200 or 320x240 paint program, or about color space. Artists and animators will have much more freedom when working on Windows game under DirectX since the output is no longer so limiting.

And as far as sound and music goes, we are going to enter a new era of emersion via 3D positioning and sound. New sound hardware is absolutely incredible, but very little of the abilities of sound cards is taken advantage of even with 3rd party APIs. Now the full memory, processing, DSP, and synthesis capabilities of sound hardware will finally come to bear. Sound designers can now think of games that have actually 3D sound content. Explosions, that rattle your teeth, growls that feel like they are behind you etc. All allowing for a more realistic and rich experience.

Game designers will also applaud DirectX and its abilities. Now your vision will be more accurately reproduced since the programmers will have more time to work on game play, artificial intelligence, physics, and multiplayer aspects rather than writing drivers for everything and cutting corners to meet schedule.

X-perience the Truth

After reading all that, your probably saying, "ya right, what's the real deal?" Well, on the other side of the coin. DirectX and the philosophy of letting go is hard to swallow. You must learn Windows programming in addition to DirectX. Then you must be ready for "Revision-Idis". This is a term I use to describe how quickly Microsoft will change things on you. Expect to get a new revision of DirectX, Windows, and tools all the time. This means that you can no longer program in a dark room, you must have at least one window open and keep an eye on the world. Therefore, you have to program differently, and think differently. You must think open architecture and portability. All in all, after the initial few weeks of hell learning Windows and DirectX and you start really getting the hang of it, it's not that bad. It's kinda cool being able to create a linear memory 1280x1024 display in 16 bit color without drama.

To conclude the first round of this article which takes a more philosophical approach to DirectX, I think we have to say that DirectX is here and we are going to have to use it. And it aint so bad (slang intended). Moreover, maybe it's time that game programmers got a break from all the low level stuff and a shot at some of the high level stuff like AI, physics, and multiplayer techniques. Now I promised to show you a working DOS 32 like game console that uses DirectX, so let's take a look at that.

X-habitionism

There is no way that I am going to teach DirectX programming in the next 2 pages, so what I'm going to do is focus on DirectDraw and just enough functions to get us up and running. The shell that we create will allow you to write a game that can directly access the video buffer, use the keyboard, and best of all has 32 bit addressing and the FLAT memory model. Now before we engage the anti-matter flow into the reactor and hit warp speed, there are a couple of things you will need.

A C++ compiler such as MS VC++ 4.0 or better.

The DirectX 5.0+ SDK. You can get it from www.microsoft.com. It's about 30 megs.

A lot of patience.

Ready, set, go! What we want to create is a Windows program that uses DirectX. We want it to do all the things that a Windows program needs to do to start up and process messages, but after that is done, we want it to start up DirectX, put the computer in a full screen graphics mode, and leave us alone with access to the video buffer and keyboard. Amazingly, this is not that hard to do. Before we get started on the code, let's briefly review the Windows programming model. Refer to Figure 1.0.

Windows 95 is a multitasking OS event driven OS. What this means is that more than one application can be executing at once, and input is sent to us as "events" or messages. We don't poll devices as we do in a DOS game. This architecture has a profound affect on how we must design our game. We must have what is called an "event loop", in addition we must have an "event handler" to handle these events. Whenever Windows gets an event it will call the event handler function and the function will handle the event. This isn't too bad, but we want to try and disconnect ourselves from all this as much as possible to create our game console.

Here is our plan of attack: we will perform all the initialization that is needed to create a working window, event handler, and set up DirectX. Then we will create an event loop that calls a single function: void Game_Main(void) every cycle. Within this function we can do whatever we want. This is where our entire game will live. Also, we will have two more functions: void Game_Init(void), and void Game_Shutdown(void). These functions will be called once and only once at the start of the game and when the main windows event loop is exited. We need these since we may lose control after exiting void Game_Main(void) and never have a chance to clean up.

Figure 2.0 shows the structure of our system. The beauty of it is that we don't even care that we are running in windows, we just write to the video buffer, call functions, allocate memory etc. just like in a DOS 32 Protected Mode program! Now, since I don't just want to dump a listing on you and say, "here ya go", let's at least talk in high level terms about what is involved in getting DirectDraw to work.

Step 1: A DirectDraw object must be created, this allows us to communicate with DirectDraw. You can think of this step as initiating communication with the graphics card.

Step 2: The cooperation level with the rest of the system and other DirectX apps. must be set. This is necessary since Windows shares resources.

Step 3: Set the video mode to the desired resolution and bit depth. We will stick with 640x480 in 256 colors, but other resolutions are possible with a simple change. However, we want to stick to 8 bit color.

Step 4: Create a primary drawing surface. This means nothing more than asking DirectDraw for the a handle to the video ram.

Step 5: Create an 8 bit palette and attach it. The palette will be initialized with shades of gray, red, green, and blue. You will be able to change palette registers with the single function int Set_Pal_Entry(int index, int red, int green, int blue).

That's about all there is too it. Listing 1.0 contains all the above functionality along with an example of how to write to the video buffer and access the keyboard. Simply use it as a model.


// LISTING 1.0 - DIRECT X 5.0 GAME CONSOLE ////////////////////////////////////

// INCLUDES ///////////////////////////////////////////////////////////////////

#define WIN32_LEAN_AND_MEAN // make sure certain headers are included correctly

#include   // include the standard windows stuff
#include  // include the 32 bit stuff
#include  // include the multi media stuff
                            // note you need winmm.lib also
#include     // include direct draw components

#include    // include all the good stuff
#include 
#include 
#include 

// DEFINES ////////////////////////////////////////////////////////////////////

#define WINDOW_CLASS_NAME "WINDOW_CLASS" // this is the name of the window class

// defines for screen parameters

#define SCREEN_WIDTH 640   // the width of the viewing surface
#define SCREEN_HEIGHT 480  // the height of the viewing surface
#define SCREEN_BPP 8       // the bits per pixel
#define MAX_COLORS 256     // the maximum number of colors

// TYPES //////////////////////////////////////////////////////////////////////

typedef unsigned char UCHAR;

// MACROS /////////////////////////////////////////////////////////////////////

// these query the keyboard in real-time

#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

// PROTOTYPES /////////////////////////////////////////////////////////////////

int DD_Init(HWND hwnd);
int DD_Shutdown(void);
int Set_Pal_Entry(int index, int red, int green, int blue);

void Game_Init(void);
void Game_Main(void);
void Game_Shutdown(void);

// DIRECTDRAW GLOBALS ////////////////////////////////////////////////////////

LPDIRECTDRAW lpdd = NULL;                // dd object
LPDIRECTDRAWSURFACE lpddsprimary = NULL; // dd primary surface
LPDIRECTDRAWPALETTE lpddpal = NULL;      // a pointer to the created dd palette
PALETTEENTRY color_palette[256];         // holds the shadow palette entries
DDSURFACEDESC ddsd;               // a direct draw surface description struct
DDSCAPS ddscaps;                  // a direct draw surface capabilities struct
HRESULT ddrval;                   // result back from dd calls
HWND main_window_handle = NULL;   // used to store the window handle
UCHAR *video_buffer = NULL;       // pointer to video ram


// GAME GLOBALS GO HERE ///////////////////////////////////////////////////// 

// DIRECT X FUNCTIONS /////////////////////////////////////////////////////////

int DD_Init(HWND hwnd)
{
    // this function is responsible for initializing direct draw, it creates a
    // primary surface 

    int index; // looping index

    // now that the windows portion is complete, start up direct draw
    if (DirectDrawCreate(NULL,&lpdd,NULL)!=DD_OK)
    {
        // shutdown any other dd objects and kill window
        DD_Shutdown();
        return(0);
    } // end if

    // now set the coop level to exclusive and set for full screen and mode x
    if (lpdd->SetCooperativeLevel(hwnd, DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE |
        DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX)!=DD_OK)
    {
        // shutdown any other dd objects and kill window
        DD_Shutdown();
        return(0);
    } // end if

    // now set the display mode
    if (lpdd->SetDisplayMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP)!=DD_OK)
    {
        // shutdown any other dd objects and kill window
        DD_Shutdown();
        return(0);
    } // end if

    // Create the primary surface
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_CAPS;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

    if
    (lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL)!=DD_OK)
    {
        // shutdown any other dd objects and kill window
        DD_Shutdown();
        return(0);
    } // end if

    // create the palette and attach it to the primary surface

    // clear all the palette entries to RGB 0,0,0
    memset(color_palette,0,256*sizeof(PALETTEENTRY));
    
    // set all of the flags to the correct value
    for (index=0; index<256; index++)
    {
        // create the GRAY/RED/GREEN/BLUE palette, 64 shades of each
        if ((index / 64)==0)
        {
            color_palette[index].peRed = index*4;
            color_palette[index].peGreen = index*4;
            color_palette[index].peBlue = index*4;
        } // end if
        else
        if ((index / 64)==1)
            color_palette[index].peRed = (index%64)*4;
        else
        if ((index / 64)==2) 
            color_palette[index].peGreen = (index%64)*4;
        else
        if ((index / 64)==3)
            color_palette[index].peBlue = (index%64)*4;

        // set the no collapse flag
        color_palette[index].peFlags = PC_NOCOLLAPSE;

    } // end for index

    // now create the palette object, note that it is a member of the dd object itself
    if (lpdd->CreatePalette((DDPCAPS_8BIT | DDPCAPS_INITIALIZE),color_palette,&lpddpal,NULL)!=DD_OK)
    {
        // shutdown any other dd objects and kill window
        DD_Shutdown();
        return(0);
    } // end if

    // now attach the palette to the primary surface
    lpddsprimary->SetPalette(lpddpal);

    // return success if we got this far
    return(1);

} // end DD_Init

///////////////////////////////////////////////////////////////////////////////

int DD_Shutdown(void)
{
    // this function tests for dd components that have been created and releases
    // them back to the operating system

    // test if the dd object exists
    if (lpdd)
    {
        // test if there is a primary surface
        if(lpddsprimary)
        {
            // release the memory and set pointer to NULL
            lpddsprimary->Release();
            lpddsprimary = NULL;
        } // end if

        // now release the dd object itself
        lpdd->Release();
        lpdd = NULL;

        // return success
        return(1);

    } // end if
    else
        return(0);

} // end DD_Shutdown

//////////////////////////////////////////////////////////////////////////////

int Set_Pal_Entry(int index, int red, int green, int blue)
{
    // this function sets a palette entry with the sent color

    PALETTEENTRY color; // used to build up color

    // set RGB value in structure
    color.peRed = (BYTE)red;
    color.peGreen = (BYTE)green;
    color.peBlue = (BYTE)blue;
    color.peFlags = PC_NOCOLLAPSE;

    // set the color palette entry
    lpddpal->SetEntries(0,index,1,&color);

    // make copy in shadow palette
    memcpy(&color_palette[index],
        &color,
        sizeof(PALETTEENTRY));

    // return success
    return(1);

} // end Set_Pal_Entry

// WINDOWS CALLBACK FUNCTION ////////////////////////////////////////////////// 

LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    // this is the main message handler of the system

    HDC hdc; // handle to graphics context
    PAINTSTRUCT ps; // used to hold the paint info

    // what is the message?

    switch(msg)
    { 
    case WM_CREATE:
        {
            // do windows inits here
            return(0);
        } break;

    case WM_PAINT:
        {
            // this message occurs when your window needs repainting
            hdc = BeginPaint(hwnd,&ps); 
            EndPaint(hdc,&ps);

            return(0);
        } break;

    case WM_DESTROY:
        {
            // this message is sent when your window is destroyed
            PostQuitMessage(0);
            return(0);
        } break;

    default:break;

    } // end switch

    // let windows process any messages that we didn't take care of 
    return (DefWindowProc(hwnd, msg, wparam, lparam));

} // end WinProc

// WINMAIN ////////////////////////////////////////////////////////////////////

int WINAPI WinMain( HINSTANCE hinstance,
                    HINSTANCE hprevinstance,
                    LPSTR lpcmdline,
                    int ncmdshow)
{
    WNDCLASSEX winclass;  // this holds the windows class info
    HWND hwnd;            // this holds the handle of our new window
    MSG msg;              // this holds a generic message

    // first fill in the window class stucture

    winclass.cbSize = sizeof(WNDCLASSEX);
    winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    winclass.lpfnWndProc = WindowProc;
    winclass.cbClsExtra = 0;
    winclass.cbWndExtra = 0;
    winclass.hInstance = hinstance;
    winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    winclass.hbrBackground = GetStockObject(BLACK_BRUSH);
    winclass.lpszMenuName = NULL;
    winclass.lpszClassName = WINDOW_CLASS_NAME;

    // register the window class
    if (!RegisterClassEx(&winclass))
        return(0);

    // create the window
    if (!(hwnd = CreateWindowEx(WS_EX_TOPMOST,
            WINDOW_CLASS_NAME,      // class
            "You can't See This!",  // title
            WS_VISIBLE | WS_POPUP,
            0,0, // x,y
            GetSystemMetrics(SM_CXSCREEN),
            GetSystemMetrics(SM_CYSCREEN), 
            NULL,           // parent
            NULL,           // menu
            hinstance,      // instance
            NULL)))         // creation parms
        return(0);

    // hide the mouse cursor
    ShowCursor(0);

    // save the window handle
    main_window_handle = hwnd;

    // initialize direct draw
    if (!DD_Init(hwnd))
    {
        DestroyWindow(hwnd);
        return(0);
    } // end if

    // initialize game
    Game_Init();

    // enter main event loop
    while(1)
    {
        if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
        { 
            // test if this is a quit
            if (msg.message == WM_QUIT)
                break;

            // translate any accelerator keys
            TranslateMessage(&msg);

            // send the message to the window proc
            DispatchMessage(&msg);
        } // end if
        else
        {
            // do asynchronous processing here

            // acquire pointer to video ram, note it is always linear
            memset(&ddsd,0,sizeof(ddsd));
            ddsd.dwSize = sizeof(ddsd);
            lpddsprimary->Lock(NULL,&ddsd,DDLOCK_SURFACEMEMORYPTR,NULL);
            video_buffer = (UCHAR *)ddsd.lpSurface;

            // call main logic module
            Game_Main();

            // release pointer to video ram
            lpddsprimary->Unlock(ddsd.lpSurface);

        } // end else

    } // end while

    // shut down direct draw
    DD_Shutdown();

    // shutdown game
    Game_Shutdown();

    // return to Windows
    return(msg.wParam);

} // end WinMain

// HERE ARE OUR GAME CONSOLE FUNCTIONS ///////////////////////////////////////////////////////

void Game_Init(void)
{
    // do any initialization here
} // end Game_Init

/////////////////////////////////////////////////////////////////////////////////////////////

void Game_Shutdown(void)
{
    // cleanup and release all resources here
} // end Game_Shutdown

/////////////////////////////////////////////////////////////////////////////////////////////

void Game_Main(void)
{
    // process your game logic and draw next frame
    // this function must exit each frame and return back to windows
    // also this function is responsible for controlling the frame rate
    // therefore, if you want the game to run at 30fps, then the function should
    // take 1/30th of a second before returning

    static int px = SCREEN_WIDTH/2,  // position of player
    py = SCREEN_HEIGHT/2,
    color = 0;                       // color of blob 0..3, gray, red, green, blue

    // on entry video_buffer is valid

    // get input, note how keyboard is accessed with constants
    "VK_"
    // these are defined in "winuser.h" are basically the
    scan codes
    // for letters just use capital ASCII codes

    if (KEY_DOWN(VK_ESCAPE))
        PostMessage(main_window_handle,WM_CLOSE,0,0); // this is how you exit you game

    // which way is player moving
    if (KEY_DOWN(VK_RIGHT))
        if (++px>SCREEN_WIDTH-8) px=8;

    if (KEY_DOWN(VK_LEFT))
        if (--px<8) px=SCREEN_WIDTH-8;

    if (KEY_DOWN(VK_UP))
        if (--py<8) py=SCREEN_HEIGHT-8;

    if (KEY_DOWN(VK_DOWN))
        if (++py>SCREEN_HEIGHT-8) py=8;

    // adjust color, notice 
    if (KEY_DOWN('C'))
    {
        if (++color>=4)
        color=0;
        Sleep(100);
    } // end if

    // draw graphics
    for (int pixels=0; pixels<32; pixels++)
    video_buffer[(px-4+rand()%8)+(py-4+rand()%8)*SCREEN_WIDTH] = (color*64)+rand()%64;

    // sync time (optional)

    // use sleep to slow system down to 70ish fps
    Sleep(14); // note that Sleep is extremly inaccurate, IRL you would use something more robust

    // return and let windows have some time

} // end Game_Main

To compile the program and run it, you need to include the library DDRAW.LIB, and DDRAW.H from the game SDK. This can be accomplished in your IDE by setting the search directories or including them directly in your project. Your target app. should be a standard Windows application. After building the program, run it, and you should see a glowing blob that you can move around with the arrow keys and change color with the key. To exit, press . Now you have everything you need to start experimenting with DirectX 5.0 basics. Try implementing a double buffer, changing the resolution via the constants SCREEN_WIDTH and SCREEN_HEIGHT, and playing with the color palette.

Now there is one detail that might cause a problem for you...If your video card simply can not create a linear memory video mode for DirectX then you must fudge the video access slightly. For example, if you create a video mode with 640 pixels per line as in 640x480, then in a 256 color mode, to get to the next line you would add 640, or in general to access a pixel, you would write:


video_buffer[x + y*640] = pixel;

However, if you're card just won't support linear memory then you need to modify this a little by taking into consideration the "linear memory pitch". This value is greater than the pixel pitch, for example it may be 1024 even in a 640 pixel per line mode. The linear pitch can be accessed via the DirectDraw surface description after locking the surface, like this:


// here's how we lock
lpddsprimary->Lock(NULL,&ddsd,DDLOCK_SURFACEMEMORYPTR,NULL);
video_buffer = (UCHAR *)ddsd.lpSurface;

// here's the pitch acquisition
int lpitch = ddsd.lPitch;

Then to write to video memory taking into consideration the memory pitch, you do this:


video_buffer[x + y*lpitch] = pixel;

So the saftest method is to always use the linear pitch, however, this kills a lot of optimization opportunities, so you might want to test to see of the linear pitch is equal to the number of pixels per line and then use different rendering functions based on this?

X Marks the Spot

In conclusion, DirectX isn't as bad as we thought it was, and it really does work and allows accelerators and other hardware to be supported very easily. However, I have to leave you with these thoughts in your mind before we all blindly use "Uncle Bill's" new technology. Once we are all dependent on DirectX, what would stop MS from at some point charging us to get the next release of DirectX? Or worse yet, what if MS finds a way to make us pay a royalty to them to release a game on the PC platform just like Nintendo or Sony does? These are interesting suppositions. And if I was the richest man in the world, I know I would be thinking about them. Finally, what will happen 20 years from now when we are all hooked on APIs? Will any of us remember how to do anything ourselves? Only time will tell...

Download the source code!

Discuss this article in the forums


Date this article was posted to GameDev.net: 7/31/1999
(Note that this date does not necessarily correspond to the date the article was written)

See Also:
General

© 1999-2011 Gamedev.net. All rights reserved. Terms of Use Privacy Policy
Comments? Questions? Feedback? Click here!