View
221
Download
0
Embed Size (px)
Citation preview
2
Page Flipping
1. Clear back buffer
2. Render scene to back buffer surface
3. Flip primary surface with back buffer surface
4. Lock to frame rate (e.g. 30 fps)
5. Repeat step 1
3
Creating Primary Surface with Back Buffer
• Add DDSD_BACKBUFFERCOUNT to the dwFlags field so DirectDraw can check number surfaces at creation
• Add DFDSCAPS_COMPLEX and DDSCAPS_FLIP to capabilities word of DDSURFACE2 contained in ddsCaps.dwCaps filed
• Create primary surface as usual and request attached buffer using GetAttachedSurface( )
5
Inside Game_Init( )// enable valid fields
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
// set the backbuffer count field for double buffering
ddsd.dwBackBufferCount = 1;
// request a complex, flippable
ddsd.ddsCaps.dwCaps =
DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;
// create the primary surface
if (FAILED(lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL)))
return(0);
// now query for attached surface from the primary surface
// this line is needed by the call
ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
// get the attached back buffer surface
if (FAILED(lpddsprimary->GetAttachedSurface(&ddsd.ddsCaps, &lpddsback)))
return(0);
6
Inside Game_Shutdown( )// first the paletteif (lpddpal){ lpddpal->Release(); lpddpal = NULL;} // end if // now the back buffer surfaceif (lpddsback){ lpddsback->Release(); lpddsback = NULL;} // end if // now the primary surfaceif (lpddsprimary){ lpddsprimary->Release(); lpddsprimary = NULL;} // end if// then the DirectDrawInterface
7
Inside Game_Main( )// draw the next frame into the back buffer, notice that we// must use the lpitch since it's a surface and may not be linear // plot 5000 random pixelsfor (int index=0; index < 5000; index++){ int x = rand()%SCREEN_WIDTH; int y = rand()%SCREEN_HEIGHT; UCHAR col = rand()%256; back_buffer[x+y*ddsd.lPitch] = col;} // end for index // unlock the back bufferif (FAILED(lpddsback->Unlock(NULL))) return(0); // perform the flip (both primary and back buffer must be unlockedwhile (FAILED(lpddsprimary->Flip(NULL, DDFLIP_WAIT)));
8
Using the Blitter
• The blitter can be used to paste bit images from off screen surfaces to the primary surface
• The function Blt( ) does blitting using DirectDraw clippers
• The function BltFast( ) does not do clipping and runs faster
9
Blitting to do Simple 8 Bit Fills
• Place the color index or RGB color you want to use to fill the surface in the dwFillColor field of a DDBLTFX struct
• Define a RECT in the area you want to fill on the destination surface
• Call Blt( ) from destination surface interface pointer using control falgs DDBT_COLORFILL | DDBLT_WAIT
10
Inside Game_Init( )// create IDirectDraw interface 7.0 object
// set cooperation to full screen
// set display mode
// clear ddsd and set size
DDRAW_INIT_STRUCT(ddsd);
// enable valid fields
ddsd.dwFlags = DDSD_CAPS;
// request a complex, flippable
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
// create the primary surface
11
Inside Game_Main( )
DDBLTFX ddbltfx; // the blitter fx structure
RECT dest_rect; // used to hold the destination RECT
// first initialize the DDBLTFX structure
DDRAW_INIT_STRUCT(ddbltfx);
// now set the color word info to the color we desire
// in this case, we are assuming an 8-bit mode, hence,
// well use a color index from 0-255
ddbltfx.dwFillColor =
_RGB16BIT565(rand()%256, rand()%256, rand()%256);
12
Inside Game_Main( )// now set up the RECT structure to fill the region from
// (x1,y1) to (x2,y2) on the destination surface
dest_rect.left = x1;
dest_rect.top = y1;
dest_rect.right = x2;
dest_rect.bottom = y2;
// make the blitter call
if (FAILED(lpddsprimary->Blt(&dest_rect, // pointer to dest RECT
NULL, // pointer to source surface
NULL, // pointer to source RECT
DDBLT_COLORFILL | DDBLT_WAIT,
// do color fill wait if you have to
&ddbltfx))) // pointer to DDBLTFX info
return(0);
13
Inside Game_Main( )
• Please note, in this example the call to Blt( ) has NULL pointers to both the surface and the source rectangle
• The reason for this is that the color fills are handled by the blitter hardware support (or emulation if needed)
14
Copying Bitmaps Between Surfaces
• When using the Blt( ) function you are sending both a source and destination rectangle to use in performing the blit
• Blitting when the source and destination surfaces are different is the basis for most sprite engines
• A sprite is a bitmap the seems to move on the screen
15
Game_Main( )RECT source_rect, // used to hold the destination RECT dest_rect; // used to hold the destination RECT
// now set up the RECT structure to fill the region from// (x1,y1) to (x2,y2) on the source surfacesource_rect.left = x1;source_rect.top = y1;source_rect.right = x2;source_rect.bottom = y2; // now set up the RECT structure to fill the region from// (x3,y3) to (x4,y4) on the destination surfacedest_rect.left = x3;dest_rect.top = y3;dest_rect.right = x4;dest_rect.bottom = y4;
16
Game_Main( )// make the blitter call
if (FAILED(lpddsprimary->Blt(&dest_rect, // pointer to dest RECT
lpddsback, // source surface
&source_rect,// pointer source RECT
DDBLT_WAIT, // control flags
NULL))) // pointer to DDBLTFX info
return(0);
17
Clipping
• Suppose you want to clip a pixel with coordinates (x,y) to a viewport from (x1,y1) to (x2,y2)
void Plot_Pixel_Clip8(int x, int y,
UCHAR color, UCHAR *video_buffer)
{
// test for coordinates in range
if (x >= x1 && x <<= x2 && y >= y && y <= y2)
video_buffer[x + y * 640] = color;
}
18
Clipping Bitmaps the Hardway
• Method 1 (image space clipping)– Clip each pixel of the bitmap individually as
each is generated (simple but slow)
• Method 2 (object space clipping)– Clip the bounding rectangle of the bitmap
to the viewport and then only draw the pixels that are in range (complex, but very fast)
19
Blit_Clipped( )
void Blit_Clipped(int x, int y, // position to draw bitmap
int width, int height, // size of bitmap in pixels
UCHAR *bitmap, // pointer to bitmap data
UCHAR *video_buffer, // pointer to video buffer surface
int mempitch) // video pitch per line
{
// this function blits and clips the image sent in bitmap to the
// destination surface pointed to by video_buffer
// the function assumes a 640x480x8 mode
// first do trivial rejections of bitmap, is it totally invisible?
if ((x >= SCREEN_WIDTH) || (y>= SCREEN_HEIGHT) ||
((x + width) <= 0) || ((y + height) <= 0))
return;
20
Blit_Clipped( )// clip source rectangle// pre-compute the bounding rect to make life easyint x1 = x;int y1 = y;int x2 = x1 + width - 1;int y2 = y1 + height -1; // upper left hand corner firstif (x1 < 0) x1 = 0;if (y1 < 0) y1 = 0; // now lower left hand cornerif (x2 >= SCREEN_WIDTH) x2 = SCREEN_WIDTH-1;if (y2 >= SCREEN_HEIGHT) y2 = SCREEN_HEIGHT-1;
21
Blit_Clipped( )// now we know to draw only the portions of the bitmap
// from (x1,y1) to (x2,y2)
// compute offsets into bitmap on x,y axes, we need this
// to compute starting point
// to rasterize from
int x_off = x1 - x;
int y_off = y1 - y;
// compute number of columns and rows to blit
int dx = x2 - x1 + 1;
int dy = y2 - y1 + 1;
// compute starting address in video_buffer
video_buffer += (x1 + y1*mempitch);
// compute starting address in bitmap to scan data from
bitmap += (x_off + y_off*width);
22
Blit_Clipped( ) // bitmap is pointing to the first pixel in bitmap that needs to // be blitted, and video_buffer is pointing to memory location on // the destination buffer to put it, so now enter rasterizer loop UCHAR pixel; // used to read/write pixels for (int index_y = 0; index_y < dy; index_y++) { // inner loop, where the action takes place for (int index_x = 0; index_x < dx; index_x++) { // read pixel from source bitmap, test transparency and plot if ((pixel = bitmap[index_x])) video_buffer[index_x] = pixel; } // end for index_x // advance pointers video_buffer += mempitch; // bytes per scanline bitmap += width; // bytes per bitmap row } // end for index_y
} // end Blit_Clipped
23
Creating Happy Face// the happy face structuretypedef struct HAPPY_FACE_TYP{ int x,y; // position of happy face int xv, yv; // velocity of happy face} HAPPY_FACE, *HAPPY_FACE_PTR;// a low tech bitmap that uses palette entry 1 for the color :)UCHAR happy_bitmap[64] = {0,0,0,0,0,0,0,0, 0,0,1,1,1,1,0,0, 0,1,0,1,1,0,1,0, 0,1,1,1,1,1,1,0, 0,1,0,1,1,0,1,0, 0,1,1,0,0,1,1,0, 0,0,1,1,1,1,0,0, 0,0,0,0,0,0,0,0};
HAPPY_FACE happy_faces[100]; // this holds all the happy faces
24
Game_Init( )// create IDirectDraw interface 7.0 object and test for error// set cooperation to full screen// set display mode // clear ddsd and set size// set the backbuffer count field to 1// create the primary surface (complex and flipable)// now query for attached surface from the primary surface// get the attached back buffer surface// build up the palette data array and create the palette object // finally attach the palette to the primary surface // initialize all the happy facesfor (int face = 0; face < 100; face++){ happy_faces[face].x = rand()%SCREEN_WIDTH; happy_faces[face].y = rand()%SCREEN_HEIGHT; happy_faces[face].xv = -2 + rand()%5; happy_faces[face].yv = -2 + rand()%5;} // end for face
25
Game_Main( )DDBLTFX ddbltfx; // the blitter fx structure // now set the color word info to the color we desireddbltfx.dwFillColor = 0; // make the blitter callif (FAILED(lpddsback->Blt(NULL, // pointer dest RECT NULL, // pointer to source surface NULL, // pointer to source RECT DDBLT_COLORFILL | DDBLT_WAIT, // do a color fill and wait if you have to &ddbltfx))) // pointer to DDBLTFX info return(0); // initialize ddsdDDRAW_INIT_STRUCT(ddsd);// lock the back buffer surfaceif (FAILED(lpddsback->Lock(NULL,&ddsd, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL))) return(0);
26
Game_Main( )// draw all the happy faces
for (int face=0; face < 100; face++)
{
Blit_Clipped(happy_faces[face].x,
happy_faces[face].y,
8,8,
happy_bitmap,
(UCHAR *)ddsd.lpSurface,
ddsd.lPitch);
} // end for
27
Game_Main( )// move all happy facesfor (face=0; face < 100; face++){ // move happy_faces[face].x+=happy_faces[face].xv; happy_faces[face].y+=happy_faces[face].yv; // check for off screen, if so wrap if (happy_faces[face].x > SCREEN_WIDTH) happy_faces[face].x = -8; else if (happy_faces[face].x < -8) happy_faces[face].x = SCREEN_WIDTH; if (happy_faces[face].y > SCREEN_HEIGHT) happy_faces[face].y = -8; else if (happy_faces[face].y < -8) happy_faces[face].y = SCREEN_HEIGHT;} // end face
28
DirectDraw Clipping
• Create a DirectDraw clipper object
• Create a clipping list
• Send clipping list to clipper using SetClipList( )
• Attach clipper to window and/or surface using SetClipper( )
29
Creating Clipper ObjectLPDIRECTDRAWCLIPPER lpddclipper; // pointer to the
// newly created dd clipper
LPRGNDATA region_data; // pointer to the region
// data that contains
// the header and clip list
// first create the direct draw clipper
if (FAILED(lpdd->CreateClipper(0,&lpddclipper,NULL)))
return(NULL);
30
Creating Clipping List// first allocate memory for region dataregion_data = (LPRGNDATA)malloc(sizeof(RGNDATAHEADER)
+num_rects*sizeof(RECT)); // now copy the rects into region datamemcpy(region_data->Buffer, clip_list, sizeof(RECT)*num_rects); // set up fields of headerregion_data->rdh.dwSize = sizeof(RGNDATAHEADER);region_data->rdh.iType = RDH_RECTANGLES;region_data->rdh.nCount = num_rects;region_data->rdh.nRgnSize = num_rects*sizeof(RECT); region_data->rdh.rcBound.left = 64000;region_data->rdh.rcBound.top = 64000;region_data->rdh.rcBound.right = -64000;region_data->rdh.rcBound.bottom = -64000;
31
Creating Clipping List
// find bounds of all clipping regions
for (index=0; index<num_rects; index++)
{
// test if the next rectangle unioned with current bound is larger
if (clip_list[index].left < region_data->rdh.rcBound.left)
region_data->rdh.rcBound.left = clip_list[index].left;
if (clip_list[index].right > region_data->rdh.rcBound.right)
region_data->rdh.rcBound.right = clip_list[index].right;
if (clip_list[index].top < region_data->rdh.rcBound.top)
region_data->rdh.rcBound.top = clip_list[index].top;
if (clip_list[index].bottom > region_data->rdh.rcBound.bottom)
region_data->rdh.rcBound.bottom = clip_list[index].bottom;
} // end for index
32
Set the Clipping List
// we have computed the bounding rectangle region and set up the data
// now let's set the clipping list
if (FAILED(lpddclipper->SetClipList(region_data, 0)))
{
// release memory and return error
free(region_data);
return(NULL);
} // end if
33
Attach Clipper and Clean Up
// we have computed the bounding rectangle region and set up the data
// now attach the clipper to the surface
if (FAILED(lpdds->SetClipper(lpddclipper)))
{
// release memory and return error
free(region_data);
return(NULL);
} // end if
// all is well, so release memory and send back the pointer to
// the new clipper
free(region_data);