Note
Struttura
OpenGL + MFC
Claudio Rocchini
Introduzione
Vedremo come si realizza una applicazione OpenGL in ambiente Windows, utilizzando le Microsoft Foundation Class.
MFC
Le Microsoft Foundation Class formano la nota libreria di sviluppo di applicazioni per ambiente Windows. Vedremo come siano state realizzate delle opportune chiamate di libreria per l’interfacciamento di OpenGL.
OpenGL
OpenGL è il famoso standard per la visualizzazione grafica bi e tridimensionale. E’ supportato da tutte le piattaforme. Le schede grafiche di ultima generazione lo implementano in hardware.
Cenni su OpenGL (1)
Una introduzione ad OpenGL e’ fuori dagli scopi di questa presentazione.
Ne evidenzieremo le caratteristiche necessarie all’interfacciamento con il sistema a finestre.
http://www.opengl.org
Cenni su OpenGL (2)
I comandi di OpenGL sottintendono un contesto grafico di default, la seguente serie di comandi visualizza un triangolo:
glBegin(GL_TRIANGLES);
glVertex3f(0,0,-10);
glVertex3f(10,0,-10);
glVertex3f(0,10,-10);
glEnd();
Cenni su OpenGL (3)
In ogni applicazione Windows si puo’ definire un contesto OpenGL corrente che riceve i comandi grafici.
Un applicazione multifinestra puo’ alternare vari contesti grafici OpenGL.
MFC+OpenGL
Una volta costruita una applicazione standard MFC sono necessarie alcune aggiunte per l’utilizzo di OpenGL all’interno di una finestra.
Punti principali
Scelta del formato pixel.
Creazione di un contesto OpenGL (GLRC).
Creazione di un Device contest (DC) associato ad una finestra.
Collegamento fra GLRC e DC.
Inclusione file header
Aggiungere nel file header della view:
#include <GL\\gl.h>
#include <GL\\glu.h>
Inclusione librerie
File di
Libreria
aggiuntivi
Dati aggiuntivi della View
I seguenti dati sono aggiunti alla classe view:
CPalette    m_cPalette; // Palette Personale
CPalette    *m_pOldPalette; // Vecchia Palette
CClientDC *m_pDC; // DC della finestra
HGLRC m_hrc; // Contesto OpenGL
Dati per multifinestra
In caso di gestione multifinestra e’ necessario aggiunge alla view il seguente:
// IL proprietario del contesto GL
static CGlwrapperView * m_glOwner;
Nel file …view.cpp si dichiara:
CGlwrapperView * CGlwrapperView::m_glOwner = 0;
Inizializzazione variabili
// E’ importante ricordarsi di inizializzare le variabili nel costruttore della classe.
CGlwrapperView::CGlwrapperView()
{
m_pOldPalette  = 0;
m_pDC   = 0;
m_hrc   = 0;
}
Funzione di switch
inline BOOL SetGL()
{ if(m_glOwner!=this)
  { if(!wglMakeCurrent(m_pDC->GetSafeHdc(),m_hrc))
{
AfxMessageBox("GlMakeCurrent Error");
return FALSE;
}
m_glOwner = this;
}
return TRUE;
}
Messaggi e Virtual
Attraverso il Class Wizard e’ necessario inserire alcuni gestori di messaggi
Gestori necessari
I gestori necessari sono:
Create
PrecreateWindow(*)
OnDraw(*)
OnSize
OnEraseBkgnd
OnDestroy
(*) Gia’ dichiarati dall’Application Wizard
Create (1/5)
//Creazione finestra
if( !CWnd::Create(lpszClassName, lpszWindowName, dwStyle,
       rect, pParentWnd, nID, pContext) )
return FALSE;
Create (2/5)
// Creazione DC (Contesto grafico standard)
m_pDC = new CClientDC(this);
  ASSERT(m_pDC != NULL);
// Setta il pixel format e crea la palette
if (!SetupPixelFormat(m_pDC))
        return FALSE;
Create (3/5)
// Creazione palette (se necessaria)
if( CreateRGBPalette(m_pDC,m_cPalette) )
{
m_pOldPalette = m_pD->SelectPalette( &m_cPalette, FALSE);
  m_pDC->RealizePalette();
}
Create (4/5)
// Crea e setta il contesto OPENGL
m_hrc = wglCreateContext(
m_pDC->GetSafeHdc());
if(m_hrc==NULL)
{
AfxMessageBox("OpenGL contest fail");
return FALSE;
}
Create (5/5)
// Inizializzazioni GL
SetGL();
InitGL();
Gestione palette
La palette serve solo in caso di schermo a 256 colori(obsoleto).
Si realizza tramite le specifiche della Microsoft.
Si puo’ scaricare l’implementazione di default dal sito: vcg.iei.pi.cnr.it/˜rocchini/corso.html
Pixel Format (1/2)
Prima di inizializzare OpenGL e’ necessario settare il tipo di supporto pixel, che comprende:
Numero di pixel colore
Profondita’ z-buffer
Tipo di buffer (singolo/doppio)
…
I settaggi vengono inseriti nella struttura PIXELFORMATDESCRIPTOR.
Pixel Format(2/2)
// Esempio di codice di richiesta di formato
if ( (pixelformat = ChoosePixelFormat(pDC->GetSafeHdc(), &pfd)) == 0 )
{
AfxMessageBox("ChoosePixelFormat failed");
return FALSE;
}
if (SetPixelFormat(pDC->GetSafeHdc(), pixelformat, &pfd) == FALSE)
{
AfxMessageBox("SetPixelFormat failed");
return FALSE;
}
Inizializzazioni OpenGL
Le inizializzazioni di OpenGL non fanno parte del sistema di finestre, ma sono state citate perche’ indispensabili per la visualizzazione della finestra OpenGl (per evitare l’effetto “finestra nera”).
Esempi di settaggi OpenGL
glClearDepth(1.0f);
glShadeModel( GL_SMOOTH );
glEnable(GL_DEPTH_TEST);
glEnable(GL_COLOR_MATERIAL);
glDisable(GL_CULL_FACE);
glEnable(GL_LIGHT0);
fv4[0]=0.1f; fv4[1]=0.1f; fv4[2]=0.1f; fv4[3]=1.0f;
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fv4);
glLightfv(GL_LIGHT0, GL_POSITION, fv4);
glLightfv(GL_LIGHT0, GL_DIFFUSE, fv4);
glEnable(GL_LIGHTING);
OnDestroy (1/2)
Alla fine dell’utilizzo delle strutture OpenGl e’ importante chiamare le funzioni di rilascio delle risorse.
Queste funzioni devono essere chiamate subito prima la distruzione della finestra View.
OnDestroy (2/2)
void CGlwrapperView::OnDestroy()
{
SetGL();
glFinish();
wglMakeCurrent(NULL,  NULL);
  if (m_hrc) ::wglDeleteContext(m_hrc);
  if (m_pOldPalette)
m_pDC->SelectPalette(m_pOldPalette, FALSE);
if (m_pDC) delete m_pDC;
m_glOwner = NULL;
CView::OnDestroy();
}
PreCreateWindow
// Lo stile della finestra deve contenere le seguenti specifiche:
BOOL CGlwrapperView::PreCreateWindow(
    CREATESTRUCT& cs)
{
cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CS_OWNDC;
return CView::PreCreateWindow(cs);
}
Erase Background
// Questa callback impedisce al sistema di disegnare lo sfondo bianco di defalt:
BOOL CGlwrapperView::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
OnSize (1/3)
OnSize e’ chiamata alla creazione della Finestra e ad ogni cambiamento di dimensione.
Si deve occupare di comunicare ad OpenGL l’effettiva dimensione della finestra (viewport)
Deve settare il tipo di proiezione di visualizzazione (ortogonale/prospettica)
OnSize (2/3)
void CGlwrapperView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
if(cx!=0 && cy!=0)
{
… gestione dimensione…
}
}
OnSize (3/3)
const float fNearP  =  0.1f;
const float fFarP   = 20.0f;
const float fPAngle = 45.0f;
SetGL();
glViewport(0, 0, cx, cy);
GLfloat fAspect;
if (cy) fAspect = GLfloat(cx)/cy;
else fAspect = 1.0f;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fPAngle, fAspect, fNearP, fFarP);
glMatrixMode(GL_MODELVIEW);
OnDraw, introduzione
OnDraw, finalmente, visualizza effettivamente il contesto OpenGl.
Deve contenere il codice utente per la visualizzazione
OnDraw, sezione critica (1/2)
OpenGL e intrinsecamente asincrono, vale a dire che la visualizzazione continua in parallelo con il programma.
E’ possibile che il sistema chiami più volte OnDraw, prima che la visualizzazione sia terminata.
OnDraw, sezione critica (2/2)
void CGlwrapperView::OnDraw(CDC* pDC)
{
if(pDC==NULL) return;
static BOOL bBusy = FALSE;
if(bBusy)
return;
bBusy = TRUE;
… SEZIONE PRINCIPALE…
bBusy = FALSE;
}
OnDraw, sezione principale
SetGL(); // Setta il contesto corrente
// Cancellazione buffer
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
… COMANDI UTENTE…
glFinish();
SwapBuffers(wglGetCurrentDC());
OnDraw Ottimizzata
SetGL(); // Setta il contesto corrente
glLoadIdentity();
… COMANDI UTENTE…
glFinish();
SwapBuffers(wglGetCurrentDC());
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
OnDraw, comandi utente
Esempio di comandi utente (visualizzazione triangolo):
glBegin(GL_TRIANGLES);
glVertex3f(0,0,-10);
glVertex3f(10,0,-10);
glVertex3f(0,10,-10);
glEnd();
Applicazione
L’applicazione e’ finalmente pronta per essere compilata ed eseguita.
Un comodo tool: Trackball
Nel sito web del corso e’ possibile scaricare il sorgente del programma, che comprende la classe Trackball per La manipolazione della vista.
Visualizzazioni animate, intro
La visualizzazione della finestra OpenGl avviene su richiesta del sistema, quando e’ necessario ridisegnarne il contenuto.
In alcune applicazioni e’ necessario invece ridisegnare la finestra OpenGL su richiesta dell’applicazione.
Animazioni, il Timer
Una prima possibilità è quella di usare un timer (classe Ctimer), che periodicamente genera il messaggio WM_TIMER.
Il messaggio può essere gestito dall’applicazione per generare animazioni.
Questa soluzione non è però efficace; infatti il timer non è affidabile per intervalli inferiori al secondo.
Animazioni, OnIdle
Una soluzione migliore è l’utilizzo della funzione OnIdle, della classe Applicazione.
Tale funzione viene chiamata tutte le volte che il sistema ha finito le sue operazioni.
Esercizi
Scaricare lo scheletro di applicazione glwrapper, oppure rigenerarlo da zero e:
Visualizzare grafici di funzioni da R2 a R.
Visualizzare oggetti animati nel tempo, utilizzando Onidle
Realizzare una versione di graphedit visualizzata in OpenGL.
Contatti
Claudio Rocchini
Visual Computing Group
Istituto Elaborazione Informazione
Area della Ricerca di Pisa
Email: rocchini@iei.pi.cnr.it
Tel: 050-3152926