Salve a tutti!
In questo articolo presentiamo un metodo per inserire nella nostra scena modelli 3D e creare un’animazione di quest’ultimo. Partiamo prima di tutto con l’utilizzo delle classi, assai utili per dare struttura al nostro codice.
In javascript ci sono alcuni metodi per creare delle classi, uno dei più semplici è utilizzare una funzione.
Grazie a questo espediente, automatizzeremo la creazione dei buffer e il disegno di ogni singolo elemento della scena.
(la nostra funzione DrawScene perciò si semplificherà parecchio e aggiungere nuovi elementi sarà nettamente più semplice, basterà istanziare un elemento nella fase di initBuffer e chiamare il metodo per disegnarlo nella Draw).

Possiamo prima di tutto suddividere gli oggetti da disegnare in 2 categorie:

1)STATICI
Elementi che nel tempo non cambiano (nessuna animazione) , per esempio i muri della stanza.

Costruttore:
function ele_sta(nome,arr_vertici,arr_texture,src_texture)

Input:
nome -> nome dell’oggetto , opzionale
arr_vertici -> array delle coordinate dei vertici da cui è composto l’oggetto
arr_texture -> array delle coordinate texture dei vertici
src_texture -> percorso relativo dell’immagine texture associata.

Possiamo notare che questa struttura obbliga ogni elemento ad avere una texture e delle coordinate; per tale ragione, in caso volessimo rappresentare un oggetto con un solo colore, una soluzione potrebbe essere usare un immagine di 1px x 1px di quel colore ed usare un array di coordinate texture completamente uguali fra loro.
( tale sistema è una complicazione ma non ho avuto tempo di cambiare il codice shader per gestire entrambe).

Mostriamo ora un esempio:

var vertici= [-0.235965, -3.718093, -0.866445,-0.241930, -3.749770, -0.738099,-0.267395,…];
var arr_tex= [0.131152,0.926724,0.132811,0.925555,0.133007,0.925903,…];

bambola=new ele_sta( “bambola”,vertici,arr_tex,”doll.jpg”);

per disegnarla basta invocare il metodo Draw:

mvPushMatrix();
// qua sposto l’oggetto dove voglio disegnarlo
mvRotate(250,[1,0,0]);
mvTranslate([0, -1.4, 0]);
//disegno
bambola.Draw();
mvPopMatrix();

un esempio di elemento statico

2)DINAMICI
Elementi la cui disposizione dei vertici varia nel tempo

Costruttore:
function ele_din(nome,arr_texture,src_texture,arr_frame,limite_frame)

Input:
nome -> nome dell’oggetto , opzionale
arr_texture-> array delle coordinate texture dei vertici
src_texture-> percorso relativo dell’immagine texture associata.
arr_frame -> array bidimensionale dei vertici,ogni elemento è un array della posizione dei vertici in un dato istante dell’animazione; la durata di quest’ultima è perciò proporzionale alla lunghezza di tale array.
limite_frame -> indica dopo quanti cicli di DrawScene() cambia si passa all’istanza di animazione successiva.

Esempio:

var bipede_frame=new Array();
bipede_frame[0]=[0.057151, 0.036153, 0.050801,0.057148,…]
bipede_frame[1]=[0.057151, 0.036153, 0.050801,0.057148,…]
bipede_frame[…]

var text=[0.220420,0.441480,0.207077,0.439886,0.203473,0.427281,0.220420,…];
bipede=new ele_din( “bipedemesh”,text,”bipedemesh.jpg”,bipede_frame,4);

Per disegnare l’elemento basta anche qua invocare il metodo Draw().

Nel nostro esempio questo bipede si muove continuamente.

ANIMAZIONE:
nella DrawScene dobbiamo ricordarci di traslare il nostro bipede in modo tale che sembri davvero camminare ( se non facessimo questo, camminerebbe sul posto e boh), per fare ciò,una soluzione ( non la più performante però) è crearsi già dall’inizializzazione le matrici di traslazione da applicare ad ogni frame dell’animazione,facendo attenzione che alla fine di tale ciclo di sia una rotazione di 180 ed un ribaltamento della matrici di traslazione ( così sembrerà che stia andando avanti ed indietro come una guardia).

Esempio:

mvTranslate(Trasl_1[bipede.frame_count]);
if (bipede.frame_count==20 )
{
Trasl_1=Trasl_1.reverse();
inv=!inv;
}
if(inv) mvRotate(180,[0,1,0]);
bipede.Draw();

Anche la funzione handleLoadedgeneral(…) ha avuto dei cambiamenti: stavolta verrano istanziati degli ele_sta , e questi verrano poi inseriti in un array appositamente chiamato arr_ele_statici: in fase di disegno verrà semplicemente scorso per invocare la relativa funzione di disegno .
Questo ovviamente è possibile perchè i vertici degli elementi sono posizionati ad hoc e non possiedono animazioni od altre variazioni tramite input, per gli altri elementi invece abbiamo preferito staccarli ed occuparcene di persona ogni volta.

NOTA:
In questo esercizio abbiamo avuto bisogno di espandere le funzionalità di glUtils.js e MatriUtilityFunc.js, più specificatamente dovevamo scalare un oggetti lungo gli assi x,y,z perchè troppo grande. Abbiamo inserito una modifica in entrambi i file per avere tale possibilità, perciò per poter apprezzare questa lezione,è necessario scaricare la nuova versione dei suddetti files.

Esempio:

mvRotate(250,[1,0,0]);
mvTranslate([0, -1.4, 0]);
Scale([0.01, 0.01, 0.01]);
bambola.Draw();

RIASSUMENDO:
Un qualunque modello 3D lo si può inserire nella nostra applicazione istanziando semplicemente in fase InitBuffer() o un ele_sta o un ele_din ( a seconda dei bisogni). Se l’oggetto è complesso, dovremo dividere il modello in un array di ele_sta o ele_din singoli, in modo tale che ogni elemento abbia la sua texture. In fase di DrawScene, dovremmo invocare il metodo Draw per tutti gli elementi che lo compongono; nel caso tale oggetto abbia una determinata posizione, bisognerà invocare i metodi di traslazione, rotazione e scalamento prima del relativo Draw.

L’animazione da noi ottenuta ha però un pò di problemi:
– non è adattabile: praticamente per dare l’idea di movimento abbiamo disegnato varie fotografie da noi costruite in precedenza ad hoc per il progetto. Il movimento si vede ma in questo modo abbiamo ingabbiato l’animazione. Se volessimo modificare il movimento ( cambiando una posizione, una texture oppure aggiungendo/togliendo degli elementi), saremmo costretti a calcolarci un nuovo array bidimensionale a parte .
Nel mio caso, sarei costretto ad aprire Blender ( programma di grafica 3D9 e a ricreare una nuova scena da cui estrapolare gli array.
-occupazione memoria: poichè tutti gli elementi sono già precalcolati, la nostra applicazione occuperà un pò di spazio per occupare questi immensi array la cui dimensione è proporizionale alla complessità del progetto ( per tale ragione nei videogiochi si preferisce calcolare in tempo reale le coordinate degli oggetti, sia per evitare un consumo astruso di memoria, sia per essere più dinamici).L’applicazione infatti risulterà davvero lenta ( ho cronometrato che la prima volta impiega quasi 40 secondi ad avviarsi tanto è grande il file dei vertici! Le volte successive , quando si è messo in cache le informazioni base, arriva sui 10 secondi).Per tale ragione sono state create due versioni, una versione lesson10a.html con solo il modello 3d della bambola (niente animazione) ed una, lesson10.html, con in più il bipede che si muove; Vi conviene lo stesso lavorarci su un server locale per evitare ritardi inutili.

Per tali ragione si consiglia questo tipo di animazioni solo per applicazioni didattiche semplici come la nostra.
P.S. il modello 3d del bipede l’ho creato tramite Blender seguendo come esempio questa guida davvero ben fatta.

Per potere vedere l’esempio appena mostrato ( lo trovate come lesson10a.zip e lesson10.zip ) e scaricare il codice che è stato presentato nell’articolo, collegarsi al solito repository Lab .

Grazie per l’attenzione,

Andrea