The T3DLIB1 Library
At this point, we're ready to take a look at all the #defines, macros, data structures, and functions that compose the graphics module API of the T3DLIB game libraryT3DLIB1.CPP.
NOTE
If you have already read the first Tricks, you will notice that the engine is almost identical except for the additional 16-bit support sprinkled throughout, along with windowed support. However, the code is still compatible, so you can take the demos from the first Tricks and compile them with this new version of T3DLIB1.CPP and they will still work! And of course, everything will compile with DirectX 8.0 or 9.0+.
The module consists of two files: T3DLIB1.CPP|H. Hence, you simply link these into your programs and then use the API.
The DirectX Graphics Engine Architecture
T3DLIB1 is a fairly simple 2D engine, as shown in Figure 3.9. Basically, it's a 2D, 816-bit color, doubled-buffered DirectX engine that has support for any resolution along with clipping. The engine also supports both windowed and full-screen displays, and takes care of all the setup. So, whether you're in a windowed display or full-screen mode, you always write to the offscreen or secondary buffer, and logic takes care of the detailed process of copying or flipping the secondary buffer to the primary display. This is done regardless of whether the primary display window is a window or full-screen.
To build an application using the library, you'll need to include T3DLIB1.CPP|H, along with DDRAW.LIB (DirectDraw Library) and WINMM.LIB (Win32 Multimedia Library).
NOTE
You only need WINMM.LIB if you are using Visual C++.
Figure 3.9 The architecture of the graphics engine.
Basic Definitions
The engine has one header file, T3DLIB1.H, and within are a number of #defines that the engine uses. Here they are for your reference:
// watch for multiple inclusions #ifndef T3DLIB1 #define T3DLIB1 // 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 sequences #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 (256 color modes only) #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
Working Macros
Next are all the macros we've written thus far. Again, you've seen them all in one place or another, but here they all are 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) #define RAD_TO_DEG(rads) ((rads)*180/PI) #define RAND_RANGE(x,y) ( (x) + (rand()%((y)-(x)+1)))
Data Types and Structures
The next set of code elements are the types and data structures that the engine uses. Many of them aren't really of concern, because a lot of them are for the 2D aspect from the first Tricks, but it doesn't hurt to see all of it.
// 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; // contains the bitmapfile _header BITMAPINFOHEADER bitmapinfoheader; //info including _the palette PALETTEENTRY palette[256]; // palette _stored here UCHAR *buffer; // 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 bpp; // bits per pixel 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 // the bitmap images DD surfaces LPDIRECTDRAWSURFACE7 images[MAX_BOB_FRAMES]; } 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 int bpp; // bits per pixel 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; // matrix defines 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; 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; 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; 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;
You'll notice some math support at the end. This is from the 2D polygon transformation support from the first Tricks. We are going to rewrite much of it when we create the new 3D math library.
Function Prototypes
Next I want you to take a look at the complete function prototype list, so you can see everything at once. I'm not going to cover every single function in the function API in the following sections because many functions are different versions of each other.
// DirectDraw functions int DDraw_Init(int width, int height, int bpp, int windowed=0); int DDraw_Shutdown(void); LPDIRECTDRAWCLIPPER DDraw_Attach_Clipper(LPDIRECTDRAWSURFACE7 lpdds, int num_rects, LPRECT clip_list); LPDIRECTDRAWSURFACE7 DDraw_Create_Surface(int width, int height, int mem_flags=0, USHORT color_key_value=0); int DDraw_Flip(void); int DDraw_Wait_For_Vsync(void); int DDraw_Fill_Surface(LPDIRECTDRAWSURFACE7 lpdds, USHORT color, RECT *client=NULL); UCHAR *DDraw_Lock_Surface(LPDIRECTDRAWSURFACE7 lpdds,int *lpitch); int DDraw_Unlock_Surface(LPDIRECTDRAWSURFACE7 lpdds); UCHAR *DDraw_Lock_Primary_Surface(void); int DDraw_Unlock_Primary_Surface(void); UCHAR *DDraw_Lock_Back_Surface(void); int DDraw_Unlock_Back_Surface(void); // BOB functions int Create_BOB(BOB_PTR bob,int x, int y,int width, int height, int num_frames,int attr, int mem_flags=0, USHORT color_key_value=0, int bpp=8); int Clone_BOB(BOB_PTR source, BOB_PTR dest); int Destroy_BOB(BOB_PTR bob); int Draw_BOB(BOB_PTR bob, LPDIRECTDRAWSURFACE7 dest); int Draw_Scaled_BOB(BOB_PTR bob, int swidth, int sheight, LPDIRECTDRAWSURFACE7 dest); int Draw_BOB16(BOB_PTR bob, LPDIRECTDRAWSURFACE7 dest); int Draw_Scaled_BOB16(BOB_PTR bob, int swidth, int sheight, LPDIRECTDRAWSURFACE7 dest); int Load_Frame_BOB(BOB_PTR bob, BITMAP_FILE_PTR bitmap, int frame, int cx,int cy,int mode); int Load_Frame_BOB16(BOB_PTR bob, BITMAP_FILE_PTR bitmap, int frame, int cx,int cy,int mode); int Animate_BOB(BOB_PTR bob); int Move_BOB(BOB_PTR bob); int Load_Animation_BOB(BOB_PTR bob, int anim_index, int num_frames, int *sequence); int Set_Pos_BOB(BOB_PTR bob, int x, int y); int Set_Vel_BOB(BOB_PTR bob,int xv, int yv); int Set_Anim_Speed_BOB(BOB_PTR bob,int speed); int Set_Animation_BOB(BOB_PTR bob, int anim_index); int Hide_BOB(BOB_PTR bob); int Show_BOB(BOB_PTR bob); int Collision_BOBS(BOB_PTR bob1, BOB_PTR bob2); // general utility functions DWORD Get_Clock(void); DWORD Start_Clock(void); DWORD Wait_Clock(DWORD count); int Collision_Test(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2); int Color_Scan(int x1, int y1, int x2, int y2, UCHAR scan_start, UCHAR scan_end, UCHAR *scan_buffer, int scan_lpitch); int Color_Scan16(int x1, int y1, int x2, int y2, USHORT scan_start, USHORT scan_end, UCHAR *scan_buffer, int scan_lpitch); // graphics functions int Draw_Clip_Line(int x0,int y0, int x1, int y1, int color, UCHAR *dest_buffer, int lpitch); int Draw_Clip_Line16(int x0,int y0, int x1, int y1, int color, UCHAR *dest_buffer, int lpitch); int Clip_Line(int &x1,int &y1,int &x2, int &y2); int Draw_Line(int xo, int yo, int x1,int y1, int color, UCHAR *vb_start,int lpitch); int Draw_Line16(int xo, int yo, int x1,int y1, int color, UCHAR *vb_start,int lpitch); int Draw_Pixel(int x, int y,int color, UCHAR *video_buffer, int lpitch); int Draw_Rectangle(int x1, int y1, int x2, int y2, int color,LPDIRECTDRAWSURFACE7 lpdds); void HLine(int x1,int x2,int y,int color, UCHAR *vbuffer, int lpitch); void VLine(int y1,int y2,int x,int color, UCHAR *vbuffer, int lpitch); void HLine16(int x1,int x2,int y,int color, UCHAR *vbuffer, int lpitch); void VLine16(int y1,int y2,int x,int color, UCHAR *vbuffer, int lpitch); void Screen_Transitions(int effect, UCHAR *vbuffer, int lpitch); int Draw_Pixel(int x, int y,int color,UCHAR *video_buffer, int lpitch); int Draw_Pixel16(int x, int y,int color,UCHAR *video_buffer, int lpitch); // palette functions int Set_Palette_Entry(int color_index, LPPALETTEENTRY color); int Get_Palette_Entry(int color_index, LPPALETTEENTRY color); int Load_Palette_From_File(char *filename, LPPALETTEENTRY palette); int Save_Palette_To_File(char *filename, LPPALETTEENTRY palette); int Save_Palette(LPPALETTEENTRY sav_palette); int Set_Palette(LPPALETTEENTRY set_palette); int Rotate_Colors(int start_index, int end_index); int Blink_Colors(int command, BLINKER_PTR new_light, int id); // simple bitmap image functions int Create_Bitmap(BITMAP_IMAGE_PTR image, int x, int y, int width, int height, int bpp=8); int Destroy_Bitmap(BITMAP_IMAGE_PTR image); int Draw_Bitmap(BITMAP_IMAGE_PTR source_bitmap,UCHAR *dest_buffer, int lpitch, int transparent); int Draw_Bitmap16(BITMAP_IMAGE_PTR source_bitmap,UCHAR *dest_buffer, int lpitch, int transparent); int Load_Image_Bitmap(BITMAP_IMAGE_PTR image,BITMAP_FILE_PTR bitmap, int cx,int cy,int mode); int Load_Image_Bitmap16(BITMAP_IMAGE_PTR image,BITMAP_FILE_PTR bitmap, int cx,int cy,int mode); int Scroll_Bitmap(BITMAP_IMAGE_PTR image, int dx, int dy=0); int Copy_Bitmap(BITMAP_IMAGE_PTR dest_bitmap, int dest_x, int dest_y, BITMAP_IMAGE_PTR source_bitmap, int source_x, int source_y, int width, int height); int Flip_Bitmap(UCHAR *image, int bytes_per_line, int height); // bitmap file functions int Load_Bitmap_File(BITMAP_FILE_PTR bitmap, char *filename); int Unload_Bitmap_File(BITMAP_FILE_PTR bitmap); // gdi functions int Draw_Text_GDI(char *text, int x,int y, COLORREF color, LPDIRECTDRAWSURFACE7 lpdds); int Draw_Text_GDI(char *text, int x,int y, int color, LPDIRECTDRAWSURFACE7 lpdds); // error functions int Open_Error_File(char *filename, FILE *fp_override=NULL); int Close_Error_File(void); int Write_Error(char *string, ...); // 2d 8-bit, 16-bit triangle rendering void Draw_Top_Tri(int x1,int y1,int x2,int y2, int x3,int y3, int color,UCHAR *dest_buffer, int mempitch); void Draw_Bottom_Tri(int x1,int y1, int x2,int y2, int x3,int y3, int color,UCHAR *dest_buffer, int mempitch); void Draw_Top_Tri16(int x1,int y1,int x2,int y2, int x3,int y3, int color,UCHAR *dest_buffer, int mempitch); void Draw_Bottom_Tri16(int x1,int y1, int x2,int y2, int x3,int y3, int color,UCHAR *dest_buffer, int mempitch); void Draw_Top_TriFP(int x1,int y1,int x2,int y2, int x3,int y3, int color,UCHAR *dest_buffer, int mempitch); void Draw_Bottom_TriFP(int x1,int y1, int x2,int y2, int x3,int y3, int color,UCHAR *dest_buffer, int mempitch); void Draw_Triangle_2D(int x1,int y1,int x2,int y2,int x3,int y3, int color,UCHAR *dest_buffer, int mempitch); void Draw_Triangle_2D16(int x1,int y1,int x2,int y2,int x3,int y3, int color,UCHAR *dest_buffer, int mempitch); void Draw_TriangleFP_2D(int x1,int y1,int x2,int y2,int x3,int y3, int color,UCHAR *dest_buffer, int mempitch); inline void Draw_QuadFP_2D(int x0,int y0,int x1,int y1, int x2,int y2,int x3, int y3, int color,UCHAR *dest_buffer, int mempitch); // general 2D 8-bit, 16-bit polygon rendering and transforming functions void Draw_Filled_Polygon2D(POLYGON2D_PTR poly, UCHAR *vbuffer, int mempitch); void Draw_Filled_Polygon2D16(POLYGON2D_PTR poly, UCHAR *vbuffer, int mempitch); int Translate_Polygon2D(POLYGON2D_PTR poly, int dx, int dy); int Rotate_Polygon2D(POLYGON2D_PTR poly, int theta); int Scale_Polygon2D(POLYGON2D_PTR poly, float sx, float sy); void Build_Sin_Cos_Tables(void); int Translate_Polygon2D_Mat(POLYGON2D_PTR poly, int dx, int dy); int Rotate_Polygon2D_Mat(POLYGON2D_PTR poly, int theta); int Scale_Polygon2D_Mat(POLYGON2D_PTR poly, float sx, float sy); int Draw_Polygon2D(POLYGON2D_PTR poly, UCHAR *vbuffer, int lpitch); int Draw_Polygon2D16(POLYGON2D_PTR poly, UCHAR *vbuffer, int lpitch); // math functions int Fast_Distance_2D(int x, int y); float Fast_Distance_3D(float x, float y, float z); // collision detection functions int Find_Bounding_Box_Poly2D(POLYGON2D_PTR poly, float &min_x, float &max_x, float &min_y, float &max_y); int Mat_Mul_1X2_3X2(MATRIX1X2_PTR ma, MATRIX3X2_PTR mb, MATRIX1X2_PTR mprod); int Mat_Mul_1X3_3X3(MATRIX1X3_PTR ma, MATRIX3X3_PTR mb, MATRIX1X3_PTR mprod); int Mat_Mul_3X3(MATRIX3X3_PTR ma, MATRIX3X3_PTR mb, MATRIX3X3_PTR mprod); inline int Mat_Init_3X2(MATRIX3X2_PTR ma, float m00, float m01, float m10, float m11, float m20, float m21); // memory manipulation functions inline void Mem_Set_WORD(void *dest, USHORT data, int count); inline void Mem_Set_QUAD(void *dest, UINT data, int count);
You'll notice that all the calls to any rasterization functions that take pointers to the frame buffer always take a UCHAR*. Additionally, all memory pitch parameters are always in terms of bytes. Remember, this was our convention. Also note that about 90% of the entire library supports 16-bit graphics, but some things only work for 8-bit color. Finally, there's a lot of 2D polygon functions in there. These are all from the first Tricks, and although we could just use them for this book, we are going to write all of them again from scratch. This is because they are part of the rasterization process in 3D graphics, and I'm going to have to think up new names for them!
Global Domination
You know that I like globals because they are so fast. Moreover, they are appropriate for a lot of system-level variables (which any game engine has a lot of), so here are the globals for the engineagain, I know this is hard to follow, but just try and get a feel for them. I have commented them to help out.
FILE *fp_error; // general error file char error_filename[80]; // error file name // notice that interface 7.0 is used on a number of interfaces LPDIRECTDRAW7 lpdd; // dd object LPDIRECTDRAWSURFACE7 lpddsprimary; // dd primary surface LPDIRECTDRAWSURFACE7 lpddsback; // dd back surface LPDIRECTDRAWPALETTE lpddpal; // a pointer to the created dd palette LPDIRECTDRAWCLIPPER lpddclipper; // dd clipper for back surface LPDIRECTDRAWCLIPPER lpddclipperwin; // dd clipper for window PALETTEENTRY palette[256]; // color palette PALETTEENTRY save_palette[256]; // used to save palettes DDSURFACEDESC2 ddsd; // a direct draw surface description struct DDBLTFX ddbltfx; // used to fill DDSCAPS2 ddscaps; // a direct draw surface capabilities struct HRESULT ddrval; // result back from dd calls UCHAR *primary_buffer; // primary video buffer UCHAR *back_buffer; // secondary back buffer int primary_lpitch; // memory line pitch int back_lpitch; // memory line pitch BITMAP_FILE bitmap8bit; // a 8 bit bitmap file BITMAP_FILE bitmap16bit; // a 16 bit bitmap file BITMAP_FILE bitmap24bit; // a 24 bit bitmap file DWORD start_clock_count; // used for timing int windowed_mode; // tracks if dd is windowed or not // these defined the general clipping rectangle for software clipping int min_clip_x, // clipping rectangle max_clip_x, min_clip_y, max_clip_y; // these are overwritten globally by DD_Init() int screen_width, // width of screen screen_height, // height of screen screen_bpp, // bits per pixel screen_windowed; // is this a windowed app? int dd_pixel_format; // default pixel format int window_client_x0; // used to track the starting (x,y) client area for int window_client_y0; // for windowed mode directdraw operations // storage for our lookup tables float cos_look[360]; float sin_look[360]; // function ptr to RGB16 builder that builds 5.5.5 or 5.6.5 data // depending on mode USHORT (*RGB16Bit)(int r, int g, int b);
Make note of all the global names of the DirectX interface. Also, take a look at the various clipping and screen size globals.
The DirectDraw Interface
Now that you've seen all the data support, let's take a look at all the DirectDraw support functions. The DirectDraw system from the first Tricks (with a little upgrading) has the following features:
Double-buffered, with both a primary and secondary (offscreen frame buffer)
8-bit color with palette
16-bit color with auto pixel format detection
Windowed support for both 8- and 16-bit color modes
2D polygon and raster bitmap clipping
Access to both the primary and secondary buffers
Page flipping and buffer copying for windowed displays
NOTE
Note the last feature. DirectX does not allow Windowed applications to be double buffered, or in DirectX speak, to be complex surfaces. Therefore, when a windowed display is requested, instead of creating a standard full-screen complex surface with a primary buffer and a back buffer, only a primary buffer (which is the entire screen), and an offscreen plain buffer that's the size of the client area of the window are created. Then when a flip is requested, the logic manually copies the offscreen back buffer to the window's client area. Of course, this is all transparent to you.
Now let's take a look at each function, along with an example.
Function Prototype:
int DDraw_Init(int width, // width of display int height, // height of display int bpp, // bits per pixel int windowed=0); // 0 for full screen, 1 for windowed
Purpose:
DDraw_Init() starts up and initializes DirectDraw. You can send any resolution and color depth. Also, if you want a windowed display, send a 1 for the last parameter; otherwise it defaults to full-screen. Returns TRUE if successful.
Examples:
// put the system into full screen 800x600 with 256 colors DDraw_Init(800,600,8); // put the system into windowed mode screen 400x400 with 16-bit color // note that the desktop must be in 16-bit color DDraw_Init(400,400,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=0, // control flags USHORT color_key_value=0); // color key value
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 are the standard DirectDraw DDSCAP* flags, such as DDSCAPS_SYSTEMMEMORY and DDSCAPS_VIDEOMEMORY for system memory and VRAM, respectively. The function has internal logic to create either an 8- or 16-bit surface based on the previously set graphics mode. Additionally, the last parameter controls the value of the color key, which defaults to (int)0. In 8-bit color mode, this equates to index 0; in 16-bit color mode, this equates to RGB 0.0.0. If you want another value to be used as the transparent color key, send the value to override the default. If the function is successful, it returns a pointer to the new surface; otherwise it returns NULL.
Examples:
// let's create a 64x64 surface in VRAM LPDIRECTDRAWSURFACE7 image = DDraw_Create_Surface(64,64, DDSCAPS_VIDEOMEMORY); // let's create a 128x128 surface in off screen memory with a color key of 16 // assuming this is a 256 color mode LPDIRECTDRAWSURFACE7 image = DDraw_Create_Surface(128,128, DDSCAPS_OFFSCREENPLAIN ,16);
Function Prototype:
int DDraw_Flip(void);
Purpose:
DDraw_Flip() simply flips the primary surface with the secondary surface in full-screen mode; in windowed mode, it copies the virtual secondary or back buffer to the client area of the windowed display. The call waits until the flip can take place, so it might 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, 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 to fill // either 8-bit index or 16-bit RGB RECT *client); // client area, null for whole surface
Purpose:
DDraw_Fill_Surface() is used to fill a surface with a color. The color must be in the color depth format of the surface. For example, a single byte in 256-color mode or an RGB descriptor in high-color modes. Also, if you want to fill a subregion of the entire surface, you can send a RECT* that defines it; otherwise, the entire surface is filled. 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 are most likely invalid and should not be used. Returns the non-NULL address of the surface memory if successful, NULL otherwise.
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 Prototype(s):
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, because this is a double-buffered system, but the capability to lock the primary surface is there if you need it. 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 are free to manipulate the surface memory as you want; however, the blitter will be blocked and all hardware acceleration is inhibited during a lock. 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 yourselfthey are used to track state changes in the locking functions. Changing them yourself might make the engine go crazy.
Example:
// let lock the primary surface and write a pixel to the // upper left hand corner assuming an 8-bit mode 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 is no effect. Returns TRUE if successful.
Example:
// unlock the secondary back buffer DDraw_Unlock_Back_Surface();
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 it's great to build up demos. The functions work and do the job. However, there are better ways to do all of this stuff, but that's why you're glued to the book, right?
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 // 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); // 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 UCHAR *dest_buffer, // destination buffer int mempitch); // memory pitch
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, because the function uses software and not the blitter to draw lines. Draw_TriangleFP_2D() does the exact same thing, but uses fixed-point math internally and is slightly faster, but with slightly less accuracy. All functions return nothing.
Examples:
// 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); // do the same with 16 bit version // with RGB color pure red Draw_Triangle_2D16(100,10,150,50,50,60, RGB16BIT565(31,0,0), // assume 5.6.5 format 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 in 8-bit mode only. Notice there isn't a 16-bit version. 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 in either 8- or 16-bit color mode. The function simply takes the polygon to render, a pointer to the video buffer along with pitch, and that's it! Note that the function renders relative to the polys (x0,y0), so make sure these are initialized. Returns nothing.
Examples:
// draw a polygon in the primary buffer // in 8-bit mode Draw_Filled_Polygon2D(&poly, primary_buffer, primary_lpitch); // draw a polygon in the primary buffer // in 16-bit mode Draw_Filled_Polygon2D16(&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 that 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 counter-clockwise fashion about its origin. The angle must be an integer from 0359. 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 everythingkind of a potpourri of graphics primitives. Nothing you haven't seen, at least I don't think so. Also, the 16-bit support is there, but not completely orthogonal to the 8-bit support.
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 (8- or 16-bit). Returns TRUE if successful.
Examples:
// draw a line in the back buffer from (10,10) to (100,200) //assume an 8-bit system Draw_Clip_Line(10,10,100,200, 5, // color index 5 back_buffer, back_lpitch); // draw a line in the back buffer from (10,10) to (100,200) //assume an 16-bit system Draw_Clip_Line16(10,10,100,200, RGB16BIT565(0,31,0), // RGB green 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 mathematically, so bit depth is irrelevant. 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 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) // 8-bit mode Draw_Line(10,10,100,200, 5, // color 5 back_buffer, back_lpitch); // draw a line in the back buffer from (10,10) to (100,200) // 16-bit mode Draw_Line16(10,10,100,200, RGB16BIT(31,31,31), // RGB white 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 sent display surface memory buffer. 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. If speed isn't your concern, the function does the jobat least it's inline! Returns TRUE if successful.
Example:
// draw pixel with color index 100 in the center of the 640x480x8 screen Draw_Pixel(320,240, 100, back_buffer, back_lpitch); // draw blue pixel in the center of the 640x480x16 screen Draw_Pixel16(320,240, RGB16BIT(0,0,31), 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, // 8-bit color index or // 16-bit RGB word LPDIRECTDRAWSURFACE7 lpdds); // dd surface
Purpose:
Draw_Rectangle() draws a rectangle on the sent DirectDraw surface. Note that the surface must be unlocked for the call to work. Moreover, the function uses the blitter, so it's very fast, and it works in either 8- or 16-bit color mode. 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 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 compared to the general line drawing function. Returns nothing.
Example:
// draw a fast line in 8-bit mode from 10,100 to 100,100 with color index 20 HLine(10,100,100, 20, back_buffer, back_lpitch); // draw a fast line in 16-bit mode from 10,100 to 100,100 with blue HLine(10,100,100, RGB16Bit(0,0,255), 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 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 is 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 8-bit line from 320,0 to 320,479 with color index 54 VLine(0,479,320,54, primary_buffer, primary_lpitch); // draw a 16-bit line from 320,0 to 320,479 with green VLine(0,479,320,RGB16Bit(0,255,0), primary_buffer, primary_lpitch);
Function Prototype:
void Screen_Transitions(int effect, // screen transition UCHAR *vbuffer,// video buffer int lpitch); // memory pitch // 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
Purpose:
Screen_Transition() performs various in-memory screen transitions as listed in the preceding header information. Note that the transformations are destructive, so please save the image and/or palette if you need them after the transition. Note that this only works for 8-bit color modes. Returns nothing.
Example:
// fade the primary display screen to black Screen_Transition(SCREEN_DARKNESS, 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 in either 8- or 16-bit color modes. The function is overloaded to take both a COLORREF in the form of the RGB() macro or a 256 (8-bit) color index. 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); Draw_Text_GDI("This is a test",100,50, RGB16Bit(100,100,0),lpddsprimary);
Math and Error Functions
We will rewrite the math library quite a bit, but we might as well take a look at what there is to offer, because I might use it in some of the early demos. Additionally, there is some error support that you can use to write errors or diagnostic text as the engine runs. This is helpful, because debugging DirectX applications is so hard because of their resource sharing, or lack thereof <GRIN>.
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% error truncated to an integer. It uses a Taylor series approximation, if you're interested.
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% margin of 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 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 is 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("\nx-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-, 32-bit bitmaps as well as to extract images from them and create simple BITMAP_IMAGE objects (which are not DirectDraw surfaces). In addition, functionality exists to draw these images in 8- and 16-bit modes only, but there is no clipping support. Hence, you can modify the source yourself if you need clipping support, or step up to the BOB (Blitter Object), which is described at the end of the section. We will probably use these functions (or variants) to help load texture and light maps for our 3D rendering, so it's a good idea to take a close look at them.
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 format bitmap file off disk into the sent BITMAP_FILE structure, where you can manipulate it as you will. 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 have 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 of bitmap int bpp=8); // the bit depth of bitmap
Purpose:
Create_Bitmap() creates an 8- or 16-bit system memory bitmap at the given position with the given size. The bitmap is initially blank and stored in the BITMAP_IMAGE image. The bitmap is not a DirectDraw surface, so there is no acceleration or clipping available. The function defaults to 8-bit bitmaps unless you override the bpp parameter with 16 for 16-bit. Returns TRUE if successful.
NOTE
There is 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.
Examples:
// let's create a 64x64 8-bit bitmap image at (0,0) BITMAP_IMAGE ship; Create_Bitmap(&ship, 0,0, 64,64); // and now a 16-bit 32x32 image at (100,100) BITMAP_IMAGE ship2; // note the addition of 16 to override the default 8-bit depth Create_Bitmap(&ship, 0,0, 32,32,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 (either 8- or 16-bit). You should call this function on your object when you're all done working with itusually during the shutdown of the game, or if it's 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 areathis is how you actually get objects and image bits into a BITMAP_IMAGE. Of course, the bitmap file and the bitmap object must be the same bit depth (8- or 16-bit), and you have to use the appropriate version of the function.
To use the function, you first must have a BITMAP_FILE loaded and have created 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-thick border between each cell, which usually range from 8x8, 16x16, 32x32, 64x64, and so on. Take a look at TEMPLATE*.BMP on the CD from this chapterit contains a number of templates. Cell numbers range from left to right, top to bottom, and 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.
Example:
8-bit example // assume the source bitmap .BMP file is 640x480x8 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), 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); // now scan out the data Load_Image_Bitmap(&ship, &bitmap_file, 2,1, BITMAP_EXTRACT_MODE_CELL); // the exact same example in 16-bit mode // assume the source bitmap .BMP file is 640x480x16 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), 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 ship2; Create_Bitmap(&ship2, 0,0, 32,32,16); ************* 0,0,31,31,16? it's ok:al // now scan out the data Load_Image_Bitmap16(&ship2, &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, we have to figure out the coordinatesremember, there's a 1 pixel-thick partitioning wall on each side of the image.
// 8-bit example Load_Image_Bitmap(&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 in either 8- or 16-bit mode with or without transparency. If transparent is 1, transparency is enabled and any pixel that is color index 0 or RGB word 0.0.0 in 16-bit mode will not be copied. Function returns TRUE if successful.
Example:
// draw our little ship on the back buffer // in 8-bit mode Draw_Bitmap( &ship, back_buffer, back_lpitch, 1); // and now in 16-bit mode Draw_Bitmap16( &ship2, 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, but you might want to use it to flip an image yourself (8- or 16-bit). The function does an in-memory flip, and actually inverts the bitmap line by line, and your original sent data will be inverted, so watch out! Returns TRUE if successful.
Example:
// for fun flip the image bits of our little ship // assuming 1 byte per pixel in 8 bit mode Flip_Bitmap(ship->buffer, ship->width, ship->height); // and here's the call for 16-bit mode, same call // but we need to double the width since there are // 2 bytes per pixel Flip_Bitmap(ship2->buffer, 2*ship2->width, ship2->height);
Function Prototype:
int Copy_Bitmap(BITMAP_IMAGE_PTR dest_bitmap, int dest_x, int dest_y, BITMAP_IMAGE_PTR source_bitmap, int source_x, int source_y, int width, int height);
Purpose:
Copy_Bitmap() copies a rectangular region from one bitmap to another. The source and destination can be the same bitmap; however, the regions must not overlapotherwise, undefined results might occur. Returns TRUE if successful.
Example:
// load in the .BMP file into memory BITMAP_FILE bitmap_file; Load_Bitmap_File(&bitmap_file,"playfield.bmp"); // initialize a bitmap to hold playfield BITMAP_IMAGE playfield; Create_Bitmap(&playfield, 0,0, 400,400,16); // now scan out the data Load_Image_Bitmap16(&playfield, &bitmap_file, 0,0, BITMAP_EXTRACT_MODE_ABSOLUTE); // now copy the top portion of the same bitmap to the bottom // really chunky scrolling! Copy_Bitmap(&playfield, 0,200, &playfield, 0,0, 200,200);
Function Prototype:
int Scroll_Bitmap(BITMAP_IMAGE_PTR image, int dx, int dy=0);
Purpose:
Scroll_Bitmap() scrolls the sent bitmap horizontally and/or vertically an amount dx,dy in the x- and y-axes, respectively. Positive numbers scroll right and down; negative numbers scroll left and up. Returns TRUE if successful.
Example:
// scroll the playfield 2 pixels to the right Scroll_Bitmap(&playfield, 2,0);
8-Bit Palette Functions
The following functions make up the 8-bit 256 color palette interface. These functions are only relevant if you have the display set for a 256-color mode (8-bit color). However, in windowed display modes, the first and last 10 colors are used by Windows, and you can't alter them. (Of course, in full-screen mode you can.)
Additionally, the basic data structure used to hold a color is a native Win32 structure called PALETTEENTRY:
typedef struct tagPALETTEENTRY { // pe BYTE peRed; // red channel 8-bits BYTE peGreen; // green channel 8-bits BYTE peBlue; // blue channel 8-bits BYTE peFlags; // flags control, PC_EXPLICIT for windows colors // PC_NOCOLLAPSE for all others } PALETTEENTRY;
With that in mind, let's look at the palette functions.
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 that this function is slow, so if you need to update the entire palette, use Set_Palette(). Returns TRUE if successful, 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, and it doesn't disturb the hardware at all. However, if you make changes to the system palette with the use of Set_Palette_Entry() or Set_Palette(), the shadow palette will not be updated and the data retrieved might not be valid. Returns TRUE if successful, 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 paletteso watch out! Returns TRUE if successful, 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 file name along with storage for all 256 entries and the palette is loaded off 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, FALSE otherwise.
Example:
// load the previously saved palette Load_Palette_From_File("/palettes/custom1.pal",&my_palette);
NOTE
When you create a 256-color display mode via a call to DDRAW_INIT(), the function will load a standard palette that has almost optimal color space coverage. The name of the palette file is PALDATA2.DAT. It's nothing more than an ASCII file with 256 line entries, each with a red, green, and blue value in the range of 0..255.
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, FALSE otherwise.
Example:
// lets load the palette into the hardware Set_Palette(&my_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. It manipulates the color palette hardware directly. Returns TRUE if successful, 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, but please refer to the previous chapter for a more in-depth description.
Example:
N/A
Utility Functions
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 some 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 call. However, the function will NOT return until the time difference has elapsed.
Example:
// wait 30 milliseconds Start_Clock(); // code... Wait_Clock(30);
NOTE
Later in the book, we will probably upgrade to using the high performance timers that Windows supports for better resolution.
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 hand corner coordinates of each rectangle, along with its width and height. Returns TRUE if there is an overlap, FALSE otherwise.
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
Purpose:
Color_Scan() is another collision detection algorithm that scans a rectangle for a single 8-bit value or sequence of values in some continuous range. You can use it to determine whether a color index is present within some area. Of course, it only works with 8-bit images, but the source is easily extensible to 16-bit or higher modes. Returns TRUE if the color(s) is found.
Example:
// scan for colors in range from 122-124 inclusive Color_Scan(10,10, 50, 50, 122,124, back_buffer, back_lpitch);
The BOB (Blitter Object) Engine
Although with a bit of programming you can get the BITMAP_IMAGE type to do what you want, it is lacking in a serious wayit doesn't use DirectDraw surfaces, and hence there is no support for acceleration. Therefore, I have created a type called a BOB, or blitter object, that is very similar to a sprite. A sprite (for those of you that have been in a game programming cave) is nothing more than an object you can move around the screen that usually doesn't disturb the background. In our case, this isn't true; therefore, I called my animation object a BOB rather than a sprite, so there!
We will use the BOB engine very little in this book, but I still wanted you to see it, because it's a good example of using DirectDraw surfaces and full 2D acceleration. So let's talk briefly about what a BOB is. First here's the data structure for a BOB:
// 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 bpp; // bits per pixel needed for 8/16 bit support 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 // the bitmap images DD surfaces LPDIRECTDRAWSURFACE7 images[MAX_BOB_FRAMES]; } BOB, *BOB_PTR;
A BOB is basically a graphical object represented by one or more DirectDraw surfaces (up to 64 as currently #defined). You can move a BOB, draw a BOB, animate a BOB, and set it in motion. BOBs consider the current DirectDraw clipper, so they are clipped as well as acceleratedwhich is a good thing! Figure 3.10 shows a BOB and it relationship to its animation frames.
Figure 3.10 The BOB animation system.
Also, the BOB engine supports 8- or 16-bit images and animation sequences, so you can load in a set of frames and an animation sequence, and the sequence will play feeding from the frames. This is a very cool feature. Additionally, BOBs work with both 8- and 16-bit color, but most of the functions have internal logic that know what bit depth the system is in, so you don't have to worry it. The only functions that you need to explicitly call a different version for 16-bit mode are Load_Frame_BOB*() (when you load) and Draw_BOB*() (when you draw a BOB), but we will get to that, along with reviewing examples of both 8- and 16-bit demos, at the end of the chapter.
Finally, all the BOB functions return TRUE if successful and FALSE otherwise. So let's take a look at them all....
Function Prototype:
int Create_BOB(BOB_PTR bob, // ptr to bob to create int x, int y, // initial position of bob int width, int height, // size of bob int num_frames, // total number of frames for bob int attr, // attributes of bob int mem_flags=0, // surface memory flags, 0 is VRAM USHORT color_key_value=0, // color key value is interpreted as // 8-bit index, or 16-bit RGB based // on the bpp setting below int bpp=8); // bits per pixel, default is 8
Purpose:
Create_BOB() creates a single BOB object and sets it all up. The function assigns all the internal variables, in addition to creating a separate DirectDraw surface for each frame. Most of the parameters are self-explanatorythe only value that needs a little explanation is the attribute variable attr. Take a look at Table 3.1 to see a better description of each of the attributes you can logically OR together and send in this field.
Table 3.1 Valid BOB Attributes
Value |
Meaning |
BOB_ATTR_SINGLE_FRAME |
Create a BOB with a single frame. |
BOB_ATTR_MULTI_FRAME |
Create a BOB with multiple frames, but the animation of the BOB will be a linear sequence through the frames 0..n. |
BOB_ATTR_MULTI_ANIM |
Create a multiple frame BOB that supports animation sequences. |
BOB_ATTR_ANIM_ONE_SHOT |
If this is set then when an animation sequence plays, the animation sequence will only play once and then stop. At this point, the internal variable anim_state will be set. To play the animation again, reset this variable. |
BOB_ATTR_BOUNCE |
This flag tells the BOB to bounce off the screen boundaries like a ball. This only works if you use Move_BOB(). |
BOB_ATTR_WRAPAROUND |
This flag tells the BOB to wrap around to the other side of the screen as it moves. This only works if you use Move_BOB(). |
Example:
Here are some examples creating BOBs. First, a single frame 8-bit BOB at (50,100) with a size of 96x64:
BOB car; // a car bob // create the bob if (!Create_BOB(&car, 50,100, // address of bob and initial position 96,64, // size of bob 1, // number of frames BOB_ATTR_SINGLE_FRAME, // attribute 0, // memory flags 0, // color key, for 8-bit this is the index 8)) // bits per pixel, 8 for 256 color modes { /* error */ }
And here's a multiple frame 16-bit BOB with 8 frames and a size of 32x32:
BOB ship; // a space ship bob // create the bob if (!Create_BOB(&ship, 0,0, // address of bob and initial position 32,32, // size of bob 8, // number of frames BOB_ATTR_MULTI_FRAME, // attribute 0, // memory flags 0, // color key, for 16-bit mode this is the RGB word 16)) // bits per pixel, 16 for 16 bit modes { /* error */ }
Finally, a multiple frame 8-bit BOB that supports animation sequences:
BOB greeny; // a little green man bob // create the bob if (!Create_BOB(&greeny, 0,0, 32,32,32,BOB_ATTR_MULTI_ANIM,0,0,8)) { /* error */ }
TIP
Note that the last three parameters to Create_BOB() all have default values. Therefore, you don't need to override them if you don't want to. If 0,0,8 is okay with you, you don't have to type them.
Function Prototype:
int Destroy_BOB(BOB_PTR bob); // ptr to bob to destroy
Purpose:
Destroy_BOB() destroys a previously created BOB. It doesn't matter if it's 8- or 16-bit.
Example:
// destroy the BOB above, you would do this Destroy_BOB(&greeny);
Function Prototype:
int Draw_BOB(BOB_PTR bob, // ptr of bob to draw LPDIRECTDRAWSURFACE7 dest); // dest surface to draw on // and the 16-bit version int Draw_BOB16(BOB_PTR bob, // ptr of bob to draw LPDIRECTDRAWSURFACE7 dest); // dest surface to draw on
Purpose:
Draw_BOB*() is a very powerful function. It draws the sent BOB on the DirectDraw surface you send it. The BOB is drawn in its current position and current frame (as defined by its animation parameters). Also, make sure to use the correct version of the function depending on whether your BOB is 8- or 16-bit; otherwise, you might get half a BOB!
CAUTION
For this function to work, the destination surface must NOT be locked.
Example:
// this is how you would position a multiframe BOB at // (50,50) and draw the first frame of it on the back // surface: BOB ship; // a space ship bob // create the bob, 8-bit default if (!Create_BOB(&ship, 0,0, 32,32,8,BOB_ATTR_MULTI_FRAME,0)) // load the bob images in..we'll get to this in a bit // set the position and frame of bob ship.x = 50; ship.y = 50; ship.curr_frame = 0; // this contains the frame to draw // draw bob Draw_BOB(&ship, lpddsback);
Function Prototype:
int Draw_Scaled_BOB(BOB_PTR bob, // ptr of bob to draw int swidth, int sheight, // new width and height of bob LPDIRECTDRAWSURFACE7 dest); // dest surface to draw on // and the 16-bit version int Draw_Scaled_BOB16(BOB_PTR bob, // ptr of bob to draw int swidth, int sheight, // new width and height of bob LPDIRECTDRAWSURFACE7 dest); // dest surface to draw on
Purpose:
Draw_Scaled_BOB*() works exactly like Draw_BOB(), except you can send any width and height to draw the BOB and the BOB will be scaled appropriately. This is very cool, and if you have acceleration, it's a great way to scale a BOB to make it look 3D! In fact, we will use a technique like this when we create billboards for our 3D applications.
NOTE
Billboards are simply 2D images that are drawn in 3D, but are always perpendicular to the view direction. For example, in Doom, all the creatures are billboard sprites, while the world is polygon-based.
Example:
// an example of drawing the ship 128x128 even though // it was created as only 32x32 pixels // 8-bit version Draw_Scaled_BOB(&ship, 128,128,lpddsback);
Function Prototype:
int Load_Frame_BOB( BOB_PTR bob, // ptr of bob to load frame into BITMAP_FILE_PTR bitmap,// ptr of bitmap file to scan data int frame, // frame number to place image into 0,1,2... int cx,int cy, // cell pos or abs pos to scan from int mode); // scan mode, same as Load_Frame_Bitmap() // and the 16-bit version int Load_Frame_BOB16( BOB_PTR bob, // ptr of bob to load frame into BITMAP_FILE_PTR bitmap,// ptr of bitmap file to scan data int frame, // frame number to place image into 0,1,2... int cx,int cy, // cell pos or abs pos to scan from int mode); // scan mode, same as Load_Frame_Bitmap()
Purpose:
The Load_Frame_BOB*() function works identically to the Load_Frame_Bitmap() function, so refer to it for details. The only addition is the control of the frame to load via frame. If you create a BOB that has four frames, you will load the frames one by one. Additionally, you must call the correct version of the function (8- or 16-bit) based on the bit depth of the data and screen.
Example:
// here's an example of loading 4 frames into a 16-bit BOB from a // bitmap file in cell mode BOB ship; // the bob // loads frames 0,1,2,3 from cell position (0,0), (1,0), // (2,0), (3,0) // from bitmap16bit bitmap file, assume it has been loaded for (int index=0; index<4; index++) Load_Frame_BOB16(&ship,&bitmap16bit, index, index,0, BITMAP_EXTRACT_MODE_CELL );
NOTE
The next set of BOB manipulation functions work on the data structure elements of the BOB, so they work identically on both 8- and 16-bit BOBs.
Function Prototype:
int Load_Animation_BOB( BOB_PTR bob, // bob to load animation into int anim_index, // which animation to load 0..15 int num_frames, // number of frames of animation int *sequence); // ptr to array holding sequence
Purpose:
Load_Animation() takes a little explaining. The function is used to load one of 16 arrays internal to the BOB that contain animation sequences. Each sequence contains an array of indices or frame numbers to display in sequence. This is shown at the bottom of Figure 3.10.
Example:
You might have a BOB that has eight frames 0,1,...7, but you might have four animations defined as follows:
int anim_walk[] = {0,1,2,1,0}; int anim_fire[] = {5,6,0}; int anim_die[] = {3,4}; int anim_sleep[] = {0,0,7,0,0};
Then to load the animations into the BOB, you would do this:
// create a mutli animation bob // create the bob if (!Create_BOB(&alien, 0,0, 32,32,8,BOB_ATTR_MULTI_ANIM,0)) { /* error */ } // load the bob frames in... // load walk into animation 0 Load_Animation_BOB(&alien, 0,5,anim_walk); // load fire into animation 1 Load_Animation_BOB(&alien, 1,3,anim_fire); // load die into animation 2 Load_Animation_BOB(&alien, 2,2,anim_die); // load sleep into animation 3 Load_Animation_BOB(&alien, 3,5,anim_sleep);
After loading the animations, you can set the active animation and play them with functions you'll see in a minute.
Function Prototype:
int Set_Pos_BOB(BOB_PTR bob, // ptr to bob to set position int x, int y); // new position of bob
Purpose:
Set_Pos_BOB() is a simple way to set the position of the BOB. This does nothing more than assign the internal (x,y) variables, but it's nice to have a function.
Example:
// set the position of the alien BOB above Set_Pos_BOB(&alien, player_x, player_y);
Function Prototype:
int Set_Vel_BOB(BOB_PTR bob, // ptr to bob to set velocity int xv, int yv); // new x,y velocity
Purpose:
Each BOB has an internal velocity contained in (xv,yv). Set_Vel_BOB() simply assigns these values the new values sent in the function. The velocity values in the BOB won't do anything unless you use the function Move_BOB() to move your BOBs. However, even if you don't, you can use xv,yv to track the velocity of the BOB yourself.
Example:
// make the BOB move in a straight horizontal line Set_Vel_BOB(&alien, 10,0);
Function Prototype:
int Set_Anim_Speed_BOB(BOB_PTR bob, // ptr to bob int speed); // speed of animation
Purpose:
Set_Anim_Speed() sets the internal animation rate for a BOB anim_count_max. The higher this number is, the slower the animation; the lower the number (0 is the lowest), the faster the animation. However, this function only matters if you use the internal BOB animation function Animate_BOB(). And of course, you must have created a BOB that has multiple frames.
Example:
// set the rate to change frames every 30 frames Set_Anim_Speed_BOB(&alien, 30);
Function Prototype:
int Set_Animation_BOB( BOB_PTR bob, // ptr of bob to set animation int anim_index); // index of animation to set
Purpose:
Set_Animation_BOB() sets the current animation that will be played by the BOB. In the earlier example of Load_Animation_BOB(), we created four animations.
Example:
// make animation sequence number 2 active Set_Animation_BOB(&alien, 2);
NOTE
This also resets the BOB animation to the first frame in the sequence.
Function Prototype:
int Animate_BOB(BOB_PTR bob); // ptr to bob to animate
Purpose:
Animate_BOB() animates a BOB for you. Normally, you would call this function once a frame to update the animation of the BOB.
Example:
// erase everything... // move everything... // animate everything Animate_BOB(&alien);
Function Prototype:
int Move_BOB(BOB_PTR bob); // ptr of bob to move
Purpose:
Move_BOB() moves the BOB a delta of xv,yv (set with the Set_Vel_BOB() function), then, depending on the attributes, will either bounce the BOB off the walls, wrap it around, or do nothing. Similarly to the Animate_BOB() function, you would place this call once in the main loop right after (or before) Animate_BOB().
Example:
// animate bob Animate_BOB(&alien); // move it Move_BOB(&alien);
Function Prototype:
int Hide_BOB(BOB_PTR bob); // ptr to bob to hide
Purpose:
Hide_BOB() simply sets the BOB's internal visibility flag to 0, after which Draw_BOB() won't display the BOB anymore.
Example:
// hide the bob Hide_BOB(&alien);
Function Prototype:
int Show_BOB(BOB_PTR bob); // ptr to bob to show
Purpose:
Show_BOB() sets the visibility flag to 1 on a BOB, so it will be drawn (it undoes a Hide_BOB() call). Here's an example of hiding and showing a BOB because you are displaying a GDI object or something, and don't want the BOB to occlude it:
Example:
// Hide_BOB(&alien); // make calls to Draw_BOB and GDI etc. Show_BOB(&alien);
Function Prototype:
int Collision_BOBS(BOB_PTR bob1, // ptr to first bob BOB_PTR bob2); // ptr to second bob
Purpose:
Collision_BOBS() detects whether the bounding rectangles of two BOBs are overlapping. This can be used for collision detection in a game to see whether a player BOB hits a missile BOB or whatever.
Example:
// check if a missile BOB hit a player BOB: if (Collision_BOBS(&missile, &player)) { /* make explosion sound */ }
That's about it for the graphics module of the T3DLIB game library (T3DLIB1.CPP|H). Now let's move on to the sound module.