JavaScript EditorFree JavaScript Editor     Ajax Editor 



Main Page
  Previous Section Next Section

The T3DLIB1 Library

At this point, you're ready to take a look at all the defines, macros, data structures, and functions that you've created throughout the book. Moreover, I've put them all into a single pair of files: T3DLIB1.CPP|H. You can link these into your programs and use everything you've learned so far without hunting down all the code from the dozens of programs you've written.

In addition to all the stuff you've already written, I've created a few more 2D sprite functions to help facilitate 2D game programming. Actually, I used this stuff to create some of the demos at the end of the chapter, so you get to see that code clearly defined. Anyway, I'm not going to take too much time explaining everything here, but I'll give you enough to help you figure it out. Let's take a look at each code element one by one.

The Engine Architecture

Thus far you have a fairly simple 2D engine going, as shown in Figure 8.54. Basically, the engine along with the additions I have made is now a 2D 8/16-bit color back buffered DirectX engine that has support for any resolution, along with clipping to the primary display surface, and has the ability to transparently operate in windowed mode.

Figure 8.54. The architecture of the graphics engine.

graphics/08fig54.gif

To build an application using the library, you'll need to include T3DLIB1.CPP|H (from the CD) along with DDRAW.LIB (DirectDraw Library), WINMM.LIB (Win32 Multimedia Library), and a main game program T3DCONSOLE2.CPP which is based on the simpler T3DCONSOLE.CPP (you made this earlier in the book). Then you're ready to go.

Of course, you wrote a lot of code, and you're free to modify, use, abuse all this stuff, or even burn it if you like. I just thought you might like it all explained and put together in a couple of easy-to-use files.

T3DCONSOLE2.CPP: The New Game Programming Console

Before we cover all the functionality and so forth of the engine, take a quick look at the latest incarnation of the console, version 2.0 which now has hooks and support for 8- or 16-bit mode windowed or full screen. Basically, by changing a few #defines at the top of the file you can select 8- or 16-bit mode, full screen or windowed display— it's really cool! Here it is for your review:

// T3DCONSOLE2.CPP -
// Use this as a template for your applications if you wish
// you may want to change things like the resolution of the
// application, if it's windowed, the directinput devices
// that are acquired and so forth...
// currently the app creates a 640x480x16 windowed display
// hence, you must be in 16 bit color before running the application
// if you want fullscreen mode then simple change the WINDOWED_APP
// value in the #defines below value to FALSE (0). Similarly, if
// you want another bitdepth, maybe 8-bit for 256 colors then
// change that in the call to DDraw_Init() in the function
// Game_Init() within this file.

// READ THIS!
// To compile make sure to include DDRAW.LIB, DSOUND.LIB,
// DINPUT.LIB, WINMM.LIB in the project link list, and of course
// the C++ source modules T3DLIB1.CPP,T3DLIB2.CPP, and T3DLIB3.CPP
// and the headers T3DLIB1.H,T3DLIB2.H, and T3DLIB3.H must
// be in the working directory of the compiler

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

#define INITGUID       // make sure all the COM interfaces are available
                       // instead of this you can include the .LIB file
                       // DXGUID.LIB

#define WIN32_LEAN_AND_MEAN

#include <windows.h>   // include important windows stuff
#include <windowsx.h>
#include <mmsystem.h>
#include <iostream.h> // include important C/C++ stuff
#include <conio.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <math.h>
#include <io.h>
#include <fcntl.h>

#include <ddraw.h>  // directX includes
#include <dsound.h>
#include <dmksctrl.h>
#include <dmusici.h>
#include <dmusicc.h>
#include <dmusicf.h>
#include <dinput.h>
#include "T3DLIB1.h" // game library includes
#include "T3DLIB2.h"
#include "T3DLIB3.h"

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

// defines for windows interface
#define WINDOW_CLASS_NAME "WIN3DCLASS"  // class name
#define WINDOW_TITLE      "T3D Graphics Console Ver 2.0"
#define WINDOW_WIDTH      640   // size of window
#define WINDOW_HEIGHT     480

#define WINDOW_BPP        16    // bitdepth of window (8,16,24 etc.)
                                // note: if windowed and not
                                // fullscreen then bitdepth must
                                // be same as system bitdepth
                                // also if 8-bit the a pallete
                                // is created and attached

#define WINDOWED_APP      1     // 0 not windowed, 1 windowed

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

// game console
int Game_Init(void *parms=NULL);
int Game_Shutdown(void *parms=NULL);
int Game_Main(void *parms=NULL);

// GLOBALS ////////////////////////////////////////////////

HWND main_window_handle           = NULL; // save the window handle
HINSTANCE main_instance           = NULL; // save the instance
char buffer[256];                          // used to print text


// FUNCTIONS //////////////////////////////////////////////

LRESULT CALLBACK WindowProc(HWND hwnd,
                                                UINT msg,
                            WPARAM wparam,
                            LPARAM lparam)
{
// this is the main message handler of the system
PAINTSTRUCT ps;                // used in WM_PAINT
HDC                         hdc;       // handle to a device context

// what is the message
switch(msg)
   {
   case WM_CREATE:
        {
            // do initialization stuff here
            return(0);
            } break;

    case WM_PAINT:
         {
         // start painting
         hdc = BeginPaint(hwnd,&ps);

         // end painting
         EndPaint(hwnd,&ps);
         return(0);
        } break;
   case WM_DESTROY:
            {
            // kill the application
            PostQuitMessage(0);
            return(0);
            } break;

   default:break;

    } // end switch

// 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)
{
// this is the winmain function
WNDCLASSEX winclass; // this will hold the class we create
HWND          hwnd;     // generic window handle
MSG           msg;      // generic message
HDC        hdc;      // graphics device context

// 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.hCurso    = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground     = (HBRUSH)GetStockObject(BLACK_BRUSH);
winclass.lpszMenuName      = NULL;
winclass.lpszClassName     = WINDOW_CLASS_NAME;
winclass.hIconSm        = LoadIcon(NULL, IDI_APPLICATION);

// save hinstance in global
main_instance = hinstance;

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

// create the window
if (!(hwnd = CreateWindowEx(NULL,// extended style
       WINDOW_CLASS_NAME, // class
   WINDOW_TITLE, // title
   WINDOWED_APP ? (WS_OVERLAPPED | WS_SYSMENU | WS_VISIBLE) :
                       (WS_POPUP | WS_VISIBLE)),
   0,0,     // initial x,y
   WINDOW_WIDTH,WINDOW_HEIGHT,  // initial width, height
   NULL,     // handle to parent
   NULL,     // handle to menu
   hinstance,// instance of this application
   NULL)))   // extra creation parms
return(0);

// save the window handle and instance in a global
main_window_handle = hwnd;
main_instance      = hinstance;

// resize the window so that client is really width x height
if (WINDOWED_APP)
{
// now resize the window, so the client area is the
// actual size requested since there may be borders
// and controls if this is going to be a windowed app
// if the app is not windowed then it won't matter
RECT window_rect = {0,0,WINDOW_WIDTH,WINDOW_HEIGHT};

// make the call to adjust window_rect
AdjustWindowRectEx(&window_rect,
     GetWindowStyle(main_window_handle),
     GetMenu(main_window_handle) != NULL,
     GetWindowExStyle(main_window_handle));

// save the global client offsets, they are needed in DDraw_Flip()
window_client_x0 = -window_rect.left;
window_client_y0 = -window_rect.top;

// now resize the window with a call to MoveWindow()
MoveWindow(main_window_handle,
           CW_USEDEFAULT, // x position
           CW_USEDEFAULT, // y position
           window_rect.right - window_rect.left, // width
           window_rect.bottom - window_rect.top, // height
           TRUE);


// show the window, so there's no garbage on first render
ShowWindow(main_window_handle, SW_SHOW);
} // end if windowed

// perform all game console specific initialization
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

    // main game processing goes here
    Game_Main();

   } // end while

// shutdown game and release all resources
Game_Shutdown();


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

} // end WinMain

// T3D II GAME PROGRAMMING CONSOLE FUNCTIONS ////////////////

int Game_Init(void *parms)
{
// this function is where you do all the initialization
// for your game

// start up DirectDraw (replace the parms as you desire)
DDraw_Init(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_BPP, WINDOWED_APP);

// initialize directinput
DInput_Init();

// acquire the keyboard
DInput_Init_Keyboard();

// add calls to acquire other directinput devices here...


// initialize directsound and directmusic
DSound_Init();
DMusic_Init();

// hide the mouse
ShowCursor(FALSE);
// seed random number generator
srand(Start_Clock());

// all your initialization code goes here...


// return success
return(1);

} // end Game_Init

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

int Game_Shutdown(void *parms)
{
// this function is where you shutdown your game and
// release all resources that you allocated

// shut everything down

// release all your resources created for the game here....


// now directsound
DSound_Stop_All_Sounds();
DSound_Delete_All_Sounds();
DSound_Shutdown();

// directmusic
DMusic_Delete_All_MIDI();
DMusic_Shutdown();

// shut down directinput
DInput_Release_Keyboard();

DInput_Shutdown();

// shutdown directdraw last
DDraw_Shutdown();

// return success
return(1);
} // end Game_Shutdown

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

int Game_Main(void *parms)
{
// this is the workhorse of your game it will be called
// continuously in real-time this is like main() in C
// all the calls for you game go here!

int index; // looping var
// start the timing clock
Start_Clock();

// clear the drawing surface
DDraw_Fill_Surface(lpddsback, 0);

// read keyboard and other devices here
DInput_Read_Keyboard();

// game logic here...

// flip the surfaces
DDraw_Flip();

// sync to 30ish fps
Wait_Clock(30);

// check of user is trying to exit
if (KEY_DOWN(VK_ESCAPE) || keyboard_state[DIK_ESCAPE])
    {
    PostMessage(main_window_handle, WM_DESTROY,0,0);
    } // end if

// return success
return(1);

} // end Game_Main

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

Basically, by controlling these #defines:

#define WINDOW_WIDTH      640   // size of window
#define WINDOW_HEIGHT     480

#define WINDOW_BPP        16    // bitdepth of window (8,16,24 etc.)
                                // note: if windowed and not
                                // fullscreen then bitdepth must
                                // be same as system bitdepth
                                // also if 8-bit the a pallete
                                // is created and attached

#define WINDOWED_APP      1     // 0 not windowed, 1 windowed

you can select the screen resolution (for full screen modes), the bitdepth (for full screen modes), and if the display is windowed (windowed displays use the current desktop color depth and resolution, so all you control is the window size).

Basic Definitions

The engine has one header file, T3DLIB1.H, and within it are a number of #defines that the engine uses. Here they are for your reference:

// DEFINES ////////////////////////////////////////////////
// default screen values, these are all overriden by the
// call to DDraw_Init() and are just here to have something
// to set the globals to instead of constant values
#define SCREEN_WIDTH        640  // size of screen
#define SCREEN_HEIGHT       480
#define SCREEN_BPP          8    // bits per pixel
#define MAX_COLORS_PALETTE  256


#define DEFAULT_PALETTE_FILE "PALDATA2.PAL"

// used for selecting full screen/windowed mode
#define SCREEN_FULLSCREEN    0
#define SCREEN_WINDOWED      1

// bitmap defines
#define BITMAP_ID            0x4D42 // universal id for a bitmap
#define BITMAP_STATE_DEAD    0
#define BITMAP_STATE_ALIVE   1
#define BITMAP_STATE_DYING   2
#define BITMAP_ATTR_LOADED   128

#define BITMAP_EXTRACT_MODE_CELL  0
#define BITMAP_EXTRACT_MODE_ABS   1

// directdraw pixel format defines, used to help
// bitmap loader put data in proper format
#define DD_PIXEL_FORMAT8        8
#define DD_PIXEL_FORMAT555      15
#define DD_PIXEL_FORMAT565      16
#define DD_PIXEL_FORMAT888      24
#define DD_PIXEL_FORMATALPHA888 32


// defines for BOBs
#define BOB_STATE_DEAD         0    // this is a dead bob
#define BOB_STATE_ALIVE        1    // this is a live bob
#define BOB_STATE_DYING        2    // this bob is dying
#define BOB_STATE_ANIM_DONE    1    // done animation state
#define MAX_BOB_FRAMES         64   // maximum number of bob frames
#define MAX_BOB_ANIMATIONS     16   // maximum number of animation sequeces

#define BOB_ATTR_SINGLE_FRAME   1   // bob has single frame
#define BOB_ATTR_MULTI_FRAME    2   // bob has multiple frames
#define BOB_ATTR_MULTI_ANIM     4   // bob has multiple animations
#define BOB_ATTR_ANIM_ONE_SHOT  8   // bob will perform the animation once
#define BOB_ATTR_VISIBLE        16  // bob is visible
#define BOB_ATTR_BOUNCE         32  // bob bounces off edges
#define BOB_ATTR_WRAPAROUND     64  // bob wraps around edges
#define BOB_ATTR_LOADED         128 // the bob has been loaded
#define BOB_ATTR_CLONE          256 // the bob is a clone
// screen transition commands
#define SCREEN_DARKNESS  0         // fade to black
#define SCREEN_WHITENESS 1         // fade to white
#define SCREEN_SWIPE_X   2         // do a horizontal swipe
#define SCREEN_SWIPE_Y   3         // do a vertical swipe
#define SCREEN_DISOLVE   4         // a pixel disolve
#define SCREEN_SCRUNCH   5         // a square compression
#define SCREEN_BLUENESS  6         // fade to blue
#define SCREEN_REDNESS   7         // fade to red
#define SCREEN_GREENNESS 8         // fade to green

// defines for Blink_Colors
#define BLINKER_ADD           0    // add a light to database
#define BLINKER_DELETE        1    // delete a light from database
#define BLINKER_UPDATE        2    // update a light
#define BLINKER_RUN           3    // run normal

// pi defines
#define PI         ((float)3.141592654f)
#define PI2        ((float)6.283185307f)
#define PI_DIV_2   ((float)1.570796327f)
#define PI_DIV_4   ((float)0.785398163f)
#define PI_INV     ((float)0.318309886f)

// fixed point mathematics constants
#define FIXP16_SHIFT     16
#define FIXP16_MAG       65536
#define FIXP16_DP_MASK   0x0000ffff
#define FIXP16_WP_MASK   0xffff0000
#define FIXP16_ROUND_UP  0x00008000

You've seen all of these in one place or another.

Working Macros

Next are all the macros you've written thus far. Again, you've seen them all in one place or another, but here they are all at once:

// these read the keyboard asynchronously
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code)   ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

// this builds a 16 bit color value in 5.5.5 format (1-bit alpha mode)
#define _RGB16BIT555(r,g,b) ((b & 31) + ((g & 31) << 5) + ((r & 31) << 10))

// this builds a 16 bit color value in 5.6.5 format (green dominate mode)
#define _RGB16BIT565(r,g,b) ((b & 31) + ((g & 63) << 5) + ((r & 31) << 11))

// this builds a 24 bit color value in 8.8.8 format
#define _RGB24BIT(a,r,g,b) ((b) + ((g) << 8) + ((r) << 16) )

// this builds a 32 bit color value in A.8.8.8 format (8-bit alpha mode)
#define _RGB32BIT(a,r,g,b) ((b) + ((g) << 8) + ((r) << 16) + ((a) << 24))

// bit manipulation macros
#define SET_BIT(word,bit_flag)   ((word)=((word) | (bit_flag)))
#define RESET_BIT(word,bit_flag) ((word)=((word) & (~bit_flag)))

// initializes a direct draw struct
// basically zeros it and sets the dwSize field
#define DDRAW_INIT_STRUCT(ddstruct) {memset(&ddstruct,0,sizeof(ddstruct));
 ddstruct.dwSize=sizeof(ddstruct); }

// used to compute the min and max of two expresions
#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
#define MAX(a, b)  (((a) > (b)) ? (b) : (a))

// used for swapping algorithm
#define SWAP(a,b,t) {t=a; a=b; b=t;}

// some math macros
#define DEG_TO_RAD(ang) ((ang)*PI/180.0)
#define RAD_TO_DEG(rads) ((rads)*180.0/PI)

#define RAND_RANGE(x,y) ( (x) + (rand()%((y)-(x)+1)))

Data Types and Structures

The next set of code elements includes the types and data structures that the engine uses. I'm going to list them all, but be warned that there are a couple you haven't seen yet that have to do with the Blitter Object Engine (BOB). To be consistent, let's take a look at everything at once:

// basic unsigned types
typedef unsigned short USHORT;
typedef unsigned short WORD;
typedef unsigned char  UCHAR;
typedef unsigned char  BYTE;
typedef unsigned int   QUAD;
typedef unsigned int   UINT;


// container structure for bitmaps .BMP file
typedef struct BITMAP_FILE_TAG
        {
        BITMAPFILEHEADER bitmapfileheader;  // this contains the
                                            // bitmapfile header
        BITMAPINFOHEADER bitmapinfoheader;  // this is all the info
                                            // including the palette
        PALETTEENTRY     palette[256];      // we will store
                                            // the palette here
        UCHAR            *buffer;           // this is a pointer
                                            // to the data

        } BITMAP_FILE, *BITMAP_FILE_PTR;

// the blitter object structure BOB
typedef struct BOB_TYP
        {
        int state;          // the state of the object (general)
        int anim_state;     // an animation state variable, up to you
        int attr;           // attributes pertaining
                            // to the object (general)
        float x,y;            // position bitmap will be displayed at
        float xv,yv;          // velocity of object
        int width, height;  // the width and height of the bob
        int width_fill;     // internal, used to force 8*x wide surfaces
        int counter_1;      // general counters
        int counter_2;
        int max_count_1;    // general threshold values;
        int max_count_2;
        int varsI[16];      // stack of 16 integers
        float varsF[16];    // stack of 16 floats
        int curr_frame;     // current animation frame
        int num_frames;     // total number of animation frames
        int curr_animation; // index of current animation
        int anim_counter;   // used to time animation transitions
        int anim_index;     // animation element index
        int anim_count_max; // number of cycles before animation
        int *animations[MAX_BOB_ANIMATIONS]; // animation sequences

        LPDIRECTDRAWSURFACE7 images[MAX_BOB_FRAMES]; // the bitmap images
                                                     // DD surfaces

        } BOB, *BOB_PTR;

// the simple bitmap image
typedef struct BITMAP_IMAGE_TYP
        {
        int state;          // state of bitmap
        int attr;           // attributes of bitmap
        int x,y;            // position of bitmap
        int width, height;  // size of bitmap
        int num_bytes;      // total bytes of bitmap
        UCHAR *buffer;      // pixels of bitmap

        } BITMAP_IMAGE, *BITMAP_IMAGE_PTR;

// blinking light structure
typedef struct BLINKER_TYP
               {
               // user sets these
               int color_index;       // index of color to blink
               PALETTEENTRY on_color; // RGB value of "on" color
               PALETTEENTRY off_color;// RGB value of "off" color
               int on_time;           // number of frames to keep "on"
               int off_time;          // number of frames to keep "off"

               // internal member
               int counter;     // counter for state transitions
               int state;       // state of light, -1 off, 1 on, 0 dead
               } BLINKER, *BLINKER_PTR;
// a 2D vertex
typedef struct VERTEX2DI_TYP
        {
        int x,y; // the vertex
        } VERTEX2DI, *VERTEX2DI_PTR;

// a 2D vertex
typedef struct VERTEX2DF_TYP
        {
        float x,y; // the vertex
        } VERTEX2DF, *VERTEX2DF_PTR;


// a 2D polygon
typedef struct POLYGON2D_TYP
        {
        int state;        // state of polygon
        int num_verts;    // number of vertices
        int x0,y0;        // position of center of polygon
        int xv,yv;        // initial velocity
        DWORD color;      // could be index or PALETTENTRY
        VERTEX2DF *vlist; // pointer to vertex list

        } POLYGON2D, *POLYGON2D_PTR;


// 3x3 matrix /////////////////////////////////////////////
typedef struct MATRIX3X3_TYP
        {
        union
        {
        float M[3][3]; // array indexed data storage

        // storage in row major form with explicit names
        struct
             {
             float M00, M01, M02;
             float M10, M11, M12;
             float M20, M21, M22;
             }; // end explicit names

        }; // end union
        } MATRIX3X3, *MATRIX3X3_PTR;

// 1x3 matrix /////////////////////////////////////////////
typedef struct MATRIX1X3_TYP
        {
        union
        {
        float M[3]; // array indexed data storage

        // storage in row major form with explicit names
        struct
             {
             float M00, M01, M02;

             }; // end explicit names
        }; // end union
        } MATRIX1X3, *MATRIX1X3_PTR;

// 3x2 matrix /////////////////////////////////////////////
typedef struct MATRIX3X2_TYP
        {
        union
        {
        float M[3][2]; // array indexed data storage

        // storage in row major form with explicit names
        struct
             {
             float M00, M01;
             float M10, M11;
             float M20, M21;
             }; // end explicit names

        }; // end union
        } MATRIX3X2, *MATRIX3X2_PTR;

// 1x2 matrix /////////////////////////////////////////////
typedef struct MATRIX1X2_TYP
        {
        union
        {
        float M[2]; // array indexed data storage

        // storage in row major form with explicit names
        struct
             {
             float M00, M01;

             }; // end explicit names
        }; // end union
        } MATRIX1X2, *MATRIX1X2_PTR;

Not bad; nothing new, really. Basic types, all the bitmap stuff, polygon support, and a little matrix math.

Global Domination

You know that I like globals because they're so fast. Moreover, they're really appropriate for a lot of system-level variables (which a 2D/3D engine has a lot of). So here are the globals for the engine. Again, you've seen many of them, but the ones that look alien have comments, so read them:

extern FILE *fp_error;          // general error file
extern char error_filename[80]; // error file name

// notice that interface 4.0 is used on a number of interfaces
extern LPDIRECTDRAW7        lpdd;         // dd object
extern LPDIRECTDRAWSURFACE7 lpddsprimary; // dd primary surface
extern LPDIRECTDRAWSURFACE7 lpddsback;    // dd back surface
extern LPDIRECTDRAWPALETTE  lpddpal;      // dd palette
extern LPDIRECTDRAWCLIPPER  lpddclipper; // dd clipper for back surface
extern LPDIRECTDRAWCLIPPER  lpddclipperwin;   // dd clipper for window
extern PALETTEENTRY     palette[256];     // color palette
extern PALETTEENTRY     save_palette[256];// used to save palettes
extern DDSURFACEDESC2   ddsd;             // a dd surface description struct
extern DDBLTFX          ddbltfx;          // used to fill
extern DDSCAPS2         ddscaps;          // a dd surface capabilities struct
extern HRESULT          ddrval;           // result back from dd calls
extern UCHAR            *primary_buffer;  // primary video buffer
extern UCHAR            *back_buffer;     // secondary back buffer
extern int              primary_lpitch;   // memory line pitch
extern int              back_lpitch;      // memory line pitch
extern BITMAP_FILE      bitmap8bit;       // a 8 bit bitmap file
extern BITMAP_FILE      bitmap16bit;      // a 16 bit bitmap file
extern BITMAP_FILE      bitmap24bit;      // a 24 bit bitmap file

extern DWORD            start_clock_count;    // used for timing
extern int              windowed_mode;        // tracks if dd is windowed or not

// these defined the general clipping rectangle for software clipping
extern int min_clip_x,   // clipping rectangle
           max_clip_x,
           min_clip_y,
           max_clip_y;

// these are overwritten globally by DD_Init()
extern int screen_width,    // width of screen
           screen_height,   // height of screen
           screen_bpp,      // bits per pixel
           screen_windowed; // is this a windowed app?


extern int dd_pixel_format;  // default pixel format set by call
                             // to DDraw_Init

extern int window_client_x0; // used to track the starting
                             // (x,y) client area for
extern int window_client_y0; // for windowed mode dd operations

// storage for our lookup tables
extern float cos_look[361]; // 1 extra so we can store 0-360 inclusive
extern float sin_look[361]; // 1 extra so we can store 0-360 inclusive

// function ptr to RGB16 builder
extern USHORT (*RGB16Bit)(int r, int g, int b);
// root functions
extern USHORT RGB16Bit565(int r, int g, int b);
extern USHORT RGB16Bit555(int r, int g, int b);

The DirectDraw Interface

Now that you've seen all the data support, let's take a look at all the DirectDraw support functions that we've written along with some additions that I have made for full 16-bit windowed support. Let's take a quick look at each function.

Function Prototype:

int DDraw_Init(int width,  // width of display
               int height, // height of display
               int bpp,   // bits per pixel
               int windowed=0); // controls windowed

Purpose:

DDraw_Init() starts up and initializes DirectDraw. You can send any resolution and color depth and select if you desire a windowed mode by setting windowed to 1. If you select a windowed mode then you must have created a window previously that was not full screen. DDraw_Init() must set up DirectDraw differently for windowed modes since the primary buffer is now the entire display, clipping must be performed to just the client area of the window and of course you have no control over the screen resolution or color depth on a windowed mode, so bpp is ignored.

Additionally, this function does a lot for you, it loads in a default palette paldata2.pal for 8-bit modes, sets up the clipping rectangle for both 8 and 16-bit modes windowed or full screen and in 16-bit modes determines the pixel format and tests if it's 5.5.5 or 5.6.5 and based on this points the function pointer:

USHORT (*RGB16Bit)(int r, int g, int b) = NULL;

At either:

USHORT RGB16Bit565(int r, int g, int b)
{
// this function simply builds a 5.6.5 format 16 bit pixel
// assumes input is RGB 0-255 each channel
r>>=3; g>>=2; b>>=3;
return(_RGB16BIT565((r),(g),(b)));

} // end RGB16Bit565

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

USHORT RGB16Bit555(int r, int g, int b)
{
// this function simply builds a 5.5.5 format 16 bit pixel
// assumes input is RGB 0-255 each channel
r>>=3; g>>=3; b>>=3;
return(_RGB16BIT555((r),(g),(b)));

which allows you to make the call RGB16Bit(r,g,b) to create a properly formatted RGB word in 16-bit mode and not have to worry about the pixel format. Isn't that nice? And of course R,G,B all must be in the range 0–255. Also, DDraw_Init() is smart enough in windowed mode to set all the clipping up for you, so you don't have to do anything!

Returns TRUE if successful.

Example:

// put the system into 800x600 with 256 colors
DDraw_Init(800,600,8);

Example:

// put the system into a windowed mode
// with a window size of 640x480 and 16-bit color
DDraw_Init(640,480,16,1);

Function Prototype:

int DDraw_Shutdown(void);

Purpose:

DDraw_Shutdown() shuts down DirectDraw and releases all interfaces.

Example:

// in your system shutdown code you might put
DDraw_Shutdown();

Function Prototype:

LPDIRECTDRAWCLIPPER
 DDraw_Attach_Clipper(
    LPDIRECTDRAWSURFACE7 lpdds, // surface to attach to
    int num_rects,     // number of rects
    LPRECT clip_list); // pointer to rects

Purpose:

DDraw_Attach_Clipper() attaches a clipper to the sent surface (the back buffer in most cases). In addition, you must send the number of rectangles in the clipping list and a pointer to the RECT list itself. Returns TRUE if successful.

Example:

// creates a clipping region the size of the screen
RECT clip_zone = {0,0,SCREEN_WIDTH-1, SCREEN_HEIGHT-1};
DDraw_Attach_Clipper(lpddsback, 1, &clip_zone);

Function Prototype:

LPDIRECTDRAWSURFACE7
   DDraw_Create_Surface(int width, // width of surface
                        int height, // height of surface
                        int mem_flags, // control flags
                        USHORT color_key_value=0); // the color key

Purpose:

DDraw_Create_Surface() is used to create a generic offscreen DirectDraw surface in system memory, VRAM, or AGP memory. The default is DDSCAPS_OFFSCREENPLAIN. Any additional control flags are logically ORed with the default. They're the standard DirectDraw DDSCAP* flags, such as DDSCAPS_SYSTEMMEMORY and DDSCAPS_VIDEOMEMORY for system memory and VRAM, respectively. Also, you may select a color key value, it currently defaults to 0. If the function is successful, it returns a pointer to the new surface. Otherwise, it returns NULL.

Example:

// let's create a 64x64 surface in VRAM
LPDIRECTDRAWSURFACE7 image =

          DDraw_Create_Surface(64,64, DDSCAPS_VIDEOMEMORY);

Function Prototype:

int DDraw_Flip(void);

Purpose:

DDraw_Flip() simply flips the primary surface with the secondary surface for full screen modes, or in windowed mode copies the offscreen back buffer to the client area of the windowed display. The call waits until the flip can take place, so it may not return immediately. Returns TRUE if successful.

Example:

// flip em baby
DDraw_Flip();

Function Prototype:

int DDraw_Wait_For_Vsync(void);

Purpose:

DDraw_Wait_For_Vsync() waits until the next vertical blank period begins (when the raster hits the bottom of the screen). Returns TRUE if successful and FALSE if something really bad happened.

Example:

// wait 1/70th of sec
DDraw_Wait_For_Vsync();

Function Prototype:

int DDraw_Fill_Surface(LPDIRECTDRAWSURFACE7 lpdds, // surface to fill
                        int color, // color, index or RGB value
                        RECT *client=NULL) // rect to fill

Purpose:

DDraw_Fill_Surface() is used to fill a surface or rectangle within a surface with a color. The color must be in the color depth format of the surface, such as a single byte in 256-color mode or a RGB descriptor in high-color modes, if you want to fill the entire surface send set client to NULL which is its default, otherwise, you may send a RECT pointer as client and fill a region only of the surface. Returns TRUE if successful.

Example:

// fill the primary surface with color 0
DDraw_Fill_Surface(lpddsprimary,0);

Function Prototype:

UCHAR *DDraw_Lock_Surface(LPDIRECTDRAWSURFACE7 lpdds,int *lpitch);

Purpose:

DDraw_Lock_Surface() locks the sent surface (if possible) and returns a UCHAR pointer to the surface, along with updating the sent lpitch variable with the linear memory pitch of the surface. While the surface is locked, you can manipulate it and write pixels to it, but the blitter will be blocked, so remember to unlock the surface ASAP. In addition, after unlocking the surface, the memory pointer and pitch most likely become invalid and should not be used. DDraw_Lock_Surface() returns the non-NULL address of the surface memory if successful and NULL otherwise. Also, remember if you are in 16-bit mode then there are 2 bytes per pixel, but the pointer returned is still a UCHAR * and value written in lpitch is in bytes not pixels, so beware!

Example:

// holds the memory pitch
int lpitch = 0;

// let's lock the little 64x64 image we made
UCHAR *memory = DDraw_Lock_Surface(image, &lpitch);

Function Prototype:

int DDraw_Unlock_Surface(LPDIRECTDRAWSURFACE7 lpdds);

Purpose:

DDraw_Unlock_Surface() unlocks a surface previously locked with DDraw_Lock_Surface(). You need only send the pointer to the surface. Returns TRUE if successful.

Example:

// unlock the image surface
DDraw_Unlock_Surface(image);

Function Prototypes:

UCHAR *DDraw_Lock_Back_Surface(void);
UCHAR *DDraw_Lock_Primary_Surface(void);

Purpose:

These two functions are used to lock the primary and secondary rendering surfaces. However, in most cases you'll only be interested in locking the secondary surface in the double buffered system, but the ability to lock the primary surface is there if you need it. Additionally, If you call DDraw_Lock_Primary_Surface(), the following globals will become valid:

extern UCHAR  *primary_buffer;      // primary video buffer
extern int     primary_lpitch;       // memory line pitch

Then you're free to manipulate the surface memory as you want; however, the blitter will be blocked. Also, note that in windowed modes the primary buffer will point to the entire screen surface, not just your window, however, the secondary buffer will point to a rectangle exactly the size of the window's client area. Anyway, making the call to DDraw_Lock_Back_Surface() will lock the back buffer surface and validate the following globals:

extern UCHAR  *back_buffer;         // secondary back buffer
extern int     back_lpitch;          // memory line pitch

NOTE

Do not change any of these globals yourself; they're used to track state changes in the locking functions. Changing them yourself may make the engine go crazy.


Example:

// let lock the primary surface and write a pixel to the
// upper left hand corner
DDraw_Lock_Primary();

primary_buffer[0] = 100;

Function Prototype:

int DDraw_Unlock_Primary_Surface(void);
int DDraw_Unlock_Back_Surface(void);

Purpose:

These functions are used to unlock the primary or back buffer surfaces. If you try to unlock a surface that wasn't locked, there's no effect. Returns TRUE if successful.

Example:

// unlock the secondary back buffer
DDraw_Unlock_Back();

2D Polygon Functions

The next set of functions make up the 2D polygon system. This is by no means advanced, fast, or cutting-edge, but just your work up to this point. The functions do the job. There are better ways to do all of this stuff, but that's why you're glued to the book, right? <BG> Also, some of the functions have both an 8-bit and 16-bit version, the 16-bit versions are usually denoted by and extra "16" concatenated on the function name.

Function Prototype(s):

void Draw_Triangle_2D(int x1,int y1, // triangle vertices
                  int x2,int y2,
                  int x3,int y3,
                  int color,  // 8-bit color index
                  UCHAR *dest_buffer, // destination buffer
                  int mempitch); // memory pitch

// 16-bit version
void Draw_Triangle_2D16(int x1,int y1, // triangle vertices
                  int x2,int y2,
                  int x3,int y3,
                  int color,  // 16-bit RGB color descriptor
                  UCHAR *dest_buffer, // destination buffer
                  int mempitch); // memory pitch

// fixed point high speed version, slightly less accurate
void Draw_TriangleFP_2D(int x1,int y1,
                        int x2,int y2,
                        int x3,int y3,
                        int color,
                        UCHAR *dest_buffer,
                        int mempitch);

Purpose:

Draw_Triangle_2D*() draws a filled triangle in the given memory buffer with the sent color. The triangle will be clipped to the current clipping region set in the globals, not by the DirectDraw clipper. This is because the function uses software, not the blitter, to draw lines. Note: Draw_TriangleFP_2D() does the exact same thing, but it uses fixed-point math internally, is slightly faster, and is slightly less accurate. Both functions return nothing.

Example:

// draw a triangle (100,10) (150,50) (50,60)
// with color index 50 in the back buffer surface
Draw_Triangle_2D(100,10,150,50,50,60,
                 50, // color index 50
                 back_buffer,
                 back_lpitch);

// same example, but in a 16-bit mode
// draw a triangle (100,10) (150,50) (50,60)
// with color RGB(255,0,0) in the back buffer surface
Draw_Triangle_2D16(100,10,150,50,50,60,
                 RGB16Bit(255,0,0),
                 back_buffer,
                 back_lpitch);

Function Prototype:

inline void Draw_QuadFP_2D(int x0,int y0, // vertices
            int x1,int y1,
            int x2,int y2,
            int x3,int y3,
            int color, // 8-bit color index
            UCHAR *dest_buffer, // destination video buffer
            int mempitch); // memory pitch of buffer

Purpose:

Draw_QuadFP_2D() draws the sent quadrilateral as a composition of two triangles. Returns nothing.

Example:

// draw a quadrilateral, note vertices must be ordered
// either in cw or ccw order
Draw_QuadFP_2D(0,0, 10,0, 15,20, 5,25,
               100,
               back_buffer, back_lpitch);

Function Prototype:

void Draw_Filled_Polygon2D(
          POLYGON2D_PTR poly, // poly to render
          UCHAR *vbuffer, // video buffer
          int mempitch);  // memory pitch

// 16-bit version
void Draw_Filled_Polygon2D16(
          POLYGON2D_PTR poly, // poly to render
          UCHAR *vbuffer, // video buffer
          int mempitch);  // memory pitch

Purpose:

Draw_Filled_Polygon2D*() draws a general filled polygon with n sides. The function simply takes the polygon to render, a pointer to the video buffer, and the pitch, and that's it! Although, the calling parameters are the exact same for the 8-bit and 16-bit versions, internally the functions call different rasterizers, thus you must use the correct call. Note: The function renders relative to the poly's origin (x0,y0), so make sure these are initialized. Returns nothing.

Example:

// draw a polygon in the primary buffer
Draw_Filled_Polygon2D(&poly,
                      primary_buffer,
                      primary_lpitch);

Function Prototype:

int Translate_Polygon2D(
           POLYGON2D_PTR poly, // poly to translate
           int dx, int dy); // translation factors

Purpose:

Translate_Polygon2D() translates the given polygon's origin (x0,y0). Note: The function does not transform or modify the actual vertices making up the polygon. Returns TRUE if successful.

Example:

// translate polygon 10,-5
Translate_Polygon2D(&poly, 10, -5);

Function Prototype:

int Rotate_Polygon2D(
                 POLYGON2D_PTR poly, // poly to rotate
                 int theta); // angle 0-359

Purpose:

Rotate_Polygon2D() rotates the sent polygon in a counterclockwise fashion about its origin. The angle must be an integer from 0–359. Returns TRUE if successful.

Example:

// rotate polygon 10 degrees
Rotate_Polygon2D(&poly, 10);

Function Prototype:

int Scale_Polygon2D(POLYGON2D_PTR poly, // poly to scale
                    float sx, float sy); // scale factors

Purpose:

Scale_Polygon2D() scales the sent polygon by scale factors sx and sy in the x- and y-axes, respectively. Returns nothing.

Example:

// scale the poly equally 2x
Scale_Polygon2D(&poly, 2,2);

2D Graphic Primitives

This set of functions contains a few of everything; it's kind of a potpourri of graphics primitives. Nothing you haven't seen—at least I don't think so, but then I've had so much Snapple Raspberry that I'm freaking out and little purple mechanical spiders are crawling all over me! And once again, some of the functions have both an 8-bit and 16-bit version, the 16-bit versions are usually denoted by and extra "16" concatenated on the function name.

Function Prototype:

int Draw_Clip_Line(int x0,int y0, // starting point
                   int x1, int y1, // ending point
                   int color, // 8-bit color
                   UCHAR *dest_buffer,  // video buffer
                   int lpitch); // memory pitch

// 16-bit version
int Draw_Clip_Line16(int x0,int y0, // starting point
                   int x1, int y1, // ending point
                   int color, // 16-bit RGB color
                   UCHAR *dest_buffer,  // video buffer
                   int lpitch); // memory pitch

Purpose:

Draw_Clip_Line*() clips the sent line to the current clipping rectangle and then draws a line in the sent buffer in either 8 or 16-bit mode. Returns TRUE if successful.

Example:

// draw a line in the back buffer from (10,10) to (100,200)
// 8-bit call
Draw_Clip_Line(10,10,100,200,
               5, // color 5
               back_buffer,
               back_lpitch);

Function Prototype:

int Clip_Line(int &x1,int &y1,   // starting point
              int &x2, int &y2); // ending point

Purpose:

Clip_Line() is for the most part internal, but you can call it to clip the sent line to the current clipping rectangle. Note that the function modifies the sent endpoints, so save them if you don't want this side effect. Also, the function does not draw anything; it only clips the endpoints. Returns TRUE if successful.

Example:

// clip the line defined by x1,y1 to x2,y2
Clip_Line(x1,y1,x2,y2);

Function Prototype:

int Draw_Line(int xo, int yo,  // starting point
              int x1,int y1,   // ending point
              int color,     // 8-bit color index
              UCHAR *vb_start, // video buffer
              int lpitch);     // memory pitch

// 16-bit version
int Draw_Line16(int xo, int yo,  // starting point
              int x1,int y1,   // ending point
              int color,       // 16-bit RGB color
              UCHAR *vb_start, // video buffer
              int lpitch);     // memory pitch

Purpose:

Draw_Line*() draws a line in 8 or 16 bit mode without any clipping, so make sure that the endpoints are within the display surface's valid coordinates. This function is slightly faster than the clipped version because the clipping operation is not needed. Returns TRUE if successful.

Example:

// draw a line in the back buffer from (10,10) to (100,200)
// in 16-bit mode
Draw_Line16(10,10,100,200,
          RGB16Bit(0,255,0), // bright green
          back_buffer,
          back_lpitch);

Function Prototype:

inline int Draw_Pixel(int x, int y, // position of pixel
                      int color, // 8-bit color
                      UCHAR *video_buffer,  // gee hmm?
                      int lpitch); // memory pitch
// 16-bit version
inline int Draw_Pixel16(int x, int y, // position of pixel
                      int color, // 16-bit RGB color
                      UCHAR *video_buffer,  // gee hmm?
                      int lpitch); // memory pitch

Purpose:

Draw_Pixel() draws a single pixel on the display surface memory. In most cases, you won't create objects based on pixels because the overhead of the call itself takes more time than plotting the pixel. But if speed isn't your concern, the function does the job. At least it's inline! Returns TRUE if successful.

Example:

// draw a pixel in the center of the 640x480 screen
// 8-bit example
Draw_Pixel(320,240, 100, back_buffer, back_lpitch);

Function Prototype:

int Draw_Rectangle(int x1, int y1, // upper left corner
                int x2, int y2, // lower right corner
                int color, // color descriptor, index for
                           // 8-bit modes, RGB value for 16-bit
                           // modes
                LPDIRECTDRAWSURFACE7 lpdds); // dd surface

Purpose:

Draw_Rectangle() draws a rectangle on the sent DirectDraw surface. This function works the same in either 8 or 16-bit mode since it's a pure DirectDraw call. Note that the surface must be unlocked for the call to work. Moreover, the function uses the blitter, so it's very fast. Returns TRUE if successful.

Example:

// fill the screen using the blitter
Draw_Rectangle(0,0,639,479,0,lpddsback);

Function Prototype:

void HLine(int x1,int x2, // start and end x points
           int y,         // row to draw on
           int color,     // 8-bit color
           UCHAR *vbuffer, // video buffer
           int lpitch); // memory pitch

// 16-bit version
void HLine16(int x1,int x2, // start and end x points
           int y,          // row to draw on
           int color,      // 16-bit RGB color
           UCHAR *vbuffer, // video buffer
           int lpitch); // memory pitch

Purpose:

HLine*() draws a horizontal line very quickly as compared to the general line drawing function. Works in both 8 and 16-bit modes. Returns nothing.

Example:

// draw a fast line from 10,100 to 100,100
// 8-bit mode
HLine(10,100,100,
      20, back_buffer, back_lpitch);

Function Prototype:

void VLine(int y1,int y2, // start and end row
           int x,         // column to draw in
           int color,     // 8-bit color
           UCHAR *vbuffer,// video buffer
           int lpitch);   // memory pitch

// 16-bit version
void VLine16(int y1,int y2, // start and end row
           int x,         // column to draw in
           int color,     // 16-bit RGB color
           UCHAR *vbuffer,// video buffer
           int lpitch);   // memory pitch

Purpose:

VLine*() draws a fast vertical line. It's not as fast as HLine(), but it's faster than Draw_Line(), so use it if you know a line is going to be vertical in all cases. Returns nothing.

Example:

// draw a line from 320,0 to 320,479
// 16-bit version
VLine16(0,479,320,RGB16Bit(255,255,255),
      primary_buffer,
      primary_lpitch);

Function Prototype:

void Screen_Transitions(int effect,    // screen transition
                        UCHAR *vbuffer,// video buffer
                        int lpitch);   // memory pitch

Purpose:

Screen_Transition() performs various in-memory screen transitions, as listed in the previous header information. Note that the transformations are destructive, so please save the image and/or palette if you need them after the transition. Additionally, the color manipulation transitions only work in 8-bit palettized modes. However, the screen swipes, and scrunches work in either mode. Returns nothing.

Example:

// fade the primary display screen to black
// only works for 8-bit modes
Screen_Transition(SCREEN_DARKNESS, NULL, 0);
// scrunch the screen, works in 8/16 bit modes
Screen_Transition(SCREEN_SCRUNCH, NULL, 0);

Function Prototype(s):

int Draw_Text_GDI(char *text, // null terminated string
            int x,int y, // position
            COLORREF color, // general RGB color
            LPDIRECTDRAWSURFACE7 lpdds); // dd surface

int Draw_Text_GDI(char *text, // null terminated string
               int x,int y, // position
               int color, // 8-bit color index
               LPDIRECTDRAWSURFACE7 lpdds); // dd surface

Purpose:

Draw_Text_GDI() draws GDI text on the sent surface with the desired color and position. The function is overloaded to take both a COLORREF in the form of the Windows RGB() macro for 8- or 16-bit modes or a 256-color 8-bit color index for 256-color modes only. Note that the destination surface must be unlocked for the function to operate because it locks it momentarily to perform the text blitting with GDI. Returns TRUE if successful.

Example:

// draw text with color RGB(100,100,0);
// note this call would work in either
// 8 or 16-bit modes
Draw_Text_GDI("This is a test",100,50,
              RGB(100,100,0),lpddsprimary);

// draw text with color index 33
// note this call would work ONLY in
// 8-bit modes
Draw_Text_GDI("This is a test",100,50,
              33,lpddsprimary);

Math and Error Functions

The math library thus far is almost nonexistent, but that will soon change once you get to the math section of the book. I'll pump your brain full of all kinds of fun mathematical information and functions. Until then, sip the sweet simplicity because it will be your last…

Function Prototype:

int Fast_Distance_2D(int x, int y);

Purpose:

Fast_Distance() computes the distance from (0,0) to (x,y) using a fast approximation. Returns the distance within a 3.5 percent error truncated to an integer.

Example:

int x1=100,y1=200; // object one
int x2=400,y2=150; // object two

// compute the distance between object one and two
int dist = Fast_Distance_2D(x1-x2, y1-y2);

Function Prototype:

float Fast_Distance_3D(float x, float y, float z);

Purpose:

Fast_Distance_3D() computes the distance from (0,0,0) to (x,y,z) using a fast approximation. The function returns the distance within an 11 percent error.

Example:

// compute the distance from (0,0,0) to (100,200,300)
float dist = Fast_Distance_3D(100,200,300);

Function Prototype:

int Find_Bounding_Box_Poly2D(
                POLYGON2D_PTR poly, // the polygon
                float &min_x, float &max_x, // bounding box
                float &min_y, float &max_y);

Purpose:

Find_Bounding_Box_Poly2D() computes the smallest rectangle that contains the sent polygon in poly. Returns TRUE if successful. Also, notice that the function takes parameters by reference.

Example:

POLYGON2D poly; // assume this is initialized
int min_x, max_x, min_y, max_y; // hold result

// find bounding box
Find_Bounding_Box_Poly2D(&poly,min_x,max_x,min_y,max_y);

Function Prototype:

int Open_Error_File(char *filename);

Purpose:

Open_Error_File() opens a disk file that receives error messages sent by you via the Write_Error() function. Returns TRUE if successful.

Example:

// open a general error log
Open_Error_File("errors.log");

Function Prototype:

int Close_Error_File(void);

Purpose:

Close_Error_File() closes a previously opened error file. Basically, it shuts down the stream. If you call this and an error file is not open, nothing will happen. Returns TRUE if successful.

Example:

// close the error system, note no parameter needed
Close_Error_File();

Function Prototype:

int Write_Error(char *string, ...); // error formatting string

Purpose:

Write_Error() writes an error out to the previously opened error file. If there is no file open, the function returns a FALSE and there's no harm. Note that the function uses the variable parameter indicator, so you can use this function as you would printf(). Returns TRUE if successful.

Example:

// write out some stuff
Write_Error("\nSystem Starting...");
Write_Error("x-vel = %d", y-vel = %d", xvel, yvel);

Bitmap Functions

The following function set makes up the BITMAP_IMAGE and BITMAP_FILE manipulation routines. There are functions to load 8-, 16-, 24-, and 32-bit bitmaps, as well as to extract images from them and create simple BITMAP_IMAGE objects (which are not DirectDraw surfaces). In addition, there's functionality to draw these images in both 8- and 16-bit modes, but there's no clipping support. Hence, you can modify the source yourself if you need clipping or want to step up to the BOB objects, described at the end of the section. And as usual, some of the functions have both an 8-bit and 16-bit version, the 16-bit versions are usually denoted by an extra "16" concatenated on the function name.

Function Prototype:

int Load_Bitmap_File(BITMAP_FILE_PTR bitmap, // bitmap file
                     char *filename); // disk .BMP file to load

Purpose:

Load_Bitmap_File() loads a .BMP bitmap file from disk into the sent BITMAP_FILE structure where you can manipulate it. The function loads 8-, 16-, and 24-bit bitmaps, as well as the palette information on 8-bit .BMP files. Returns TRUE if successful.

Example:

// let's load "andre.bmp" off disk
BITMAP_FILE bitmap_file;

Load_Bitmap_File(&bitmap_file, "andre.bmp");

Function Prototype:

int Unload_Bitmap_File(BITMAP_FILE_PTR bitmap);
                      // bitmap to close and unload

Purpose:

Unload_Bitmap_File() deallocates the memory associated with the image buffer of a loaded BITMAP_FILE. Call this function when you've copied the image bits and/or are done working with a particular bitmap. You can reuse the structure, but the memory must be freed first. Returns TRUE if successful.

Example:

// close the file we just opened
Unload_Bitmap_File(&bitmap_file);

Function Prototype:

int Create_Bitmap(BITMAP_IMAGE_PTR image, // bitmap image
                  int x, int y, // starting position
                  int width, int height // size
                  int bpp=8); // bits per pixel, either 8 or 16

Purpose:

Create_Bitmap() creates either an 8- or 16-bit system memory bitmap at the given position with the given size. The bitmap is initially blank and is stored in the BITMAP_IMAGE image. The bitmap is not a DirectDraw surface, so there's no acceleration or clipping available. Returns TRUE if successful.

NOTE

There's a big difference between a BITMAP_FILE and a BITMAP_IMAGE. A BITMAP_FILE is a disk .BMP file, whereas a BITMAP_IMAGE is a system memory object like a sprite that can be moved and drawn.


Example:

// let's create an 8-bit 64x64 bitmap image at (0,0)
BITMAP_IMAGE ship;

Create_Bitmap(&ship, 0,0, 64,64,8);

// and here's the same example in 16-bit mode
BITMAP_IMAGE ship;
Create_Bitmap(&ship, 0,0, 64,64,16);

Function Prototype:

int Destroy_Bitmap(BITMAP_IMAGE_PTR image); // bitmap image to destroy

Purpose:

Destroy_Bitmap() is used to release the memory allocated during the creation of a BITMAP_IMAGE object. You should call this function on your object when you're all done working with it—usually during the shutdown of the game, or if the object has been destroyed in a bloody battle. Returns TRUE if successful.

Example:

// destroy the previously created BITMAP_IMAGE
Destroy_Bitmap(&ship);

Function Prototype:

int Load_Image_Bitmap(
 BITMAP_IMAGE_PTR image, // bitmap to store image in
 BITMAP_FILE_PTR bitmap, // bitmap file object to load from
 int cx,int cy, // coordinates where to scan (cell or abs)
 int mode); // image scan mode: cell based or absolute

// 16-bit version
int Load_Image_Bitmap16(
 BITMAP_IMAGE_PTR image, // bitmap to store image in
 BITMAP_FILE_PTR bitmap, // bitmap file object to load from
 int cx,int cy, // coordinates where to scan (cell or abs)
 int mode); // image scan mode: cell based or absolute


#define BITMAP_EXTRACT_MODE_CELL  0
#define BITMAP_EXTRACT_MODE_ABS   1

Purpose:

Load_Image_Bitmap*() is used to scan an image from a previously loaded BITMAP_FILE object into the sent BITMAP_IMAGE storage area. This is how you get objects and image bits into a BITMAP_IMAGE. To use the function, you first must load a BITMAP_FILE and create the BITMAP_IMAGE. Then you make the call to scan an image of the same size out of the bitmap data stored in the BITMAP_FILE. There are two ways the function works, cell mode or absolute mode:

  • In cell mode, BITMAP_EXTRACT_MODE_CELL, the image is scanned making the assumption that all the images are in the .BMP file in a template that is some given size, mxn, with a 1-pixel border between each cell. The cells usually range from 8x8, 16x16, 32x32, 64x64, and so on. Take a look at TEMPLATE*.BMP on the CD; it contains a number of templates. Cell numbers range from left to right, top to bottom, and they start with (0,0).

  • The second mode of operation is absolute coordinate mode, BITMAP_EXTRACT_MODE_ABS. In this mode, the image is scanned at the exact coordinates sent in cx, cy. This method is good if you want to load your artwork with various-sized images on the same .BMP; hence, you can't template them.

Also, you must use the correct version of the function based on the bit depth you created the bitmap in and the bit depth of the image you are scanning. Therefore, use Load_Image_Bitmap() for 8-bit bitmaps and Load_Image_Bitmap16() for 16-bit images.

Example:

// assume the source bitmap .BMP file is 640x480 and
// has a 8x8 matrix of cells that are each 32x32
// then to load the 3rd cell to the right on the 2nd
// row (cell 2,1) in 8-bit mode, you would do this

// load in the .BMP file into memory
BITMAP_FILE bitmap_file;
Load_Bitmap_File(&bitmap_file,"images.bmp");


// initialize the bitmap
BITMAP_IMAGE ship;
Create_Bitmap(&ship, 0,0, 32,32,8);


// now scan out the data
Load_Image_Bitmap(&ship, &bitmap_file, 2,1,
                  BITMAP_EXTRACT_MODE_CELL);

// same example in 16-bit mode
// assume the source bitmap .BMP file is 640x480 and
// has a 8x8 matrix of cells that are each 32x32
// then to load the 3rd cell to the right on the 2nd
// row (cell 2,1) in 16-bit mode, you would do this
// load in the .BMP file into memory
BITMAP_FILE bitmap_file;
Load_Bitmap_File(&bitmap_file,"images24bit.bmp");


// initialize the bitmap
BITMAP_IMAGE ship;
Create_Bitmap(&ship, 0,0, 32,32,16);


// now scan out the data
Load_Image_Bitmap16(&ship, &bitmap_file, 2,1,
                  BITMAP_EXTRACT_MODE_CELL);

To load the exact same image, assuming it's still in the template, but using the absolute mode, you have to figure out the coordinates. Remember that there's a 1-pixel partitioning wall on each side of the image.

Load_Image_Bitmap16(&ship, &bitmap_file,
                  2*(32+1)+1,1*(32+1)+1,
                  BITMAP_EXTRACT_MODE_ABS);

Function Prototype:

int Draw_Bitmap(BITMAP_IMAGE_PTR source_bitmap, // bitmap to draw
                UCHAR *dest_buffer, // video buffer
                int lpitch,  // memory pitch
                int transparent); // transparency?

// 16-bit version
int Draw_Bitmap16(BITMAP_IMAGE_PTR source_bitmap, // bitmap to draw
                UCHAR *dest_buffer, // video buffer
                int lpitch,  // memory pitch
                int transparent); // transparency?

Purpose:

Draw_Bitmap*() draws the sent bitmap on the destination memory surface with or without transparency. If transparent is 1, transparency is enabled and any pixel with a color index of 0 will not be copied. Again, simply use the 16-bit version when you are working with 16-bit modes and bitmaps. Function returns TRUE if successful.

Example:

// draw our little ship on the back buffer
// 8-bit mode
Draw_Bitmap( &ship, back_buffer, back_lpitch, 1);

Function Prototype:

int Flip_Bitmap(UCHAR *image, // image bits to vertically flip
                int bytes_per_line, // bytes per line
                int height); // total rows or height

Purpose:

Flip_Bitmap() is usually called internally to flip upside-down .BMP files during loading to make them right-side up, but you might want to use it to flip an image yourself. The function does an in-memory flip and actually inverts the bitmap line by line, so your original sent data will be inverted. Watch out! Works on any bit depth since it works with bytes per line. Returns TRUE if successful.

Example:

// for fun flip the image bits of our little ship
Flip_Bitmap(ship->buffer, ship->width, ship_height);

Function Prototype:

int Scroll_Bitmap(BITMAP_IMAGE_PTR image,  // bitmap to scroll
                  int dx,    // amount to scroll on x-axis
                  int dy=0); // amount to scroll on y-axis

Purpose:

Scroll_Bitmap() is used to scroll a bitmap horizontally or vertically. The function works on both 8- and 16-bit bitmaps and determines their bit depth internally, so you need only call one function. To use the function simple call it with a pointer to the bitmap you want to scroll along with the x and y scrolling values in pixels. The values can be either positive or negative, positive values mean right and down, while negative values mean left and up. Additionally, you may scroll on both axis simulataneosly, or just one. It's up to you. Function returns TRUE if it was successful.

Example:

// scroll an image 2 pixels to the right
Scroll_Bitmap(&image, 2, 0);

Function Prototype:

int Copy_Bitmap(BITMAP_IMAGE_PTR dest_bitmap, // destination bitmap
                int dest_x, int dest_y,  // destination position
                BITMAP_IMAGE_PTR source_bitmap, // source bitmap
                int source_x, int source_y, // source position
                int width, int height); // size of bitmap chunk to copy

Purpose:

Copy_Bitmap() is used to copy a rectangular region the source bitmap to the destination bitmap. The function internally scans the bitdepth of the source and destination, so the function work on either 8- or 16-bit modes with the same call. The bitmaps must of course be the same color depth though. To use the function simply send the destination bitmap, the point you want to copy the bitmap to, along with the source bitmap, the point you want to copy the bitmap from, and finally the width and height of the rectangle you want to copy. The function return TRUE if it's successful.

Example:

// copy a 100x100 rectangle from bitmap2 to bitmap1
// from the upper hand corner to the same
Copy_Bitmap(&bitmap1, 0,0,
            &bitmap2, 0,0,
            100,100);

Palette Functions

The following functions make up the 256-color palette interface. These functions are only relevant if you have the display set for a 256-color mode—that is, 8-bit color. Additionally, when you start the system up in 8-bit mode via a call to DDraw_Init() it will load in a default palette off disk, or try to at least, the default palette files are palette1.pal, palette2.pal, and palette3.pal—currently palette2.pal is loaded.

Function Prototype:

int Set_Palette_Entry(
               int color_index, // color index to change
               LPPALETTEENTRY color); // the color

Purpose:

Set_Palette_Entry() is used to change a single color in the color palette. You simply send the color index 0..255, along with a pointer to PALETTEENTRY holding the color, and the update will occur on the next frame. In addition, this function updates the shadow palette. Note: This function is slow; if you need to update the entire palette, use Set_Palette(). Returns TRUE if successful and FALSE otherwise.

Example:

// set color 0 to black
PALETTEENTRY black = {0,0,0,PC_NOCOLLAPSE};
Set_Palette_Entry(0,&black);

Function Prototype:

int Get_Palette_Entry(
              int color_index, // color index to retrieve
              LPPALETTEENTRY color); // storage for color

Purpose:

Get_Palette_Entry() retrieves a palette entry from the current palette. However, the function is very fast because it retrieves the data from the RAM-based shadow palette. Hence, you can call this as much as you like because it doesn't disturb the hardware at all. However, if you make changes to the system palette by using Set_Palette_Entry() or Set_Palette(), the shadow palette won't be updated and the data retrieved may not be valid. Returns TRUE if successful and FALSE otherwise.

Example:

// let's get palette entry 100
PALETTEENTRY color;
Get_Palette_Entry(100,&color);

Function Prototype:

int Save_Palette_To_File(
               char *filename,  // filename to save at
               LPPALETTEENTRY palette); // palette to save

Purpose:

Save_Palette_To_File() saves the sent palette data to an ASCII file on disk for later retrieval or processing. This function is very handy if you generate a palette on-the-fly and want to store it on disk. However, the function assumes that the pointer in the palette points to a 256-entry palette, so watch out! Returns TRUE if successful and FALSE otherwise.

Example:

PALETTEENTRY my_palette[256]; // assume this is built

// save the palette we made
// note file name can be anything, but I like *.pal
Save_Palette_To_file("/palettes/custom1.pal",my_palette);

Function Prototype:

int Load_Palette_From_File(
          char *filename,  // file to load from
          LPPALETTEENTRY palette); // storage for palette

Purpose:

Load_Palette_From_File() is used to load a previously saved 256-color palette from disk via Save_Palette_To_File(). You simply send the filename along with storage for all 256 entries, and the palette is loaded from disk into the data structure. However, the function does not load the entries into the hardware palette; you must do this yourself with Set_Palette(). Returns TRUE if successful and FALSE otherwise.

Example:

// load the previously ksaved palette
PALETTEENTRY disk_palette[256];

Load_Palette_From_Disk("/palettes/custom1.pal",&disk_palette);

Function Prototype:

int Set_Palette(LPPALETTEENTRY set_palette);
               // palette to load into hardware

Purpose:

Set_Palette() loads the sent palette data into the hardware and updates the shadow palette also. Returns TRUE if successful and FALSE otherwise.

Example:

// lets load the palette into the hardware
Set_Palette(disk_palette);

Function Prototype:

int Save_Palette(LPPALETTEENTRY sav_palette); // storage for palette

Purpose:

Save_Palette() scans the hardware palette out into sav_palette so that you can save it to disk or manipulate it. sav_palette must have enough storage for all 256 entries.

Example:

// retrieve the current DirectDraw hardware palette
PALETTEENTRY hardware_palette[256];
Save_Palette(hardware_palette);

Function Prototype:

int Rotate_Colors(int start_index, // starting index 0..255
                  int end_index); // ending index 0..255

Purpose:

Rotate_Colors() rotates a bank of colors in a cyclic manner in 8-bit modes. It manipulates the color palette hardware directly. Returns TRUE if successful and FALSE otherwise.

Example:

// rotate the entire palette
Rotate_Colors(0,255);

Function Prototype:

int Blink_Colors(int command, // blinker engine command
                 BLINKER_PTR new_light,  // blinker data
                 int id); // id of blinker

Purpose:

Blink_Colors() is used to create asynchronous palette animation. The function is too long to explain here, so please refer to Chapter 7, "Advanced DirectDraw and Bitmapped Graphics," for a more in-depth description.

Example:

None

Utility Functions

The next set of functions are just utility functions that I seem to use a lot, so I thought you might want to use them too.

Function Prototype:

DWORD Get_Clock(void);

Purpose:

Get_Clock() returns the current clock time in milliseconds since Windows was started.

Example:

// get the current tick count
DWORD start_time = Get_Clock();

Function Prototype:

DWORD Start_Clock(void);

Purpose:

Start_Clock() basically makes a call to Get_Clock() and stores the time in a global variable for you. Then you can call Wait_Clock(), which will wait for a certain number of milliseconds since your call to Start_Clock(). Returns the starting clock value at the time of the call.

Example:

// start the clock and set the global
Start_Clock();

Function Prototype:

DWORD Wait_Clock(DWORD count); // number of milliseconds to wait

Purpose:

Wait_Clock() simply waits the sent number of milliseconds since the call was made to Start_Clock(). Returns the current clock count at the time of the call. However, the function will not return until the time difference has elapsed.

Example:

// wait 30 milliseconds
Start_Clock();

// code...

Wait_Clock(30);

Function Prototype:

int Collision_Test(int x1, int y1, // upper lhs of obj1
                   int w1, int h1, // width, height of obj1
                   int x2, int y2, // upper lhs of obj2
                   int w2, int h2);// width, height of obj2

Purpose:

Collision_Test() basically performs an overlapping rectangle test on the two sent rectangles. The rectangles can represent whatever you like. You must send the upper-left-corner coordinates of each rectangle, along with its width and height. Returns TRUE if there is an overlap and FALSE if not.

Example:

// do these two BITMAP_IMAGE's overlap?
if (Collision_Test(ship1->x,ship1->y,ship1->width,ship1->height,
                   ship2->x,ship2->y,ship2->width,ship2->height))
   {// hit

   } // end if

Function Prototype:

int Color_Scan(int x1, int y1, // upper left of rect
               int x2, int y2, // lower right of rect
               UCHAR scan_start, // starting scan color
               UCHAR scan_end,  // ending scan color
               UCHAR *scan_buffer,  // memory to scan
               int scan_lpitch); // linear memory pitch

// 16-bit version
int Color_Scan16(int x1, int y1, // upper left of rect
               int x2, int y2, // lower right of rect
               USHORT scan_start, // scan RGB value 1
               USHORT scan_end,  // scan RGB value 2
               UCHAR *scan_buffer,  // memory to scan
               int scan_lpitch); // linear memory pitch

Purpose:

Color_Scan*() is another collision-detection algorithm that scans a rectangle for either a single 8-bit value or sequence of values in some continuous range in 8-bit modes or when used in 16-bit modes scans for up to 2 RGB values. You can use it to determine if a color index is present within some area. Returns TRUE if the color(s) was found.

Example:

// scan for colors in range from 122-124 inclusive in 8-bit mode
Color_Scan(10,10, 50, 50, 122,124, back_buffer, back_lpitch);

// scan for the RGB colors 10,30,40 and 100,0,12
Color_Scan(10,10, 50, 50, RGB16Bit(10,30,40), RGB16Bit(100,0,12),
back_buffer, back_lpitch);
      Previous Section Next Section
    



    JavaScript EditorAjax Editor     JavaScript Editor