The T3DLIB1 LibraryAt 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 ArchitectureThus 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.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 ConsoleBefore 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 DefinitionsThe 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 MacrosNext 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 StructuresThe 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 DominationYou 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 InterfaceNow 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 FunctionsThe 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 PrimitivesThis 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 FunctionsThe 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 FunctionsThe 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:
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 FunctionsThe 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 FunctionsThe 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); |