OpenGL okno v dialogu

Zobrazφme d∞tskΘ OpenGL okno v dialogu a budeme mu p°edßvat hodnoty zφskanΘ z ovlßdacφch prvk∙ (editboxy a radiobuttony). PeriodickΘ p°ekreslovßnφ OpenGL okna zajiÜ¥uje zprßva WM_TIMER - troj·helnφk a Φtverec budou rotovat.

Zdrojov² k≤d pro tento Φlßnek vychßzφ z programu Projekt - Dialog, kter² mi poslal Max Zelen². Program jsem upravil, aby vφce demonstroval mo₧nost ovliv≥ovßnφ scΘny hodnotami v ovlßdacφch prvcφch. Musφm ale p°iznat, ₧e bez n∞j by tento Φlßnek nem∞l Üanci vzniknout, proto₧e bych nem∞l dostateΦnΘ znalosti, jak vytvo°it d∞tskΘ OpenGL okno. Dφky.

ZaΦneme vygenerovßnφm klasickΘ Dialog aplikace pou₧φvajφcφ MFC. Upravφme zdroj dialogu tak, aby vypadal p°ibli₧n∞ jako na obrßzku. V levΘ Φßsti nechßme volnΘ mφsto pro zobrazenφ d∞tskΘho OpenGL okna.

Zdroj dialogu

Pomocφ ClassWizardu p°ipojφme prom∞nnΘ a funkce k dialogov²m prvk∙m:

Aby se po stisku klßvesy ENTER dialog nezavφral p°epφÜeme virtußlnφ funkci OnOK(), tak aby nic ned∞lala.

void CDialogDlg::OnOK()// Aby se po stisku klßvesy ENTER dialog nezav°el

{

// TODO: Add extra validation here

// CDialog::OnOK();// ZruÜit

}

Do deklarace t°φdy dialogu p°idßme ukazatel na prom∞nnou t°φdy COpenGL.

class CDialogDlg : public CDialog// T°φda dialogu

{

public:

COpenGL* gl_okno;// Ukazatel na d∞tskΘ OpenGL okno

CDialogDlg(CWnd* pParent = NULL);// Konstruktor

~CDialogDlg();// Destruktor

// Prom∞nnΘ a funkce generovanΘ ClassWizzardem

};

Na konci konstruktoru ukazatel inicializujeme na NULL.

CDialogDlg::CDialogDlg(CWnd* pParent) : CDialog(CDialogDlg::IDD, pParent)

{

//{{AFX_DATA_INIT(CDialogDlg)

c_hloubka = -7.0;

t_hloubka = -7.0;

rotace_ano = 0;

//}}AFX_DATA_INIT

// Note that LoadIcon does not require a subsequent DestroyIcon in Win32

m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

gl_okno = NULL;// Aby destruktor v∞d∞l, jestli bylo okno vytvo°eno

}

Funkce OnInitDialog() se volß hned po vytvo°enφ dialogu, t∞sn∞ p°ed prvnφm vykreslenφm - nejlepÜφ mφsto pro vytvo°enφ d∞tskΘho OpenGL okna.

BOOL CDialogDlg::OnInitDialog()// Inicializace

{

CDialog::OnInitDialog();

// Set the icon for this dialog. The framework does this automatically

// when the application's main window is not a dialog

SetIcon(m_hIcon, TRUE);// Set big icon

SetIcon(m_hIcon, FALSE);// Set small icon

// TODO: Add extra initialization here

Vytvo°φme obdΘlnφk, kter² urΦuje pozici a velikost zam²ÜlenΘho d∞tskΘho okna.

CRect rect(7, 7, 300, 300);// Pozice a velikost okna (obdΘlnφk)

Alokujeme pro okno dynamickou pam∞¥ a inicializujeme jeho ΦlenskΘ prom∞nnΘ. T_hloubka a c_hloubka urΦujφ hloubku troj·helnφku a Φtverce ve scΘn∞.

gl_okno = new COpenGL;// Dynamick² objekt t°φdy

gl_okno->rc = rect;// Nastavenφ atribut∙ t°φdy

gl_okno->t_hloubka = t_hloubka;

gl_okno->c_hloubka = c_hloubka;

Pomocφ funkce Create() okno vytvo°φme.

// Vytvo°enφ a zobrazenφ okna

gl_okno->Create(NULL, NULL, WS_CHILD|WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_VISIBLE, rect, this, 0);

return TRUE;// Return TRUE unless you set the focus to a control

}

V destruktoru ov∞°ujeme, jestli je ukazatel stßle nastaven na NULL. Pokud ne uvolnφme jeho pam∞¥.

CDialogDlg::~CDialogDlg()// Destruktor

{

if(gl_okno != NULL)// Bylo okno vytvo°eno?

{

delete gl_okno;// Sma₧e ho

}

}

Poslednφ funkcφ, kterß v tΘto t°φd∞ stojφ za vysv∞tlenφ je reakce na tlaΦφtko IDC_AKTUALIZOVAT, ale °ekneme si o nφ vφce, a₧ budeme v∞d∞t, jak vypadß a funguje t°φda COpenGL.

Pozn.: Sprßvn∞ bychom m∞li vytvo°it jeÜt∞ jednu t°φdu - potomka COpenGL a a₧ tu upravovat. V²sledn² Φlßnek by se vÜak velmi znep°ehlednil. Zapouzd°enost dat radÜi v∙bec nezmi≥uji...

class COpenGL : public CWnd// T°φda OpenGL okna

{

public:

COpenGL();// Konstruktor

virtual ~COpenGL();// Destruktor

Funkci MySetPixelFormat() volßme ve funkci OnCreate(). Mß za ·kol nastavit okno tak, aby podporovalo OpenGL.

MySetPixelFormat(HDC hdc);// Nastavuje pixel formßt

Prom∞nnou rc jsme inicializovali ve funkci CDialogDlg::OnInitDialog() t∞sn∞ p°ed zavolßnφm Create(). Obsahuje velikost okna, kterß je d∙le₧itß pro nastavenφ perspektivy. Kontext za°φzenφ a rendering kontext u₧ urΦit∞ znßte. OpenGL se bez nich neobejde.

CRect rc;// Velikost okna

HDC m_hgldc;// Kontext za°φzenφ

HGLRC m_hglRC;// Rendering kontext

Nßsledujφcφ Φty°i prom∞nnΘ slou₧φ pro ulo₧enφ hloubky objekt∙ ve scΘn∞ a ·hlu natoΦenφ. P°epφnaΦ rotace zapφnß/vypφnß otßΦenφ objekt∙.

double t_hloubka;// Hloubka troj·helnφku ve scΘn∞

double c_hloubka;// Hloubka Φtverce ve scΘn∞

float t_rot;// ┌hel rotace troj·helnφku

float c_rot;// ┌hel rotace Φtverce

bool rotace;// Zapnutß/vypnutß rotace objekt∙

Deklarace funkcφ generovan²ch ClassWizzardem.

protected:

//{{AFX_MSG(COpenGL)

afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

afx_msg void OnPaint();

afx_msg void OnTimer(UINT nIDEvent);

afx_msg void OnDestroy();

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

};

V konstruktoru t°φdy inicializujeme prom∞nnΘ.

COpenGL::COpenGL()// Konstruktor

{

t_rot = 0.0f;// Nastavenφ parametr∙ na v²chozφ hodnoty

c_rot = 0.0f;

rotace = true;

}

Ve funkci MySetPixelFormat() nastavujeme okno, aby podporovalo OpenGL. Nebudu ji vysv∞tlovat, proto₧e je tato operace velmi dob°e popsanß nap°. v NeHe tutorißlu 1.

int COpenGL::MySetPixelFormat(HDC hdc)// Nastavφ pixel formßt

{

PIXELFORMATDESCRIPTOR *ppfd;

int pixelformat;

PIXELFORMATDESCRIPTOR pfd =

{

sizeof(PIXELFORMATDESCRIPTOR),// Velikost struktury

1,// ╚φslo verze

PFD_DRAW_TO_WINDOW |// Podpora okna

PFD_SUPPORT_OPENGL |// OpenGL

PFD_DOUBLEBUFFER,// Dva buffery

PFD_TYPE_RGBA,// RGBA formßt

32,// Barevnß hloubka

0, 0, 0, 0, 0, 0,// Bity barev ignorovßny

8,// Äßdn² alpha buffer

0,// Shift Bit ignorovßn

8,// Äßdn² Accumulation buffer

0, 0, 0, 0,// Accumulation bity ignorovßny

64,// Z-Buffer

8,// Stencil buffer

8,// Auxiliary buffer

PFD_MAIN_PLANE,// Hlavnφ vykreslovacφ vrstva

0,// Rezervovßno

0, 0, 0// Layer Masks ignorovßny

};

ppfd = &pfd;

if ((pixelformat = ChoosePixelFormat(hdc, ppfd)) == 0)// Poda°ilo se najφt Pixel Format?

{

::MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK);

return FALSE;

}

if (SetPixelFormat(hdc, pixelformat, ppfd) == FALSE)// Poda°ilo se nastavit Pixel Format?

{

::MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK);

return FALSE;

}

return TRUE;

}

Funkce OnCreate() se volß po vytvo°enφ okna, p°ed prvnφm vykreslenφm. Inicializujeme v nφ OpenGL okno.

int COpenGL::OnCreate(LPCREATESTRUCT lpCreateStruct)// Inicializace

{

Zφskßme kontext za°φzenφ a nastavφme Pixel Format, aby okno podporovalo OpenGL. Potom nastavφme rendering kontext.

m_hgldc = ::GetDC(m_hWnd);// Zφskß kontext za°φzenφ

if(!MySetPixelFormat(m_hgldc))// Poda°ilo se nastavit Pixel Format?

{

::MessageBox(m_hWnd,"MySetPixelFormat Failed!","Error",MB_OK);

return -1;

}

// Vytvo°φ a nastavφ Rendering kontext

m_hglRC = wglCreateContext(m_hgldc);

wglMakeCurrent(m_hgldc, m_hglRC);

Nastavφme OpenGL perspektivu. Tento k≤d by se m∞l volat p°i ka₧dΘ zm∞n∞ velikosti okna, ale proto₧e se rozm∞ry dialogu nem∞nφ, staΦφ pouze jednou p°i inicializaci. V objektu rc mßme ulo₧enou velikost okna.

// Inicializace OpenGL okna

if(rc.bottom == 0)// Proti d∞lenφ nulou

{

rc.bottom = 1;

}

glViewport(0, 0, rc.right, rc.bottom);// Resetuje aktußlnφ nastavenφ

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

glLoadIdentity();// Reset matice

gluPerspective(45.0f, (GLfloat)rc.right / (GLfloat)rc.bottom, 1.0f, 100.0f);// V²poΦet perspektivy

glMatrixMode(GL_MODELVIEW);// Zvolφ matici Modelview

glLoadIdentity();// Reset matice

Inicializujeme OpenGL podle konkrΘtnφch po₧adavk∙.

// U₧ivatelskß inicializace

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

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

glClearDepth(1.0f);// Nastavφ Depth Buffer

glEnable(GL_DEPTH_TEST);// Povolφ hloubkovΘ testovßnφ

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

glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// Perspektiva

Spustφme timer, kter² bude zajiÜ¥ovat periodickΘ p°ekreslovßnφ scΘny.

SetTimer(1, 30, NULL);// Spustφ timer pro p°ekreslovßnφ

return 0;

}

OÜet°φme zprßvu ΦasovaΦe. Volßnφ funkce Invalidate() mß za nßsledek p°ekreslenφ okna.

void COpenGL::OnTimer(UINT nIDEvent)// ╚asovaΦ

{

Invalidate();// P°ekreslenφ okna

CWnd::OnTimer(nIDEvent);// Metoda rodiΦovskΘ t°φdy

}

Funkce OnDestroy() se volß t∞sn∞ p°ed zav°enφm okna. Vypneme ΦasovaΦ a sma₧eme kontexty.

void COpenGL::OnDestroy()// Deinicializace

{

CWnd::OnDestroy();// Metoda rodiΦovskΘ t°φdy

KillTimer(1);// Vypnutφ ΦasovaΦe

wglMakeCurrent(NULL, NULL);// Neaktivnφ rendering kontext

wglDeleteContext(m_hglRC);// Sma₧e rendering kontext

::ReleaseDC(m_hWnd, m_hgldc);// UkonΦφ vykreslovßnφ

}

OnPaint() zajiÜ¥uje renderovßnφ OpenGL scΘny. Vytvo°φme n∞co ve stylu NeHe tutorißlu 4. Troj·helnφk rotuje kolem osy y Φtverec kolem osy x.

void COpenGL::OnPaint()// Vykreslovßnφ

{

CPaintDC dc(this);// Kontext za°φzenφ

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// VyΦistφ buffery

glLoadIdentity();// Reset matice

glTranslated(-1.5, 0.0, t_hloubka);// Posun doleva a do hloubky

glRotatef(t_rot, 0.0f, 1.0f, 0.0f);// Rotace na ose y

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

glColor3f(1.0f, 0.0f, 0.0f);// ╚ervenß barva

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

glColor3f(0.0f, 1.0f, 0.0f);// Zelenß barva

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

glColor3f(0.0f, 0.0f, 1.0f);// Modrß barva

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

glEnd();

glLoadIdentity();// Reset matice

glTranslated(1.5, 0.0, c_hloubka);// Posun o 3 jednotky doprava

glRotatef(c_rot, 1.0f, 0.0f, 0.0f);// Rotace na ose x

glColor3f(0.5f, 0.5f, 1.0f);// Sv∞tle modrß barva

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();

if(rotace)// Je zapnutß rotace?

{

t_rot += 3.0f;// Zv∞tÜφ ·hel

c_rot += 2.0f;

}

Nakonec nesmφme zapomenout prohodit buffery.

SwapBuffers(m_hgldc);// Prohodφ buffery

}

Nynφ vφte, jak se vytvß°φ OpenGL okno a jak pracuje. Nezapomn∞li jsme na n∞co? JeÜt∞ musφme p°ipojit ovlßdacφ prvky dialogu tak, aby mohly ovliv≥ovat vykreslovanou scΘnu. Po stisku tlaΦφtka IDC_AKTUALIZOVAT zφskßme obsah ovlßdacφch prvk∙ a nastavφme polo₧ky objektu t°φdy COpenGL. Opravdu nic slo₧itΘho.

void CDialogDlg::OnAktualizovat()// Stisk tlaΦφtka

{

if(UpdateData() == 0)// Nagrabuje hodnoty z ovlßdacφch prvk∙

return;

gl_okno->t_hloubka = t_hloubka;// Nastavφ prom∞nnΘ

gl_okno->c_hloubka = c_hloubka;

if(!gl_okno->rotace && rotace_ano == 0)// Zapnout rotaci

{

gl_okno->rotace = true;

gl_okno->SetTimer(1, 30, NULL);

}

if(gl_okno->rotace && rotace_ano == 1)// Vypnout rotaci

{

gl_okno->rotace = false;

gl_okno->KillTimer(1);

}

Preventivn∞ p°ekreslφme OpenGL scΘnu. Kdyby nebyla aktivnφ rotace, timer by byl vypnut² a tudφ₧ by nevolal periodickΘ p°ekreslovßnφ. Zm∞ny by se neprojevily.

gl_okno->Invalidate();// P°ekreslenφ OpenGL okna

}

Pokud programujete pod MFC, nem∞lo by vßm napojenφ dialogov²ch prvk∙ d∞lat v∞tÜφ problΘmy. Jß jsem se v zaΦßtcφch nedostal p°es vytvß°enφ OpenGL okna, jeÜt∞ jednou proto d∞kuji Maxi ZelenΘmu, kter² mi poslal ukßzkov² program.

napsal: Michal Turek - Woq

ZdrojovΘ k≤dy

Dialog