Zegary Zegary (timers) umożliwiają cykliczne w danych odstępach czasu wykonać określone operacje. Zaczniemy od funkcji przetwarzania komunikatów: //procedura okna LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) static HWND wndbutton; switch(msg) case WM_CREATE: SetTimer(hWnd, 101, 2500, NULL); case WM_DESTROY: KillTimer(hWnd, 101); PostQuitMessage(0); case WM_TIMER: if(wparam==101) MessageBox(hWnd, "tekst okna dialogowego","okno dialogowe", 0); return DefWindowProc(hWnd, msg, wparam, lparam); Zegar tworzy funkcją SetTimer. Jej pierwszy parametr określa okno z którym jest związany. Parametr drugi to identyfikator zegara, trzeci określa odstęp czasu pomiędzy kolejnymi uruchomieniami podany w milisekundach (1sekunda = 1000milisekund). Zegar obsługujemy komunikatem WM_TIMER. Sprawdzamy wartość parametru wparam jeśli jest równa identyfikatorowi naszego zegara (101) wówczas zostanie wyświetlone okno dialogowe. Ponieważ rodzicem okna jest formularz więc okienko nie zostanie wyświetlone na pasku start.
Teraz dodamy drugi zegar, który będzie wyświetlał współrzędne myszy: #include <stdio.h> case WM_CREATE: SetTimer(hWnd, 102, 1, NULL); case WM_DESTROY: KillTimer(hWnd,102); case WM_TIMER: else if(wparam==102) static POINT screenpoint; static char posx[10]; static char posy[10]; static char posstr[10]; GetCursorPos(&screenPoint); sprintf(posx, "%d", screenpoint.x); sprintf(posy, "%d", screenpoint.y); strcpy(posstr,""); strcat(posstr, posx); strcat(posstr,":"); strcat(posstr,posy); SetWindowText(hWnd, posstr); Ustawienie trzeciego parametru funkcji SetTimer na 1 oznacza, że odświeżanie współrzędnych następuje co 1 milisekundę. Funkcja GetCursorPos umożliwia pobranie zmiennej typu POINT która zawiera współrzędne naszego kursora.
Wątki Wątki (threads) umożliwiają uruchamianie w aplikacji kilku elementów naraz. W grach jeden wątek może być odpowiedzialny za grafikę, drugi za fizykę, inny za sieć itd. Do przedstawienia działania wątków stworzymy aplikację konsolową: #include<windows.h> #include<stdio.h> DWORD WINAPI mythread(lpvoid indata) int i; for(i=0; i<3; i++) int value = *(int*)(indata); printf("%d \n", value); return 0; int main() HANDLE thread; int data = 10; DWORD id; thread = CreateThread(NULL, 0, mythread, &data, 0, &id); CloseHandle(thread); printf("id WATKOW: %d \n",id); getchar(); return 0; Funkcja CreateThread tworzy nam nowy wątek. Jej pierwszym parametrem może być struktura opisująca bezpieczeństwo, my ustawimy atrybuty na domyślne poprzez wstawienie wartości NULL. Drugi parametr to ilość bajtów przydzielonych wątkowi. Podając 0 przydzielamy domyślną ilość pamięci 1MB. Trzeci parametr to wskaźnik na funkcję wątku. Czwarty to dodatkowe informacje jakie możemy przekazać funkcji wątku. Piąty parametr określa flagi towarzyszące tworzeniu wątku, 0 oznacza że zostanie uruchomiony zaraz po utworzeniu. Ostatni parametr to adres do zmiennej w której zostanie zapisany numer wątku, nie musimy go podawać.
Funkcja CreateThread zwraca uchwyt do wątku w postaci typu HANDLE. Gdy wątek nie jest już potrzebny i chcemy go usunąć uruchamiamy funkcję CloseHandle i przekazać jej uchwyt wątku. Funkcja mythread musi mieć klauzulę WINAPI, zwraca wartość całkowitą i może mieć jeden parametr wskazujący typ void(lpvoid).
OpenGL Rendering w OpenGL musi wykonywany być cały czas. Dlatego operacje rysowania najlepiej przeprowadzać w pętli obsługi komunikatów. Niestety GetMessage działa tylko wtedy kiedy wiadomości są wysyłane do aplikacji, dlatego też stosuje się PeekMessage która nie powoduje wstrzymania aplikacji z momencie braku komunikatu w kolejce. Pompa komunikatów działająca z OpenGL wygląda następująco: //pompa komunikatow while(1) if(peekmessage(&msg, 0, 0, 0, PM_REMOVE)) if(msg.message == WM_QUIT) TranslateMessage(&Msg); DispatchMessage(&Msg);
OpenGL Tworząc nowy projekt i wybierając aplikacje typu opengl otrzymujemy gotowy szkielet aplikacji. Składa się ona z: Funkcji określającej format pikseli: void EnableOpenGL(HWND hwnd, HDC* hdc, HGLRC* hrc) PIXELFORMATDESCRIPTOR pfd; int iformat; /* get the device context (DC) */ *hdc = GetDC(hwnd); /* set the pixel format for the DC */ ZeroMemory(&pfd, sizeof(pfd)); pfd.nsize = sizeof(pfd); pfd.nversion = 1; pfd.dwflags = PFD_DRAW_TO_WINDOW PFD_SUPPORT_OPENGL PFD_DOUBLEBUFFER; pfd.ipixeltype = PFD_TYPE_RGBA; pfd.ccolorbits = 24; pfd.cdepthbits = 16; pfd.ilayertype = PFD_MAIN_PLANE; iformat = ChoosePixelFormat(*hDC, &pfd); SetPixelFormat(*hDC, iformat, &pfd); /* create and enable the render context (RC) */ *hrc = wglcreatecontext(*hdc); wglmakecurrent(*hdc, *hrc); Kończącej działanie OpenGL: void DisableOpenGL (HWND hwnd, HDC hdc, HGLRC hrc) wglmakecurrent(null, NULL); wgldeletecontext(hrc); ReleaseDC(hwnd, hdc);
Funkcji przetwarzającej komunikaty: LRESULT CALLBACK WindowProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) switch (umsg) case WM_CLOSE: PostQuitMessage(0); case WM_DESTROY: return 0; case WM_KEYDOWN: switch (wparam) case VK_ESCAPE: PostQuitMessage(0); default: return DefWindowProc(hwnd, umsg, wparam, lparam); return 0;
Główną pętlą programu jest pętla przetwarzająca komunikaty: while (!bquit) /* check for messages */ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) /* handle or dispatch messages */ if (msg.message == WM_QUIT) bquit = TRUE; else TranslateMessage(&msg); DispatchMessage(&msg); else /* OpenGL animation code goes here */ glclearcolor(0.0f, 0.0f, 0.0f, 0.0f); glclear(gl_color_buffer_bit); glpushmatrix(); glrotatef(theta, 0.0f, 0.0f, 1.0f); glbegin(gl_triangles); glcolor3f(1.0f, 0.0f, 0.0f); glvertex2f(0.0f, 1.0f); glcolor3f(0.0f, 1.0f, 0.0f); glvertex2f(0.87f, -0.5f); glcolor3f(0.0f, 0.0f, 1.0f); glvertex2f(-0.87f, -0.5f); glend(); glpopmatrix(); SwapBuffers(hDC); theta += 1.0f; Sleep (1);
Prymitywy Prymitywem określamy najprostszy z elementów geometrycznych. Są to elementy takie jak punkt, linia, trójkąt, czworokąt czy wielokąt. Aby poprawnie wyświetlały nam się nasze prymitywy, potrzebujemy upewnić się, że wyczyszczone są odpowiednie bufory opengl. Dlatego, przed rozpoczęciem rysowania czegokolwiek piszemy (w pętli głównej programu): glclear(gl_color_buffer_bit GL_DEPTH_BUFFER_BIT); glclearcolor(0.0f, 0.0f, 0.0f, 1.0f); Na koniec pętli głównej dodajemy także funkcję zamieniającą bufory: SwapBuffers(hDC); Przed pętlą główną wpisujemy glenable(gl_depth_test); Punkty Punkt jest najprostszym elementem. Procedura tworząca punkt na środku ekranu prezentuje się następująco: glbegin(gl_points); glend(); glvertex3f(0.0f, 0.0f, 0.0f); Utworzy ona punkt na środku naszego obszaru. Rozmiar punktu: Możliwe jest zmienianie rozmiaru punktu, służy do tego funkcja glpointsize() przyjmująca za parametr rozmiar punktu. W OpenGL wywołanie jednej funkcji wpływającej na zachowanie jakiegoś prymitywu powoduje, że działa ona na wszystkie następne, do czasu ponownego jej wywołania i zmienienia właściwości. glpointsize(15.0f);
Wygładzanie krawędzi punktu: Domyślnie punkt jest kwadratem, widać to przede wszystkim jeśli rozmiar ustawimy na większy od 1. Aby wymusić zaokrąglenie punktów, musimy uruchomić opcję GL_POINT_SMOOTH: glenable(gl_point_smooth); Opcję wyłącza się za pomocą: gldisable(gl_point_smooth); Kolory: Aby zmienić kolor stosujemy funkcję: glcolor3f(); Funkcja ta wczytuje 3 floaty odpowiedzialne za natężenie czerwieni, zieleni i niebieskiego: glcolor3f(1.0f, 1.0f, 1.0f);