|
||||||
ROBOTICA
video
immagini
papers
progettati
costruiti
toolbox
vrml
simulatori
Vrml + Java |
E' giunto il momento
di fare un salto di qualita' nella creazione di mondi vrml. Fino ad ora ci
siamo occupati di analizzare i meccanismi resi disponibili dalle specifiche
2.0 per poter modellizzare il mondo e interagire con esso.
In precedenza ci eravamo
gia' accorti che vrml da solo non puo' bastare per una vasta gamma di applicazioni.
E avevamo preso in considerazione l'utilizzo di vrmlscript per creare comportamenti
piu' complessi, non possibili con i meccanismi standard. Ma vrmlscript costituisce
solo un piccolo passo verso la soluzione a questi problemi; infatti questo
linguaggio di scripting, sottoinsieme di javascript (che viene usato in alcuni
browser vrml 2.0 come ad esempio WorldView), non consente di avere accesso
alla potenza che un vero linguaggio di programmazione puo' rendere disponibile.
Puo' essere impiegato per compiere alcuni calcoli matematici, per gestire
in modo piu' pulito alcuni eventi da indirizzare al mondo vrml, ma poco piu'.
Supponiamo di volere
creare una interfaccia a finestre da cui controllare il mondo vrml. L'utente
selezionando dai vari bottoni presenti sulla finestra puo' imporre che avvengano
certe azioni nel mondo. Per esempio potrebbe manovrare un manichino virtuale.
(questi riferimenti non
sono campati in aria, e in effetti corrispondono a progetti in corso presso
il gruppo 'Vrml Dreamers' dove questo tutorial viene reso disponibile).
In tutte queste situazioni
vrml non puo' farcela da solo; e neppure vrmlscript o javascript possono essere
di aiuto.
L'uso di Java combinato
a vrml rappresenta un mezzo potentissimo per la gestione di mondi virtuali
su internet. Come vedremo tra breve il suo impiego e' abbastanza semplice,
anche se viene richiesta una conoscenza del linguaggio, senza la quale non
e' possibile ottenere risultati apprezzabili (anzi... non e' possibile ottenere
proprio alcun risultato!).
Java e' un linguaggio
di programmazione a tutti gli effetti. Ha conosciuto grossa diffusione (a
partire dal 1995) grazie ad una caratteristica fondamentale: la portabilita'.
Infatti il codice Java viene compilato in una forma intermedia, nota come
byte code. Il byte code viene poi interpretato dalla Java Virtual Machine
che lo esegue. Questo meccanismo consente ad un programma scritto in java
di essere eseguito su qualsiasi macchina, purche' questa abbia l'interprete
del byte code. Non voglio qui trattare
le caratteristiche di java; questo e' un tutorial su vrml dopotutto. Se siete
interessati ad usare vrml in modo avanzato, quello che posso consigliarvi
e' di acquistarvi anche un buon libro di java o di recuperare in rete alcuni
dei molti tutorials sull'argomento.
Detto questo, da qui
in avanti assumo che conosciate a grandi linee come scrivere un programma
java. Nei primi esempi cerchero' comunque di andare molto piano e di fornire
qualche commento qua e la'.
Prima di iniziare, faccio
presente che il codice presentato e' stato compilato utilizzando il JDK 1.0.2
(il JDK 1.1 va comunque benissimo). Potete trovarlo sul sito della Sun http://www.javasoft.com.
Un primo
semplice esempio.
Java puo' essere impiegato
in modi diversi all'interno di un mondo vrml. Qui di seguito viene presentata
la modalita' standard, come prevista dalle specifiche vrml 2.0, lasciando
l'uso dell'interfaccia esterna (EAI), non ancora standardizzata, ad una successiva
lezione.
Java in un mondo vrml
non gira come un applet; infatti la classe deve essere definita come estensione
di una classe Script che viene fornita con il browser vrml. Assieme al browser
infatti vengono distribuite tutte le classi che servono per interfacciare
il mondo vrml con la classe.
Non analizzeremo ora
in dettaglio la struttura delle classi rese disponibili. E' mio intendimento
procedere tramite esempi, in modo da rendere piu' pratica la discussione.
Per dettagli tecnici sulla gerarchia delle classi e sulle varie caratteristiche
dei singoli tipi potete rivolgervi direttamente alle specifiche.
Supponiamo ora di volere
implementare una classe java da collegare ad un mondo vrml che risolve il
problema, per cui si era trovata una soluzione con vrmlscript, di far partire
una animazione in conseguenza di un click su un certo oggetto (per il momento
restiamo sul molto semplice; in seguito vedremo applicazioni piu' complesse,
sino ad arrivare ad interfacce a finestra per controllare il mondo).
Ricapitolando velocemente,
il seguente sorgente vrml presentava un problema (si dovrebbe far rolotare
una sfera da un punto ad un altro quando si clicca su un certo sensore).
Questo esempio presenta
un comportamento scorretto. Come avevamo gia' visto a suo tempo, il campo
isActive del sensore passa a TRUE quando viene cliccato e a FALSE quando viene
rilasciato. Questo comporta la generazione di 2 eventi. Il primo fa partire
l'animazione, mentre il secondo la deattiva. Ne segue che per far completare
il viaggio della sfera l'utente deve tenere premuto in continuazione il sensore...
che in questo caso e' rappresentato dalla sfera stessa; il massimo della scomodita'!
Avevamo visto che con
vrmlscript si puo' risolvere ampiamente il problema. Vediamo ora come risolvere
con Java.
Si deve inserire ancora
una volta un nodo Script; la parte relativa agli eventi da importare, quelli
da esportare e ai campi da definire rimane uguale.
Quindi anche in questo
caso il nodo Script avra' la seguente forma:
Come si puo' notare si
mantiene molto della struttura gia' vista; del resto l'interfaccia del nodo
Script deve restare uguale. Deve cambiare solo il modo con il quale il lavoro
viene svolto.
Nel campo url si specifica
il nome della classe java (ottenuta per compilazione di un file .java).
Ricordo brevemente che
i field vanno inizializzati (nel nostro caso abbiamo un field premuto che
viene inizializzato a FALSE). I campi eventOut sono eventi in uscita dal nodo
Script. Quando vengono generati un eventuale ROUTE puo' ridirigerli verso
un altro nodo. In questo caso l'evento in uscita 'inizia' verra' indirizzato
al campo enabled del sensore di tempo per farlo partire. Un campo eventIn
riceve invece eventi dal mondo vrml e questo provoca l'esecuzione di alcune
funzionalita' all'interno della classe java. Il programmatore non deve fare
altro che programmare le opportune risposte agli eventi in ingresso; queste
risposte vengono definite all'interno della classe java.
Passiamo ora a considerare
la classe java che consente di risolvere il problema che ci siamo posti.
Cerchiamo ora di analizzare
i punti fondamentali della classe.
Inizialmente si importano
i packages che ci servono. Si tratta di una serie di classi che ci servono
per gestire all'interno di una classe java i tipi vrml, nonche' l'arrivo di
eventi dal mondo vrml. Nell'esempio qui riportato ho importato tutto cio'
che era importabile; in teoria si potrebbe importare solo i tipi e le classi
che vengono utilizzate.
Come dicevo precedentemente
la classe viene derivata dalla classe Script. Non serve un metodo main nella
classe per farla partire; viene lanciata direttamente dal browser vrml.
Seguono le dichiarazioni
delle variabili della classe. In particolare qui definisco 3 variabili che
rispecchiano i 3 campi del nodo Script, field e campi eventOut. Non mi servono
variabili per i campi eventIn in quanto il loro valore viene impostato automaticamente
quando viene sollevato l'evento.
Il metodo Initialize
serve per effettuare delle operazioni di inizializzazione sulle variabili.
Viene invocato quando la classe viene caricata per la prima volta. In particolare,
in essa possiamo legare le variabili della classe ai corrispondenti campi
nel nodo Script. Questo e' possibile tramite i metodi getEventOut e getField.
In particolare settando
il valore di una variabile che rappresenta un eventOut genera un evento in
uscita dal nodo Scritp, in maniera analoga a quanto gia' visto per vrmlscript.
Per gestire gli eventi
in ingresso utilizziamo il metodo ProcessEvents. ProcessEvents viene invocato
automaticamente quando il nodo Script riceve un evento in ingresso. I due
parametri indicano rispettivamente il numero di eventi ricevuti e la lista
di tali eventi. All'interno del corpo di questo metodo vengono analizzati
tutti gli eventi ricevuti; testando il nome dell'evento (con un semplice if)
si possono invocare i metodi opportuni che devono gestire le risposte agli
eventi. Nel caso in esame abbiamo
un solo evento e quindi il test risulta superfluo. Invochiamo il metodo decidi_partenza
passando come parametro il tempo di pressione che viene mappato su un double
(numero reale).
All'interno del metodo
decidi_partenza mi domando se la variabile premuto ha valore false. In caso
affermativo faccio partire l'animazione. Quando l'utente rilascera' il mouse,
arrivera' un secondo evento il quale verra' processato dal metodo ProcessEvents.
Ancora una volta verra' invocato il metodo decidi_partenza. Questa volta pero'
il valore della variabile premuto e' a true; quindi non si fa altro che mettere
premuto a false. Non si manda in particolare nessun evento false al campo
enabled; l'animazione pertanto continua a girare.
Potrebbe sembrare molto
complicato a prima vista ricorrere a java per un esempio di questo tipo. E
infatti per questi casi l'uso di javascript o vrmlscript risulta piu' consono.
Per compiti piu' complicati l'approccio java risultera' pero' l'unica soluzione
plausbile.
Si tenga presente che
il metodo Initialize e ProcessEvents ci saranno prati- camente sempre nella
classe java. Il metodo decidi_partenza e' stato invece impiegato per programmare
una risposta all'evento.
Infine, vale la pena
considerare un aspetto un po' ambiguo presente all'interno del mondo appena
realizzato. Secondo voi... cosa succede se l'utente dopo avere premuto e rilasciato
una prima volta, riesce a cliccare sulla sfera in movimento? Qui di seguito riporto
il sorgente complessivo, dove aggiungo quei pochi ROUTE che ancora servono,
su cui penso non abbiate alcuna difficolta' di comprensione.
Per provare direttamente
il sortgente: Movimento sfera con Java
Ricordo che questa non
vuole essere una discussione esaustiva delle possibilita' di interfacciamento
tra java e vrml, ma solo una semplice panoramica. Nella prossima lezione vedremo
di approfondire ulteriormente.
Oppure ancora, vogliamo realizzare un semplice mondo vrml in multi-utenza,
a cui si possono collegare diversi utenti di internet, ed in cui ognuno viene
rappresentato con il suo avatar.
Un uso molto interessante di java si ha con le applets, che consistono in
programmi java che vengono scaricati da internet ed eseguiti in locale all'interno
di pagine html.
#VRML V2.0 utf8
#esempio errato
#ripiano su cui poggia la sfera
Shape {
appearance Appearance {
material Material { diffuseColor 0 1 0 }
}
geometry Box { size 12 1 2 }
}
DEF pos_sfera Transform {
translation -5 1.5 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 0 0 }
}
geometry Sphere {}
}
DEF touch TouchSensor {}
]
}
DEF tempo TimeSensor {
cycleInterval 8
enabled FALSE
loop FALSE
startTime 1
}
DEF posizioni_sfera PositionInterpolator {
key [0 .5 1]
keyValue [-5 1 0, 5 1 0, -5 1 0]
}
ROUTE touch.isActive TO tempo.enabled
ROUTE touch.touchTime TO tempo.startTime
ROUTE tempo.fraction_changed TO posizioni_sfera.set_fraction
ROUTE posizioni_sfera.value_changed TO pos_sfera.set_translation
DEF trigger Script {
eventIn SFBool click
eventOut SFBool inizia
eventOut SFTime tempo_partenza
field SFBool premuto FALSE
url "nomeclasse.class"
}
import vrml.*;
import vrml.field.*;
import vrml.node.*;
class trigger_sfera extends Script {
private SFBool start_sfera;
private SFTime tempo_start;
private SFBool premuto;
public void initialize() {
start_sfera = (SFBool) getEventOut("inizia");
tempo_start = (SFTime) getEventOut("tempo_partenza");
premuto = (SFBool) getField("premuto");
premuto.setValue(false);
}
public void processEvents(int count, Event events[]) {
int i;
for (i = 0; i < count; i++)
if (events[i].getName().equals("click"))
decidi_partenza(events[i].getTimeStamp());
}
public void decidi_partenza(double tempo) {
if (premuto.getValue() == false) {
premuto.setValue(true);
start_sfera.setValue(true);
tempo_start.setValue(tempo);
}
else
premuto.setValue(false);
}
}
Su una variabile cosi' definita e' possibile effettuare una operazione di
assegnamento, come per il caso del field premuto (premuto.setValue(false)),
oppure operazioni di lettura (premuto.getValue()).
Su una variabile di tipo Event sono possibili le operazioni di:
La risposta non e' poi cosi' banale; diciamo pure che al momento ce la caviamo
di lusso. La sfera continua la sua marcia come se niente fosse; cosa che del
resto ci va bene. Giunge alla fine della sua corsa e poi potremo riattivarla.
Questo funziona anche se la seconda pressione comporta la generazione di un
evento su start_sfera e uno su tempo_start. Mentre il primo evento non cambia
il valore TRUE del campo enabled del TimeSensor, il secondo cercherebbe di
cambiargli il tempo di inizio, riportando la sfera di colpo all'inizio della
animazione. Questo pero' non avviene in quanto il campo startTime puo' essere
settato solo se il TimeSensor non sta gia' funzionando, se cioe' il campo
enabled e' FALSE. E questo avverra' automaticamente solo quando l'animazione
sara' finita (in quanto abbiamo imposto loop a false).
#VRML V2.0 utf8
#esempio corretto
#ripiano su cui poggia la sfera
Shape {
appearance Appearance {
material Material { diffuseColor 0 1 0 }
}
geometry Box { size 12 1 2 }
}
DEF pos_sfera Transform {
translation -5 1.5 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 0 0 }
}
geometry Sphere {}
}
DEF touch TouchSensor {}
]
}
DEF tempo TimeSensor {
cycleInterval 8
enabled FALSE
loop FALSE
startTime 1
}
DEF posizioni_sfera PositionInterpolator {
key [0 .5 1]
keyValue [-5 1.5 0, 5 1.5 0, -5 1.5 0]
}
DEF trigger Script {
eventIn SFBool click
eventOut SFBool inizia
eventOut SFTime tempo_partenza
field SFBool premuto FALSE
url "trigger_sfera.class"
}
ROUTE touch.isActive TO trigger.click
ROUTE trigger.inizia TO tempo.enabled
ROUTE trigger.tempo_partenza TO tempo.set_startTime
ROUTE tempo.fraction_changed TO posizioni_sfera.set_fraction
ROUTE posizioni_sfera.value_changed TO pos_sfera.set_translation