Capitolo 4: Le API Java 3D
__________________________________________________________________
Capitolo4
Le API Java 3D
4.1 Introduzione
Lo slogan “Write once, run anywhere” è stato adattato dal marketing Sun di Java
3D a “Write once, view anywhere”, focalizzando l’attenzione sulla portabilità
delle applicazioni e applet realizzate con Java 3D.
Java 3D è una estensione standard di java 2 JDK. Tale interfaccia permette di
creare applicazioni e applet basate su grafica tridimensionale. Le API prevedono
una collezione di costrutti ad alto livello per creare e manipolare geometrie 3D, e
strutture per renderizzare tali geometrie.
In questo capitolo mostreremo il funzionamento generale dell’engine Java 3D,
della sua caratteristica struttura ad albero, e parleremo senza entrare troppo nei
dettagli di texture, luci, e collisioni.
62
Capitolo 4: Le API Java 3D
__________________________________________________________________
4.2 Le API
Le API definiscono più di 100 classi presenti nel pacchetto javax.media.j3d. A tali
classi ci si riferiscie come Java 3D core classes. In aggiunta al pacchetto java 3D
core, esistono dei pacchetti supplementari usati per scrivere programmi Java 3D.
Uno di questi è com.sun.j3d.utils al
quale ci si riferisce generalmente come
Javax.media.j3d
Java 3D utility classes. Tali classi sono
d’estrema utilità poiché le core classes
includono solo le classi a basso livello
strettamente
necessarie
Virtual universe
Locele
nella
programmazione Java 3D. Le utility
sono classi potenti e utili di supporto.
View
Phisical Body
Questo pacchetto si divide in quattro
PhisicalUniverse
categorie:
Screen3D

Content loaders

Scene graph construction aids

Geometry classes

Convenience utilities
Convas3D
ScreenGraphObject
Node
Le
future
funzionalità
saranno
Group
aggiunte come utilità nel pacchetto
java 3D.
Leaf
In aggiunta a queste classi ogni
programma
java
3D
utilizza
il
pacchetto javax.vecmath nel quale
NodeComponent
Transform3D
sono definiti vettori, matrici e altri
oggetti matematici.
Alpha
Figura 4.1 – Gerarchia delle Classi
63
Capitolo 4: Le API Java 3D
__________________________________________________________________
4.3 Il Motore di Java 3D
Java 3D, consente la produzione in tempo reale
Applet o Applicazione
di grafica 3D tramite un insieme di API
JAVA 3D
omogenee e multipiattaforma.
Per riuscire ad avere un buon rendimento e delle
buone
prestazione
è
stata
sacrificata
JAVA 2
la
portabilità delle classi. Java 3D, infatti, non si
occupa del rendering a basso livello delle
Native Graphics Calls
OpenGL
immagini: operazione delegata dal motore di
rendering di java 3D a librerie native (OpenGL,
Direct3D
Future
Apis
Proprietary Hardware
Direct3D). Le capacità, e le possibilità di java
3D sono, infatti, un insieme standard di
caratteristiche riscontrabili in queste librerie
Figura 4.2 – Relazione tra
Hardware e software
native: non è possibile ad esempio gestire le ombre e le texture animate. Queste
non sono limitazioni di Java 3D, ma delle librerie native. Le operazioni citate
sono, infatti, molto dispendiose in termini di risorse e non sono utilizzate in
grafica 3D in tempo reale. E’ lecito aspettarsi che quando l’evoluzione
tecnologica porterà gli standard 3D a supportare questo tipo di caratteristiche,
anche Java 3D ne fornirà il supporto.
Java 3D non è però solo un semplice wrapper su API esistenti: il suo motore si
occupa anche di ottimizzazione e operazioni aggiuntive che non sono previste
nelle API native. Il motore sceglie l’ordine di attraversamento dell’albero degli
oggetti 3D e non è limitato ad una direzione di tipo sinistra-destra o alto-basso
(eccetto per quanto riguarda attributi con limiti di spazio come le sorgenti di luce
o l’effetto nebbia), quindi può ottimizzare la visualizzazione.
Il motore è concettualmente un ciclo infinito che effettua una fase di render ogni
ciclo rielaborando ogni volta eventuali modifiche apportate e passando ad una fase
di idle al termine del ciclo.
64
Capitolo 4: Le API Java 3D
__________________________________________________________________
While (true) {
Process input
If (request to exit) break
Perform Behaviors
Traverse the scene graph and render visible objects
}
Cleanup and exit
Il sistema è inoltre aperto all’elaborazione parallela.
4.4 La Struttura Dati
VirtualUniverse
Locale
BranchGroup
Shape3D
node
Appearance
TransformGroup
Geometry
Canvas3
D
View
Creen3D
View Platform
Phisical Body
Phisical Envoronment
Figura 4.3 - SimpleUniverse
Java 3D permette di definire “SceneGraph” (ambienti in cui sono presenti oggetti
3D) ed organizza gli oggetti necessari a descrivere l’universo virtuale tramite un
albero. Gli oggetti appartenenti all’albero non sono omogenei, ma sono di tipo
diverso. La radice dell’albero è un oggetto di tipo VirtualUniverse e serve come
punto di riferimento per gli altri oggetti. Da questo discende un oggetto di classe
Locale che consente di definire la posizione all’interno dell’universo.
65
Capitolo 4: Le API Java 3D
__________________________________________________________________
Teoricamente è possibile inserire più istanze di questo oggetto all’interno della
stessa scena.
Da Locale discendono oggetti di classe BranchGroup e TransformGroup.
Come possiamo vedere dalla Figura 4.3 a questo punto l’albero si divide in due
grosse rami che possiamo definire come albero dei contenuti a sinistra, e albero di
input e output a destra, che prende il nome di View Branch. Questo ramo è
piuttosto complesso e fornisce a Java 3D molta flessibilità: è grazie ad esso,
infatti, che Java 3D è in grado di usare dispositivi di input come guanti, e
dispositivi di visione come i caschi. Continuiamo con la descrizione del View
Branch. La radice è un oggetto di tipo ViewPlatform che indica dove la scena
deve essere vista (posizione e orientamento), a cui è collegato un View, che indica
invece come la scena deve essere vista (tipo di proiezione, antialiasing), e infine
troviamo Canvas3D che estende Canvas con il double buffering. Inoltre troviamo
PhysicalBody, che contiene una serie d’informazioni sulla posizione dell’utente, e
Physical Enviroment che contiene informazioni su tipi differenti di sensori di
input e periferiche di output. Sul ramo sinistro dell’albero, Content Branch, sono
presenti gli oggetti 3D, sottoclassi di Shape3D.
La costruzione di un albero Java 3D non è banale, ma fortunatamente è presente
un’utilità, la classe SimpleUniverse, che raccoglie tutto il View Branch e che
consente di creare in automatico un “semplice universo”, in cui sono presenti i
dispositivi standard disponibili quali mouse e monitor. L’universo base creato da
SimpleUniverse è simile a quello che abbiamo descritto fino a questo momento,
con la sola eccezione del ramo sinistro dell’albero, in cui l’insieme degli oggetti
che dovranno essere presenti nella scena, devono essere creati manualmente o
importati.
L’unica classe presente in Java 3D che definisce una forma geometrica è
Shape3D. Per definire forme specifiche è necessario fare riferimento alle classi
utility presenti nel pacchetto. Alcune forme disponibili sono Box, Spere, Cylinder,
Cone. La scelta di non comprendere nei core package forme specifiche è dettata
dal fatto che le API di Java3D si intendono mantenere il più semplice possibile.
Anche funzionalità future verranno molto probabilmente inserite nel pacchetto di
utilità.
66
Capitolo 4: Le API Java 3D
__________________________________________________________________
Per collegare gli oggetti 3D al ramo sinistro dell’oggetto Locale è necessario
utilizzare oggetti di classe BranchGroup e TransformGroup. Oggetti di queste
classi permettono di collegare le forme geometriche tra di loro (Branch) e di
applicare trasformazioni (Transform), quali la geometria (Geometry), la posizione
della luce, la vista.
Per ottimizzare ulteriormente le prestazioni è possibile compilare tutta una parte
dell’albero. Una volta compilato il ramo, però, non sarà più possibile effettuare
variazioni strutturali su di esso.
4.4.1 Le Texture
Un modo semplice per dare l'apparenza di un oggetto complesso è di applicarvi
sulle facce una texture, cioè un’immagine. Si pensi ad esempio ad un muro
composto di tante pietre irregolari: il modo migliore per visualizzarlo è avere un
oggetto per ogni pietra, o per lo meno avere un unico oggetto più o meno a forma
di parallelepipedo, con un lato irregolare a simulare le varie pietre. Questo metodo
presenta due svantaggi: é lungo e tedioso da generare per il grafico e soprattutto
richiede un grosso lavoro al calcolatore, con un’eccessiva perdita prestazionale.
Generalmente, quindi, si ricopre un oggetto liscio con un’immagine. Questa
tecnica permette di utilizzare molti meno poligoni con risultati paragonabili,
almeno fino a quando non ci si avvicina troppo al soggetto. Per cercare di
eliminare questo problema si utilizza una tecnica chiamata mip-mapping: in
pratica, quando l'elaboratore deve "inventarsi" dei pixel perché la texture è troppo
vicina, non si limiterà a considerare solo il pixel più vicino al punto di
osservazione, ma anche quelli limitrofi. Oppure una tecnica avanzata che prevede
texture molto sofisticate in cui i particolari appaiono avvicinandosi; in questo
modo non si hanno bruschi cambiamenti di colore nella texture, anche se sembra
esserci un po' di sfocatura.
Vediamo come Java3D gestisce tutto questo:
La classe TextureLoader, una classe d’utilità, carica un'immagine (da un file o da
un URL) e restituisce una Texture. Per motivi tecnici le dimensioni delle texture
67
Capitolo 4: Le API Java 3D
__________________________________________________________________
devono essere una potenza di due. Inoltre grazie ad opportuni metodi, la texture
può essere ruotata, spostata e scalata; può essere piazzate sull'oggetto una volta
(clamping) oppure ripetersi per coprire l'intera superficie (wrapping). Le texture
possono fondenrsi in varia misura con il colore dell'oggetto; inoltre possono
essere ancorate all'oggetto oppure al mondo: questo permette di utilizzare una
tecnica chiamata reflection mapping, che permette dà l'effetto delle riflessioni su
un oggetto senza troppi calcoli.
4.4.2 Le Luci
Per aumentare il realismo delle scene è possibile aggiungere delle luci. Bisogna
però stare attenti a non esagerare, perché le luci sono computazionalmente
impegnative e tendono a rallentare la scena. Anche le ombre proiettate sono
troppo dispendiose e normalmente non sono usate. I tipi di luce che si possono
utilizzare sono quattro:

Ambient: una luce diffusa che illumina uniformemente tutti gli oggetti. Di
solito se ne utilizza una sola

Directional: raggi paralleli che puntano in una direzione; possono
simulare per esempio il Sole

Point: i raggi sono emessi da un punto e si irradiano in tutte le direzioni;
ad esempio una lampadina

Spot: i raggi sono emessi da un punto e si irradiano dentro un cono.
Tutte i tipi di luce hanno in comune metodi per attivarle o disattivarle e per
cambiarne il colore. Inoltre un fattore estremamente importante da considerare è
che ogni luce deve avere dei limiti ben fissati da un oggetto Bounds. I limiti
servono a confinare le luci in una certa area, oltre che per motivi estetici anche per
evitare un grosso carico computazionale. Se si hanno, ad esempio, due stanze non
comunicanti entrambe con una luce all'interno e se non si limita l'area d'effetto
68
Capitolo 4: Le API Java 3D
__________________________________________________________________
delle luci esse avranno effetto su entrambe le stanze, creando un effetto sbagliato
oltre che rallentando inutilmente la scena.
4.4.3 I behaviour
Fino ad ora il contenuto della nostra scena è statico; vediamo adesso come
renderlo un po' più dinamico. Java3D fonda il suo meccanismo per gestire
l'animazione, il movimento, l'interazione con l'utente e le collisioni sui Behavior
(letteralmente "Comportamenti").
I behaviour sono delle classi utilizzate per gestire alcuni raffinati processi in una
scena 3D. Tutti i behaviour devono essere definiti dentro una regione per motivi
di performance e comodità. I metodi fondamentali sono initialize() e
processStimulus(): il primo è chiamato solo una volta, all’inizio; di solito contiene
le definizioni delle condizioni di WakeUp (svegliarsi) e la specifica del primo
criterio per il quale svegliarsi. Invece processStimulus viene chiamato quando si
verifica il criterio specificato da wakeupOn.
I criteri per cui un behavior viene svegliato possono essere di vario tipo: eventi
dell'AWT, collisioni, un certo tempo passato e molti altri ancora.
Vediamo, quindi, i vari WakeUpCriterion:

WakeupOnAWTEvent, quando avviene un evento dell'AWT.

WakeupOnBehaviourPost, quando un behaviour specificato lancia un preciso evento.

WakeupOnActivation e WakeupOnDeactivation, quando un behaviour entra o esce dai
limiti di attivazione.

WakeupOnElapsedFrames, dopo che sono stati disegnati un certo numero di frames.

WakeupOnElapsedTime, dopo che è passato un certo tempo.

WakeupOnSensorEntry e WakeupOnSensorExit, il centro di un Sensor entra o esce
da una certa regione.

WakeupOnViewplatformEntry e WakeupOnViewplatformExit, il centro di una
ViewPlatform entra o esce da una certa regione.

WakeupOnTransformChange, quando un certo nodo viene sottoposto a una
trasformazione.
69
Capitolo 4: Le API Java 3D
__________________________________________________________________

WakeupOnCollisionEntry, WakeupOnCollisionExit,WakeupOnCollisionMovement,
per la gestione delle collisioni.
I behaviour sono piuttosto flessibili e possono essere combinati con degli AND e
OR logici, tramite quattro classi:

WakeupAnd: una serie di criteri legati da un AND (WakeupCriterion &&
WakeupCriterion && ...)

WakeupOr: una seria di criteri legati da un OR (WakeupCriterion || WakeupCriterion ||
...)

WakeupAndOfOrs: una serie di WakeupOr legati da un AND (WakeupOr &&
WakeupOr && ...)

WakeupOrOfAnds: una serie di WakeupAnd legati da un OR (WakeupAnd ||
WakeupAnd || ...)
Per far girare una sfera ad esempio dovremmo quindi definire un Behavior che
abbastanza spesso si svegli, crei una trasformazione per orientare la sfera, la
applichi e torni a dormire; invece di agire così, è consigliabile utilizzare una
sottoclasse dei Behavior: Interpolator. Gli Intepolator servono ad esprimere
semplici Behavior che cambiano un parametro da un valore iniziale ad un valore
finale in un intervallo di tempo. Sono fornite delle sottoclassi di Interpolator per
gli utilizzi più comuni, tra cui proprio una che serve per ruotare un gruppo di
oggetti. E' necessario creare un oggetto Alpha che controlla il modo in cui
l'interpolatore agisce.
Facendo un po’ di test su varie porzione di codice si arriva però alla conclusione
che la gestione delle collisioni è altamente imprecisa.
Se si passa al behaviur lo shape dell’oggetto (quindi tutta la geometria
dell’oggetto) si ottiene un effetto meno preciso rispetto a passare gli oggetti come
Node, in modo da controllare solamente il centro, (ovviamente solo nei pochi casi
in cui è possibile farlo). Probabilmente questo è dovuto ad una non corretta
implementazione del metodo getShape.
70
Capitolo 4: Le API Java 3D
__________________________________________________________________
4.5 Java 3D negli Origami
Entriamo ora nel dettaglio delle problematiche da affrontare per implementare la
nostra applicazione. Lo scopo è di visualizzare un foglio di carta diviso in
poligoni, in modo tale che ognuno di questi sia un oggetto indipendente (shape
3d) libero di ruotare intorno ad uno qualunque dei suoi lati.
I problemi da affrontare sono quindi i seguenti:

Creazione dello ScheneGraph

Creazioni dei poligoni

Rotazione dei poligoni
4.5.1 Creazione dello SceneGraph
VirtualUniver
se
Local
e
TG - scala
TG - luci
BranchGrou
p
ORIGAMI
TG
TransformGroup
TG
TransformGroup
TransformGroup
View
View
Platform
TG
TG
Phisical
TransformGroupBody
TransformGroup
Canvas3
Creen3D
Phisical
Envoronment
Figura 4.4 – Shene Graph
71
Capitolo 4: Le API Java 3D
__________________________________________________________________
Come si vede dalla Figura 4.4 lo SheneGraph viene costruito seguendo le
indicazioni che abbiamo dato nel paragrafo 4.4. Soffermiamoci un momento sulla
porzione sinistra dell’albero. Il primo nodo è di tipo TrasfornGroup, utilizzato per
settare il fattore di scala, tale metodo risulta molto comodo poiché dà la possibilità
di variare facilmente le dimensioni degli oggetti della scena, senza dover
riscrivere tutti i parametri che li descrivono. In cascata segue un altro nodo di tipo
TrasforGroup dove viene creata l’illuminazione della scena, ponendo una luce
d’ambiente, e una luce direzionale inclinata di 45°. In fine iniziano i nodi che
descrivono l’origami: ogni nodo di tipo TrasformGroup descrive un poligono.
Nella Figura 4.4 non viene rappresentato per ragioni di leggibilità, ma è chiaro
che ognuno di questi nodi sarà composto a sua volta da un oggetto di tipo
Geometry necessario a descrivere la geometria del poligono, a da un oggetto di
tipo Apparence per settare i parametri che ne identificano appunto l’apparenza: il
grado di riflessione, il colore, eventuali texture ecc. Come avviene la costruzione
della porzione di albero che descrive l’origami sarà illustrato dettagliatamente nel
capitolo 5, deve essere chiaro però che tale struttura dipenderà dal tipo di pattern
che intendiamo simulare.
4.5.2 Creazione dei poligoni
Immaginiamo di avere tutti i punti che descrivono il poligono, il modo per
ricavarli sarà chiarito nel capitolo 5.
Java 3D fornisce molti metodi differenti che permettono di creare degli oggetti.
Nella figura 4.5 possiamo osservare la gerarchia della classe Geometry
72
Capitolo 4: Le API Java 3D
__________________________________________________________________
Figura 4.5 – Geararchia della classe Geometry
GeometryStripArray è una super classe di LineStripArray, TriangleStripArray e
TriangleFanArray. La figura 4.6 mostra il comportamento dei tre metodi su un
insieme di punti.
Figura 4.6 – Sottoclasse GeometryStripArray
Il metodo più idoneo al nostro caso è TriangleFanArray; guardiamone la sintassi.
TriangleFanArray(int vtxCount, int vertexFormat, int stripVertexCounts[]))
VtxCount è un intero che indica il numero di punti di cui è composto il poligono;
vertexFormat indica il modo con cui sono passati i punti nell’array
stripVertexCounts.
73
Capitolo 4: Le API Java 3D
__________________________________________________________________
Per utilizzare tale metodo, però, è necessario ordinare i punti, poiché il primo
dell’array ha la funzione di v0 nella Figura 4.6, ossia è l’unico vertice che
appartiene a tutti i triangoli.
Tale metodo deve essere invocato due volte, passandogli stripVertexCounts
ordinato in manira prima crescente e poi decrescente, in questo modo java 3D
disegnerà prima il semipiano frontale, e poi quello posteriore.
4.5.3 Rotazione dei Poligoni
Per addentrarci nelle problematiche della rotazione dei poligoni è opportuno
descrivere in modo un po’ più dettagliato il pacchetto javax.vecmath in cui sono
definiti vettori, matrici e altri oggetti matematici, che risultano indispensabili per
applicare trasformazioni sugli oggetti, quali rotazioni, traslazioni, deformazioni.
Figura 4.7 – gerarchia del pacchetto javax.vecmath
74
Capitolo 4: Le API Java 3D
__________________________________________________________________
Ogni vertice di un oggetto geometrico può essere specificato da quattro tipi di
oggetti appartenenti a javax.vecmath, rappresentanti coordinate, colori, normali
alla superficie, e coordinate della texture. Le classi sono

Point*

Color*

Vector*

TexCoord*
L’asterisco rappresenta la variazione al nome della classe. Per esempio Tuple*
sono tutte le classi: Tuple2f, Tuple2d, Tuple3b, Tuple3f, Tuple3d, Tuple4b,
Tuple4f, e Tuple4d. In tutti i casi il numero indica la quantità di elementi che
compongono il tuple, e variano da due a quattro, la lettera indica invece il tipo. ‘f’
per la precisione singola floating point, ‘d’ la doppia precisione e ‘b’ il bytes.
Noi abbiamo bisogno di un metodo che ci permetta di ruotare il poligono intorno
ad un vettore qualunque nello spazio di un determinato angolo.
Tale metodo è AxisAngle4d , che ha appunto quattro argomenti double, tre dei
quali (x,y,z,) rappresentano le componenti del vettore lungo le tre direzioni e il
quarto rappresenta l’angolo di rotazione. E’ importante notare però che tale
vettore non è centrato nell’origine degli assi. Per avere una rotazione corretta del
poligono è necessario compiere, quindi, una composizione di trasformazioni:
Traslazione - Rotazione - Traslazione.
Di seguito è riportata parte del codice utilizzata per compiere questa operazione
all’interno dell’applicazione
75
Capitolo 4: Le API Java 3D
__________________________________________________________________
Transform3D trasla = new Transform3D();
trasla.setTranslation(new Vector3d((V.getCentro()).x,(V.getCentro()).y,0));
AxisAngle4d Ax = new AxisAngle4d( s );
Transform3D rot = new Transform3D();
rot.setRotation(Ax);
trasla.mul(rot);
Transform3D retrasla = new Transform3D();
retrasla.setTranslation(new Vector3d(-(V.getCentro()).x,-(V.getCentro()).y,0));
trasla.mul(retrasla);
T.setTransform(trasla);
4.5 Conclusioni
Java3D è un’API piuttosto estesa, flessibile e complessa, che consente con
relativa semplicità di produrre applicazioni o applet dotate di grafica 3D, ed è
adatta specialmente per progetti sofisticati, dove strumenti principalmente
descrittivi come il VRML non sono sufficienti, e si desidera utilizzare un’API a
più alto livello rispetto ad OpenGL e Direct3D.
L’utilizzo di librerie native e una serie di strumenti come i limiti per i behaviour e
per le luci, i capabilites bit,(che permettono ad esempio di specificare se un
oggetto sarà modificato durante l’esecuzione) la possibilità di precompilare
porzioni di albero, garantiscono prestazioni accettabili, anche se purtroppo è
proprio sui grandi progetti che si incontra i problemi più grossi.
L’interfaccia Java fornisce portabilità e una buona semplicità d’uso, anche se dà
l’idea di essere stato sviluppato un po’ in disparte rispetto alle altre tecnologie
Java. L’incompatibilità con Swing ne è un esempio (come la classe Locale che
76
Capitolo 4: Le API Java 3D
__________________________________________________________________
trova un duplicato in java.util), che potrebbe frenare progetti ambiziosi su Java3D.
Nonostante ciò Java3D completa l’offerta della piattaforma Java con un elemento
chiave quale la grafica 3D fornendo un ulteriore e valido tassello alla tecnologia
globale Java.
77