2d图形旋转
1.这里旋转使用数学公式:
xr = x * cos(theta) - y * sin(theta);
yr = x * sin(theta) + y * cos(theta);
2.如果以左上角为原点(0,0),x方向向右增加,y方向向下增加的话,对于原图形旋转的100度并不等于连续20次的旋转5度的结果,可以举例对比一下。
3.memcpy对于复制对象真的很好用。
4.在这里我重画背景使用的是gdi的FillRect:
HDC hdc;
lpddsprimary->GetDC(&hdc);
FillRect(hdc, &client, (HBRUSH)GetStockObject(WHITE_BRUSH));
lpddsprimary->ReleaseDC(hdc);
该程序以左上角(0,0)为原点,支持移动、改变窗口大小重画360°图形。
#include <Windows.h>
#include <ddraw.h>
#include <math.h>
#pragma comment(lib, "ddraw.lib")
#pragma comment(lib, "dxguid.lib")
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
#define SCREEN_BPP 32
#define SCREEN_WIDTH 1920
#define SCREEN_HEIGHT 1080
#define KEY_DOWN(vk_code) (GetAsyncKeyState(vk_code) & 0x8000 ? 1 : 0)
#define KEY_UP(vk_code) (GetAsyncKeyState(vk_code) & 0x8000 ? 0 : 1)
#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
typedef struct VERTEX2DI_TYP
{
int x, y;
}VERTEX2DI, * VERTEX2DI_PTR;
typedef struct POLYGON2D_TYP
{
int state;
int num_verts;
int x0, y0;
int xv, yv;
LONG color;
VERTEX2DI* vlist;
} POLYGON2D, * POLYGON2D_PTR;
HWND main_hwnd = nullptr;
RECT client;
LPDIRECTDRAW7 lpdd7 = nullptr;
LPDIRECTDRAWSURFACE7 lpddsprimary = nullptr;
LPDIRECTDRAWCLIPPER lpddclipper = nullptr;
DDSURFACEDESC2 ddsd;
int pixel;
int window_close = 0;
POLYGON2D triangles[4];
float cos_look[360];
float sin_look[360];
int theta = -5; //旋转度数
int Game_Init(void* params = nullptr, int num_params = 0);
int Game_Main(void* params = nullptr, int num_params = 0);
int Game_Shutdown(void* params = nullptr, int num_params = 0);
int Draw_Line(int x1, int y1, int x2, int y2, LONG color, LONG* vb_start, int iPitch);
int Clip_Line(int& x1, int& y1, int& x2, int& y2);
int GetInterSectionPoint(int p_code, int& xc, int& yc, int x1, int y1, int x2, int y2, int min_clip_x, int min_clip_y, int max_clip_x, int max_clip_y);
int Draw_Clip_Line(int x1, int y1, int x2, int y2, LONG color, LONG* vb_start, int iPitch);
int Draw_Polygon2D(POLYGON2D_PTR poly, LONG* vb_strat, int iPitch);
int Rotate_Polygon2D(POLYGON2D_PTR poly, int theta);
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
TCHAR szAppName[] = TEXT("normal window");
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_VREDRAW | CS_HREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = nullptr;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(nullptr, TEXT("Register Class Failed"), szAppName, MB_ICONERROR);
return 0;
}
client = {0, 0, WINDOW_WIDTH-1, WINDOW_HEIGHT-1};
AdjustWindowRect(&client, WS_OVERLAPPEDWINDOW, false);
main_hwnd = CreateWindow(
szAppName, TEXT("normal_window"), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, client.right - client.left, client.bottom - client.top,
nullptr, nullptr, hInstance, nullptr
);
Game_Init();
while (true)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Game_Main();
}
Game_Shutdown();
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
int xPos;
int yPos;
switch (message)
{
case WM_CREATE:
//create
return 0;
case WM_SIZE:
//change size
theta = -5;
return 0;
case WM_MOVE:
//move
theta = -5;
return 0;
case WM_LBUTTONDOWN:
xPos = LOWORD(lParam);
yPos = HIWORD(lParam);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
//draw
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
window_close = 1;
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
int Game_Init(void* params, int num_params)
{
if (FAILED(DirectDrawCreateEx(nullptr, (LPVOID*)&lpdd7, IID_IDirectDraw7, nullptr)))
return 0;
lpdd7->SetCooperativeLevel(main_hwnd, DDSCL_NORMAL);
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
if (FAILED(lpdd7->CreateSurface(&ddsd, &lpddsprimary, nullptr)))
return 0;
//获取像素格式
DDPIXELFORMAT ddpixelformat;
memset(&ddpixelformat, 0, sizeof(ddpixelformat));
ddpixelformat.dwSize = sizeof(ddpixelformat);
lpddsprimary->GetPixelFormat(&ddpixelformat);
pixel = ddpixelformat.dwRGBBitCount;
if (FAILED(lpdd7->CreateClipper(0, &lpddclipper, nullptr)))
return 0;
if (FAILED(lpddclipper->SetHWnd(0, main_hwnd)))
return 0;
if (FAILED(lpddsprimary->SetClipper(lpddclipper)))
return 0;
//init triangles
for (int i = 0; i < 4; i++)
{
triangles[i].state = 1;
triangles[i].num_verts = 4;
RECT rect = { 300 + i * 50, 300 + i * 50, 300 + i * 50 + 50, 300 + i * 50 + 50 };
triangles[i].x0 = (rect.left + rect.right) / 2;
triangles[i].y0 = (rect.top + rect.bottom) / 2;
triangles[i].xv = triangles[i].yv = 10;
triangles[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
triangles[i].vlist = new VERTEX2DI[triangles[i].num_verts];
triangles[i].vlist[0] = { rect.left, rect.top };
triangles[i].vlist[1] = { rect.right, rect.top };
triangles[i].vlist[2] = { rect.right, rect.bottom };
triangles[i].vlist[3] = { rect.left, rect.bottom };
}
//generate the tables
for (int ang = 0; ang < 360; ang++)
{
float theta = (float)ang * 3.14159 / 180;
sin_look[ang] = sin(theta);
cos_look[ang] = cos(theta);
}
return 1;
}
int Game_Main(void* params, int num_params)
{
DDBLTFX ddbltfx;
if (KEY_DOWN(VK_ESCAPE))
{
window_close = 1;
SendMessage(main_hwnd, WM_CLOSE, 0, 0);
return 0;
}
if (window_close)
return 0;
//GetClientRect(main_hwnd, &client);
GetWindowRect(main_hwnd, &client);
if (theta == -5) //界面初始化为白色
{
HDC hdc;
lpddsprimary->GetDC(&hdc);
FillRect(hdc, &client, (HBRUSH)GetStockObject(WHITE_BRUSH));
lpddsprimary->ReleaseDC(hdc);
}
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
if (FAILED(lpddsprimary->Lock(nullptr, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, nullptr)))
return 0;
LONG* video_buffer = (LONG*)ddsd.lpSurface;
int ipitch = ddsd.lPitch;
if (pixel == 32)
{
if (theta < 355)
{
theta += 5;
if (theta == 360)
{
int x = 0;
}
//init temp_triangles
POLYGON2D temp_triangles[4];
memcpy(temp_triangles, triangles, sizeof(triangles));
for (int i = 0; i < 4; i++)
{
temp_triangles[i].vlist = new VERTEX2DI[temp_triangles[i].num_verts];
memcpy(temp_triangles[i].vlist, triangles[i].vlist, temp_triangles[i].num_verts * sizeof(VERTEX2DI));
}
for (int i = 0; i < 4; i++)
{
Rotate_Polygon2D(temp_triangles + i, theta);
Draw_Polygon2D(temp_triangles + i, video_buffer, ipitch >> 2);
}
//destroy temp_triangles.vlist
for (int i = 0; i < 4; i++)
delete[] temp_triangles[i].vlist;
}
}
if (FAILED(lpddsprimary->Unlock(nullptr)))
return 0;
//Sleep(10000);
return 1;
}
int Game_Shutdown(void* params, int num_params)
{
if (lpddsprimary)
{
lpddsprimary->Release();
lpddsprimary = nullptr;
}
if (lpddclipper)
{
lpddclipper->Release();
lpddclipper = nullptr;
}
if (lpdd7)
{
lpdd7->Release();
lpdd7 = nullptr;
}
//destroy triangles
for (int i = 0; i < 4; i++)
delete[] triangles[i].vlist;
return 1;
}
int Draw_Line(int x1, int y1, int x2, int y2, LONG color, LONG* vb_start, int iPitch)
{
int dx = x2 - x1;
int dy = y2 - y1;
int inx, iny, dx2, dy2, error;
if (dx >= 0)
inx = 1;
else
{
inx = -1;
dx = -dx;
}
if (dy >= 0)
iny = iPitch;
else
{
iny = -iPitch;
dy = -dy;
}
dx2 = dx << 1;
dy2 = dy << 1;
vb_start += x1 + y1 * iPitch;
if (dx > dy)
{
error = dy2 - dx;
for (int i = 0; i <= dx; i++)
{
*vb_start = color;
if (error >= 0)
{
error -= dx2;
vb_start += iny;
}
error += dy2;
vb_start += inx;
}
}
else
{
error = dx2 - dy;
for (int i = 0; i <= dy; i++)
{
*vb_start = color;
if (error >= 0)
{
error -= dy2;
vb_start += inx;
}
error += dx2;
vb_start += iny;
}
}
return 1;
}
int Clip_Line(int& x1, int& y1, int& x2, int& y2)
{
int min_clip_x = client.left;
int min_clip_y = client.top;
int max_clip_x = client.right;
int max_clip_y = client.bottom;
int xc1 = x1, yc1 = y1, xc2 = x2, yc2 = y2;
int p1_code = 0, p2_code = 0;
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 (y2 < min_clip_y)
p2_code |= CLIP_CODE_N;
else if (y2 > max_clip_y)
p2_code |= CLIP_CODE_S;
if (x2 < min_clip_x)
p2_code |= CLIP_CODE_W;
else if (x2 > max_clip_x)
p2_code |= CLIP_CODE_E;
if (p1_code & p2_code)
return 0;
if (p1_code == 0 && p2_code == 0)
return 1;
int iSpStatus1 = GetInterSectionPoint(p1_code, xc1, yc1, x1, y1, x2, y2, min_clip_x, min_clip_y, max_clip_x, max_clip_y);
int iSpStatus2 = GetInterSectionPoint(p2_code, xc2, yc2, x1, y1, x2, y2, min_clip_x, min_clip_y, max_clip_x, max_clip_y);
if (!(iSpStatus1 | iSpStatus2))
return 0;
x1 = xc1;
y1 = yc1;
x2 = xc2;
y2 = yc2;
return 1;
}
int GetInterSectionPoint(int p_code, int& xc, int& yc, int x1, int y1, int x2, int y2, int min_clip_x, int min_clip_y, int max_clip_x, int max_clip_y)
{
switch (p_code)
{
case CLIP_CODE_N:
yc = min_clip_y;
xc = x1 + (min_clip_y - y1) * (x2 - x1) / (y2 - y1);
break;
case CLIP_CODE_S:
yc = max_clip_y;
xc = x1 + (max_clip_y - y1) * (x2 - x1) / (y2 - y1);
break;
case CLIP_CODE_W:
xc = min_clip_x;
yc = y1 + (min_clip_x - x1) * (y2 - y1) / (x2 - x1);
break;
case CLIP_CODE_E:
xc = max_clip_x;
yc = y1 + (max_clip_x - x1) * (y2 - y1) / (x2 - x1);
break;
case CLIP_CODE_NW:
yc = min_clip_y;
xc = x1 + (min_clip_y - y1) * (x2 - x1) / (y2 - y1);
if (xc < min_clip_x)
{
xc = min_clip_x;
yc = y1 + (min_clip_x - x1) * (y2 - y1) / (x2 - x1);
}
break;
case CLIP_CODE_SW:
yc = max_clip_y;
xc = x1 + (max_clip_y - y1) * (x2 - x1) / (y2 - y1);
if (xc < min_clip_x)
{
xc = min_clip_x;
yc = y1 + (min_clip_x - x1) * (y2 - y1) / (x2 - x1);
}
break;
case CLIP_CODE_NE:
yc = min_clip_y;
xc = x1 + (min_clip_y - y1) * (x2 - x1) / (y2 - y1);
if (xc > max_clip_x)
{
xc = max_clip_x;
yc = y1 + (max_clip_x - x1) * (y2 - y1) / (x2 - x1);
}
break;
case CLIP_CODE_SE:
yc = max_clip_y;
xc = x1 + (max_clip_y - y1) * (x2 - x1) / (y2 - y1);
if (xc > max_clip_x)
{
xc = max_clip_x;
yc = y1 + (max_clip_x - x1) * (y2 - y1) / (x2 - x1);
}
break;
}
if (xc < min_clip_x || xc > max_clip_x || yc < min_clip_y || yc > max_clip_y)
return 0;
return 1;
}
int Draw_Clip_Line(int x1, int y1, int x2, int y2, LONG color, LONG* vb_start, int iPitch)
{
int iDcStatus = Clip_Line(x1, y1, x2, y2);
if (!iDcStatus)
return 0;
Draw_Line(x1, y1, x2, y2, color, vb_start, iPitch);
return 1;
}
int Draw_Polygon2D(POLYGON2D_PTR poly, LONG* vb_strat, int iPitch)
{
if (poly->state)
{
for (int i = 0; i < poly->num_verts - 1; i++)
{
Draw_Clip_Line(poly->vlist[i].x, poly->vlist[i].y, poly->vlist[i + 1].x, poly->vlist[i + 1].y, poly->color, vb_strat, iPitch);
}
Draw_Clip_Line(poly->vlist[poly->num_verts - 1].x, poly->vlist[poly->num_verts - 1].y, poly->vlist[0].x, poly->vlist[0].y, poly->color, vb_strat, iPitch);
return 1;
}
return 0;
}
int Rotate_Polygon2D(POLYGON2D_PTR poly, int theta)
{
if (!poly)
return 0;
for (int curr_vert = 0; curr_vert < poly->num_verts; curr_vert++)
{
float xr = poly->vlist[curr_vert].x * cos_look[theta] - poly->vlist[curr_vert].y * sin_look[theta];
float yr = poly->vlist[curr_vert].x * sin_look[theta] + poly->vlist[curr_vert].y * cos_look[theta];
poly->vlist[curr_vert].x = xr;
poly->vlist[curr_vert].y = yr;
}
return 1;
}
下面是效果图: