martedì 31 dicembre 2013

Testing

Un programma che non è stato testato non funziona. L'ideale di progettare e verificare un programma funzionante in prima battuta non è ottenuto per la maggior parte dei programmi. Dobbiamo puntare verso l'ideale, ma non dobbiamo pensare che queste verifiche siano semplici.
“Come testare?” - E' una domanda alla quale non si può dare una risposta generale; “Quando testare?”, invece ha una risposta semplice, presto ed il più spesso possibile. Le strategie di prova possono essere generate come parte della progettazione e degli sforzi d'implementazione, o sviluppate in modo parallelo ad essi. Ritardare un testing serio sino al termine dell'implementazione è una prescrizione data per consegne ritardate e rilasci incompleti.
Quando è possibile il sistema deve essere progettato specificatamente per essere facile da verificare; in particolare i meccanismi di test possono essere inclusi all'interno del sistema stesso. A volte non viene realizzato a causa della paura di creare costi eccessivi ed una ridondanza non necessaria del codice quando si tratta di fare test di consistenza di grandi strutture dati. Questa paura è mal riposta, in quanto l'eccesso di codice necessario per i test, può essere rimosso prima della consegna del sistema. I commenti sono qualcosa di utile in questo caso. Più importanti di una serie di test è l'idea che la struttura di un sistema debba avere una possibilità ragionevole per convincere noi stessi e i nostri clienti che possiamo eliminare gli errori attraverso un controllo statico, una analisi statica e le verifiche. Quando una strategia per una tolleranza all'errore viene sviluppata, una strategia di test può essere sviluppata come complementare e vicina al relativo aspetto del sistema completo. Se la fase di testing viene scartata durante la progettazione, durante i test, quindi poco prima della consegna, potrebbero risultare problemi di manutenzione. L'interfaccia tra le classi e le loro dipendenza sono un buon punto per iniziare a lavorare sulla strategia di test.

Determinare quanto verificare è di solito difficile. Comunque, poche verifiche sono un problema più comune rispetto all'eccesso; esattamente quante risorse debbano essere allocate per i test confrontandoli con la progettazione e l'implementazione dipende dalla natura del sistema. Comunque come regola di massima, si suggerisce di allocare più risorse all'inizio dello sviluppo del sistema per verificarlo. I test inoltre devono essere focalizzati sui problemi che hanno conseguenze disastrose e che occorrono con frequenza.

lunedì 30 dicembre 2013

Uso dei modelli

Quando si scrive un articolo, è necessario trovare un modello adatto da seguire. Piuttosto che cercare di trovare articoli che trattano lo stesso argomento. Bisogna trovare una struttura adatta all'argomento trattato. In questo modo è possibile modificare il testo in modo da aggiungere frasi, informazioni e dettagli in modo logico, senza dover alterare la struttura di tutto l'articolo. L'uso di sistemi già esistenti per realizzare nuovi progetti è la norma piuttosto che l'eccezione in tutte le forme di costruzione creativa. Quando è possibile, la progettazione e la programmazione devono essere basate su lavori precedenti. I limiti di questa libertà consente al programmatore di basare la propria attenzione solo su pochi argomenti alla volta. Iniziare un progetto principale, “dalla bozza su carta” può essere divertente. Comunque spesso una descrizione più attenta di questo fenomeno è “ubriacante” e il risultato conduce a progetti alternativi lasciati a metà. Avere un modello non è una costrizione e non richiede che il modello sia seguito fedelmente; libera semplicemente il designer dal considerare un aspetto del progetto alla volta.
L'uso dei modelli è inevitabile, in quanto il progetto verrà sintetizzato dall'esperienza di tutti i progettisti. Avere un modello esplicito rende la scelta di un modello una decisione cosciente, fa assunzioni esplicite, definisce un vocabolario comune, fornisce una struttura iniziale per il progetto, e aumenta la probabilità dei progettisti di ottenere un comune approccio. Naturalmente la scelta del modello iniziale è un passo importante in fase di progettazione e spesso può essere fatto solo dopo una ricerca di modelli potenziali e attenta valutazione delle alternative. Inoltre in molti casi è adattabile solo dopo una comprensione delle modifiche necessarie per adattarlo alle idee particolari della nuova applicazione.
La progettazione del software è complessa, bisogna cercare maggior aiuto possibile. Non bisogna rifiutare l'uso di modelli con sdegno liquidandoli come “imitazioni”. L'imitazione è una forma di appiattimento, ma l'uso di modelli provenienti da precedenti lavori è d'ispirazione, nei confini definiti dalla legge del copyright, è una tecnica innovativa che funziona in tutte le arre di lavoro. Alcuni definiscono questo uso di modelli come “riuso del progetto”.
E' una buona idea per il progettista conoscere tutte le soluzioni popolari esistenti per una cerca applicazione. Come programmatore si preferisce le soluzioni che abbiano del codice associato ad esse come esempio concreto. Le persone che usano delle soluzioni standard spesso usano un vocabolario specializzato per comunicare tra loro stesse.

Sfortunatamente questo può diventare un linguaggio privato che tende ad escludere degli estranei dalla comprensione; è essenziale assicurare una comunicazione adatta tra le persone coinvolte nelle diverse parti del progetto, e anche con la comunità dei progettisti e programmatori nell'insieme. Ogni sistema di grandi dimensioni che ha successo è la ri-progettazione di un sistema di minori dimensioni. Non esistono eccezioni a questa regola. L'esempio più prossimo che si possa fare è quello di sistemi che hanno fallito e sono rimasti impanatati per anni con grandi costi, poi eventualmente sono diventati successi negli anni successivi alla loro data di completamento. Questi progetti casualmente e spesso inavvertitamente, hanno dapprima costruito una rete di sistemi non funzionanti, quindi sono stati trasformati in un sistema funzionante, e finalmente la ri-progettazione ha raggiunto gli scopi iniziali. Questo implica che è una follia progettare grandi sistemi direttamente dalla bozza. Maggiori sono le dimensioni del sistema da costruire, maggiore importanza avrà il modello con il quale si lavora. E per sistemi di grandi dimensioni, il solo modello accettabile deve essere un sistema più piccolo ma funzionante e simile ad esso.

giovedì 29 agosto 2013

Il ciclo di Sviluppo

Sviluppare un sistema è un'attività iterativa. Il suo ciclo principale è formato da ripetuti passi attraverso la sequenza:
  1. Esaminare il problema;
  2. Creare un progetto generale;
  3. Trovare componenti standard (e personalizzarli per il progetto);
  4. Creare nuovi componenti standard (personalizzarli per il progetto);
  5. Costruire il progetto.

L'analogia da usare, è la linea di assemblaggio di un'auto. Per il progetto in generale, deve esistere il design della nuova auto da creare. Poi si passa alle caratteristiche da fornire alla nuova auto definendone le proprietà nel dettaglio. Decidere quali proprietà sono volute, o fornire una linea guida di quali proprietà si vuole ottenere. Se ben realizzata questo tipo di lavoro viene svolto da una sola persona illuminata, e spesso viene chiamata vision. E' abbastanza comune per progetti privi di obbiettivi chiari di fallire per l'una o l'altra ragione. Il primo passo da considerare è quali componenti sono disponibili nell'inventario aziendale, o acquistabili da fornitore. I componenti trovati devono quindi trovare posto nella realizzazione della nuova auto, ma posso anche non essere del tutto adatti. Tuttavia si può pensare che “al successivo lancio”, i componenti saranno stati modificati per adattarsi all'auto. Ad esempio si può trovare un motore delle dimensioni adatte, ma che sia di potenza minorata. Il fornitore del motore potrebbe aggiungere una turbina per compensare alla potenza mancante, ma ciò non deve influenzare il disegno di base del motore.Nel caso dello sviluppo del software le classi polimorfiche e i templates sono utilizzati come componenti personalizzabili. Tuttavia non è facile creare estensioni arbitrarie a queste classi senza previsione o cooperazione da parte del gestore di queste classi. Da notare che questo modello di sviluppo funziona solo quando si ragiona a lungo termine; se il vostro orizzonte riguarda solo il prossimo rilascio del software, la creazione e la manutenzione delle componenti standard non ha senso. Questo tipo di organizzazione riguarda compagnie la cui vita abbia la durata di molti progetti, le cui dimensioni richiedano l'investimento in questa attrezzatura extra (per la progettazione, programmazione, gestione dei progetti) e formazione ( di programmatori, progettisti e manager). Si tratta di una struttura base di una fabbrica di software. Inoltre i componenti standard non possono essere considerati come universali. Puntare all'universalità in un progetto iniziale è la ricetta sicura per il fallimento; uno dei motivi per la creazione del ciclo di sviluppo è quello di ottenere esperienza.

giovedì 22 agosto 2013

Il processo di Sviluppo

Lo sviluppo del software è un processo iterativo ed incrementale. Ogni passo del processo è rivisto ripetutamente durante lo sviluppo, ed ogni revisione rifinisce il prodotto finale. In generale, il processo non ha un inizio ed una fine. Quando si progetta e implementa un sistema, si comincia da una base formata da librerie, progetti e applicazioni scritte da altri. Quando si finisce si lascia il grosso della progettazione e del codice ad altri per essere rifinito, e portato su altri sistemi. Naturalmente ogni progetto specifico ha un inizio e un termine, ciò è importante per definirne l'obiettivo e le dimensioni nel tempo. Comunque la pretesa di cominciare da zero può causare seri problemi. Inoltre la pretesa di una data di consegna inderogabile causa seri problemi ai propri successori. Il processo di sviluppo è formato da tre livelli:
  • Analisi, definire l'obiettivo del problema da risolvere
  • Progettazione, creare una struttura generale del sistema
  • Implementazione, scrivere e verificare il codice
Si ricordi la natura iterativa di questo processo, in quanto i tre livelli non sono in ordine: Da notare che alcuni aspetti maggiori del programma di sviluppo non sono in livelli separati perché si tratta di processi pemeabili tra loro.
  • Sperimentazione
  • Verifica con test
  • Analisi della progettazione ed implementazione
  • Documentazione
  • Gestione

La “gestione” del software è quella che ha maggiori iterazioni nel processo di sviluppo; è importante che analisi, progettazione ed implementazione non siano troppo staccate tra loro nel programma di sviluppo, e le persone coinvolte condividano le proprie conoscenze e comunichino effettivamente. In progetti di grandi dimensioni, spesso non accade. Gli individui si spostano da un livello all'altro durante il progetto, il solo modo di trasferire queste informazioni è la testa delle persone. Tuttavia le compagnie tendono a creare delle barriere tra i progettisti di maggiore calibro come protezione dal licenziamento, e per esempio, coloro che sono semplici programmatori. Le persono dovrebbero essere incoraggiate a condividere le informazioni tra loro, attraverso una etichetta con un grado di formalità appropriato.

sabato 17 agosto 2013

Scopi e obbiettivi

L'obiettivo di un programmatore professionista è consegnare un prodotto che soddisfa i suoi utenti. Il principale significato è produrre un programma dotato di una chiara struttura interna che possa crescere grazie ad un gruppo di programmatori abili e abbastanza motivati per rispondere in modo rapido alle opportunità ed ai cambiamenti.
Perché? La struttura interna di un programma e il processo con il quale viene creato non ha effetto sull'utente. Quindi che importanza può avere la struttura interna del programma per chi scrive il software.
Un programma ha bisogno di una struttura interna chiara, per gestire meglio:
  • testing (prove funzionali)
  • porting (adattare ad altri sistemi)
  • manutenzione
  • riorganizzazione e
  • comprensione
La maggiore importanza per un software di successo è data dalla vita d'uso, estesa da una serie di programmatori e progettisti, adattato per nuovo tipo di hardware, modificato per nuove applicazioni e riorganizzato ripetutamente. Nessuna pianificazio per questo implica il fallimento. Non solo un utente non vuole conoscere la struttura interna del programma, ma non vuole neppure pensare ad essa. Un punto di equilibrio deve essere raggiunto tra la mancanza di un design in generale per un frammento di software e una struttura troppo enfatizzata. La priam porta a preoccuparsi di smussare gli spigoli (“usa questo, correggeremo i bug nella prossiam versione”). La seconda porta a ritardi nella consegna a caqusa di una sovraorganizzazione nel progetto (“la nuova struttura è meglio della procedente, la gente aspetterà”). Inoltre spesso si ottiene qulacosa che richiede risorse che l'utente non riesce a dare. Le scelte da fare sono difficili, e per progetti di dimensioni maggiori diventano sempre più difficili. Un programma deve essere prodotto e gestito da una organizzazione, in modo che nonostante i cambi del personale, direzione e struttura di management non possa influenzarlo. Un approccio molto usato dalle aziende è creare una classe di programmatori facili da addestrare a basso livello “coders”, e una classe di programmatori facili da sostituire anche se meno economici “designers”. Tuttavia questo approccio fallisce in quanto tende a creare sistemi di grandi dimensioni con scarse performance.
I problemi di questo approccio sono:
  • comunicazione insufficiente tra codificatori e progettisti, che producono opportunità mancate, ritardi, inefficienze e problemi per mancanza di esperienza;
  • poca focalizzazione per i codificatori, mancanza di crescita professionale, stanchezza e alto cambio di personale.

Principalmente, questo sistema manca di un meccanismo di ritorno che permette alle persone di beneficiare dell'esperienza degli altri. E' uno spreco del talento umano. Creare una struttura dove le persone possano usare il loro talento, sviluppare nuove abilità, contribuire alle idee e divertirsi nonè solo uan cosa decente da fare, ma ha anche senso economico.

domenica 11 agosto 2013

Design con il C++ II

Si prega di ricordare la straordinaria diversità delle aree di applicazione e degli ambienti di sviluppo. Le osservazioni sono fatte su progetti reali e si applicano ad un'ampia varietà di situazioni, ma non possono essere considerate universali. Pertanto guardate a queste osservazioni con un certo grado di scetticismo. C++ può essere utilizzato come un C migliorato, tuttavia questo approccio lascia le tecniche più potenti e caratteristiche del linguaggio inutilizzate, in modo da guadagnare solo una piccola frazione dei benefici potenziali dall'uso del C++. In questo capitolo ci si focalizza sugli approcci al design che permettono di usare le astrazioni dati e la programmazione orientata agli oggetti del C++; tali tecniche sono chiamate programmazione orientata agli oggetti.
I maggiori argomenti di questo capitolo sono:
  • La cosa più importante da tenere a mente nello sviluppo del software è sapere chiaramente che cosa si vuole costruire.
  • Lo sviluppo di software di successo è attività a lungo termine.
  • I sistemi che costruiamo tendono ad essere limitati dalla complessità che noi o il nostro strumento può gestire.
  • Non esiste un manuale che possa sostituire l'intelligenza, esperienza e il buon gusto nella programmazione e design.
  • La sperimentazione è necessaria per lo sviluppo di software non triviali
  • Programmazione e design non possono essere considerati senza tener conto della gestione pianificata di queste attività.

E' facile e costoso sotto-stimare questi punti. E' difficile trasformare delle idee astratte in qualcosa da mettere in pratica. Si noti il bisogno di esperienza. Così come costruire mobili, biciclette, la programmazione non è un'abilità che può essere dominata senza uno studio teorico da solo. Spesso, noi umani dimentichiamo gli aspetti della costruzione dei sistemi e consideriamo lo sviluppo del software una serie di passi ben definiti, per ognuna delle quali vi sono delle azioni specifiche e degli input, il quale produce in base ad una serie di regole degli output. Design e programmazione sono attività umane; dimenticare questo e si perde tutto. Questo capitola si occupa della progettazione di vari sistemi che sono relativi all'esperienza e alle risorse delle persone che costruiscono il sistema. E' nella natura delle persone affrontare progetti che raggiungono i limiti della loro abilità. Progetti che non offrono sfide, non sono considerati come design. Solo quando qualcosa di ambizioso viene tentato si sente il bisogno di creare nuovi strumenti ed affrontare nuove tecniche. Ma c'è anche la tendenza ad assegnare progetti peri quali “sappiamo come fare” a novizi che non lo sanno. Chiaramente queste cose riguardano lo sviluppano di software di grandi dimensioni; i lettori che non sono coinvolti in tale sviluppo possono sedersi e godersi la descrizioni degli orrori da evitare. In alternativa possono guardare un sottoinsieme delle discussioni relative al lavoro individuale; non esiste un limite inferiore nella dimensione dei programmi per il quale esiste un particolare approccio alla documentazione e al design. Il problema fondamentale nello sviluppo del software è la complessità, esiste un solo modo per gestire la complessità: dividi et impera. Un problema può essere separato in sotto-problemi che possono essere maneggiati, questo semplice principio si applica in molti modi. In particolare se si usa un modulo o una classe nel design di sistemi separati del programma, che vengono connessi tramite una interfaccia be definita. Questo approccio fondamentale nella gestione dei problemi, in maniera simile viene fatto nella fase di progettazione del programma, la quale può essere idealmente divisa in iterazioni ben definite tra le persone coinvolte. Questo è l'approccio di base per al gestione di problemi di complessità inerente al processo di sviluppo e alla gente coinvolta.

sabato 10 agosto 2013

Design con il C++

Costruire un software non triviale può essere un compito difficile e complesso da realizzare. Anche per il singolo programmatore, la creazione delle procedure reali del programma è solo un parte del processo. Tipicamente, l'analisi del problema, sovrasta il design del programma, la documentazione, testing e manutenzione. Così come la gestione di tutto questo, è ben poco se comparata al lavoro di scrittura e debug del singolo frammento di codice. Naturalmente, uno può pensare che la totalità di queste attività sono “programmazione”, e quindi giungere alla conclusione che “Io non faccio design, io programmo” ma comunque sia chiamata questa attività, è importante focalizzarsi sia sulle piccole parti che sul programma in generale. I singoli dettagli, come il quadro in generale non devono essere persi di vista, anche nella fretta di consegnare il prodotto, tuttavia spesso è ciò che accade. In questo capitolo ci focalizzeremo sulle parti di sviluppo del programma che non coinvolgono direttamente la scrittura e il debug delle singole parti di codice. La discussione è meno precisa e dettagliata di come discutere sulle singole caratteristiche del linguaggio e le specifiche tecniche di programmazione presentate altrove in questo libro. Tuttavia è necessario perché non esiste un ricettario capace di dare un metodo per creare del buon software.
Tipologie dettagliate di “Come fare...” esistono per applicazioni già bene definite, ma non per applicazioni in generale. Non esiste alcun sostituto all'intelligenza, esperienza, e istinto nella programmazione. Di conseguenza questo capitolo offre solo consigli in generale e approcci alternativi, insieme a caute osservazioni.

La discussione è guidata dalla natura astratta del software, per cui scrivere un programma di piccole dimensioni (circa 10.000 linee di codice) richiede tecniche, che difficilmente possono essere applicate per progetti medio/larghi. Per questo motivo, alcune discussioni sono formulate in termini di analogie con discipline ingegneristiche meno astratte piuttosto che in termini di codice. Discussioni sulle questioni di design in termini di C++ sono presentati nei successivi capitoli. Le idee espresse in questo capitolo sono riflessi sia dal linguaggio C++ e dalla presentazione di esempi individuali attraverso il blog.

martedì 7 maggio 2013

Specificare i vertici

Con OpenGL tutti gli oggetti geometrici sono descritti in termini di vertici raggruppati ordinatamente. Si usa il comando glVertex*() per specificarlo.
void glVertex{2 3 4}{sifd}[v](coordinate);
Specifica un vertice come oggetto geometrico. Si possono fornire le coordinate (x,y,z,w) per un vertice in particolare o per pochi selezionando una versione appropriata del comando. Se si sua una versione che non specifica esplicitamente z o w, essi sono intesi con un valore pari a 0 e 1. La chiamata di glVertex*() sono solo utilizzabili tra le istruzioni glBegin() e glEnd().
glVertex2s(5,6);
glVertex3d(1.0,1.0, 3.1415);
Gldouble dvettore[3] = { 4.0, 5.0, 29.0};
glVertex3v(dvettore);

Nella prima istruzione vien specificato un vertice le cui coordinate appartengono allo spazio bidimensionale (x,y). Nella seconda istruzione viene specificato un vertice le cui coordinate appartengono ad uno spazio tridimensionale (x,y,z). Nelle istruzioni successive si definisce un vettore di coordinate tridimensionali, le quali sono passate come argomento all'istruzione glVertex3v per la definizione di una serie di vertici, nell'esempio vengono definite solo le coordinate del primo vertice.

domenica 28 aprile 2013

Descrivere Punti, linee e poligoni

Questo articolo descrive come OpenGL realizza le primitive geometriche. Tutte le primitive geometriche sono descritte in base ai loro vertici, coordinate che descrivono i punti stessi, e i termini dei segmenti agli angoli dei poligoni. La sezione successiva descrive come queste primitive sono visualizzate e quale controllo si ha su questa operazione.

Cosa sono punti, linee e poligoni?

E' molto probabile che si abbia una buona idea in termini matematici della definizione di punto, linea e poligono. In OpenGL il significato è simile. La sola differenza è data dai limiti del calcolo basato sul computer. In ogni istruzione OpenGL, i calcoli a virgola mobile hanno dei limiti di precisione, e possono avere errori di routine; di conseguenza le coordinate di punti, linee e poligoni in OpenGL soffrono degli stessi problemi. Un altra differenza importante sorge dalle limitazioni del sistema di visualizzazione grafica. In uno schermo, l'unità visualizzabile minima è il pixel, che può valere meno di un centesimo di pollice , ma è molto lontano dal concetto matematico di infinitamente piccolo (per il punto) o infinitamente sottile (per la linea). Quando OpenGL effettua dei calcoli si assume che i punti siano vettori di numeri a virgola mobile. Comunque un punto è tipicamente ma non sempre disegnato come un singolo pixel. E molti punti con coordinate leggermente diverse possono essere disegnate sullo stesso pixel.

Punti
Un punto è rappresentato da un insieme di numeri in virgola mobile che rappresentano un vertice. Tutti i calcoli interni sono fatto come se i vertici fossero tridimensionali. Vertici specificati dall'utente come bidimensionali (cioè con solo coordinate x ed y) vengono assegnati a coordinata z pari a zero da OpenGL.

Avanzato
OpenGL lavora nelle coordinate omogenee della geometria proiettiva tridimensionale, quindi per calcoli interni, tutti i vertici sono rappresentati con quattro coordinate virgola mobile (x, y, z, w). Se w è diverso da zero, queste coordinate corrispondono al punto tridimensionale euclideo (x / w, y / w, z / w). È possibile specificare la coordinata w nei comandi OpenGL, ma di fatto avviene raramente. Se la coordinata w non è specificato, ottiene il valore 1,0.

Linee
In OpenGL, la linea terminata, si riferisce ad un segmento di linea, non la sua versione matematica che si estende all'infinito in entrambe le direzioni. Ci sono semplici modi per specificare una serie di segmenti di linea collegati, o anche un poligono, formato da serie di segmenti legati. In tutti i casi, però, le linee che costituiscono la serie connesse, sono specificate in termini di vertici come loro punti terminali.


Poligoni
I poligoni sono aree chiuse formate da anelli di segmenti, ove i segmenti sono specificati mediante i loro vertici come punti terminali. I poligoni sono riempiti con pixel di colore, ma possono anche essere disegnati come linee di contorno o insiemi di punti. I poligoni sono oggetti complessi, pertanto OpenGL pone dei limiti a ciò che può costituire un poligono. Per prima cosa i bordi dei poligoni non possono intersecarsi tra loro ( in matematica si parla di poligono semplice). Per seconda cosa, i poligoni devono essere convessi, e non possono avere tacche al loro interno. OpenGL non restringe il numero di segmenti che possono costituire un poligono convesso; ma un poligono con un foro non può essere descritto. Essi sono non convessi pertanto, non viene disegnato come lo si immagina. In alcuni sistemi i poligoni poco meno che convessi possono essere riempiti.
La ragione per cui OpenGL restringe i tipi di poligoni validi, è che più semplice fornire calcoli di rendering veloce solo per poche classi di poligoni. Per massimizzare le performance, OpenGL assume che tutti i poligoni siano semplici. Tuttavia molte superfici reali consistono di poligoni non semplici, e non convessi, o poligoni con fori. Sicché tutti i poligoni complessi possono essere generati da poligoni semplici, per costruire oggetti complessi, esistono funzioni nella libreria GLU.

Rettangoli
Dato che i rettangoli sono molti utilizzati nelle applicazioni grafiche, OpenGL fornisce una funzione per disegnare rettangoli pieni, questa istruzione viene chiamata glRect*(). La sintassi può essere:

void glRect{sifd}( valore x1, valore y1, valore x2, valore y2);
void glRect{sifd}( vettore *v1, vettore *v2);

Disegna un rettangolo definito dai punti degli angoli (x1, y1) e (x2,y2). Il rettangolo giace sul piano z=0, e i lati sono paralleli agli assi x e y. Se viene usata la forma vettoriale, i vertici sono definiti dai puntatori ai due vettori, i quali contengono le coppie di x, y.

Curve e superfici curve
Ogni linea curva può essere approssimata come una serie di rette collegate tra loro. Quindi suddividendo le linee curve e le superfici si approssimano usando una serie di segmenti piatti o poligoni, alcuni di dimensioni inferiori al pixel visualizzato sullo schermo. Anche se non esistono funzioni primitive per le curve, OpenGL consente un supporto diretto alla suddivisione e le disegna.

sabato 20 aprile 2013

Un sistema di coordinate in breve

Qualunque sia il sistema con il quale la finestra viene creata, o ridimensionata, il sistema invia una notifica all'utente. Anche se si sta usando GLUT, la notifica è automatica; questo tipo di routine è memorizzata dalla funzione glutReshapeFunc(). Bisogna registrare una chiamata alla funzione che:
  • Ristabilisca l'area del rettangolo ove viene calcolata l'immagine;
  • Definire il sistema di coordinate dell'oggetto che deve essere disegnato.
Un esempio di chiamata alla funzione di dimensionamento:

void riforma (int l, int a)
{
glViewport (0,0, (GLsizei) l, (Glsizei) a);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, (GLdouble) l, 0.0, (Gldouble) a);
}

Le funzioni di GLUT ricevono internamente due parametri, lunghezza ed altezza della finestra nuova, mossa, o ridimensionata; glViewport() aggiusta i pixel dell'immagine per il disegno dell'intera finestra. Nelle tre istruzioni successive, le coordinate del disegno sono impostate sull'angolo in basso a sinistra come (0,0) e sull'angolo in lato a destra come (l,a). Si può fare l'esempio con un foglio di carta millimetrata. I valori l e a definiti in riforma(), rappresentano il numero di righe e colonne dell'immagine. Quindi si fissa un asse sul foglio millimetrato mediante la funzione gluOrtho2D(), la quale fissa l'origine a (0,0) come l'angolo più in basso a sinistra. Pertanto quando vengono calcolati le linee, i punti ed i poligoni essi appariranno come oggetti bidimensionali in posizioni prevedibili.

sabato 13 aprile 2013

Forzare il disegno

Come già illustrato in precedenza, i sistemi grafici moderni possono essere pensati come una catena di montaggio. L'unità centrale di processo (CPU) invia i comandi di disegno. Tuttavia l'hardware installato deve effettuare altre operazioni e trasformazioni geometriche. Viene effettuato il clipping, seguita da ombreggiatura e texture. Infine i valori sono scritti in piani di bit per lo schermo. Nelle architetture ad alto livello queste operazioni sono effettuate da componenti hardware diverse, le quali sono state progettate per operazioni specifiche da completare velocemente. In questo modo la CPU non deve attendere che ogni comando di disegno sia completato per eseguirne un altro. Mentre la CPU si trova al vertice della catena di montaggio, le altre componenti effettuano le operazioni successive. Inoltre l'applicazione può essere eseguita più macchine contemporaneamente, attraverso connessioni di rete che costituiscono un legame client - server. Tutto questo può essere estremamente inefficiente se la rete esegue un comando per volta, in quanto vi può essere un notevole ritardo nella trasmissione. Solitamente il client raccogli un insieme di comandi prima di inviare il pacchetto di dati. Per questo motivo OpenGL fornisce il comando glFlush(), il quale forza il client ad inviare il pacchetto di rete anche se non fosse al completo. Dove non vi è una rete collegata tutti i comandi sono eseguiti immediatamente sul server, e glFlush() non ha alcun effetto. Comunque se si sta scrivendo una applicazione che possa lavorare bene con o senza rete, è bene aggiungere il comando glFlush() al termine di ogni immagine o scena. Da notare che glFlush() non attende che il fotogramma sia completo, solo forza il disegno ad essere eseguito, quindi garantisce che tutti i precedenti comandi esecutivi in un tempo finito e non vi siano altri comandi eseguiti. Altre situazioni alle quali glFlush() è utile:
  • Render via software che costruisce una immagine in memoria e non vuole aggiornare lo schermo continuativamente;
  • Implementare una raccolta dei comandi per ammortizzare i costi di avvio. Come citato nell'esempio della rete di macchine.

mercoledì 10 aprile 2013

Specificare un colore

Con OpenGL, la descrizione della forma di un oggetto che deve essere disegnato è indipendente dal suo colore. Qualunque sia la geometria dell'oggetto disegnato, userà lo schema di colore corrente. Lo schema di colore può essere semplice come “disegna tutto rosso scarlatto” oppure complicato come “ prendi un oggetto fatto di plastica blu, con un riflettore giallo puntato su di esso, in modo che vi sia una luce diffusa rossastra su tutto il resto”. In generale, il programmatore OpenGL deve impostare il colore o lo schema di colore per disegnare l'oggetto. Non appena lo schema di colore viene modificato, tutti gli oggetti saranno disegnati con quello schema di colore. Questo metodo aiuta OpenGL a raggiungere ottime performance che non si otterrebbero se non tenesse traccia dello schema di colore. Un esempio di pseudo-codice:
imposta_colore(rosso);
disegna_oggetto(A);

imposta_colore(verde);

disegna_oggetto(B);

disegna_oggetto(C);

Colorazione, ombreggiatura, illuminazione sono grandi argomenti che richiedono capitoli di libri, o lunghi paragrafi dedicati ad essi. Tuttavia, per disegnare semplici forme primitive, necessita solo una conoscenza di base su come impostare il colore corrente, questa informazione viene fornita nel paragrafo seguente. Per impostare il colore si utilizza il comando glColor3f(). Richiede 3 parametri il cui valore è compreso tra 0.0 e 1.0, virgola mobile. Questi rappresentano le componenti Rosso, Verde, Blu del colore. Utilizzare il valore 1.0 per un solo parametro, azzerando gli altri, crea un colore puro.
glColor3f(1.0, 0.0, 0.0);

Crea il colore rosso più luminoso del sistema di disegno, senza componenti verdi o blu. Tutti i parametri a 0 creano un colore nero, tutti a 1 il colore bianco. Qui vi sono gli otto comandi del colore che possono essere impostati per esempio.
glColor3f(0.0, 0.0, 0.0); nero

glColor3f(1.0, 0.0, 0.0); rosso

glColor3f(0.0, 1.0, 0.0); verde

glColor3f(1.0, 1.0, 0.0); giallo

glColor3f(0.0, 0.0, 1.0); blu

glColor3f(1.0, 0.0, 1.0); magenta

glColor3f(0.0, 1.0, 1.0); ciano

glColor3f(1.0, 1.0, 1.0); bianco

Come si può notare, nella precedente routine era stato spiegato come impostare il colore di pulizia dello schermo in glClearColor() , usa quattro parametri, i primi tre sono i parametri di glColor3f(); il quarto è il valore di alfa.

sabato 6 aprile 2013

Un kit di disegno indispensabile

Questo articolo spiega come azzerare la finestra e prepararla per il disegno, impostare i colori dell'oggetto da disegnare, e forza il completamento del disegno. Nessuno di questi argomenti ha che fare con il disegno in modo diretto, ma ogni programma che disegna oggetti geometrici deve gestire queste cose.

Azzerare la Finestra

Per disegnare sul computer, ci si comporta come se si dovesse disegnare su un foglio bianco, e poi si traccia l'immagine. Su di un computer, la memoria contiene l'ultima immagine utilizzata per riempire lo schermo, pertanto bisogna azzerarla usando un colore di sfondo prima di disegnare una nuova scena. Il colore utilizzato dipende dall'applicazione, per un programma di scrittura si usa un colore bianco (il colore della carta) prima di tracciare il testo. Se si sta disegnando una immagine vista nello spazio, il colore scelto può essere il nero, prima di disegnarne le stelle ed i pianeti. Alcune volte non è necessario riscrivere l'intera immagine ma solo alcune parti. A questo punto ci si può chiedere cosa voglia dire azzerare la finestra, non si potrebbe disegnare un rettangolo delle stesse dimensioni della stessa? Esiste una operazione più semplice , in quanto OpenGL semplifica con un comando speciale dedicato al solo azzeramento della finestra, inoltre su alcune macchine, l'hardware grafico consiste di buffer multipli contenenti i colori dei pixel che sono mostrati. Questi ed altri buffer devono essere ripuliti di tanto in tanto, pertanto è utile possedere un comando dedicato allo scopo.
Bisogna inoltre sapere che i colori dei pixel sono immagazzinati nell'hardware grafico come bitplane. Vi sono due modi per memorizzare, il primo (RGBA), rosso, verde, blu e alfa, immagazzina i valori direttamente come bitplane. Oppure esiste un singolo indice di valore che fa riferimento al colore di tabella. Il colore RGBA è quello comunemente usato. Un esempio di linea di codice nel quale la finestra viene azzerata con il nero è:
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
 
La prima linea di codice serve comunicare di azzerare lo schermo con il nero, il comando successivo ripulisce l'intera finestra con il colore corrente. Il singolo parametro glClear() indica quale buffer di memoria deve essere ripulito. In questo caso vien pulito solo il buffer del colore, dove viene conservata l'immagine. Tipicamente, viene impostato il colore una sola volta, all'inizio dell'applicazione, e l'operazione di pulizia avviene spesso. OpenGL tiene traccia del colore corrente di pulizia come variabile di stato. Se bisogna ripulire anche il buffer della profondità dell'immagine s i usa la sequenza di comandi:
glClearColor(0.0, 0.0, 0.0, 0.0);
glClearDepth(1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

In questo caso la chiamata a glClearColor() è la stessa di prima, il comando glClearDepth() specifica che il valore di ogni pixel dio profondità deve essere impostato, e il parametro glClear() consiste ora di OR bitwise, di tutti i buffer che devono essere puliti.


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.

domenica 17 marzo 2013

Il Refresh che mette in pausa

Per alcune implementazioni di OpenGL, inoltre la routine scambia_i_buffer(), oltre a invertire il buffer di memoria in disegno e quello visualizzato, aspetta sino a quando il periodo di rinfresco dello schermo non è concluso, e la schermata non viene visualizzata. Questa routine permette al nuovo buffer di memoria di essere visualizzato completamente, partendo dall'inizio. Assumendo una frequenza di 60 volte al secondo questo significa che il numero di fotogrammi massimi raggiungibili è di 60 frame per second (fps), e se tutti i frame sono azzerati in meno di un sessantesimo di secondo, l'animazione scorre fluidamente. Quel che può accadere solitamente è la costruzione di un fotogramma è troppo complessa per essere disegnata in 1/60 di secondo, pertanto il fotogramma vien mostrato più di una volta. Se il tempo necessario per calcolare è di 1/45 di secondo, la velocità raggiungibile di 30 fps, la grafica ha un tempo di ozio pari a 1/90 di secondo. Inoltre il tempo di rinfresco dei monitor è costante, pertanto bisogna aspettarsi delle conseguenze sulle prestazioni dell'applicazione. Bisogna valutare attentamente le caratteristiche dell'applicazione in fase di sviluppo, aggiungere delle componenti può non avere alcuna conseguenza sino ad un certo punto quando un nuovo componente del programma costringe il sistema a rallentare da 60 fps a 30 fps. Inoltre se la complessità della scena è prossima al valore intero (1/60, 1/30, 1/20) può subire variazioni casuali, le quali causano una visualizzazione irregolare, fastidiosa agli occhi di un osservatore. In questo caso è necessario semplificare la scena, in modo che tutti i frame siano abbastanza veloci, o rallentarli tutti allo stesso valore. Una gestione sofisticata del frame rate si rende necessaria in queste applicazioni.

venerdì 8 marzo 2013

Animazione con OpenGL

Una delle cose più eccitanti da fare con la grafica al computer è disegnare immagini in movimento. Che tu sia un ingegnere che vuole osservare un componente meccanico in tutti i lati, o un pilota che vuole imparare a far volare un aeroplano usando una simulazione, o semplicemente un appassionato; l'animazione è una parte importante della computer grafica. Al cinema il movimento viene ottenuto mediante una sequenza di immagini proiettate per un ventiquattresimo di secondo sullo schermo. Ogni immagine viene posta dietro l'obiettivo, il diaframma si apre e l'immagine vine mostrata; il diaframma si richiude temporaneamente, e la pellicola viene fatta avanzare di un fotogramma, quindi il fotogramma vien proiettato e così via. Se si osservano 24 immagini al secondo, il cervello umano provvede ad amalgamarle in un'unica sequenza animata. Di fatto i proiettori moderni mostrano un numero doppio di fotogrammi (48) al secondo per ridurre l'evanescenza. Le immagini in computer grafica sono solitamente rinfrescate alla velocità di 60 - 76 fotogrammi al secondo, il limite massimo è dio 120 fotogrammi al secondo. Ma per il tempo di risposta dell'occhio umano, superare questo limite non produce alcun effetto visibile. La ragione per cui i film funzionano è che ogni immagine è completa quando viene proiettata. Traducendolo in pseudo-linguaggio, si può scrivere:
apri_finestra();
per (i=0; i < 1000; i++) {
azzera_finestra();
disegna_fotogramma();
aspetta_tempo_1_24esimo_secondo();
}

Se si aggiunge al sistema il tempo per pulire lo schermo e disegnare un'immagine, questo programma può dare dei risultati non graditi in quanto si avvicinano pericolosamente a 1/24 di secondo. Gli oggetti visibili per 1/24 di secondo che presentano un'immagine solida in teoria, ma quando vengono disegnati dal computer possono apparire come spettri, in quanto il computer non fa in tempo ad azzerare l'immagine presente per sostituirla con un'altra. La maggior parte delle applicazioni OpenbGL consente il doppio buffering, via hardware o software, in modo da ridurre i tempi morti. Così mentre un'immagine viene mostrata, la successiva viene creata in memoria ed i due buffer sono scambiati di posto al termine della proiezione della prima immagine. Pertanto i due buffer lavorano in parallelo alternandosi nella proiezione, è come avere due proiettori contemporanei che proiettano lo stesso film alternandone i fotogrammi. Una versione modificata del programma si scrive in questo modo:
apri_finestra_in doppio_buffer();
per (i=0; i < 1000; i++) {
azzera_finestra();
disegna_fotogramma();
scambia_i_buffer();
}

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 3 marzo 2013

File inclusione OpenGL

Per tutte le applicazioni OpenGL è necessario includere il file gl.h. Quasi tutte le applicaizoni OpenGL usano GLU la sopramenzionata OpenGL Utility Library. Così quasi ogni file sorgente OpenGL inizia con:
#include <gl.h>
#include<glu.h>

Se si sta accedendo direttamente alla libreria di interfaccia della finestra per supportare OpenGL come GLX, AGL, PGL, WGL si devono includere file addizionali di header. Ad esempio per richiamare GLX può essere necessario aggiungere le linee di codice:
#include <X11/xlib.h>
#include<GL/glx.h>

Se invece si sta utilizzando GLUT per gestire le funzioni della finestra, bisogna includere il codice:
#include<GL/glut.h>

Non è necessario includere tutti i file intestazione automaticamente, ma è necessario sceglierli. GLUT include tutte le funzioni contenute in WGL per Microsoft Windows.

domenica 24 febbraio 2013

OpenGL Librerie collegate

OpenGL fornisce un potente set di comandi di rendering primitivo ma potente, tutti i comandi di disegno ad alto livello devono essere tradotti in questi comandi. Inoltre i programmi OpenGL devono sottostare al meccanismo che regola le finestre di sistema. Un certo numero di librerie esistono allo scopo di semplificare il compito di programmazione incluse le seguenti:
La OpenGL Utility Library (GLU) contiene una serie di routine a basso livello che eseguono comandi OpenGL come l'impostazione delle matrici per specificare l'orientamento della prospettiva e la proiezione, esegue la tassellazione dei poligoni e la resa delle superfici. La libreria è fornita come parte dell'implementazione OpenGL. Tutte le routine GLU usano il prefisso glu.
Per ogni sistema operativo a finestra esiste una libreria che estende la funzionalità del sistema a finestra per supportare il rendering OpenGL. Per le macchine che usano il sistema X Window esiste l'estensione OpenGL X Window System (GLX) come aggiunta a OpenGL. Le routine GLX usano il prefisso glX. Per il sistema operativo Windows Microsoft, vi sono le WGL la quale fornisce interfaccia OpenGL. Tutte le routine WGL usano il prefisso wgl.
Open Inventor è un programma ad oggetti basato su OpenGL, il quale fornisce oggetti e metodi per creare applicazioni grafiche a tre dimensioni. E' stato scritto in C++ e fornisce oggetti precostruiti ed eventi già descritti per interagire con essi, si tratta di una applicazione ad alto livello capace di creare ed editare scenari a tre dimensioni, inoltre può salvare oggetti su disco e scambiare dati in vari formati grafici. Open Inventor è separato da OpenGL.

lunedì 11 febbraio 2013

Modellare solidi con OpenGL II

 Seconda parte del programma:
 
GLvoid UccidiFinestraGL(GLvoid)       // Uccide la finestra
{
    if (schermopieno)                                       
    {
        ChangeDisplaySettings(NULL,0);                   
        ShowCursor(TRUE);                               
    }

    if (hRC)                                            // Abbiamo il contesto di rendering
    {
        if (!wglMakeCurrent(NULL,NULL))                   
        {
            MessageBox(NULL,"Rilascio fallito di DC e RC .","ERRORE SHUTDOWN",MB_OK | MB_ICONINFORMATION);
        }

        if (!wglDeleteContext(hRC))                       
        {
            MessageBox(NULL,"Rilascio fallito del contesto di rendering.","ERRORE SHUTDOWN",MB_OK | MB_ICONINFORMATION);
        }
        hRC=NULL;                                        // Imposta RC a NULL
    }

    if (hDC && !ReleaseDC(hWnd,hDC))                    // Possiamo impostare il DC
    {
        MessageBox(NULL,"Rilascio fallito del Dispositivo Contesto.","ERRORE SHUTDOWN",MB_OK | MB_ICONINFORMATION);
        hDC=NULL;                                        // Imposta DC a NULL
    }

    if (hWnd && !DestroyWindow(hWnd))                    // Possiamo distruggere la finestra?
    {
        MessageBox(NULL,"Non riesce a rilasciare hWnd.","ERRORE SHUTDOWN",MB_OK | MB_ICONINFORMATION);
        hWnd=NULL;                                        // Set hWnd To NULL
    }

    if (!UnregisterClass("OpenGL",hInstance))            // Are We Able To Unregister Class
    {
        MessageBox(NULL,"Non riesce a cancellare la classe.","ERRORE SHUTDOWN",MB_OK | MB_ICONINFORMATION);
        hInstance=NULL;                                    // Set hInstance To NULL
    }
}



BOOL CreaFinestraGL(char* titolo, int larghezza, int altezza, int bits, bool schermopienoflag)
{
    GLuint        PixelFormat;           
    WNDCLASS    wc;                       
    DWORD        dwExStyle;               
    DWORD        dwStyle;               
    RECT        WindowRect;               
    WindowRect.left=(long)0;           
    WindowRect.right=(long)larghezza;   
    WindowRect.top=(long)0;               
    WindowRect.bottom=(long)altezza;       

    schermopieno=schermopienoflag;           

    hInstance            = GetModuleHandle(NULL);               
    wc.style            = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;   
    wc.lpfnWndProc        = (WNDPROC) WndProc;                   
    wc.cbClsExtra        = 0;                                   
    wc.cbWndExtra        = 0;                                   
    wc.hInstance        = hInstance;                           
    wc.hIcon            = LoadIcon(NULL, IDI_WINLOGO);       
    wc.hCursor            = LoadCursor(NULL, IDC_ARROW);           
    wc.hbrBackground    = NULL;                                   
    wc.lpszMenuName        = NULL;                                   
    wc.lpszClassName    = "OpenGL";                               

    if (!RegisterClass(&wc))                                   
    {
        MessageBox(NULL,"Classe Window fallito rilascio.","ERRORE",MB_OK|MB_ICONEXCLAMATION);
        return FALSE;                                           
    }
   
    if (schermopieno)                                                // Prova schermopieno?
    {
        DEVMODE dmScreenSettings;                                // Modalità del dispositivo
        memset(&dmScreenSettings,0,sizeof(dmScreenSettings));   
        dmScreenSettings.dmSize=sizeof(dmScreenSettings);       
        dmScreenSettings.dmPelsWidth    = larghezza;           
        dmScreenSettings.dmPelsHeight    = altezza;               
        dmScreenSettings.dmBitsPerPel    = bits;                   
        dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

        // Try To Set Selected Mode And Get Results.  NOTE: CDS_schermopieno Gets Rid Of Start Bar.
        if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
        {
            // If The Mode Fails, Offer Two Options.  Quit Or Use Windowed Mode.
            if (MessageBox(NULL,"La scheda video non supporta modalita' a pieno schermo. Usare modalita' finestra?","Open GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
            {
                schermopieno=FALSE;        // Windowed Mode Selected.  schermopieno = FALSE
            }
            else
            {
                // Pop Up A Message Box Letting User Know The Program Is Closing.
                MessageBox(NULL,"Programa chiudera' ora.","ERRORE",MB_OK|MB_ICONSTOP);
                return FALSE;                                    // Return FALSE
            }
        }
    }

    if (schermopieno)                                                // Siamo ancora a pieno schermo?
    {
        dwExStyle=WS_EX_APPWINDOW;                               
        dwStyle=WS_POPUP;                                        // Stile finestra
        ShowCursor(FALSE);                                       
    }
    else
    {
        dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;            // Stile finestra estesa
        dwStyle=WS_OVERLAPPEDWINDOW;                           
    }

    AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);       

    // Crea la finestra
    if (!(hWnd=CreateWindowEx(    dwExStyle,                           
                                "OpenGL",                           
                                titolo,                               
                                dwStyle |                           
                                WS_CLIPSIBLINGS |                   
                                WS_CLIPCHILDREN,                   
                                0, 0,                               
                                WindowRect.right-WindowRect.left,   
                                WindowRect.bottom-WindowRect.top,   
                                NULL,                               
                                NULL,                                // No Menu
                                hInstance,                            // Instanza
                                NULL)))                                //
    {
        UccidiFinestraGL();                                // Resetta lo schermo
        MessageBox(NULL,"Errore creazione finestra.","ERRORE",MB_OK|MB_ICONEXCLAMATION);
        return FALSE;                                // Ritorna FALSE
    }

    static    PIXELFORMATDESCRIPTOR pfd=                    {
        sizeof(PIXELFORMATDESCRIPTOR),           
        1,                                           
        PFD_DRAW_TO_WINDOW |                       
        PFD_SUPPORT_OPENGL |                       
        PFD_DOUBLEBUFFER,                       
        PFD_TYPE_RGBA,                               
        bits,                                       
        0, 0, 0, 0, 0, 0,                           
        0,                                           
        0,                                           
        0,                                           
        0, 0, 0, 0,                                   
        16,                                             
        0,                                           
        0,                                       
        PFD_MAIN_PLANE,                           
        0,0, 0, 0                                       
    };
   
    if (!(hDC=GetDC(hWnd)))                            // Abbiamo contesto del dispositivo?
    {
        UccidiFinestraGL();                               
        MessageBox(NULL,"Non crea un dispositivo di Contento GL.","ERRORE",MB_OK|MB_ICONEXCLAMATION);
        return FALSE;                               
    }

    if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))   
    {
        UccidiFinestraGL();                           
        MessageBox(NULL,"Non trova un Formato Pixel.","ERRORE",MB_OK|MB_ICONEXCLAMATION);
        return FALSE;                               
    }

    if(!SetPixelFormat(hDC,PixelFormat,&pfd))        // Possiamo impostare il formato pixel?
    {
        UccidiFinestraGL();                           
        MessageBox(NULL,"Non riesce ad impostare un Formato Pixel.","ERRORE",MB_OK|MB_ICONEXCLAMATION);
        return FALSE;                               
    }

    if (!(hRC=wglCreateContext(hDC)))               
    {
        UccidiFinestraGL();                               
        MessageBox(NULL,"Non riesce a creare un Contesto GL.","ERRORE",MB_OK|MB_ICONEXCLAMATION);
        return FALSE;                           
    }

    if(!wglMakeCurrent(hDC,hRC))                   
    {
        UccidiFinestraGL();                               
        MessageBox(NULL,"Non attiva un Contesto di rendering GL.","ERRORE",MB_OK|MB_ICONEXCLAMATION);
        return FALSE;                               
    }

    ShowWindow(hWnd,SW_SHOW);                       
    SetForegroundWindow(hWnd);                       
    SetFocus(hWnd);                                   
    RidimensioneScenaGL(larghezza, altezza);                   

    if (!InizGL())                                   
    {
        UccidiFinestraGL();                           
        MessageBox(NULL,"Inizializzazione fallita.","ERRORE",MB_OK|MB_ICONEXCLAMATION);
        return FALSE;                                // Restituisci FALSO
    }

    return TRUE;                                    // Successo
}

LRESULT CALLBACK WndProc(    HWND    hWnd,           
                            UINT    uMsg,           
                            WPARAM    wParam,           
                            LPARAM    lParam)           
{
    switch (uMsg)                                    // Controlla i messaggi
    {
        case WM_ACTIVATE:                           
        {
            if (!HIWORD(wParam))                   
            {
                attivo=TRUE;                        // Programma attivo
            }
            else
            {
                attivo=FALSE;                        // Program non più attivo
            }

            return 0;                               
        }

        case WM_SYSCOMMAND:                            // Intercetta Comandi di sistema
        {
            switch (wParam)                           
            {
                case SC_SCREENSAVE:                   
                case SC_MONITORPOWER:               
                return 0;                           
            }
            break;                                    // Uscita
        }

        case WM_CLOSE:                               
        {
            PostQuitMessage(0);                       
            return 0;                               
        }

        case WM_KEYDOWN:                           
        {
            tasti[wParam] = TRUE;                   
            return 0;                               
        }
       
        case WM_KEYUP:                               
        {
            tasti[wParam] = FALSE;                   
            return 0;                           
        }

        case WM_SIZE:                                // Ridimensiona finestra OpenGL
        {
            RidimensioneScenaGL(LOWORD(lParam),HIWORD(lParam)); 
            return 0;                               
        }
    }

    // Passa i messaggi non gestiti DefWindowProc
    return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

int WINAPI WinMain(    HINSTANCE    hInstance,            // Instance
                    HINSTANCE    hPrevInstance,        // Previous Instance
                    LPSTR        lpCmdLine,            // Command Line Parameters
                    int            nCmdShow)            // Window Show State
{
    MSG        msg;                                    // Windows Message Structure
    BOOL    fatto=FALSE;                                // Bool Variable To Exit Loop

    // Chiede all'utente quale modlità schermo preferisce
    if (MessageBox(NULL,"Vuoi eseguire modalita' a pieno schermo?", "Pieno Schermo?",MB_YESNO|MB_ICONQUESTION)==IDNO)
    {
        schermopieno=FALSE;                            // Modalità finestra
    }

    // Crea la finestra OpenGL
    if (!CreaFinestraGL("Tutorial Oggetti Solidi",800,600,16,schermopieno))
    {
        return 0;                                    // Quit se la finestra non viene creata
    }

    while(!fatto)                                    // Loop del programma
    {
        if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))   
        {
            if (msg.message==WM_QUIT)               
            {
                fatto=TRUE;                       
            }
            else                                   
            {
                TranslateMessage(&msg);               
                DispatchMessage(&msg);               
            }
        }
        else                                        // Se non ci sono messaggi
        {
            // Disegna la scena.  E controlla ESC e DisegnaScenaGL()
            if ((attivo && !DisegnaScenaGL()) || tasti[VK_ESCAPE])   
            {
                fatto=TRUE;                            // ESC o DisegnaScenaGL Segnala Quit
            }
            else                                   
            {
                SwapBuffers(hDC);                    // Swap Buffers (Doppio buffer)
            }

            if (tasti[VK_F1])                       
            {
                tasti[VK_F1]=FALSE;               
                UccidiFinestraGL();                       
                schermopieno=!schermopieno;                // Toggle schermopieno / modalità finestra
                // Ricrea la finestra OpenGL
                if (!CreaFinestraGL("Tutorial oggetti solidi",800,600,32,schermopieno))
                {
                    return 0;                        // Esce dal progrmma se la finestra non viene creata
                }
            }
        }
    }

    // Spegnimento
    UccidiFinestraGL();                                    // Cancella la finstra
    return (msg.wParam);                            // Esce dal programma
}