Windows游戏编程大师技巧之三角形填充
一。三角形的种类
三角形一般可以分为如下的四种类型(这四种类型是对于计算机来说的,不是数学意义上的分类):
平顶三角形:就是在计算机中显示的上面两个顶点的Y坐标相同。
平底三角形:就是在计算机中显示的时候下面两个顶点的Y坐标相同。
右边为主三角形:这种三角形三个点的Y坐标都不相同,但是右边的一条边是最长的斜边
左边为主的三角形:这种三角形的三个点的Y坐标不相同,但是左边的一条边是最长的斜边。
附上各个不同三角形的图:
是不是所有的三角形大致可以分为这四种?而我们要进行填充的时候,前面两种的填充应该比较简单,只要从顶点开始一行一行的进行填充就可以做到了,如下图所示:
进行这样的操作应该很容易实现吧,同样的对平顶的三角形我们也可以这样来一行一行的使用像素进行填充。
至于后面两种情况的三角形,我们能够很简单的将他们分别变成两个三角形,一个平顶一个平底:
所以,我们实现了前面两个的三角形的填充,后面两个的也就很容易实现了。
二。代码实现
下面是实现平顶的三角形填充的代码:
- int Draw_Top_Trangle(int x0,int y0,
- int x1,int y1,
- int x2,int y2,
- UINT * video_buffer,
- DWORD color,int mempitch)
- {
- //先判断下输入的三角形
- if(y0==y1)
- {
- }else if(y0==y2)
- {
- Swap(x2,x1);
- Swap(y2,y1);
- }else if(y1==y2)
- {
- Swap(x0,x2);
- Swap(y0,y2);
- }else
- {
- return 1 ; //error \brief 不是平顶三角形
- }
- if(x1<x0)
- {
- Swap(x1,x0);
- Swap(y1,y0);
- }
- else if(x1 == x0)
- {
- return 1 ;// error \brief不是三角形
- }
- //计算左右误差
- float dxy_left = (x2-x0)*1.0/(y2-y0) ;
- float dxy_right = (x1-x2)*1.0/(y1-y2);
- //开始进行填充
- float xs = x0 ,xe = x1 ;
- for(int y=y0 ; y <=y2 ;y++)
- {
- Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch);
- xs += dxy_left ;
- xe += dxy_right ;
- }
- } // end Draw_Top_Trangle
int Draw_Top_Trangle(int x0,int y0, int x1,int y1, int x2,int y2, UINT * video_buffer, DWORD color,int mempitch) { //先判断下输入的三角形 if(y0==y1) { }else if(y0==y2) { Swap(x2,x1); Swap(y2,y1); }else if(y1==y2) { Swap(x0,x2); Swap(y0,y2); }else { return 1 ; //error \brief 不是平顶三角形 } if(x1<x0) { Swap(x1,x0); Swap(y1,y0); } else if(x1 == x0) { return 1 ;// error \brief不是三角形 } //计算左右误差 float dxy_left = (x2-x0)*1.0/(y2-y0) ; float dxy_right = (x1-x2)*1.0/(y1-y2); //开始进行填充 float xs = x0 ,xe = x1 ; for(int y=y0 ; y <=y2 ;y++) { Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch); xs += dxy_left ; xe += dxy_right ; } } // end Draw_Top_Trangle
上面的算法开始的时候,检查下输入的三个点是否能够构成三角形,并且按照下面图中坐标点所示,来进行顺序的重新排列:
因为用户使用的时候,可能传递的三个点不是上图中所示的那样的顺序,所以我们计算的方便,我们先将这三个点转变成上图中相对应的位置。接下来就是计算在Y方向上,每移动一个像素,左边和右边的直线上X的平均该变量是多少。获得了这个值,我们就可以慢慢的向下迭代下去,从而将三角形进行了填充。当然,你也可以使用其他的方法来。
(注:上面函数中的UINT*video_buffer,和int mempitch对于学习过DirectDraw的读者应该比较熟悉,分别是表面内存数据,和内存跨度,此函数是DirectDraw的实现版本)
同样的,来看看我们的平底三角形填充的实现:
- int Draw_Bottom_Trangle(int x0,int y0,
- int x1,int y1,
- int x2,int y2,
- UINT * video_buffer,
- DWORD color,int mempitch)
- {
- //先判断下输入的三角形
- if(y2==y1)
- {
- }else if(y2==y0)
- {
- Swap(x0,x1);
- Swap(y0,y1);
- }else if(y0==y1)
- {
- Swap(x0,x2);
- Swap(y0,y2);
- }else
- {
- return 1 ; //error \brief 不是平顶三角形
- }
- if(x1<x2)
- {
- Swap(x1,x2);
- }
- else if(x1 == x2)
- {
- return 1 ;// error \brief不是三角形
- }
- //计算左右误差
- float dxy_left = (x2-x0)*1.0/(y2-y0) ;
- float dxy_right = (x1-x0)*1.0/(y1-y0);
- //开始进行填充
- float xs = x0 ,xe = x0 ;
- for(int y=y0 ; y <=y2 ;y++)
- {
- Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch);
- xs += dxy_left ;
- xe += dxy_right ;
- }
- }// end Draw_Bottom_Trangle
int Draw_Bottom_Trangle(int x0,int y0, int x1,int y1, int x2,int y2, UINT * video_buffer, DWORD color,int mempitch) { //先判断下输入的三角形 if(y2==y1) { }else if(y2==y0) { Swap(x0,x1); Swap(y0,y1); }else if(y0==y1) { Swap(x0,x2); Swap(y0,y2); }else { return 1 ; //error \brief 不是平顶三角形 } if(x1<x2) { Swap(x1,x2); } else if(x1 == x2) { return 1 ;// error \brief不是三角形 } //计算左右误差 float dxy_left = (x2-x0)*1.0/(y2-y0) ; float dxy_right = (x1-x0)*1.0/(y1-y0); //开始进行填充 float xs = x0 ,xe = x0 ; for(int y=y0 ; y <=y2 ;y++) { Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch); xs += dxy_left ; xe += dxy_right ; } }// end Draw_Bottom_Trangle
和上面平顶的算法基本上一致,只有图中点的顺序不同:
好了,这两个函数都实现了,接下来看看我们任意的三角形绘制的实现吧:
- int Draw_Trangle_2D(int x0,int y0,
- int x1,int y1,
- int x2,int y2,
- UINT * video_buffer,
- DWORD color,int mempitch)
- {
- if((x0==x1&&x1==x2)
- ||(y0==y1&&y1==y2))
- {
- return 1 ; //error \brief传进来的点无法构成三角形
- }
- //\brief 将三个顶点按照从上到下排序
- if(y0>y1)
- {
- Swap(x0,x1);
- Swap(y0,y1);
- }
- if(y0>y2)
- {
- Swap(x0,x2);
- Swap(y0,y2);
- }
- if(y1>y2)
- {
- Swap(y1,y2);
- Swap(x1,x2);
- }
- //\brief查找最大的x坐标,和最小的y坐标
- int min = (x0<x1?x0:x1);
- min = (min<x2?min:x2);
- int max = (x0>x1?x0:x1);
- max = (max>x2?max:x2);
- //\brief 进行绘制
- if(y2<=min_clip_y||y0>=max_clip_y
- ||min>=max_clip_x||max<=min_clip_x)
- return 1 ; //\brief 全部在裁剪区之外
- if(y0 == y1) //\brief 平顶三角形
- {
- Draw_Top_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch);
- }else if(y1 == y2)
- {
- Draw_Bottom_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch);
- }else
- {
- int new_x = x0+0.5+(float)1.0*(y1-y0)*(x2-x0)/(y2-y0);
- Draw_Bottom_Trangle(x0,y0,new_x,y1,x1,y1,video_buffer,color,mempitch);
- Draw_Top_Trangle(new_x,y1,x1,y1,x2,y2,video_buffer,color,mempitch);
- }
- return 0 ; //\brief 成功画出三角形
- }// end Draw_Trangle_2D
int Draw_Trangle_2D(int x0,int y0, int x1,int y1, int x2,int y2, UINT * video_buffer, DWORD color,int mempitch) { if((x0==x1&&x1==x2) ||(y0==y1&&y1==y2)) { return 1 ; //error \brief传进来的点无法构成三角形 } //\brief 将三个顶点按照从上到下排序 if(y0>y1) { Swap(x0,x1); Swap(y0,y1); } if(y0>y2) { Swap(x0,x2); Swap(y0,y2); } if(y1>y2) { Swap(y1,y2); Swap(x1,x2); } //\brief查找最大的x坐标,和最小的y坐标 int min = (x0<x1?x0:x1); min = (min<x2?min:x2); int max = (x0>x1?x0:x1); max = (max>x2?max:x2); //\brief 进行绘制 if(y2<=min_clip_y||y0>=max_clip_y ||min>=max_clip_x||max<=min_clip_x) return 1 ; //\brief 全部在裁剪区之外 if(y0 == y1) //\brief 平顶三角形 { Draw_Top_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch); }else if(y1 == y2) { Draw_Bottom_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch); }else { int new_x = x0+0.5+(float)1.0*(y1-y0)*(x2-x0)/(y2-y0); Draw_Bottom_Trangle(x0,y0,new_x,y1,x1,y1,video_buffer,color,mempitch); Draw_Top_Trangle(new_x,y1,x1,y1,x2,y2,video_buffer,color,mempitch); } return 0 ; //\brief 成功画出三角形 }// end Draw_Trangle_2D
这个函数,先将输入的三个点按照y坐标从小到大排序,这样我们就可以y1的坐标,来寻找分离一个右边为主或者左边为主的三角形成为一个平顶一个平底的三角形了。(由于排序了,所以y1的坐标就是显示屏幕上从上到下中间的那个点了,想象是不是这样的!?)。分离了之后,我们就可以分别调用绘制平底和平顶的三角形的算法来实现了。
以下是整个工程的完整代码:
- // DEMO8_8.CPP 此Demo演示32位窗口模式下,创建任意填充三角形的算法
- // INCLUDES ///////////////////////////////////////////////
- #define WIN32_LEAN_AND_MEAN // just say no to MFC
- #define INITGUID // make sure directX guids are included
- #include <windows.h> // include important windows stuff
- #include <windowsx.h>
- #include <mmsystem.h>
- #include <iostream> // include important C/C++ stuff
- using namespace std ;
- #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> // include directdraw
- #pragma comment(lib,"ddraw.lib")
- // DEFINES ////////////////////////////////////////////////
- // defines for windows
- #define WINDOW_CLASS_NAME L"WINCLASS1"
- // default screen size
- #define SCREEN_WIDTH 640 // size of screen
- #define SCREEN_HEIGHT 480
- #define SCREEN_BPP 32 // bits per pixel
- #define MAX_COLORS 256 // maximum colors
- // TYPES //////////////////////////////////////////////////////
- // basic unsigned types
- typedef unsigned short USHORT;
- typedef unsigned short WORD;
- typedef unsigned char UCHAR;
- typedef unsigned char BYTE;
- // MACROS /////////////////////////////////////////////////
- #define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
- #define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
- // initializes a direct draw struct
- #define DD_INIT_STRUCT(ddstruct) { memset(&ddstruct,0,sizeof(ddstruct)); ddstruct.dwSize=sizeof(ddstruct); }
- //initializes a RGB value
- #define _RGB16BIT565(r,g,b) ((b & 31) + ((g & 63) << 5) + ((r & 31) << 11))
- #define _RGB32BIT(a,r,g,b) ((b) + ((g) << 8) + ((r) << 16) + ((a) << 24))
- // GLOBALS ////////////////////////////////////////////////
- HWND main_window_handle = NULL; // globally track main window
- HINSTANCE hinstance_app = NULL; // globally track hinstance
- // directdraw stuff
- LPDIRECTDRAW7 lpdd = NULL; // dd object
- LPDIRECTDRAWSURFACE7 lpddsprimary = NULL; // dd primary surface
- LPDIRECTDRAWSURFACE7 lpddsback = NULL; // dd back surface
- LPDIRECTDRAWPALETTE lpddpal = NULL; // a pointer to the created dd palette
- LPDIRECTDRAWCLIPPER lpddclipper = NULL; // dd clipper
- 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
- DWORD start_clock_count = 0; // used for timing
- LPDIRECTDRAWSURFACE7 lpddsOffScreen = NULL ; //离屏表面
- int window_close = 0 ; //标识窗口是否关闭
- // these defined the general clipping rectangle
- int min_clip_x = 0, // clipping rectangle
- max_clip_x = 1366-1,
- min_clip_y = 0,
- max_clip_y = 768-1;
- // these are overwritten globally by DD_Init()
- int screen_width = SCREEN_WIDTH, // width of screen
- screen_height = SCREEN_HEIGHT, // height of screen
- screen_bpp = SCREEN_BPP; // bits per pixel
- char buffer[80]; // general printing buffer
- //申明画线方法
- int Draw_Line(int x0, int y0, int x1, int y1, DWORD color , UINT * video_buffer , int stepx , int stepy);
- //裁剪直线算法
- int Clipper_Line(int& x0,int& y0,int& x1, int& y1,int screen_width,int screen_height);
- //交换值
- void Swap(int &x , int &y) ;
- //绘制填充平顶三角形
- int Draw_Top_Trangle(int x0,int y0,
- int x1,int y1,
- int x2,int y2,
- UINT * video_buffer,
- DWORD color,int mempitch);
- //绘制平底三角形
- int Draw_Bottom_Trangle(int x0,int y0,
- int x1,int y1,
- int x2,int y2,
- UINT * video_buffer,
- DWORD color,int mempitch);
- //简单的平行绘制直线
- int Draw_Simple_Line(int x0, int x1,int y , UINT * video_buffer,DWORD color,int mempitch);
- //绘制任意三角形
- int Draw_Trangle_2D(int x0,int y0,
- int x1,int y1,
- int x2,int y2,
- UINT * video_buffer,
- DWORD color,int mempitch);
- // 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
- char buffer[80]; // used to print strings
- // what is the message
- switch(msg)
- {
- case WM_CREATE:
- {
- // do initialization stuff here
- // return success
- return(0);
- } break;
- case WM_PAINT:
- {
- // simply validate the window
- hdc = BeginPaint(hwnd,&ps);
- // end painting
- EndPaint(hwnd,&ps);
- // return success
- return(0);
- } break;
- case WM_DESTROY:
- {
- // kill the application, this sends a WM_QUIT message
- PostQuitMessage(0);
- // return success
- 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
- ///////////////////////////////////////////////////////////
- //程序主循环
- int Game_Main(void *parms = NULL, int num_parms = 0)
- {
- // this is the main loop of the game, do all your processing
- // here
- // for now test if user is hitting ESC and send WM_CLOSE
- if(window_close)
- return 1 ;
- if (KEYDOWN(VK_ESCAPE))
- {
- PostMessage(main_window_handle,WM_CLOSE,0,0);
- window_close = 1 ;
- }
- //清空表面
- DDBLTFX bltfx ;
- DD_INIT_STRUCT(bltfx);
- bltfx.dwFillColor = 0 ;
- if(FAILED(lpddsOffScreen->Blt(NULL,NULL,NULL,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx)))
- {
- OutputDebugString(L"OffScreen Blt error");
- return 1 ;
- }
- //锁定
- DDSURFACEDESC2 ddsd ;
- DD_INIT_STRUCT(ddsd);
- if(FAILED(lpddsOffScreen->Lock(NULL,&ddsd,DDLOCK_WAIT|DDLOCK_SURFACEMEMORYPTR,NULL)))
- {
- OutputDebugString(L"Lock error");
- return 1 ;
- }
- //获取窗口位置
- RECT rect ;
- GetWindowRect(main_window_handle,&rect);
- //画填充的三角形
- int x0 = rand()%SCREEN_WIDTH+rect.left;
- int x1 = rand()%SCREEN_WIDTH+rect.left ;
- int x2 = rand()%SCREEN_WIDTH + rect.left ;
- int y0 = rand()%SCREEN_HEIGHT + rect.top;
- int y1 = rand()%SCREEN_HEIGHT+ rect.top+100;
- int y2 = rand()%SCREEN_HEIGHT + rect.top;
- Draw_Trangle_2D(x0,y0,x1,y1,x2,y2,(UINT*)ddsd.lpSurface,
- _RGB32BIT(0,255,255,255),ddsd.lPitch>>2);
- //解锁
- if(FAILED(lpddsOffScreen->Unlock(NULL)))
- {
- OutputDebugString(L"Unlock error");
- return 1 ;
- }
- //Blt到主表面
- if(FAILED(lpddsprimary->Blt(NULL,lpddsOffScreen,NULL,DDBLT_WAIT,NULL)))
- {
- OutputDebugString(L"Blt error");
- return 1 ;
- }
- // return success or failure or your own return code here
- return(1);
- } // end Game_Main
- ////////////////////////////////////////////////////////////
- int Game_Init(void *parms = NULL, int num_parms = 0)
- {
- // this is called once after the initial window is created and
- // before the main event loop is entered, do all your initialization
- // here
- // create IDirectDraw interface 7.0 object and test for error
- if (FAILED(DirectDrawCreateEx(NULL, (void **)&lpdd, IID_IDirectDraw7, NULL)))
- return(0);
- // set cooperation to normal since this will be a windowed app
- if(FAILED(lpdd->SetCooperativeLevel(main_window_handle, DDSCL_NORMAL)))
- {
- MessageBox(NULL,L"SetCooperativeLevel error",L"error",MB_OK);
- return 0 ;
- }
- //创建裁剪器
- if(FAILED(lpdd->CreateClipper(0,&lpddclipper,NULL)))
- {
- OutputDebugString(L"CreateClipper error");
- return 1 ;
- }
- //将裁减器关联窗口,也就是用窗口的尺寸作为裁剪器的裁剪序列
- if(FAILED(lpddclipper->SetHWnd(0,main_window_handle)))
- {
- OutputDebugString(L"SetHWnd error");
- return 1 ;
- }
- //创建主表面
- memset(&ddsd,0,sizeof(ddsd));
- ddsd.dwSize = sizeof(ddsd);
- ddsd.dwFlags = DDSD_CAPS ;
- ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
- if(FAILED(lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL)))
- {
- MessageBox(NULL,L"CreateSurface error",L"error",MB_OK);
- return 0 ;
- }
- //将裁减器关联到表面
- if(FAILED(lpddsprimary->SetClipper(lpddclipper)))
- {
- OutputDebugString(L"SetClipper error");
- return 1 ;
- }
- //创建一个离屏表面
- DD_INIT_STRUCT(ddsd);
- ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
- ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT ;
- ddsd.dwHeight = 786 ;
- ddsd.dwWidth = 1366 ;
- if(FAILED(lpdd->CreateSurface(&ddsd,&lpddsOffScreen,NULL)))
- {
- OutputDebugString(L"OffScreen CreateSurface error");
- return 1 ;
- }
- // return success or failure or your own return code here
- return(1);
- } // end Game_Init
- /////////////////////////////////////////////////////////////
- int Game_Shutdown(void *parms = NULL, int num_parms = 0)
- {
- // this is called after the game is exited and the main event
- // loop while is exited, do all you cleanup and shutdown here
- // simply blow away the IDirectDraw4 interface
- if(lpddclipper)
- {
- lpddclipper->Release();
- lpddclipper = NULL ;
- }
- if(lpddsprimary)
- {
- lpddsprimary->Release();
- lpddsprimary = NULL ;
- }
- if (lpdd)
- {
- lpdd->Release();
- lpdd = NULL;
- } // end if
- // return success or failure or your own return code here
- return(1);
- } // end Game_Shutdown
- // WINMAIN ////////////////////////////////////////////////
- int WINAPI WinMain( HINSTANCE hinstance,
- HINSTANCE hprevinstance,
- LPSTR lpcmdline,
- int ncmdshow)
- {
- 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.hCursor = 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
- hinstance_app = hinstance;
- // register the window class
- if (!RegisterClassEx(&winclass))
- return(0);
- // create the window
- if (!(hwnd = CreateWindowEx(NULL, // extended style
- WINDOW_CLASS_NAME, // class
- L"DirectDraw Initialization Demo", // title
- WS_OVERLAPPED|WS_VISIBLE,
- 0,0, // initial x,y
- SCREEN_WIDTH,SCREEN_HEIGHT, // initial width, height
- NULL, // handle to parent
- NULL, // handle to menu
- hinstance,// instance of this application
- NULL))) // extra creation parms
- return(0);
- // save main window handle
- main_window_handle = hwnd;
- // initialize game here
- Game_Init();
- //调整窗口大小
- RECT window_rect = {0,0,SCREEN_WIDTH,SCREEN_HEIGHT} ;
- AdjustWindowRectEx(&window_rect,GetWindowStyle(main_window_handle),GetMenu(main_window_handle)!=NULL,GetWindowExStyle(main_window_handle));
- // enter main event loop
- while(TRUE)
- {
- // test if there is a message in queue, if so get it
- 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
- // closedown game here
- Game_Shutdown();
- // return to Windows like this
- return(msg.wParam);
- } // end WinMain
- //定义交换函数
- void Swap(int &x , int &y)
- {
- int temp = y ;
- y = x ;
- x = temp ;
- }
- //定义画线函数
- int Draw_Line(int x0,int y0, int x1, int y1 , DWORD color , UINT *video_buffer, int stepx,int stepy)
- {
- int dx , //起点与终点的X方向间距
- dy , //起点与终点的Y方向间距
- dx2, //两倍的dx
- dy2, //两倍的dy
- x_inc , //实际的x步长值,带有符号
- y_inc , //实际的y步长值,带有符号
- p ; //误差项
- dx = x1 - x0 ; //计算x间距
- dy = y1 - y0 ; //计算y间距
- //计算起点的缓冲地址
- video_buffer+=x0+y0*stepy ;
- //确定x方向的步进值
- if(dx>=0)
- {
- x_inc = stepx;
- }
- else
- {
- x_inc = -stepx ;
- dx = -dx ;
- }
- //确定y方向的步进值
- if(dy>=0)
- {
- y_inc = stepy ;
- }
- else
- {
- y_inc = -stepy ;
- dy = -dy ;
- }
- //确定dx2,dy2的值
- dx2 = dx<<1;
- dy2 = dy<<1 ;
- //进行步进的选择
- if(dx <= dy) //斜率绝对值大于1
- {
- Swap(dx,dy);
- Swap(x_inc,y_inc);
- Swap(dx2,dy2);
- }
- else //斜率绝对值小于1,不需要交换
- {
- }
- //绘制直线
- p = dy2 - dx ; //计算起点的误差值
- for(int i = 0 ; i < dx ; i++)
- {
- *video_buffer = color ;
- video_buffer += x_inc ;
- if(p>=0)
- {
- video_buffer += y_inc ;
- p = p + dy2 - dx2 ;
- }
- else
- {
- p = p + dy2 ;
- }
- }// end for
- return 0 ;
- }// end Draw_Line
- //定义裁剪直线算法
- int Clipper_Line(int& x0,int& y0,int& x1, int& y1,int screen_width,int screen_height)
- {
- #define CLIP_CODE_C 0x0000
- #define CLIP_CODE_N 0x0008
- #define CLIP_CODE_S 0x0004
- #define CLIP_CODE_E 0x0002
- #define CLIP_CODE_W 0x0001
- #define CLIP_CODE_NE 0x000a
- #define CLIP_CODE_SE 0x0006
- #define CLIP_CODE_NW 0x0009
- #define CLIP_CODE_SW 0x0005
- int xc0 = x0 ,yc0 = y0 , xc1=x1 , yc1=y1 ;
- int min_clip_x = SCREEN_WIDTH/3 ,min_clip_y = SCREEN_HEIGHT/3 ,max_clip_x = screen_width*2/3-1,max_clip_y=screen_height*2/3-1 ;
- int p0_code = 0 ,p1_code = 0 ;
- //确定各个顶点所在的位置代码
- if(y0<min_clip_y)
- p0_code|=CLIP_CODE_N;
- else if(y0>max_clip_y)
- p0_code|=CLIP_CODE_S;
- if(x0<min_clip_x)
- p0_code|=CLIP_CODE_W;
- else if(x0>max_clip_x)
- p0_code|=CLIP_CODE_E;
- if(y1<min_clip_y)
- p1_code|=CLIP_CODE_N;
- else if(y1>max_clip_y)
- p1_code|=CLIP_CODE_S;
- if(x1<min_clip_x)
- p1_code|=CLIP_CODE_W;
- else if(x1>max_clip_x)
- p1_code|=CLIP_CODE_E;
- //先检测一些简单的情况
- if(p0_code&p1_code) //有相同的位置代码,表示在裁剪区外部
- return 0 ;
- if(p0_code==0&&p1_code==0) //表示两个点都在裁剪区内,不需要裁剪
- return 1 ;
- //判断第一个点的位置代码
- switch(p0_code)
- {
- case CLIP_CODE_C:
- break;
- case CLIP_CODE_N:
- {
- yc0 = min_clip_y ;
- xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);
- break ;
- }
- case CLIP_CODE_S:
- {
- yc0 = max_clip_y;
- xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);
- break ;
- }
- case CLIP_CODE_W:
- {
- xc0=min_clip_x;
- yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);
- break;
- }
- case CLIP_CODE_E:
- {
- xc0=max_clip_x;
- yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);
- break;
- }
- case CLIP_CODE_NE:
- {
- yc0 = min_clip_y;
- xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);
- if(xc0<min_clip_x||xc0>max_clip_x)
- {
- xc0=max_clip_x;
- yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);
- }
- break;
- }
- case CLIP_CODE_SE:
- {
- yc0 = max_clip_y;
- xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);
- if(xc0<min_clip_x||xc0>max_clip_x)
- {
- xc0=max_clip_x;
- yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);
- }
- break;
- }
- case CLIP_CODE_NW:
- {
- yc0=min_clip_y;
- xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);
- if(xc0<min_clip_x||xc0>max_clip_x)
- {
- xc0=min_clip_x;
- yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);
- }
- break;
- }
- case CLIP_CODE_SW:
- {
- yc0=max_clip_y;
- xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);
- if(xc0<min_clip_x||xc0>max_clip_x)
- {
- xc0=min_clip_x;
- yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);
- }
- break;
- }
- default:
- break;
- } // end switch(p0_code)
- //判断第二个点的位置代码
- switch(p1_code)
- {
- case CLIP_CODE_C:
- break;
- case CLIP_CODE_N:
- {
- yc1 = min_clip_y ;
- xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);
- break ;
- }
- case CLIP_CODE_S:
- {
- yc1 = max_clip_y;
- xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);
- break ;
- }
- case CLIP_CODE_W:
- {
- xc1=min_clip_x;
- yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);
- break;
- }
- case CLIP_CODE_E:
- {
- xc1=max_clip_x;
- yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);
- break;
- }
- case CLIP_CODE_NE:
- {
- yc1 = min_clip_y;
- xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);
- if(xc1<min_clip_x||xc1>max_clip_x)
- {
- xc1=max_clip_x;
- yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);
- }
- break;
- }
- case CLIP_CODE_SE:
- {
- yc1 = max_clip_y;
- xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);
- if(xc1<min_clip_x||xc1>max_clip_x)
- {
- xc1=max_clip_x;
- yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);
- }
- break;
- }
- case CLIP_CODE_NW:
- {
- yc1=min_clip_y;
- xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);
- if(xc1<min_clip_x||xc1>max_clip_x)
- {
- xc1=min_clip_x;
- yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);
- }
- break;
- }
- case CLIP_CODE_SW:
- {
- yc1=max_clip_y;
- xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);
- if(xc1<min_clip_x||xc1>max_clip_x)
- {
- xc1=min_clip_x;
- yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);
- }
- break;
- }
- default:
- break;
- } // end switch(p1_code)
- //进行最后的检测
- if(xc0>max_clip_x||xc0<min_clip_x||
- yc0>max_clip_y||yc0<min_clip_y||
- xc1>max_clip_x||xc1<min_clip_x||
- yc1>max_clip_y||yc1<min_clip_y)
- {
- //表示全部在裁剪区外部
- return 0 ;
- }
- //将裁减后的数据返回
- x0 = xc0 ;
- x1 = xc1 ;
- y0 = yc0 ;
- y1 = yc1 ;
- return 1 ;
- }// end Clipper_Line
- //简单的平行绘制直线
- int Draw_Simple_Line(int x0, int x1,int y , UINT * video_buffer,DWORD color,int mempitch)
- {
- //进行裁剪
- if(y<min_clip_y)
- return 1 ;
- else if(y>max_clip_y)
- return 1 ;
- if(x0<min_clip_x)
- x0 = min_clip_x;
- else if(x0>max_clip_x)
- x0 = max_clip_x ;
- if(x1<min_clip_x)
- x1 = min_clip_x ;
- else if(x1>max_clip_x)
- x1 = max_clip_x ;
- //进行绘制
- video_buffer+=y*mempitch;
- for(int x = x0 ; x<=x1;x++)
- {
- video_buffer[x]=color ;
- }
- }
- //绘制填充平顶三角形
- int Draw_Top_Trangle(int x0,int y0,
- int x1,int y1,
- int x2,int y2,
- UINT * video_buffer,
- DWORD color,int mempitch)
- {
- //先判断下输入的三角形
- if(y0==y1)
- {
- }else if(y0==y2)
- {
- Swap(x2,x1);
- Swap(y2,y1);
- }else if(y1==y2)
- {
- Swap(x0,x2);
- Swap(y0,y2);
- }else
- {
- return 1 ; //error \brief 不是平顶三角形
- }
- if(x1<x0)
- {
- Swap(x1,x0);
- Swap(y1,y0);
- }
- else if(x1 == x0)
- {
- return 1 ;// error \brief不是三角形
- }
- //计算左右误差
- float dxy_left = (x2-x0)*1.0/(y2-y0) ;
- float dxy_right = (x1-x2)*1.0/(y1-y2);
- //开始进行填充
- float xs = x0 ,xe = x1 ;
- for(int y=y0 ; y <=y2 ;y++)
- {
- Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch);
- xs += dxy_left ;
- xe += dxy_right ;
- }
- } // end Draw_Top_Trangle
- //绘制平底三角形
- int Draw_Bottom_Trangle(int x0,int y0,
- int x1,int y1,
- int x2,int y2,
- UINT * video_buffer,
- DWORD color,int mempitch)
- {
- //先判断下输入的三角形
- if(y2==y1)
- {
- }else if(y2==y0)
- {
- Swap(x0,x1);
- Swap(y0,y1);
- }else if(y0==y1)
- {
- Swap(x0,x2);
- Swap(y0,y2);
- }else
- {
- return 1 ; //error \brief 不是平顶三角形
- }
- if(x1<x2)
- {
- Swap(x1,x2);
- }
- else if(x1 == x2)
- {
- return 1 ;// error \brief不是三角形
- }
- //计算左右误差
- float dxy_left = (x2-x0)*1.0/(y2-y0) ;
- float dxy_right = (x1-x0)*1.0/(y1-y0);
- //开始进行填充
- float xs = x0 ,xe = x0 ;
- for(int y=y0 ; y <=y2 ;y++)
- {
- Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch);
- xs += dxy_left ;
- xe += dxy_right ;
- }
- }// end Draw_Bottom_Trangle
- //绘制任意三角形
- int Draw_Trangle_2D(int x0,int y0,
- int x1,int y1,
- int x2,int y2,
- UINT * video_buffer,
- DWORD color,int mempitch)
- {
- if((x0==x1&&x1==x2)
- ||(y0==y1&&y1==y2))
- {
- return 1 ; //error \brief传进来的点无法构成三角形
- }
- //\brief 将三个顶点按照从上到下排序
- if(y0>y1)
- {
- Swap(x0,x1);
- Swap(y0,y1);
- }
- if(y0>y2)
- {
- Swap(x0,x2);
- Swap(y0,y2);
- }
- if(y1>y2)
- {
- Swap(y1,y2);
- Swap(x1,x2);
- }
- //\brief查找最大的x坐标,和最小的y坐标
- int min = (x0<x1?x0:x1);
- min = (min<x2?min:x2);
- int max = (x0>x1?x0:x1);
- max = (max>x2?max:x2);
- //\brief 进行绘制
- if(y2<=min_clip_y||y0>=max_clip_y
- ||min>=max_clip_x||max<=min_clip_x)
- return 1 ; //\brief 全部在裁剪区之外
- if(y0 == y1) //\brief 平顶三角形
- {
- Draw_Top_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch);
- }else if(y1 == y2)
- {
- Draw_Bottom_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch);
- }else
- {
- int new_x = x0+0.5+(float)1.0*(y1-y0)*(x2-x0)/(y2-y0);
- Draw_Bottom_Trangle(x0,y0,new_x,y1,x1,y1,video_buffer,color,mempitch);
- Draw_Top_Trangle(new_x,y1,x1,y1,x2,y2,video_buffer,color,mempitch);
- }
- return 0 ; //\brief 成功画出三角形
- }// end Draw_Trangle_2D
- ///////////////////////////////////////////////////////////
// DEMO8_8.CPP 此Demo演示32位窗口模式下,创建任意填充三角形的算法 // INCLUDES /////////////////////////////////////////////// #define WIN32_LEAN_AND_MEAN // just say no to MFC #define INITGUID // make sure directX guids are included #include <windows.h> // include important windows stuff #include <windowsx.h> #include <mmsystem.h> #include <iostream> // include important C/C++ stuff using namespace std ; #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> // include directdraw #pragma comment(lib,"ddraw.lib") // DEFINES //////////////////////////////////////////////// // defines for windows #define WINDOW_CLASS_NAME L"WINCLASS1" // default screen size #define SCREEN_WIDTH 640 // size of screen #define SCREEN_HEIGHT 480 #define SCREEN_BPP 32 // bits per pixel #define MAX_COLORS 256 // maximum colors // TYPES ////////////////////////////////////////////////////// // basic unsigned types typedef unsigned short USHORT; typedef unsigned short WORD; typedef unsigned char UCHAR; typedef unsigned char BYTE; // MACROS ///////////////////////////////////////////////// #define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0) #define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1) // initializes a direct draw struct #define DD_INIT_STRUCT(ddstruct) { memset(&ddstruct,0,sizeof(ddstruct)); ddstruct.dwSize=sizeof(ddstruct); } //initializes a RGB value #define _RGB16BIT565(r,g,b) ((b & 31) + ((g & 63) << 5) + ((r & 31) << 11)) #define _RGB32BIT(a,r,g,b) ((b) + ((g) << 8) + ((r) << 16) + ((a) << 24)) // GLOBALS //////////////////////////////////////////////// HWND main_window_handle = NULL; // globally track main window HINSTANCE hinstance_app = NULL; // globally track hinstance // directdraw stuff LPDIRECTDRAW7 lpdd = NULL; // dd object LPDIRECTDRAWSURFACE7 lpddsprimary = NULL; // dd primary surface LPDIRECTDRAWSURFACE7 lpddsback = NULL; // dd back surface LPDIRECTDRAWPALETTE lpddpal = NULL; // a pointer to the created dd palette LPDIRECTDRAWCLIPPER lpddclipper = NULL; // dd clipper 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 DWORD start_clock_count = 0; // used for timing LPDIRECTDRAWSURFACE7 lpddsOffScreen = NULL ; //离屏表面 int window_close = 0 ; //标识窗口是否关闭 // these defined the general clipping rectangle int min_clip_x = 0, // clipping rectangle max_clip_x = 1366-1, min_clip_y = 0, max_clip_y = 768-1; // these are overwritten globally by DD_Init() int screen_width = SCREEN_WIDTH, // width of screen screen_height = SCREEN_HEIGHT, // height of screen screen_bpp = SCREEN_BPP; // bits per pixel char buffer[80]; // general printing buffer //申明画线方法 int Draw_Line(int x0, int y0, int x1, int y1, DWORD color , UINT * video_buffer , int stepx , int stepy); //裁剪直线算法 int Clipper_Line(int& x0,int& y0,int& x1, int& y1,int screen_width,int screen_height); //交换值 void Swap(int &x , int &y) ; //绘制填充平顶三角形 int Draw_Top_Trangle(int x0,int y0, int x1,int y1, int x2,int y2, UINT * video_buffer, DWORD color,int mempitch); //绘制平底三角形 int Draw_Bottom_Trangle(int x0,int y0, int x1,int y1, int x2,int y2, UINT * video_buffer, DWORD color,int mempitch); //简单的平行绘制直线 int Draw_Simple_Line(int x0, int x1,int y , UINT * video_buffer,DWORD color,int mempitch); //绘制任意三角形 int Draw_Trangle_2D(int x0,int y0, int x1,int y1, int x2,int y2, UINT * video_buffer, DWORD color,int mempitch); // 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 char buffer[80]; // used to print strings // what is the message switch(msg) { case WM_CREATE: { // do initialization stuff here // return success return(0); } break; case WM_PAINT: { // simply validate the window hdc = BeginPaint(hwnd,&ps); // end painting EndPaint(hwnd,&ps); // return success return(0); } break; case WM_DESTROY: { // kill the application, this sends a WM_QUIT message PostQuitMessage(0); // return success 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 /////////////////////////////////////////////////////////// //程序主循环 int Game_Main(void *parms = NULL, int num_parms = 0) { // this is the main loop of the game, do all your processing // here // for now test if user is hitting ESC and send WM_CLOSE if(window_close) return 1 ; if (KEYDOWN(VK_ESCAPE)) { PostMessage(main_window_handle,WM_CLOSE,0,0); window_close = 1 ; } //清空表面 DDBLTFX bltfx ; DD_INIT_STRUCT(bltfx); bltfx.dwFillColor = 0 ; if(FAILED(lpddsOffScreen->Blt(NULL,NULL,NULL,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx))) { OutputDebugString(L"OffScreen Blt error"); return 1 ; } //锁定 DDSURFACEDESC2 ddsd ; DD_INIT_STRUCT(ddsd); if(FAILED(lpddsOffScreen->Lock(NULL,&ddsd,DDLOCK_WAIT|DDLOCK_SURFACEMEMORYPTR,NULL))) { OutputDebugString(L"Lock error"); return 1 ; } //获取窗口位置 RECT rect ; GetWindowRect(main_window_handle,&rect); //画填充的三角形 int x0 = rand()%SCREEN_WIDTH+rect.left; int x1 = rand()%SCREEN_WIDTH+rect.left ; int x2 = rand()%SCREEN_WIDTH + rect.left ; int y0 = rand()%SCREEN_HEIGHT + rect.top; int y1 = rand()%SCREEN_HEIGHT+ rect.top+100; int y2 = rand()%SCREEN_HEIGHT + rect.top; Draw_Trangle_2D(x0,y0,x1,y1,x2,y2,(UINT*)ddsd.lpSurface, _RGB32BIT(0,255,255,255),ddsd.lPitch>>2); //解锁 if(FAILED(lpddsOffScreen->Unlock(NULL))) { OutputDebugString(L"Unlock error"); return 1 ; } //Blt到主表面 if(FAILED(lpddsprimary->Blt(NULL,lpddsOffScreen,NULL,DDBLT_WAIT,NULL))) { OutputDebugString(L"Blt error"); return 1 ; } // return success or failure or your own return code here return(1); } // end Game_Main //////////////////////////////////////////////////////////// int Game_Init(void *parms = NULL, int num_parms = 0) { // this is called once after the initial window is created and // before the main event loop is entered, do all your initialization // here // create IDirectDraw interface 7.0 object and test for error if (FAILED(DirectDrawCreateEx(NULL, (void **)&lpdd, IID_IDirectDraw7, NULL))) return(0); // set cooperation to normal since this will be a windowed app if(FAILED(lpdd->SetCooperativeLevel(main_window_handle, DDSCL_NORMAL))) { MessageBox(NULL,L"SetCooperativeLevel error",L"error",MB_OK); return 0 ; } //创建裁剪器 if(FAILED(lpdd->CreateClipper(0,&lpddclipper,NULL))) { OutputDebugString(L"CreateClipper error"); return 1 ; } //将裁减器关联窗口,也就是用窗口的尺寸作为裁剪器的裁剪序列 if(FAILED(lpddclipper->SetHWnd(0,main_window_handle))) { OutputDebugString(L"SetHWnd error"); return 1 ; } //创建主表面 memset(&ddsd,0,sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_CAPS ; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; if(FAILED(lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL))) { MessageBox(NULL,L"CreateSurface error",L"error",MB_OK); return 0 ; } //将裁减器关联到表面 if(FAILED(lpddsprimary->SetClipper(lpddclipper))) { OutputDebugString(L"SetClipper error"); return 1 ; } //创建一个离屏表面 DD_INIT_STRUCT(ddsd); ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT ; ddsd.dwHeight = 786 ; ddsd.dwWidth = 1366 ; if(FAILED(lpdd->CreateSurface(&ddsd,&lpddsOffScreen,NULL))) { OutputDebugString(L"OffScreen CreateSurface error"); return 1 ; } // return success or failure or your own return code here return(1); } // end Game_Init ///////////////////////////////////////////////////////////// int Game_Shutdown(void *parms = NULL, int num_parms = 0) { // this is called after the game is exited and the main event // loop while is exited, do all you cleanup and shutdown here // simply blow away the IDirectDraw4 interface if(lpddclipper) { lpddclipper->Release(); lpddclipper = NULL ; } if(lpddsprimary) { lpddsprimary->Release(); lpddsprimary = NULL ; } if (lpdd) { lpdd->Release(); lpdd = NULL; } // end if // return success or failure or your own return code here return(1); } // end Game_Shutdown // WINMAIN //////////////////////////////////////////////// int WINAPI WinMain( HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow) { 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.hCursor = 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 hinstance_app = hinstance; // register the window class if (!RegisterClassEx(&winclass)) return(0); // create the window if (!(hwnd = CreateWindowEx(NULL, // extended style WINDOW_CLASS_NAME, // class L"DirectDraw Initialization Demo", // title WS_OVERLAPPED|WS_VISIBLE, 0,0, // initial x,y SCREEN_WIDTH,SCREEN_HEIGHT, // initial width, height NULL, // handle to parent NULL, // handle to menu hinstance,// instance of this application NULL))) // extra creation parms return(0); // save main window handle main_window_handle = hwnd; // initialize game here Game_Init(); //调整窗口大小 RECT window_rect = {0,0,SCREEN_WIDTH,SCREEN_HEIGHT} ; AdjustWindowRectEx(&window_rect,GetWindowStyle(main_window_handle),GetMenu(main_window_handle)!=NULL,GetWindowExStyle(main_window_handle)); // enter main event loop while(TRUE) { // test if there is a message in queue, if so get it 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 // closedown game here Game_Shutdown(); // return to Windows like this return(msg.wParam); } // end WinMain //定义交换函数 void Swap(int &x , int &y) { int temp = y ; y = x ; x = temp ; } //定义画线函数 int Draw_Line(int x0,int y0, int x1, int y1 , DWORD color , UINT *video_buffer, int stepx,int stepy) { int dx , //起点与终点的X方向间距 dy , //起点与终点的Y方向间距 dx2, //两倍的dx dy2, //两倍的dy x_inc , //实际的x步长值,带有符号 y_inc , //实际的y步长值,带有符号 p ; //误差项 dx = x1 - x0 ; //计算x间距 dy = y1 - y0 ; //计算y间距 //计算起点的缓冲地址 video_buffer+=x0+y0*stepy ; //确定x方向的步进值 if(dx>=0) { x_inc = stepx; } else { x_inc = -stepx ; dx = -dx ; } //确定y方向的步进值 if(dy>=0) { y_inc = stepy ; } else { y_inc = -stepy ; dy = -dy ; } //确定dx2,dy2的值 dx2 = dx<<1; dy2 = dy<<1 ; //进行步进的选择 if(dx <= dy) //斜率绝对值大于1 { Swap(dx,dy); Swap(x_inc,y_inc); Swap(dx2,dy2); } else //斜率绝对值小于1,不需要交换 { } //绘制直线 p = dy2 - dx ; //计算起点的误差值 for(int i = 0 ; i < dx ; i++) { *video_buffer = color ; video_buffer += x_inc ; if(p>=0) { video_buffer += y_inc ; p = p + dy2 - dx2 ; } else { p = p + dy2 ; } }// end for return 0 ; }// end Draw_Line //定义裁剪直线算法 int Clipper_Line(int& x0,int& y0,int& x1, int& y1,int screen_width,int screen_height) { #define CLIP_CODE_C 0x0000 #define CLIP_CODE_N 0x0008 #define CLIP_CODE_S 0x0004 #define CLIP_CODE_E 0x0002 #define CLIP_CODE_W 0x0001 #define CLIP_CODE_NE 0x000a #define CLIP_CODE_SE 0x0006 #define CLIP_CODE_NW 0x0009 #define CLIP_CODE_SW 0x0005 int xc0 = x0 ,yc0 = y0 , xc1=x1 , yc1=y1 ; int min_clip_x = SCREEN_WIDTH/3 ,min_clip_y = SCREEN_HEIGHT/3 ,max_clip_x = screen_width*2/3-1,max_clip_y=screen_height*2/3-1 ; int p0_code = 0 ,p1_code = 0 ; //确定各个顶点所在的位置代码 if(y0<min_clip_y) p0_code|=CLIP_CODE_N; else if(y0>max_clip_y) p0_code|=CLIP_CODE_S; if(x0<min_clip_x) p0_code|=CLIP_CODE_W; else if(x0>max_clip_x) p0_code|=CLIP_CODE_E; if(y1<min_clip_y) p1_code|=CLIP_CODE_N; else if(y1>max_clip_y) p1_code|=CLIP_CODE_S; if(x1<min_clip_x) p1_code|=CLIP_CODE_W; else if(x1>max_clip_x) p1_code|=CLIP_CODE_E; //先检测一些简单的情况 if(p0_code&p1_code) //有相同的位置代码,表示在裁剪区外部 return 0 ; if(p0_code==0&&p1_code==0) //表示两个点都在裁剪区内,不需要裁剪 return 1 ; //判断第一个点的位置代码 switch(p0_code) { case CLIP_CODE_C: break; case CLIP_CODE_N: { yc0 = min_clip_y ; xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0); break ; } case CLIP_CODE_S: { yc0 = max_clip_y; xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0); break ; } case CLIP_CODE_W: { xc0=min_clip_x; yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0); break; } case CLIP_CODE_E: { xc0=max_clip_x; yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0); break; } case CLIP_CODE_NE: { yc0 = min_clip_y; xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0); if(xc0<min_clip_x||xc0>max_clip_x) { xc0=max_clip_x; yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0); } break; } case CLIP_CODE_SE: { yc0 = max_clip_y; xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0); if(xc0<min_clip_x||xc0>max_clip_x) { xc0=max_clip_x; yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0); } break; } case CLIP_CODE_NW: { yc0=min_clip_y; xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0); if(xc0<min_clip_x||xc0>max_clip_x) { xc0=min_clip_x; yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0); } break; } case CLIP_CODE_SW: { yc0=max_clip_y; xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0); if(xc0<min_clip_x||xc0>max_clip_x) { xc0=min_clip_x; yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0); } break; } default: break; } // end switch(p0_code) //判断第二个点的位置代码 switch(p1_code) { case CLIP_CODE_C: break; case CLIP_CODE_N: { yc1 = min_clip_y ; xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0); break ; } case CLIP_CODE_S: { yc1 = max_clip_y; xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0); break ; } case CLIP_CODE_W: { xc1=min_clip_x; yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0); break; } case CLIP_CODE_E: { xc1=max_clip_x; yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0); break; } case CLIP_CODE_NE: { yc1 = min_clip_y; xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0); if(xc1<min_clip_x||xc1>max_clip_x) { xc1=max_clip_x; yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0); } break; } case CLIP_CODE_SE: { yc1 = max_clip_y; xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0); if(xc1<min_clip_x||xc1>max_clip_x) { xc1=max_clip_x; yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0); } break; } case CLIP_CODE_NW: { yc1=min_clip_y; xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0); if(xc1<min_clip_x||xc1>max_clip_x) { xc1=min_clip_x; yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0); } break; } case CLIP_CODE_SW: { yc1=max_clip_y; xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0); if(xc1<min_clip_x||xc1>max_clip_x) { xc1=min_clip_x; yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0); } break; } default: break; } // end switch(p1_code) //进行最后的检测 if(xc0>max_clip_x||xc0<min_clip_x|| yc0>max_clip_y||yc0<min_clip_y|| xc1>max_clip_x||xc1<min_clip_x|| yc1>max_clip_y||yc1<min_clip_y) { //表示全部在裁剪区外部 return 0 ; } //将裁减后的数据返回 x0 = xc0 ; x1 = xc1 ; y0 = yc0 ; y1 = yc1 ; return 1 ; }// end Clipper_Line //简单的平行绘制直线 int Draw_Simple_Line(int x0, int x1,int y , UINT * video_buffer,DWORD color,int mempitch) { //进行裁剪 if(y<min_clip_y) return 1 ; else if(y>max_clip_y) return 1 ; if(x0<min_clip_x) x0 = min_clip_x; else if(x0>max_clip_x) x0 = max_clip_x ; if(x1<min_clip_x) x1 = min_clip_x ; else if(x1>max_clip_x) x1 = max_clip_x ; //进行绘制 video_buffer+=y*mempitch; for(int x = x0 ; x<=x1;x++) { video_buffer[x]=color ; } } //绘制填充平顶三角形 int Draw_Top_Trangle(int x0,int y0, int x1,int y1, int x2,int y2, UINT * video_buffer, DWORD color,int mempitch) { //先判断下输入的三角形 if(y0==y1) { }else if(y0==y2) { Swap(x2,x1); Swap(y2,y1); }else if(y1==y2) { Swap(x0,x2); Swap(y0,y2); }else { return 1 ; //error \brief 不是平顶三角形 } if(x1<x0) { Swap(x1,x0); Swap(y1,y0); } else if(x1 == x0) { return 1 ;// error \brief不是三角形 } //计算左右误差 float dxy_left = (x2-x0)*1.0/(y2-y0) ; float dxy_right = (x1-x2)*1.0/(y1-y2); //开始进行填充 float xs = x0 ,xe = x1 ; for(int y=y0 ; y <=y2 ;y++) { Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch); xs += dxy_left ; xe += dxy_right ; } } // end Draw_Top_Trangle //绘制平底三角形 int Draw_Bottom_Trangle(int x0,int y0, int x1,int y1, int x2,int y2, UINT * video_buffer, DWORD color,int mempitch) { //先判断下输入的三角形 if(y2==y1) { }else if(y2==y0) { Swap(x0,x1); Swap(y0,y1); }else if(y0==y1) { Swap(x0,x2); Swap(y0,y2); }else { return 1 ; //error \brief 不是平顶三角形 } if(x1<x2) { Swap(x1,x2); } else if(x1 == x2) { return 1 ;// error \brief不是三角形 } //计算左右误差 float dxy_left = (x2-x0)*1.0/(y2-y0) ; float dxy_right = (x1-x0)*1.0/(y1-y0); //开始进行填充 float xs = x0 ,xe = x0 ; for(int y=y0 ; y <=y2 ;y++) { Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch); xs += dxy_left ; xe += dxy_right ; } }// end Draw_Bottom_Trangle //绘制任意三角形 int Draw_Trangle_2D(int x0,int y0, int x1,int y1, int x2,int y2, UINT * video_buffer, DWORD color,int mempitch) { if((x0==x1&&x1==x2) ||(y0==y1&&y1==y2)) { return 1 ; //error \brief传进来的点无法构成三角形 } //\brief 将三个顶点按照从上到下排序 if(y0>y1) { Swap(x0,x1); Swap(y0,y1); } if(y0>y2) { Swap(x0,x2); Swap(y0,y2); } if(y1>y2) { Swap(y1,y2); Swap(x1,x2); } //\brief查找最大的x坐标,和最小的y坐标 int min = (x0<x1?x0:x1); min = (min<x2?min:x2); int max = (x0>x1?x0:x1); max = (max>x2?max:x2); //\brief 进行绘制 if(y2<=min_clip_y||y0>=max_clip_y ||min>=max_clip_x||max<=min_clip_x) return 1 ; //\brief 全部在裁剪区之外 if(y0 == y1) //\brief 平顶三角形 { Draw_Top_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch); }else if(y1 == y2) { Draw_Bottom_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch); }else { int new_x = x0+0.5+(float)1.0*(y1-y0)*(x2-x0)/(y2-y0); Draw_Bottom_Trangle(x0,y0,new_x,y1,x1,y1,video_buffer,color,mempitch); Draw_Top_Trangle(new_x,y1,x1,y1,x2,y2,video_buffer,color,mempitch); } return 0 ; //\brief 成功画出三角形 }// end Draw_Trangle_2D ///////////////////////////////////////////////////////////
下图是运行结果:
OK,今天就到这里的,明天继续学习
jpg改rar![](https://images2015.cnblogs.com/blog/923483/201703/923483-20170327152015795-93577575.jpg)
![](https://images2015.cnblogs.com/blog/923483/201703/923483-20170327152015795-93577575.jpg)