Strutturare una finestra 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.

Obiettivo

Abbiamo detto che una finestra creata da Glut e' "monolitica" cioe' gestita come un pezzo unico:

Non e' possibile strutturare la finestra in sotto-aree indipendenti, cioe' ciascuna con sua funzione di ridisegnamento, sue callback per gli eventi, suoi menu' pop-up, diversi da quelli delle altre sotto-aree.
Pero' si puo' simulare.

Strutturazione simulata

La struttura della finestra in sotto-aree e' gestita interamente dal programmatore, ad "insaputa" di Glut.

Esempio: finestra divisa verticalmente in due parti uguali

  +-----------------+        +--------+--------+
  |                 |        |        |        | 
  |                 |        |        |        |
  |    finestra     |h       |  area1 | area2  |h
  |                 |        |        |        |
  |                 |        |        |        |
  +-----------------+        +--------+--------+
           w                     w/2     w/2

Un diverso comportamento per area1 e area2 si simula sfruttando il fatto che e' possibile cambiare dinamicamente la viewport, le callback registrate e i menu'.

Disegnamento differenziato

La finestra ha sempre un'unica display callback, la quale

  1. pulisce tutta la finestra
  2. per ciascuna sotto-area:
  3. ripristina la viewport a tutta finestra
  4. scambia front e back buffer

Ottenere la viewport corrente

La viewport e' sempre mantenuta a tutta finestra tranne al passo 2. La viewport e' parte dello stato di OpenGL e si legge con

dove wp e' un array di 4 interi, che viene riempito con la descrizione della viewport, nell'ordine i valori x0,y0 (punto di aggancio, angolo in basso a sinistra). w,h (larghezza, altezza).
Attenzione: sono coordinate come le pensa OpenGL, cioe' con y=0 in basso.

Assegnare una viewport ristretta

  1. Calcolare il punto di aggancio e le dimensioni della sotto-area. Per il punto di aggancio ricordare che per OpenGL y=0 in basso.
    Nell'esempio di prima: area1 = (wp[0],wp[1],wp[3]/2,wp[4]), area2 = (wp[3]/2,wp[1],wp[3]/2,wp[4])
  2. Assegnare la nuova viewport con glViewport

    Disegnare nella viewport ristretta

    Esattamente come disegnare in una viewport normale. Occorre: assegnare matrici projection e modelview, impostare attributi, definire primitive.

    Ripristinare la viewport precedente

    Richiamare glViewport con i valori salvati nell'array wp.

    Callback e menu' differenziati

    Idea

    Cambiare la callback o il menu' al volo quando il puntatore del mouse passa da una sotto-area all'altra.

    Come si fa

    Si registra come motion callback e come passive potion callback una funzione f(int x, int y) che esegua le seguenti operazioni:

    Staccare e attaccare menu' mentre questi sono in uso (cioe' visibili ovvero "popped-up") provoca errore. Per evitare questo, occorre disabilitare motion e passive motion callback mentre i menu' sono in uso.
    A tale scopo si registra come menu status callback una funzione g(int s, int x, int y) che:

    Primitive raster in OpenGL

    Esistono due formati grafici:

    Le primitive OpenGL viste finora (definite come liste di vertici tra glBegin e glEnd) sono vector. I vertici sono dati in spazio di coordinate di modellazione.
    Le primitive vector subiscono una serie di trasformazioni e alla fine vengono tradotte in formato vector per essere disegnate.
    L'accuratezza di definizione delle primitive vector e' virtualmente infinita (limite la precisione floating point di macchina). L'accuratezza di definizione dell'immagine raster che viene fuori dipende dalla dimensione della viewport (piu' grande = piu' accurata).

    OpenGL ha anche primitive raster.
    Le primitive raster sono matrici che vengono disegnate sulla finestra di output cosi' come sono (un elemento della matrice = un pixel) senza sottostare alla serie trasformazioni in cui incorrono le primitive vector.
    L'accuratezza di definizione e' fissata, limitata dalle dimensioni della matrice (numero di righe e colonne).

    Classificazione

    A seconda del valore contenuto nella matrice per ogni pixel, le primitive raster si differenziano in bitmap e pixmap.

    Bitmap

    Il valore memorizzato per ogni pixel e' binario (1 bit):

    Esempi di bitmap sono quelle per le font dei caratteri.

    Pixmap

    Il valore memorizzato per ogni pixel e' un colore, che puo' essere specificato in vari modi:

    Ciascun valore pu' essere espresso mediante vari tipi numerici: Un numero maggiore di bit allarga la gamma di colori. Es: con 8 bit posso avere 256 valori distinti, con 16 posso averne 65536 ecc.

    Memorizzazione

    E' compito del programmatore memorizzare la bitmap/pixmap in una struttura dati opportuna che OpenGL possa "capire".

    La bitmap o pixmap deve essere memorizzata in un array unidimensionale dove:

    Consideriamo per esempio una pixmap con valori RGB:

    Disegno di primitive raster

    Il disegno di una primitiva raster e' influenzato da due variabili di stato OpenGL:

    In genere prima di disegnare primitive raster e' necessario chiamare

    per impedire ad OpenGL di arrotondare sempre a 4 byte (default) la lunghezza delle righe della matrice in cui e' contenuta la bitmap/pixmap.

    Disegno, e non solo, di pixmap

    E' possibile

    Per leggere: glReadPixels(x, y, w, h, formato, tipo, pixels)

    Per disegnare: glDrawPixels(w, h, formato, tipo, pixels)

    Per copiare: glCopyPixels(x, y, w, h, GL_COLOR)

    Disegno di bitmap

    Le bitmap sono state progettate allo scopo principale di disegnare font di caratteri. Percio' il disegno di una bitmap prevede:

    glBitmap(w, h, x0, y0, x1, y1, pixels)

    Per scrivere con glBitmap uno dovrebbe prima definire bitmap per tutte le lettere dell'alfabeto. Glut offre bitmap gia' definite per alcune font e una funzione piu' comoda per disegnare caratteri.

    glutBitmapCharacter(font, carattere);

    Il carattere viene disegnato trattando automaticamente il caso di carattere che sporge in basso rispetto alla raster position, e spostando gia' la raster position sulla posizione per il prossimo carattere.

    Esempio di funzione che scrive una stringa a partire dalla posizione raster corrente:

    void displayString(char * s)
    {
      int i;
      for (i=0; s[i]!='\0'; i++)
        glutBitmapCharacter(GLUT_BITMAP_8_BY_13, s[i]);
    }