Visualizzazione post con etichetta funzioni virtuali. Mostra tutti i post
Visualizzazione post con etichetta funzioni virtuali. Mostra tutti i post

venerdì 29 marzo 2013

Movimento = Ridisegno + Scambio

La struttura di una animazione reale, non differisce molto da questa descrizione. Solitamente, è più facile ridisegnare l'intero buffer dall'inizio per ogni fotogramma, piuttosto che cercare di capire cosa deve essere ridisegnato. Ciò è verissimo per i simulatori di volo, nel quale un cambiamento dell'orientamento del mezzo, costringe al riposizionamento di tutto ciò che si vede fuori dall'abitacolo. Nella maggior parte delle animazioni, gli oggetti sono ridisegnati mediante semplici trasformazioni (un cambio del punto di vista dell'osservatore, o un veicolo che si muove lungo la strada, un oggetto ruotato leggermente). Se è necessario un lungo processo di calcolo per le operazioni non grafiche, il tasso di fotogrammi subisce un brusco calo. Da tenere in mente quindi, che il tempo di attesa dopo la routine scambia_i_buffer() , viene usato per questi calcoli. OpengGL non ha una routine di comando scambia_i_buffer() in quanto ciò è legato all'hardware disponibile ed al sistema di gestione delle finestre nella macchina sulla quale il programma viene eseguito.

lunedì 4 marzo 2013

GLUT, OpenGL Utility Toolkit

Come si sa OpenGL non contiene comandi di rendering, ma è progettato per essere indipendente dal sistema operativo e dalla gestione delle finestre. Di conseguenza, non contien comandi per aprire le finestre, o leggere eventi dal mouse o dalla tastiera. Sfortunatamente, è impossibile scrivere un programma di grafica completo senza almeno aprire una finestra, e i programmi più utili richiedono una forma di input da parte dell'utente per operare i comandi del sistema operativo e del sistema a finestra. In molti casi, un programma completo crea gli esempi più interessanti, in questo GLUT semplifica il sistema di gestione delle finestre, dell'input e così via. Se si possiede una implementazione di GLUT e OpenGL sul sistema, gli esempi mostrati saranno eseguiti senza problemi. Inoltre, i comandi di disegno delle OpenGL sono limitati al disegno di geometrie semplici quali punti, linee e poligoni; GLUT include alcune routine che permettono di creare oggetti 3D più complicati quali sfere, toroidi, teiere. In questo modo le immagini mostrate dai programmi sono rese più interessanti. GLUT può essere non molto soddisfacente come applicazione, ma può essere un buon punto di partenza per imparare OpenGL.

Gestione della Finestra

Cinque routine che eseguono i compiti necessari a gestire la finestra.
  • glutInit(int *argc, char **argv) inizia GLUT e processa ogni argomento di comando (per X, possono esserci opzioni sullo schermo e la geometria). glutInit() deve essere chiamata come prima GLUT routine.
  • glutInitDisplayMode(unsigned int modo) specifica se si vuole usare RGBA o modello di colore indicizzato. Si può anche specificare se si vuole finestra a singolo o doppio buffer. (Se si lavora con la mappa dei colori ne si vorrà caricare uno; usare glutSetColor() per farlo.) Infine si può usare la routine per indicare se si vuole una finestra associata ad un tipo di profondità, tratto, buffer di accumulazione. Per esempio si vuole creare una finestra con doppio buffering, e colore RGBA, bisogna chiamare: glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH).
  • glutInitWindowPosition(int x, int y) specifica localizzazione nello schermo partendo dall'angolo in lato a sinistra.
  • glutInitWindowSize(int larghezza int taglia) specifica larghezza e altezza della finestra.
  • int glutCreateWindow(char *stringa) crea la finestra del contesto OpenGL. Restituisce un indicatore unico per la finestra. Avvertenza, sino a quando glutMainLoop() viene chiamato la finestra non è visualizzata.
      

    Le chiamate alla Visualizzazione


    glutDisplayFunc(void (*func)(void)) è la prima e la più importante funzione di chiamata alla visualizzazione. Non appena GLUT determina il contesto della finestra che deve essere visualizzata, la chiamata alla funzione registrata mediante glutDisplayFunc() viene eseguita. Pertanto le routines di cui si ha bisogno per ridisegnare la scena nello schermo attraverso il richiamo della funzione.

    Se il programma cambia i contenuti della finestra, a volte bisogna richiamare glutPostRedisplay(void), il quale fornisce una scappatoia a glutMainLoop() funzione per essere richiamata mediante questa nuove opportunità.

    Eseguire il programma

    L'ultima funzione da richiamare è glutMainLoop(void). Tutte le finestre che sono state create vengono mostrare, e il rendering di queste è ora effettivo. Il processo degli eventi comincia ed avviata la registrazione degli eventi a schermo. Una volta entrati nell'anello, non se ne esce.

domenica 30 dicembre 2012

Definire funzioni membro virtuali

Un oggetto di una classe derivata è anche un membro di una classe di base. Per esempio, il programma Capo virtuale, l'oggetto Capo è anche un oggetto Nemico. Tutto ciò ha senso in quando si tratta solo di un tipo di nemico speciale. Inoltre ha senso in quanto l'oggetto Capo ha tutti i membri dell'oggetto Nemico. Bene, e allora? Poiché una classe derivata è anche un membro della classe di base, si può usare un puntatore alla classe di base per puntare un oggetto della classe derivata. Questo è ciò che faccio in main() con la seguente istruzione, la quale istanzia un oggetto Capo su heap e crea un puntatore a Nemico che punti all'oggetto Capo.
Nemico* pCattivo = new Capo();

Cosa vuol dire tutto questo? E' utile a permetterci di gestire l'oggetto senza doverne conoscere il tipo esatto. Per esempio di può avere una funzione che accetta un puntatore a Nemico e può funzionare sia con Nemico che con Capo oggetti. La funzione non deve conoscere l'esatta tipologia di oggetto passato, può funzionare con oggetti che producono risultati diversi a seconda di quale sia utilizzato. Ma ora analizziamo cosa accade nella successiva riga di codice.
pCattivo - > Fastidio();

Invece mostra una linea di testo Il nemico vuole combatterti. Quindi non è stata richiamata la funzione Fastidio() dell'oggetto Capo ma dell'oggetto Nemico, nonostante sia stata superata dalla funzione Fastidio() nella classe di Capo. Questo accade come risultato di un legame precedente nel quale l'esatta funzione è legata al tipo di puntatore, in questo caso Nemico. Ciò di cui si ha bisogno è una funzione membro chiamata sul tipo di oggetto puntato, e non fissato dal puntatore. In questo si ottiene la flessibilità del legame successivo attraverso funzioni virtuali, che ottiene con il comportamento polimorfico. Per creare una funzione membro virtuale, si aggiunge semplicemente la parola chiave virtual prima del nome della funzione nella sua dichiarazione. Questo è ciò che viene fatto in Nemico con la seguente istruzione.
void virtual VFastidio() const
{ cout << “Il nemico vuole combatterti.\n”; }

Questo significa che VFastidio() è una funzione virtuale. E' virtuale in Nemico, e viene ereditata in Capo. Questo impila che VFastidio() in Capo possa superare con la seguente istruzione la funzione ereditata. La corretta versione di VFastidio() viene chiamata (in base al tipo di oggetto) e non sarà fissata dal tipo di puntatore.
void virtual VFastidio() const
{ cout << “Il capo vuole mettere fine alla tua esistenza.\n”; }

La prova del comportamento polimorfico viene in main() con la seguente istruzione, nel quale la funzione VFastidio() definita in Capo viene chiamata ed il testo “Il capo vuole mettere fine alla tua esistenza”. Viene mostrato su schermo.

pCattivo - > VFastidio();

Definire Distruttori virtuali

Quando si usa un puntatore alla classe di base per puntare un oggetto di una classe derivata, si può avere un potenziale problema. Quando viene cancellato il puntatore, solo il distruttore della classe viene chiamato per l'oggetto. Questo può condurre a risultati disastrosi poiché il distruttore della classe derivata è necessario per liberare la memoria ( come fa il distruttore di Capo). La soluzione come si può indovinare, è di rendere il distruttore della classe virtuale. In questo modo viene chiamato il distruttore della classe, il qual conduce (sempre) alla chiamata del distruttore della classe di base, dando ad ogni classe la possibilità di ripulirsi. La teoria viene messa in pratica quando si crea il distruttore virtuale della classe Nemico.
virtual ~Nemico(){ cout << “m_pDanno cancellato”; delete m_pDanno;}
Quando viene cancellato il puntatore che punta alla classe Capo con la seguente istruzione, il distruttore della classe Capo viene chiamato, il quale libera la memoria su heap dei dati m_pMultidanno e mostra il messaggio.
delete pCattivo;

Quindi viene chiamato il distruttore di Nemico, il quale libera la memoria su heap da m_pDanno e mostra il messaggio m_pDanno cancellato. L'oggetto è distrutto, e tutte le memorie associate ad esso sono cancellate.