Jak na Üet°iΦ obrazovky

U₧ dßvno jsem si cht∞l naprogramovat vlastnφ Üet°iΦ obrazovky. M∞l jsem sice t°φdu CScreenSaverWnd pro MFC, ale ta nepodporovala OpenGL. U NeHe tutorißlu 38 jsem naÜel odkaz na Üet°iΦ obrazovky s podporou OpenGL, kter² napsal Brian Hunsucker. Cht∞l bych mu pod∞kovat, proto₧e na jeho zdrojovΘm k≤du z v∞tÜφ Φßsti stavφ tento Φlßnek.

ZaΦneme parametry spouÜt∞nφ Üet°iΦe obrazovky. KterΘ definujφ, zda se mß spustit Üet°iΦ obrazovky jako takov² (s) nebo pouze jeho konfiguraΦnφ dialog (c). M∞li bychom ho zφskßvat z p°φkazovΘ °ßdky testovßnφm parametr∙ funkce main(), ale defakto se o n∞j nemusφme starat, proto₧e vÜe zajistφ knihovna scrnsave, kterß je souΦßstφ Visual C++. Jenom tak na okraj, k main() se v∙bec nedostaneme, proto₧e je p°edkompilovanß v scrnsave. Stejn∞ tak se nestarßme o zav°enφ Üet°iΦe a podobnΘ v∞ci. Program vÜe d∞lß automaticky.

Jedna malß poznßmka: p°i v²voji aplikace se po spuÜt∞nφ zobrazφ konfiguraΦnφ dialog. Abychom program spustili jako Üet°iΦ, musφme mu p°edat parametr s. V Project/Settings pod nabφdkou Debug se musφ napsat do Program arguments pφsmeno s.

Parametr s

Vygenerujeme klasick² Win32 Aplication projekt a m∙₧eme psßt k≤d. Vlo₧φme hlaviΦkovΘ soubory a p°ilinkujeme pot°ebnΘ knihovny.

#include <windows.h>// HlaviΦkov² soubor pro Windows

#include <scrnsave.h>// HlaviΦkov² soubor pro Üet°iΦ obrazovky

#include <GL/gl.h>// HlaviΦkov² soubor pro OpenGL

#include <GL/glu.h>// HlaviΦkov² soubor pro GLU

#include "res/resource.h"// HlaviΦkov² soubor pro Resource (konfiguraΦnφ dialog, ikona ...)

#pragma comment (lib,"opengl32.lib")// P°ilinkovßnφ OpenGL

#pragma comment (lib,"glu32.lib")// P°ilinkovßnφ GLU

#pragma comment (lib,"scrnsave.lib")// P°ilinkovßnφ knihovny Üet°iΦe obrazovky

Instance aplikace je jedinou globßlnφ prom∞nnou.

HINSTANCE hInstance;// Uklßdß instanci aplikace

V nßsledujφcφ funkci inicializujeme okno tak, aby podporovalo OpenGL. Dß se °φct, ₧e s nejv∞tÜφ pravd∞podobnostφ tuto funkci nebudete muset nikdy zm∞nit.

HGLRC InitOGLWindow(HWND hWnd)// Inicializace okna

{

HDC hDC = GetDC(hWnd);// Kontext za°φzenφ

HGLRC hRC = 0;// Renderovacφ kontext

PIXELFORMATDESCRIPTOR pfd;

int nFormat;

ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR));

// Nastavenφ okna

pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);

pfd.nVersion = 1;

pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;

pfd.cColorBits = 24;

pfd.cDepthBits = 24;

nFormat = ChoosePixelFormat(hDC, &pfd);

DescribePixelFormat(hDC, nFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);

SetPixelFormat(hDC, nFormat, &pfd);

hRC = wglCreateContext(hDC);

wglMakeCurrent(hDC, hRC);

ReleaseDC(hWnd, hDC);

return hRC;// Vrßtφ renderovacφ kontext

}

Do inicializace OpenGL p°idßme i nastavenφ perspektivy, kterß se standardn∞ vklßdß do funkce pro zm∞nu velikosti okna. Nic se nestane, proto₧e parametry okna Üet°iΦe obrazovky se nikdy nezm∞nφ. Jak takΘ? Jakmile se pohne myÜφ, program je ukonΦen.

void InitOpenGL(GLsizei width, GLsizei height)// Inicializace OpenGL

{

if (height==0)// Proti d∞lenφ nulou

{

height=1;

}

glViewport(0,0,width,height);// Reset Viewportu

glMatrixMode(GL_PROJECTION);// Zvolφ projekΦnφ matici

glLoadIdentity();// Reset matice

// Perspektiva

gluPerspective(45.0f, (GLfloat)(width)/(GLfloat)(height),1.0f, 20.0f);

glMatrixMode(GL_MODELVIEW);// Zvolφ matici Modelview

glLoadIdentity();

Na tomto mφst∞ si sami nastavte OpenGL, jak uznßte za vhodnΘ. V naÜem p°φpad∞ definujeme stφnovßnφ, perspektivnφ korekce, barvu pozadφ a nastavenφ hloubkovΘho bufferu.

// U₧ivatelskß inicializace

glShadeModel(GL_SMOOTH);// VyhlazenΘ stφnovßnφ

glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// Perspektivnφ korekce

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);// ╚ernΘ pozadφ

glClearDepth(1.0f);// Nastavenφ hloubkovΘho bufferu

glDepthFunc(GL_LEQUAL);// Typ hloubkovΘho testovßnφ

glEnable(GL_DEPTH_TEST);// Zapne test hloubky

}

Druh²m mφstem, kterΘ se p°i vytvß°enφ novΘho Üet°iΦe m∞nφ, je vykreslovacφ funkce. Abychom v∞d∞li, ₧e program funguje, vykreslφme troj·helnφk a obdΘlnφk. Nic sv∞tobornΘho...

void DrawGLScene()

{

glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Sma₧e obrazovku a hloubkov² buffer

glLoadIdentity();// Reset matice

glTranslatef(-1.5f,0.0f,-6.0f);// Posun doleva a do hloubky

glBegin(GL_TRIANGLES);// ZaΦßtek kreslenφ troj·helnφk∙

glVertex3f( 0.0f, 1.0f, 0.0f);// Hornφ bod

glVertex3f(-1.0f,-1.0f, 0.0f);// Lev² dolnφ bod

glVertex3f( 1.0f,-1.0f, 0.0f);// Prav² dolnφ bod

glEnd();

glTranslatef(3.0f,0.0f,0.0f);// Posun o 3 jednotky doprava

glBegin(GL_QUADS);// ZaΦßtek kreslenφ obdΘlnφk∙

glVertex3f(-1.0f, 1.0f, 0.0f);// Lev² hornφ bod

glVertex3f( 1.0f, 1.0f, 0.0f);// Prav² hornφ bod

glVertex3f( 1.0f,-1.0f, 0.0f);// Prav² dolnφ bod

glVertex3f(-1.0f,-1.0f, 0.0f);// Lev² dolnφ bod

glEnd();// Konec kreslenφ obdΘlnφk∙

glFlush();

}

I Üet°iΦi obrazovky posφlß systΘm zprßvy. Nenφ jich sice mnoho, ale bez n∞kter²ch bychom se urΦit∞ neobeÜli. Deklarujeme prom∞nnΘ kontextu za°φzenφ a renderovacφho kontextu a dßle v∞tvφme funkci podle doÜlΘ zprßvy.

// Procedura okna Üet°iΦe obrazovky

LRESULT WINAPI ScreenSaverProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

HDC hDC;// Kontext za°φzenφ

static HGLRC hRC;// Renderovacφ kontext

switch (message)// V∞tvφ program podle doÜlΘ zprßvy

{

Na zprßvu WM_CREATE, kterou posφlß systΘm ihned po vytvo°enφ okna, inicializujeme okno. PotΘ pomocφ funkce GetClientRect() nagrabujeme jeho Üφ°ku a v²Üku, kterou p°edßme do InitOpenGL(). Nakonec spustφme timer zajiÜ¥ujφcφ periodickΘ p°ekreslovßnφ scΘny.

case WM_CREATE:// Vytvo°enφ okna

hRC = InitOGLWindow(hWnd);// Zφskßnφ kontextu za°φzenφ

RECT WindowRect;// Pro zjiÜt∞nφ velikosti okna

int width;// èφ°ka

int height;// V²Üka

GetClientRect(hWnd, &WindowRect);// Zφskß velikost okna

width = WindowRect.right - WindowRect.left;// èφ°ka okna

height = WindowRect.bottom - WindowRect.top;// V²Üka okna

InitOpenGL(width, height);// Inicializace OpenGL

SetTimer(hWnd, 1, 20, NULL);// Zapnutφ timeru

break;

O kousek v²Üe jsme spustili timer. Nynφ definujeme, ₧e po p°φchodu jeho zprßvy se mß zφskat DC. PotΘ p°ekreslφme scΘnu, prohodφme buffery a op∞t uvolnφme DC. K≤d je tak krßtk², ₧e vytvß°et novou funkci je ·pln∞ zbyteΦnΘ.

case WM_TIMER:// Zprßva od ΦasovaΦe

hDC = GetDC(hWnd);// Zφskßnφ kontextu za°φzenφ

DrawGLScene();// Vykreslφ scΘnu

SwapBuffers(hDC);// Prohodφ buffery

ReleaseDC(hWnd, hDC);// Uvolnφ kontext za°φzenφ

break;

Na zprßvu WM_DESTROY provedeme deinicializaci.

case WM_DESTROY:// Zav°enφ okna

KillTimer(hWnd,1); // Vypnutφ timeru

wglMakeCurrent(NULL, NULL);

wglDeleteContext(hRC);// Sma₧e renderovacφ kontext

break;

}

VÜechny ostatnφ zprßvy p°edßme dßle.

return DefScreenSaverProc(hWnd, message, wParam, lParam);// NeoÜet°enΘ zprßvy

}

┌pln∞ na zaΦßtku jsme si °ekli, ₧e program mß dv∞ cesty provßd∞nφ. Klasick² Üet°iΦ u₧ mßme, nynφ se vrhneme na konfiguraΦnφ dialog. Nejprve ho vytvo°φme. Mß dv∞ tlaΦφtka OK a CANCEL (pop°. i dalÜφ). ZaΦneme mapou zprßv a dß se °φct, ₧e i skonΦφme. P°i WM_INITDIALOG m∙₧eme jednotlivΘ prvky inicializovat na hodnoty, kterΘ zφskßme nap°φklad z pomocnΘho souboru nebo z registr∙ Windows. UrΦovaly by chovßnφ Üet°iΦe, ale nßÜ program je velmi jednoduch², tak proΦ ho komplikovat. Namapovßnφm zprßvy WM_COMMAND urΦφme, co se mß ud∞lat po kliknutφ na tlaΦφtka.

// Procedura okna konfiguraΦnφho dialogu

BOOL WINAPI ScreenSaverConfigureDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)

{

switch (message)// V∞tvφ program podle doÜlΘ zprßvy

{

case WM_INITDIALOG:// Inicializace dialogu

return TRUE;// V po°ßdku

case WM_COMMAND:// P°φkaz (nap°. kliknutφ na tlaΦφtko)

switch (LOWORD(wParam))// KterΘ tlaΦφtko?

{

case IDOK:// OK

EndDialog(hDlg, TRUE);// Zav°e dialog

return TRUE;// V po°ßdku

case IDCANCEL:// Cancel

EndDialog(hDlg, TRUE);// Zav°e dialog

break;

}

}

return FALSE;// False - u₧ivatel nap°. stiskl Cancel

}

K Φemu je tato funkce, abych °ekl pravdu, nevφm, ale kompilßtor mi bez nφ hlßsφ chyby.

BOOL WINAPI RegisterDialogClasses(HANDLE hInst)// ???

{

return TRUE;

}

No a tφm jsme i skonΦili. Mo₧nß se ptßte: "A opravdu to funguje?". Ano, takΘ jsem nechßpal. Kdy₧ jsem tento k≤d vid∞l poprvΘ, bez main() a jak²chkoli jin²ch nßvaznostφ pochyboval jsem, ₧e sv∙j vlastnφ Üet°iΦ n∞kdy v ₧ivot∞ rozjedu, ale poda°ilo se. ╚lov∞k se nemusφ skoro o nic starat vÜe je p°ipraveno v knihovn∞ scrnsave. Stßhn∞te si zdrojov² k≤d a uvidφte.

Abych nezapomn∞l, v²sledn² .exe soubor je nutnΘ p°ejmenovat na .scr a zkopφrovat do Windows/System. A₧ potom budete moci v nastavenφ obrazovky vym∞nit Üet°iΦ za nov² (samoz°ejm∞ lepÜφ).

napsal: Michal Turek - Woq

ZdrojovΘ k≤dy

Woq Saver