OpenGL e Glut

Note integrative al corso di Grafica Interattiva, corso di Laurea in Informatica, nuovo ordinamento.
A cura di Paola Magillo, DISI, Universita' degli Studi di Genova.

Ruoli di OpenGL e di Glut

OpenGL e' una libreria grafica. Fornisce strumenti per "disegnare" una scena in una finestra.

Assume di avere a disposizione una finestra in cui disegnare, ma non e' in grado di procurarsene una ne' di gestire l'interazione dell'utente con la finestra.

Glut e' una libreria creata per colmare questa lacuna. Crea finestre in cui OpenGL possa disegnare e gestisce l'interazione con l'utente su tali finestre.

Glut NON e' una libreria per interfacce grafiche. Non fornisce dispositivi di interazione sofisticati (bottoni, campi di testo, ecc.).

Una finestra creata da Glut e' "monolitica", non c'e' possibilita' di darle una struttura creando sottocomponenti/sottofinestre (pannelli, bottoni, ecc., come nelle librerie per interfacce grafiche).

Una finestra Glut prevede un certo numero di eventi che possono essere catturati associandovi delle funzioni callback. La funzione callback associata ad un certo tipo di evento scattera' automaticamente al verificarsi di quell'evento sulla finestra. Una callback e' associata a tutta la finestra, non c'e' possibilita' di associare callback diverse a zone diverse della finestra (appunto perche' la finestra e' monolitica).

Inoltre Glut prevede la possibilita' di associare un menu' pop-up alla finestra, che appare quando l'utente preme il mouse all'interno della finestra. Anche qui un solo menu' pop-up e' associato a tutta la finestra, non c'e' possibilita' di associare menu' diversi a zone diverse della finestra (la finestra e' monolitica). Il menu' puo' essere gerarchico, cioe' avere sotto-menu'.
Nota: in realta' si possono avere tanti menu' quanti i tasti del mouse.

Il tipico programma

Un programma OpenGL+Glut consiste in:

Il main

Inizializzazione

glutInit(int * argc, char ** argv);

Chiamata con gli stessi argomenti con cui il programma e' invocato da command line.

Creazione delle finestre

Per ciascuna finestra che si vuole creare bisogna chiamare:

Registrazione di callback per gli eventi

Per ciascuna finestra e' necessario registrare almeno la display callback, che scatta quando la finestra diventa visibile (la prima volta, oppure dopo essere stata parzialmente o totalmente nascosta). Tale callback contiene il codice OpenGL necessario a disegnare la scena.
Sulle callback torneremo dopo.

Definizione di menu' e loro callback

Su questo torneremo dopo.

Main loop

A questo punto e' tutto predisposto, ma non appare ancora nulla su shermo.

glutMainLoop();

Rende visibile la finestra ed innesca il ciclo principale di gestione degli eventi. Questo e' un ciclo senza fine, interamente gestito da Glut, che ad ogni giro:

Il primo evento sara' l'evento di "finestra resa visibile".

Il main loop non termina mai. Va chiamato come ultima istruzione del main, dopo avere registrato le callback. Il programma puo' terminare solo eseguendo exit all'interno di una funzione callback.

Callback

Eventi/callback previsti da Glut:

Le funzioni da usare come callback devono essere scritte dal programmatore prevedendo certi parametri come argomenti. Il numero, tipo e significato degli argomenti dipendono dal tipo di evento. Per esempio:

Gli argomenti saranno generati automaticamente da Glut e passati alla funzione quando si verifichera' l'evento a cui la funzione e' stata associata come callback.

Sia XXXXX il nome di un evento Glut (vedere lista precedente):

Callback relative al disegno OpenGL

Display callback

Funzione senza argomenti, di tipo: void f(void)

Chiamata da Glut quando e' necessario ridisegnare la finestra. Contiene il codice OpenGL che ridisegna la scena.

Si puo' chiamare anche da programma mediante istruzione appostita, per ridisegnare la scena dopo aver cambiato qualcosa.

Di norma tutto il codice OpenGL trova posto nella display callback. Non mettere codice OpenGL dentro altre callback. Se una callback deve ridisegnare la scena (es. idle callback per animazione) lo deve fare chiamando glutPostRedisplay(); (vedere anche dopo).

Tipicamente il corpo della display callback contiene:

Reshape callback

Funzione con due argomenti, di tipo: void f(int, int)

Chiamata da Glut quando la finestra cambia dimensioni. Gli argomenti sono generati automaticamente e sono le nuove dimensioni in pixel della finestra (larghezza, altezza).

Per associare f alla finestra come reshape callback (nel main):
glutReshapeFunc(f);

La parte di finestra in cui OpenGL disegna e' chiamata viewport. Per default (cioe' se non viene registrata una reshape callback), Glut mantiene sempre la viewport coincidente con tutta la finestra.
Questo significa che se sto disegnando un quadrato in una finestra quadrata e l'utente deforma la finestra rendendola rettangolare, il mio quadrato diventera' un rettangolo (si altera la "aspect ratio").
Il programmatore puo' registrare una reshape callback che eviti questo problema imponendo a OpenGL di disegnare sempre in una sottoparte quadrata della finestra (mantenimento della "aspect ratio").

Tipicamente il corpo della reshape callback contiene:

Nota sulle altre callback

Le uniche funzioni callback che contengono codice OpenGL sono la display callback e la reshape callback (per il cambiamento della viewport). Non mettere codice OpenGL nelle altre funzioni callback.

Il corpo di funzione callback di solito contiene:

Callback relative alla tastiera

Keyboard callback

Funzione con tre argomenti, di tipo: void f(unsigned char c, int x, int y)

Chiamata da Glut quando l'utente preme un carattere stampabile della tastiera. Gli argomenti, generati automaticamente, sono

Per associare f alla finestra come keyboard callback (nel main):
glutKeyboardFunc(f);

Special callback

Chiamata da Glut quando premo un tasto speciale sulla finestra.

Funzione con tre argomenti, di tipo: void f(int k, int x, int y)

Chiamata da Glut quando l'utente preme un tasto speciale (non stampabile) della tastiera. Gli argomenti, generati automaticamente, sono

Per associare f alla finestra come special callback (nel main):
glutSpecialFunc(f);

Callback relative al mouse

Mouse callback

Funzione con quattro argomenti, di tipo: void f(int b, int s, int x, int y)

Chiamata da Glut quando l'utente preme o rilascia un bottone del mouse. Gli argomenti, generati automaticamente, sono

Per associare f alla finestra come mouse function:
glutMouseFunc(f);

Motion e Passive Motion callback

Funzioni a due argomenti, di tipo: void f(int x, int y)

Chiamate da Glut quando l'utente muove il mouse tenendo il bottone rispettivamente premuto o rilasciato. L'argomento e' la posizione (x,y) del mouse.

Per associare f alla finestra come motion o passive motion callback:
glutMotionFunc(f); oppure glutPassiveMotionFunc(f);

Entry callback

Funzione a un solo argomento, di tipo: void f(int s)

Chiamata da Glut quando il mouse entra od esce dalla finestra. L'argomento e' lo stato attuale: GLUT_ENTERED o GLUT_LEFT.

Per associare f alla finestra come entry callback:
glutEntryFunc(f);

Callback relative all'animazione

Idle callback

Funzione senza argomenti, di tipo: void f(void)

Chiamata da Glut "continuamente", cioe' ad ogni giro del main loop.

Per associare f alla finestra come idle callback:
glutIdleFunc(f);

Visibility callback

Funzione a un solo argomento, di tipo: void f(int s)

Chiamata da Glut quando lo stato della finestra passa da visibile a invisibile o viceversa. L'argomento e' lo stato attuale: GLUT_VISIBLE o GLUT_NOT_VISIBLE.

Tipicamente usata per togliere la idle callback quando la finestra non e' visibile e rimetterla quando torna visibile. Per togliere la idle callback: glutIdleFunc(NULL);

Per associare f alla finestra come visibility callback:
glutVisibilityFunc(f);

Timer callback

Funzione a un solo argomento, di tipo: void f(int v)

Chiamata da Glut dopo un tempo stabilito a partire da quando la funzione viene registrata come timer callback. Anche il valore di v e' stabilito al momento della registrazione.

Per associare f alla finestra come timer callback:
glutTimerFunc(k,f,v);

Menu'

Glut permette di regitrare un menu' pop-up gerarchico (anche detto "a cascata") associato ad bottone del mouse in una finestra.

Ad una finestra per un certo bottone del mouse e' associato un solo menu' pop-up, il quale puo' avere vari livelli di sottomenu'.

Costruzione del menu' della finestra

La gerarchia di menu' e' rappresentata da un albero. La radice e' il menu' principale che appare cliccando con il mouse sulla finestra (con il bottone prestabilito). Se una voce di menu' chiama un sottomenu', il sottomenu' e' figlio del menu' che lo chiama (il quale puo' essere sottomenu' di un altro). Le foglie sono menu' che non ne chiamano altri.

La gerarchia si costruisce "dal basso" iniziando dalle foglie.

Per una finestra si possono avere fino a tre gerarchie di menu', ciascuna con la radice collegata a un diverso bottone del mouse.

Creazione

int glutCreateMenu(f);

Crea un menu' (finora senza alcuna voce) avente la funzione f come callback. Ritorna un identificatore per il menu'. Il menu' appena creato diventa il menu' corrente, tutte le successive istruzioni di manipolazione di menu' si riferiscono al menu' corrente.

La funzione f e' incaricata di eseguire le azioni corrispondenti alle varie voci.
E' una funzione a un argomento del tipo: void f (int i)
Quando l'utente selezionera' la voce i-esima del menu', Glut chiamera' automaticamente la funzione f con argomento i.

Tipicamente il corpo di f contiene uno switch a seconda del valore di i.
Valgono le considerazioni fatte per le altre callback: non mettere codice OpenGL nel corpo di f!

Aggiunta di voci "ordinarie"

void glutAddMenuEntry(char * label, int v);

Aggiunge una voce al menu' corrente.

Collegamento di sottomenu'

void glutAddSubMenu(char * label, int m);

Aggiunge al menu' corrente una voce che invoca un altro menu' (sottomenu'). Il sottomenu' apparira' quando l'utente scorrera' col mouse su quella voce.

Per il menu' radice

void glutAttachMenu(int b);

Collega il menu' corrente al bottone del mouse passato in argomento, b e' GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON, oppure GLUT_RIGHT_BUTTON. Il menu' pop-up apparira' premendo quel bottone.

Callback per i menu'

E' buona norma disattivare funzioni costose (es. animazione) mentre l'utente sta interagendo con un menu' pop-up. La Menu Status callback e' chiamata da Glut quando attiva o disattiva un menu' pop-up.

La menu' callback e' una funzione a tre argomenti, di tipo:
void f(int s, int x, int y)

Per associate f come menu status callback:
glutMenuStatusFunc(f);

Altre funzioni utili per i menu'

Ne citiamo solo alcune: