Windows游戏编程大师技巧之三角形填充

一。三角形的种类

三角形一般可以分为如下的四种类型(这四种类型是对于计算机来说的,不是数学意义上的分类):
平顶三角形:就是在计算机中显示的上面两个顶点的Y坐标相同。
平底三角形:就是在计算机中显示的时候下面两个顶点的Y坐标相同。
右边为主三角形:这种三角形三个点的Y坐标都不相同,但是右边的一条边是最长的斜边
左边为主的三角形:这种三角形的三个点的Y坐标不相同,但是左边的一条边是最长的斜边。
附上各个不同三角形的图:

是不是所有的三角形大致可以分为这四种?而我们要进行填充的时候,前面两种的填充应该比较简单,只要从顶点开始一行一行的进行填充就可以做到了,如下图所示:

进行这样的操作应该很容易实现吧,同样的对平顶的三角形我们也可以这样来一行一行的使用像素进行填充。
至于后面两种情况的三角形,我们能够很简单的将他们分别变成两个三角形,一个平顶一个平底:

所以,我们实现了前面两个的三角形的填充,后面两个的也就很容易实现了。

二。代码实现

下面是实现平顶的三角形填充的代码:
  1. int Draw_Top_Trangle(int x0,int y0,  
  2.                     int x1,int y1,  
  3.                     int x2,int y2,  
  4.                     UINT * video_buffer,  
  5.                     DWORD color,int mempitch)  
  6. {  
  7.     //先判断下输入的三角形  
  8.     if(y0==y1)  
  9.     {  
  10.     }else if(y0==y2)  
  11.     {  
  12.         Swap(x2,x1);  
  13.         Swap(y2,y1);  
  14.     }else if(y1==y2)  
  15.     {  
  16.         Swap(x0,x2);  
  17.         Swap(y0,y2);  
  18.     }else  
  19.     {  
  20.         return 1 ; //error \brief 不是平顶三角形  
  21.     }  
  22.   
  23.     if(x1<x0)  
  24.     {  
  25.         Swap(x1,x0);  
  26.         Swap(y1,y0);  
  27.     }  
  28.     else if(x1 == x0)  
  29.     {  
  30.         return 1 ;// error \brief不是三角形  
  31.     }  
  32.   
  33.     //计算左右误差  
  34.     float dxy_left = (x2-x0)*1.0/(y2-y0) ;  
  35.     float dxy_right = (x1-x2)*1.0/(y1-y2);  
  36.   
  37.     //开始进行填充  
  38.     float xs = x0 ,xe = x1 ;  
  39.     for(int y=y0 ; y <=y2 ;y++)  
  40.     {  
  41.         Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch);  
  42.   
  43.         xs += dxy_left ;  
  44.         xe += dxy_right ;  
  45.     }  
  46. // 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的实现版本)

同样的,来看看我们的平底三角形填充的实现:
  1. int Draw_Bottom_Trangle(int x0,int y0,  
  2.                     int x1,int y1,  
  3.                     int x2,int y2,  
  4.                     UINT * video_buffer,  
  5.                     DWORD color,int mempitch)  
  6. {  
  7.     //先判断下输入的三角形  
  8.     if(y2==y1)  
  9.     {  
  10.     }else if(y2==y0)  
  11.     {  
  12.         Swap(x0,x1);  
  13.         Swap(y0,y1);  
  14.     }else if(y0==y1)  
  15.     {  
  16.         Swap(x0,x2);  
  17.         Swap(y0,y2);  
  18.     }else  
  19.     {  
  20.         return 1 ; //error \brief 不是平顶三角形  
  21.     }  
  22.   
  23.     if(x1<x2)  
  24.     {  
  25.         Swap(x1,x2);  
  26.     }  
  27.     else if(x1 == x2)  
  28.     {  
  29.         return 1 ;// error \brief不是三角形  
  30.     }  
  31.   
  32.     //计算左右误差  
  33.     float dxy_left = (x2-x0)*1.0/(y2-y0) ;  
  34.     float dxy_right = (x1-x0)*1.0/(y1-y0);  
  35.   
  36.     //开始进行填充  
  37.     float xs = x0 ,xe = x0 ;  
  38.     for(int y=y0 ; y <=y2 ;y++)  
  39.     {  
  40.         Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch);  
  41.   
  42.         xs += dxy_left ;  
  43.         xe += dxy_right ;  
  44.     }  
  45. }// 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

和上面平顶的算法基本上一致,只有图中点的顺序不同:

好了,这两个函数都实现了,接下来看看我们任意的三角形绘制的实现吧:
  1. int Draw_Trangle_2D(int x0,int y0,  
  2.                     int x1,int y1,  
  3.                     int x2,int y2,  
  4.                     UINT * video_buffer,  
  5.                     DWORD color,int mempitch)  
  6. {  
  7.     if((x0==x1&&x1==x2)  
  8.         ||(y0==y1&&y1==y2))  
  9.     {  
  10.         return 1 ; //error \brief传进来的点无法构成三角形  
  11.     }  
  12.   
  13.     //\brief 将三个顶点按照从上到下排序  
  14.     if(y0>y1)  
  15.     {  
  16.         Swap(x0,x1);  
  17.         Swap(y0,y1);  
  18.     }  
  19.   
  20.     if(y0>y2)  
  21.     {  
  22.         Swap(x0,x2);  
  23.         Swap(y0,y2);  
  24.     }  
  25.   
  26.     if(y1>y2)  
  27.     {  
  28.         Swap(y1,y2);  
  29.         Swap(x1,x2);  
  30.     }  
  31.   
  32.     //\brief查找最大的x坐标,和最小的y坐标  
  33.     int min = (x0<x1?x0:x1);  
  34.     min = (min<x2?min:x2);  
  35.     int max = (x0>x1?x0:x1);  
  36.     max = (max>x2?max:x2);  
  37.   
  38.     //\brief 进行绘制  
  39.     if(y2<=min_clip_y||y0>=max_clip_y  
  40.         ||min>=max_clip_x||max<=min_clip_x)  
  41.         return 1 ;  //\brief 全部在裁剪区之外  
  42.     if(y0 == y1) //\brief 平顶三角形  
  43.     {  
  44.         Draw_Top_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch);  
  45.     }else if(y1 == y2)  
  46.     {  
  47.         Draw_Bottom_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch);  
  48.     }else  
  49.     {  
  50.         int new_x = x0+0.5+(float)1.0*(y1-y0)*(x2-x0)/(y2-y0);  
  51.         Draw_Bottom_Trangle(x0,y0,new_x,y1,x1,y1,video_buffer,color,mempitch);  
  52.         Draw_Top_Trangle(new_x,y1,x1,y1,x2,y2,video_buffer,color,mempitch);  
  53.     }  
  54.   
  55.   
  56.     return 0 ; //\brief 成功画出三角形  
  57. }// 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的坐标就是显示屏幕上从上到下中间的那个点了,想象是不是这样的!?)。分离了之后,我们就可以分别调用绘制平底和平顶的三角形的算法来实现了。

以下是整个工程的完整代码:
  1. // DEMO8_8.CPP 此Demo演示32位窗口模式下,创建任意填充三角形的算法  
  2.   
  3. // INCLUDES ///////////////////////////////////////////////  
  4.   
  5. #define WIN32_LEAN_AND_MEAN  // just say no to MFC  
  6.   
  7. #define INITGUID // make sure directX guids are included  
  8.   
  9. #include <windows.h>   // include important windows stuff  
  10. #include <windowsx.h>   
  11. #include <mmsystem.h>  
  12. #include <iostream> // include important C/C++ stuff  
  13. using namespace std ;  
  14. #include <conio.h>  
  15. #include <stdlib.h>  
  16. #include <malloc.h>  
  17. #include <memory.h>  
  18. #include <string.h>  
  19. #include <stdarg.h>  
  20. #include <stdio.h>   
  21. #include <math.h>  
  22. #include <io.h>  
  23. #include <fcntl.h>  
  24.   
  25. #include <ddraw.h> // include directdraw  
  26. #pragma comment(lib,"ddraw.lib")  
  27. // DEFINES ////////////////////////////////////////////////  
  28.   
  29. // defines for windows   
  30. #define WINDOW_CLASS_NAME L"WINCLASS1"  
  31.   
  32. // default screen size  
  33. #define SCREEN_WIDTH    640  // size of screen  
  34. #define SCREEN_HEIGHT   480  
  35. #define SCREEN_BPP      32   // bits per pixel  
  36. #define MAX_COLORS      256  // maximum colors  
  37.   
  38. // TYPES //////////////////////////////////////////////////////  
  39.   
  40. // basic unsigned types  
  41. typedef unsigned short USHORT;  
  42. typedef unsigned short WORD;  
  43. typedef unsigned char  UCHAR;  
  44. typedef unsigned char  BYTE;  
  45.   
  46. // MACROS /////////////////////////////////////////////////  
  47.   
  48. #define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)  
  49. #define KEYUP(vk_code)   ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)  
  50.   
  51. // initializes a direct draw struct  
  52. #define DD_INIT_STRUCT(ddstruct) { memset(&ddstruct,0,sizeof(ddstruct)); ddstruct.dwSize=sizeof(ddstruct); }  
  53.   
  54. //initializes a RGB value  
  55. #define _RGB16BIT565(r,g,b) ((b & 31) + ((g & 63) << 5) + ((r & 31) << 11))  
  56. #define _RGB32BIT(a,r,g,b) ((b) + ((g) << 8) + ((r) << 16) + ((a) << 24))  
  57.   
  58. // GLOBALS ////////////////////////////////////////////////  
  59. HWND      main_window_handle = NULL; // globally track main window  
  60. HINSTANCE hinstance_app      = NULL; // globally track hinstance  
  61.   
  62. // directdraw stuff  
  63.   
  64. LPDIRECTDRAW7         lpdd         = NULL;   // dd object  
  65. LPDIRECTDRAWSURFACE7  lpddsprimary = NULL;   // dd primary surface  
  66. LPDIRECTDRAWSURFACE7  lpddsback    = NULL;   // dd back surface  
  67. LPDIRECTDRAWPALETTE   lpddpal      = NULL;   // a pointer to the created dd palette  
  68. LPDIRECTDRAWCLIPPER   lpddclipper  = NULL;   // dd clipper  
  69. PALETTEENTRY          palette[256];          // color palette  
  70. PALETTEENTRY          save_palette[256];     // used to save palettes  
  71. DDSURFACEDESC2        ddsd;                  // a direct draw surface description struct  
  72. DDBLTFX               ddbltfx;               // used to fill  
  73. DDSCAPS2              ddscaps;               // a direct draw surface capabilities struct  
  74. HRESULT               ddrval;                // result back from dd calls  
  75. DWORD                 start_clock_count = 0; // used for timing  
  76. LPDIRECTDRAWSURFACE7  lpddsOffScreen = NULL ;  //离屏表面  
  77. int                   window_close  =  0 ;    //标识窗口是否关闭  
  78.   
  79. // these defined the general clipping rectangle  
  80. int min_clip_x = 0,                          // clipping rectangle   
  81.     max_clip_x = 1366-1,  
  82.     min_clip_y = 0,  
  83.     max_clip_y = 768-1;  
  84.   
  85. // these are overwritten globally by DD_Init()  
  86. int screen_width  = SCREEN_WIDTH,            // width of screen  
  87.     screen_height = SCREEN_HEIGHT,           // height of screen  
  88.     screen_bpp    = SCREEN_BPP;              // bits per pixel  
  89.   
  90.   
  91. char buffer[80];                     // general printing buffer  
  92.   
  93. //申明画线方法  
  94. int Draw_Line(int x0, int y0, int x1, int y1, DWORD color , UINT * video_buffer , int stepx , int stepy);  
  95.   
  96. //裁剪直线算法  
  97. int Clipper_Line(int& x0,int& y0,int& x1, int& y1,int screen_width,int screen_height);  
  98.   
  99. //交换值  
  100. void Swap(int &x , int &y) ;  
  101.   
  102. //绘制填充平顶三角形  
  103. int Draw_Top_Trangle(int x0,int y0,  
  104.                     int x1,int y1,  
  105.                     int x2,int y2,  
  106.                     UINT * video_buffer,  
  107.                     DWORD color,int mempitch);  
  108.   
  109. //绘制平底三角形  
  110. int Draw_Bottom_Trangle(int x0,int y0,  
  111.                     int x1,int y1,  
  112.                     int x2,int y2,  
  113.                     UINT * video_buffer,  
  114.                     DWORD color,int mempitch);  
  115.   
  116. //简单的平行绘制直线  
  117. int Draw_Simple_Line(int x0, int x1,int y , UINT * video_buffer,DWORD color,int mempitch);  
  118.   
  119. //绘制任意三角形  
  120. int Draw_Trangle_2D(int x0,int y0,  
  121.                     int x1,int y1,  
  122.                     int x2,int y2,  
  123.                     UINT * video_buffer,  
  124.                     DWORD color,int mempitch);  
  125. // FUNCTIONS //////////////////////////////////////////////  
  126. LRESULT CALLBACK WindowProc(HWND hwnd,   
  127.                             UINT msg,   
  128.                             WPARAM wparam,   
  129.                             LPARAM lparam)  
  130. {  
  131. // this is the main message handler of the system  
  132. PAINTSTRUCT     ps;     // used in WM_PAINT  
  133. HDC             hdc;    // handle to a device context  
  134. char buffer[80];        // used to print strings  
  135.   
  136. // what is the message   
  137. switch(msg)  
  138.     {     
  139.     case WM_CREATE:   
  140.         {  
  141.         // do initialization stuff here  
  142.         // return success  
  143.         return(0);  
  144.         } break;  
  145.      
  146.     case WM_PAINT:   
  147.         {  
  148.         // simply validate the window   
  149.         hdc = BeginPaint(hwnd,&ps);    
  150.           
  151.         // end painting  
  152.         EndPaint(hwnd,&ps);  
  153.   
  154.         // return success  
  155.         return(0);  
  156.         } break;  
  157.   
  158.     case WM_DESTROY:   
  159.         {  
  160.   
  161.         // kill the application, this sends a WM_QUIT message   
  162.         PostQuitMessage(0);  
  163.   
  164.         // return success  
  165.         return(0);  
  166.         } break;  
  167.   
  168.     default:break;  
  169.   
  170.     } // end switch  
  171.   
  172. // process any messages that we didn't take care of   
  173. return (DefWindowProc(hwnd, msg, wparam, lparam));  
  174.   
  175. // end WinProc  
  176.   
  177. ///////////////////////////////////////////////////////////  
  178.   
  179. //程序主循环  
  180. int Game_Main(void *parms = NULL, int num_parms = 0)  
  181. {  
  182. // this is the main loop of the game, do all your processing  
  183. // here  
  184.   
  185. // for now test if user is hitting ESC and send WM_CLOSE  
  186. if(window_close)  
  187.     return 1 ;  
  188. if (KEYDOWN(VK_ESCAPE))  
  189. {  
  190.     PostMessage(main_window_handle,WM_CLOSE,0,0);  
  191.     window_close = 1 ;  
  192. }  
  193.      
  194.   
  195. //清空表面  
  196. DDBLTFX bltfx ;  
  197. DD_INIT_STRUCT(bltfx);  
  198. bltfx.dwFillColor = 0 ;  
  199. if(FAILED(lpddsOffScreen->Blt(NULL,NULL,NULL,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx)))  
  200. {  
  201.     OutputDebugString(L"OffScreen Blt error");  
  202.     return 1 ;  
  203. }  
  204.   
  205. //锁定  
  206. DDSURFACEDESC2 ddsd ;  
  207. DD_INIT_STRUCT(ddsd);  
  208. if(FAILED(lpddsOffScreen->Lock(NULL,&ddsd,DDLOCK_WAIT|DDLOCK_SURFACEMEMORYPTR,NULL)))  
  209. {  
  210.     OutputDebugString(L"Lock error");  
  211.     return 1 ;  
  212. }  
  213.   
  214. //获取窗口位置  
  215. RECT rect ;  
  216. GetWindowRect(main_window_handle,&rect);  
  217. //画填充的三角形  
  218. int x0 = rand()%SCREEN_WIDTH+rect.left;  
  219. int x1 = rand()%SCREEN_WIDTH+rect.left ;  
  220. int x2 = rand()%SCREEN_WIDTH + rect.left ;  
  221. int y0 = rand()%SCREEN_HEIGHT + rect.top;  
  222. int y1 = rand()%SCREEN_HEIGHT+ rect.top+100;  
  223. int y2 = rand()%SCREEN_HEIGHT + rect.top;  
  224. Draw_Trangle_2D(x0,y0,x1,y1,x2,y2,(UINT*)ddsd.lpSurface,  
  225.     _RGB32BIT(0,255,255,255),ddsd.lPitch>>2);  
  226.   
  227. //解锁  
  228. if(FAILED(lpddsOffScreen->Unlock(NULL)))  
  229. {  
  230.     OutputDebugString(L"Unlock error");  
  231.     return 1 ;  
  232. }  
  233.   
  234. //Blt到主表面  
  235. if(FAILED(lpddsprimary->Blt(NULL,lpddsOffScreen,NULL,DDBLT_WAIT,NULL)))  
  236. {  
  237.     OutputDebugString(L"Blt error");  
  238.     return 1 ;  
  239. }  
  240.   
  241. // return success or failure or your own return code here  
  242. return(1);  
  243.   
  244. // end Game_Main  
  245.   
  246. ////////////////////////////////////////////////////////////  
  247.   
  248. int Game_Init(void *parms = NULL, int num_parms = 0)  
  249. {  
  250. // this is called once after the initial window is created and  
  251. // before the main event loop is entered, do all your initialization  
  252. // here  
  253.   
  254. // create IDirectDraw interface 7.0 object and test for error  
  255. if (FAILED(DirectDrawCreateEx(NULL, (void **)&lpdd, IID_IDirectDraw7, NULL)))  
  256.    return(0);  
  257.   
  258. // set cooperation to normal since this will be a windowed app  
  259. if(FAILED(lpdd->SetCooperativeLevel(main_window_handle, DDSCL_NORMAL)))  
  260. {  
  261.     MessageBox(NULL,L"SetCooperativeLevel error",L"error",MB_OK);  
  262.     return 0 ;  
  263. }  
  264.   
  265. //创建裁剪器  
  266. if(FAILED(lpdd->CreateClipper(0,&lpddclipper,NULL)))  
  267. {  
  268.     OutputDebugString(L"CreateClipper error");  
  269.     return 1 ;  
  270. }  
  271.   
  272. //将裁减器关联窗口,也就是用窗口的尺寸作为裁剪器的裁剪序列  
  273. if(FAILED(lpddclipper->SetHWnd(0,main_window_handle)))  
  274. {  
  275.     OutputDebugString(L"SetHWnd error");  
  276.     return 1 ;  
  277. }  
  278.   
  279. //创建主表面  
  280. memset(&ddsd,0,sizeof(ddsd));  
  281. ddsd.dwSize = sizeof(ddsd);  
  282. ddsd.dwFlags = DDSD_CAPS ;  
  283. ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;  
  284.   
  285. if(FAILED(lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL)))  
  286. {  
  287.     MessageBox(NULL,L"CreateSurface error",L"error",MB_OK);  
  288.     return 0 ;  
  289. }  
  290.   
  291. //将裁减器关联到表面  
  292. if(FAILED(lpddsprimary->SetClipper(lpddclipper)))  
  293. {  
  294.     OutputDebugString(L"SetClipper error");  
  295.     return 1 ;  
  296. }  
  297.   
  298. //创建一个离屏表面  
  299. DD_INIT_STRUCT(ddsd);  
  300. ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;  
  301. ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT ;  
  302. ddsd.dwHeight = 786 ;  
  303. ddsd.dwWidth = 1366 ;  
  304. if(FAILED(lpdd->CreateSurface(&ddsd,&lpddsOffScreen,NULL)))  
  305. {  
  306.     OutputDebugString(L"OffScreen CreateSurface error");  
  307.     return 1 ;  
  308. }  
  309.   
  310.   
  311.   
  312. // return success or failure or your own return code here  
  313. return(1);  
  314.   
  315. // end Game_Init  
  316.   
  317. /////////////////////////////////////////////////////////////  
  318.   
  319. int Game_Shutdown(void *parms = NULL, int num_parms = 0)  
  320. {  
  321. // this is called after the game is exited and the main event  
  322. // loop while is exited, do all you cleanup and shutdown here  
  323.   
  324. // simply blow away the IDirectDraw4 interface  
  325.   
  326. if(lpddclipper)  
  327. {  
  328.     lpddclipper->Release();  
  329.     lpddclipper = NULL ;  
  330. }  
  331.   
  332. if(lpddsprimary)  
  333. {  
  334.     lpddsprimary->Release();  
  335.     lpddsprimary = NULL ;  
  336. }  
  337.   
  338. if (lpdd)  
  339.    {  
  340.    lpdd->Release();  
  341.    lpdd = NULL;  
  342.    } // end if  
  343.   
  344. // return success or failure or your own return code here  
  345. return(1);  
  346.   
  347. // end Game_Shutdown  
  348.   
  349. // WINMAIN ////////////////////////////////////////////////  
  350. int WINAPI WinMain( HINSTANCE hinstance,  
  351.                     HINSTANCE hprevinstance,  
  352.                     LPSTR lpcmdline,  
  353.                     int ncmdshow)  
  354. {  
  355.   
  356.     WNDCLASSEX winclass; // this will hold the class we create  
  357.     HWND       hwnd;     // generic window handle  
  358.     MSG        msg;      // generic message  
  359.     HDC        hdc;      // graphics device context  
  360.   
  361.     // first fill in the window class stucture  
  362.     winclass.cbSize         = sizeof(WNDCLASSEX);  
  363.     winclass.style          = CS_DBLCLKS | CS_OWNDC |   
  364.                               CS_HREDRAW | CS_VREDRAW;  
  365.     winclass.lpfnWndProc    = WindowProc;  
  366.     winclass.cbClsExtra     = 0;  
  367.     winclass.cbWndExtra     = 0;  
  368.     winclass.hInstance      = hinstance;  
  369.     winclass.hIcon          = LoadIcon(NULL, IDI_APPLICATION);  
  370.     winclass.hCursor        = LoadCursor(NULL, IDC_ARROW);   
  371.     winclass.hbrBackground  = (HBRUSH)GetStockObject(BLACK_BRUSH);  
  372.     winclass.lpszMenuName   = NULL;  
  373.     winclass.lpszClassName  = WINDOW_CLASS_NAME;  
  374.     winclass.hIconSm        = LoadIcon(NULL, IDI_APPLICATION);  
  375.   
  376.     // save hinstance in global  
  377.     hinstance_app = hinstance;  
  378.   
  379.     // register the window class  
  380.     if (!RegisterClassEx(&winclass))  
  381.         return(0);  
  382.   
  383.     // create the window  
  384.     if (!(hwnd = CreateWindowEx(NULL,                  // extended style  
  385.                                 WINDOW_CLASS_NAME,     // class  
  386.                                 L"DirectDraw Initialization Demo"// title  
  387.                                 WS_OVERLAPPED|WS_VISIBLE,  
  388.                                 0,0,      // initial x,y  
  389.                                 SCREEN_WIDTH,SCREEN_HEIGHT,  // initial width, height  
  390.                                 NULL,     // handle to parent   
  391.                                 NULL,     // handle to menu  
  392.                                 hinstance,// instance of this application  
  393.                                 NULL))) // extra creation parms  
  394.         return(0);  
  395.   
  396.     // save main window handle  
  397.     main_window_handle = hwnd;  
  398.   
  399.     // initialize game here  
  400.     Game_Init();  
  401.   
  402.     //调整窗口大小  
  403.     RECT window_rect = {0,0,SCREEN_WIDTH,SCREEN_HEIGHT} ;  
  404.     AdjustWindowRectEx(&window_rect,GetWindowStyle(main_window_handle),GetMenu(main_window_handle)!=NULL,GetWindowExStyle(main_window_handle));  
  405.   
  406.     // enter main event loop  
  407.     while(TRUE)  
  408.         {  
  409.         // test if there is a message in queue, if so get it  
  410.         if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))  
  411.            {   
  412.            // test if this is a quit  
  413.            if (msg.message == WM_QUIT)  
  414.                break;  
  415.       
  416.            // translate any accelerator keys  
  417.            TranslateMessage(&msg);  
  418.   
  419.            // send the message to the window proc  
  420.            DispatchMessage(&msg);  
  421.            } // end if  
  422.       
  423.            // main game processing goes here  
  424.            Game_Main();  
  425.          
  426.         } // end while  
  427.   
  428.     // closedown game here  
  429.     Game_Shutdown();  
  430.   
  431.     // return to Windows like this  
  432.     return(msg.wParam);  
  433.   
  434. // end WinMain  
  435.   
  436. //定义交换函数  
  437. void Swap(int &x , int &y)  
  438. {  
  439.     int temp = y ;  
  440.     y = x ;  
  441.     x = temp ;  
  442. }  
  443.   
  444. //定义画线函数  
  445. int Draw_Line(int x0,int y0, int x1, int y1 , DWORD color , UINT *video_buffer, int stepx,int stepy)  
  446. {  
  447.     int dx ,  //起点与终点的X方向间距  
  448.         dy ,  //起点与终点的Y方向间距  
  449.         dx2, //两倍的dx  
  450.         dy2,  //两倍的dy  
  451.         x_inc ,  //实际的x步长值,带有符号  
  452.         y_inc , //实际的y步长值,带有符号  
  453.         p ;     //误差项  
  454.   
  455.     dx = x1 - x0 ;  //计算x间距  
  456.     dy = y1 - y0 ;  //计算y间距  
  457.   
  458.     //计算起点的缓冲地址  
  459.     video_buffer+=x0+y0*stepy ;  
  460.   
  461.     //确定x方向的步进值  
  462.     if(dx>=0)  
  463.     {  
  464.         x_inc = stepx;  
  465.     }  
  466.     else  
  467.     {  
  468.         x_inc = -stepx ;  
  469.         dx = -dx ;  
  470.     }  
  471.   
  472.     //确定y方向的步进值  
  473.     if(dy>=0)  
  474.     {  
  475.         y_inc = stepy ;  
  476.     }  
  477.     else  
  478.     {  
  479.         y_inc = -stepy ;  
  480.         dy = -dy ;  
  481.     }  
  482.   
  483.     //确定dx2,dy2的值  
  484.     dx2 = dx<<1;  
  485.     dy2 = dy<<1 ;  
  486.   
  487.     //进行步进的选择  
  488.     if(dx <= dy) //斜率绝对值大于1  
  489.     {  
  490.         Swap(dx,dy);  
  491.         Swap(x_inc,y_inc);  
  492.         Swap(dx2,dy2);  
  493.     }  
  494.     else //斜率绝对值小于1,不需要交换  
  495.     {  
  496.     }  
  497.   
  498.   
  499.     //绘制直线  
  500.     p = dy2 - dx ;  //计算起点的误差值  
  501.     for(int i = 0 ; i < dx ; i++)  
  502.     {  
  503.         *video_buffer = color ;  
  504.           
  505.         video_buffer += x_inc ;  
  506.         if(p>=0)  
  507.         {  
  508.             video_buffer += y_inc ;  
  509.             p = p + dy2 - dx2 ;  
  510.         }  
  511.         else  
  512.         {  
  513.             p = p + dy2 ;  
  514.         }  
  515.     }// end for  
  516.   
  517.   
  518.     return 0 ;  
  519. }// end Draw_Line  
  520.   
  521. //定义裁剪直线算法  
  522. int Clipper_Line(int& x0,int& y0,int& x1, int& y1,int screen_width,int screen_height)  
  523. {  
  524. #define CLIP_CODE_C 0x0000  
  525. #define CLIP_CODE_N 0x0008  
  526. #define CLIP_CODE_S 0x0004  
  527. #define CLIP_CODE_E 0x0002  
  528. #define CLIP_CODE_W 0x0001  
  529.   
  530. #define CLIP_CODE_NE 0x000a  
  531. #define CLIP_CODE_SE 0x0006  
  532. #define CLIP_CODE_NW 0x0009  
  533. #define CLIP_CODE_SW 0x0005  
  534.     int xc0 = x0 ,yc0 = y0 , xc1=x1 , yc1=y1 ;  
  535.     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 ;  
  536.     int p0_code = 0 ,p1_code = 0 ;  
  537.   
  538.     //确定各个顶点所在的位置代码  
  539.     if(y0<min_clip_y)  
  540.         p0_code|=CLIP_CODE_N;  
  541.     else if(y0>max_clip_y)  
  542.         p0_code|=CLIP_CODE_S;  
  543.   
  544.     if(x0<min_clip_x)  
  545.         p0_code|=CLIP_CODE_W;  
  546.     else if(x0>max_clip_x)  
  547.         p0_code|=CLIP_CODE_E;  
  548.   
  549.     if(y1<min_clip_y)  
  550.         p1_code|=CLIP_CODE_N;  
  551.     else if(y1>max_clip_y)  
  552.         p1_code|=CLIP_CODE_S;  
  553.   
  554.     if(x1<min_clip_x)  
  555.         p1_code|=CLIP_CODE_W;  
  556.     else if(x1>max_clip_x)  
  557.         p1_code|=CLIP_CODE_E;  
  558.   
  559.     //先检测一些简单的情况  
  560.     if(p0_code&p1_code) //有相同的位置代码,表示在裁剪区外部  
  561.         return 0 ;  
  562.     if(p0_code==0&&p1_code==0) //表示两个点都在裁剪区内,不需要裁剪  
  563.         return 1 ;  
  564.   
  565.     //判断第一个点的位置代码  
  566.     switch(p0_code)  
  567.     {  
  568.     case CLIP_CODE_C:  
  569.         break;  
  570.     case CLIP_CODE_N:  
  571.         {  
  572.             yc0 = min_clip_y ;  
  573.             xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);  
  574.             break ;  
  575.         }  
  576.     case CLIP_CODE_S:  
  577.         {  
  578.             yc0 = max_clip_y;  
  579.             xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);  
  580.             break ;  
  581.         }  
  582.     case CLIP_CODE_W:  
  583.         {  
  584.             xc0=min_clip_x;  
  585.             yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);  
  586.             break;  
  587.         }  
  588.     case CLIP_CODE_E:  
  589.         {  
  590.             xc0=max_clip_x;  
  591.             yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);  
  592.             break;  
  593.         }  
  594.     case CLIP_CODE_NE:  
  595.         {  
  596.             yc0 = min_clip_y;  
  597.             xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);  
  598.   
  599.             if(xc0<min_clip_x||xc0>max_clip_x)  
  600.             {  
  601.                 xc0=max_clip_x;  
  602.                 yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);  
  603.             }  
  604.             break;  
  605.         }  
  606.     case CLIP_CODE_SE:  
  607.         {  
  608.             yc0 = max_clip_y;  
  609.             xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);  
  610.   
  611.             if(xc0<min_clip_x||xc0>max_clip_x)  
  612.             {  
  613.                 xc0=max_clip_x;  
  614.                 yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);  
  615.             }  
  616.             break;  
  617.         }  
  618.     case CLIP_CODE_NW:  
  619.         {  
  620.             yc0=min_clip_y;  
  621.             xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);  
  622.   
  623.             if(xc0<min_clip_x||xc0>max_clip_x)  
  624.             {  
  625.                 xc0=min_clip_x;  
  626.                 yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);  
  627.             }  
  628.             break;  
  629.         }  
  630.     case CLIP_CODE_SW:  
  631.         {  
  632.             yc0=max_clip_y;  
  633.             xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);  
  634.   
  635.             if(xc0<min_clip_x||xc0>max_clip_x)  
  636.             {  
  637.                 xc0=min_clip_x;  
  638.                 yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);  
  639.             }  
  640.             break;  
  641.         }  
  642.     default:  
  643.         break;  
  644.     } // end switch(p0_code)  
  645.   
  646.     //判断第二个点的位置代码  
  647.     switch(p1_code)  
  648.     {  
  649.     case CLIP_CODE_C:  
  650.         break;  
  651.     case CLIP_CODE_N:  
  652.         {  
  653.             yc1 = min_clip_y ;  
  654.             xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);  
  655.             break ;  
  656.         }  
  657.     case CLIP_CODE_S:  
  658.         {  
  659.             yc1 = max_clip_y;  
  660.             xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);  
  661.             break ;  
  662.         }  
  663.     case CLIP_CODE_W:  
  664.         {  
  665.             xc1=min_clip_x;  
  666.             yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);  
  667.             break;  
  668.         }  
  669.     case CLIP_CODE_E:  
  670.         {  
  671.             xc1=max_clip_x;  
  672.             yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);  
  673.             break;  
  674.         }  
  675.     case CLIP_CODE_NE:  
  676.         {  
  677.             yc1 = min_clip_y;  
  678.             xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);  
  679.   
  680.             if(xc1<min_clip_x||xc1>max_clip_x)  
  681.             {  
  682.                 xc1=max_clip_x;  
  683.                 yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);  
  684.             }  
  685.             break;  
  686.         }  
  687.     case CLIP_CODE_SE:  
  688.         {  
  689.             yc1 = max_clip_y;  
  690.             xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);  
  691.   
  692.             if(xc1<min_clip_x||xc1>max_clip_x)  
  693.             {  
  694.                 xc1=max_clip_x;  
  695.                 yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);  
  696.             }  
  697.             break;  
  698.         }  
  699.     case CLIP_CODE_NW:  
  700.         {  
  701.             yc1=min_clip_y;  
  702.             xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);  
  703.   
  704.             if(xc1<min_clip_x||xc1>max_clip_x)  
  705.             {  
  706.                 xc1=min_clip_x;  
  707.                 yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);  
  708.             }  
  709.             break;  
  710.         }  
  711.     case CLIP_CODE_SW:  
  712.         {  
  713.             yc1=max_clip_y;  
  714.             xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);  
  715.   
  716.             if(xc1<min_clip_x||xc1>max_clip_x)  
  717.             {  
  718.                 xc1=min_clip_x;  
  719.                 yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);  
  720.             }  
  721.             break;  
  722.         }  
  723.     default:  
  724.         break;  
  725.     } // end switch(p1_code)  
  726.   
  727.     //进行最后的检测  
  728.     if(xc0>max_clip_x||xc0<min_clip_x||  
  729.         yc0>max_clip_y||yc0<min_clip_y||  
  730.         xc1>max_clip_x||xc1<min_clip_x||  
  731.         yc1>max_clip_y||yc1<min_clip_y)  
  732.     {  
  733.         //表示全部在裁剪区外部  
  734.         return 0 ;  
  735.     }  
  736.   
  737.     //将裁减后的数据返回  
  738.     x0 = xc0 ;  
  739.     x1 = xc1 ;  
  740.     y0 = yc0 ;  
  741.     y1 = yc1 ;  
  742.   
  743.     return 1 ;  
  744. }// end Clipper_Line  
  745.   
  746. //简单的平行绘制直线  
  747. int Draw_Simple_Line(int x0, int x1,int y , UINT * video_buffer,DWORD color,int mempitch)  
  748. {  
  749.     //进行裁剪  
  750.     if(y<min_clip_y)  
  751.         return 1 ;  
  752.     else if(y>max_clip_y)  
  753.         return 1 ;  
  754.     if(x0<min_clip_x)  
  755.         x0 = min_clip_x;  
  756.     else if(x0>max_clip_x)  
  757.         x0 = max_clip_x ;  
  758.     if(x1<min_clip_x)  
  759.         x1 = min_clip_x ;  
  760.     else if(x1>max_clip_x)  
  761.         x1 = max_clip_x ;  
  762.   
  763.     //进行绘制  
  764.     video_buffer+=y*mempitch;  
  765.     for(int x = x0 ; x<=x1;x++)  
  766.     {  
  767.         video_buffer[x]=color ;  
  768.     }  
  769. }  
  770.   
  771. //绘制填充平顶三角形  
  772. int Draw_Top_Trangle(int x0,int y0,  
  773.                     int x1,int y1,  
  774.                     int x2,int y2,  
  775.                     UINT * video_buffer,  
  776.                     DWORD color,int mempitch)  
  777. {  
  778.     //先判断下输入的三角形  
  779.     if(y0==y1)  
  780.     {  
  781.     }else if(y0==y2)  
  782.     {  
  783.         Swap(x2,x1);  
  784.         Swap(y2,y1);  
  785.     }else if(y1==y2)  
  786.     {  
  787.         Swap(x0,x2);  
  788.         Swap(y0,y2);  
  789.     }else  
  790.     {  
  791.         return 1 ; //error \brief 不是平顶三角形  
  792.     }  
  793.   
  794.     if(x1<x0)  
  795.     {  
  796.         Swap(x1,x0);  
  797.         Swap(y1,y0);  
  798.     }  
  799.     else if(x1 == x0)  
  800.     {  
  801.         return 1 ;// error \brief不是三角形  
  802.     }  
  803.   
  804.     //计算左右误差  
  805.     float dxy_left = (x2-x0)*1.0/(y2-y0) ;  
  806.     float dxy_right = (x1-x2)*1.0/(y1-y2);  
  807.   
  808.     //开始进行填充  
  809.     float xs = x0 ,xe = x1 ;  
  810.     for(int y=y0 ; y <=y2 ;y++)  
  811.     {  
  812.         Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch);  
  813.   
  814.         xs += dxy_left ;  
  815.         xe += dxy_right ;  
  816.     }  
  817. // end Draw_Top_Trangle  
  818.   
  819. //绘制平底三角形  
  820. int Draw_Bottom_Trangle(int x0,int y0,  
  821.                     int x1,int y1,  
  822.                     int x2,int y2,  
  823.                     UINT * video_buffer,  
  824.                     DWORD color,int mempitch)  
  825. {  
  826.     //先判断下输入的三角形  
  827.     if(y2==y1)  
  828.     {  
  829.     }else if(y2==y0)  
  830.     {  
  831.         Swap(x0,x1);  
  832.         Swap(y0,y1);  
  833.     }else if(y0==y1)  
  834.     {  
  835.         Swap(x0,x2);  
  836.         Swap(y0,y2);  
  837.     }else  
  838.     {  
  839.         return 1 ; //error \brief 不是平顶三角形  
  840.     }  
  841.   
  842.     if(x1<x2)  
  843.     {  
  844.         Swap(x1,x2);  
  845.     }  
  846.     else if(x1 == x2)  
  847.     {  
  848.         return 1 ;// error \brief不是三角形  
  849.     }  
  850.   
  851.     //计算左右误差  
  852.     float dxy_left = (x2-x0)*1.0/(y2-y0) ;  
  853.     float dxy_right = (x1-x0)*1.0/(y1-y0);  
  854.   
  855.     //开始进行填充  
  856.     float xs = x0 ,xe = x0 ;  
  857.     for(int y=y0 ; y <=y2 ;y++)  
  858.     {  
  859.         Draw_Simple_Line(int(xs+0.5),int(xe+0.5),y,video_buffer,color,mempitch);  
  860.   
  861.         xs += dxy_left ;  
  862.         xe += dxy_right ;  
  863.     }  
  864. }// end Draw_Bottom_Trangle  
  865.   
  866. //绘制任意三角形  
  867. int Draw_Trangle_2D(int x0,int y0,  
  868.                     int x1,int y1,  
  869.                     int x2,int y2,  
  870.                     UINT * video_buffer,  
  871.                     DWORD color,int mempitch)  
  872. {  
  873.     if((x0==x1&&x1==x2)  
  874.         ||(y0==y1&&y1==y2))  
  875.     {  
  876.         return 1 ; //error \brief传进来的点无法构成三角形  
  877.     }  
  878.   
  879.     //\brief 将三个顶点按照从上到下排序  
  880.     if(y0>y1)  
  881.     {  
  882.         Swap(x0,x1);  
  883.         Swap(y0,y1);  
  884.     }  
  885.   
  886.     if(y0>y2)  
  887.     {  
  888.         Swap(x0,x2);  
  889.         Swap(y0,y2);  
  890.     }  
  891.   
  892.     if(y1>y2)  
  893.     {  
  894.         Swap(y1,y2);  
  895.         Swap(x1,x2);  
  896.     }  
  897.   
  898.     //\brief查找最大的x坐标,和最小的y坐标  
  899.     int min = (x0<x1?x0:x1);  
  900.     min = (min<x2?min:x2);  
  901.     int max = (x0>x1?x0:x1);  
  902.     max = (max>x2?max:x2);  
  903.   
  904.     //\brief 进行绘制  
  905.     if(y2<=min_clip_y||y0>=max_clip_y  
  906.         ||min>=max_clip_x||max<=min_clip_x)  
  907.         return 1 ;  //\brief 全部在裁剪区之外  
  908.     if(y0 == y1) //\brief 平顶三角形  
  909.     {  
  910.         Draw_Top_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch);  
  911.     }else if(y1 == y2)  
  912.     {  
  913.         Draw_Bottom_Trangle(x0,y0,x1,y1,x2,y2,video_buffer,color,mempitch);  
  914.     }else  
  915.     {  
  916.         int new_x = x0+0.5+(float)1.0*(y1-y0)*(x2-x0)/(y2-y0);  
  917.         Draw_Bottom_Trangle(x0,y0,new_x,y1,x1,y1,video_buffer,color,mempitch);  
  918.         Draw_Top_Trangle(new_x,y1,x1,y1,x2,y2,video_buffer,color,mempitch);  
  919.     }  
  920.   
  921.   
  922.     return 0 ; //\brief 成功画出三角形  
  923. }// end Draw_Trangle_2D  
  924. ///////////////////////////////////////////////////////////  
// 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
posted @ 2017-03-27 15:20  狂客  阅读(2089)  评论(0编辑  收藏  举报