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.
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