|
||||||
ROBOTICA
video
immagini
papers
progettati
costruiti
toolbox
vrml
simulatori
Grosso esempio: paese con abitanti |
Nella lezione precedente
abbiamo analizzato il comando PROTO e abbiamo visto un semplice esempio di utilizzo
per creare un piccolo paesino (molto semplice).
Per rendere ancora piu'
chiari i concetti visti, questa lezione e' interamente dedicata all'esame
di un esempio decisamente molto piu' corposo rispetto ai precedenti.
L'obiettivo finale e'
quello di estendere l'esempio del capitolo precedente in modo da creare un
paese molto piu' realistico. In particolare vedremo di aggiungere alcuni alberi
(cosa banale) e soprattutto degli abitanti che passeggiano per la piazza (cosa
decisamente meno banale!).
Dato che gli abitanti
del paese sono abbastanza numerosi (nell'esempio ne uso 5) non possiamo permetterci
di duplicare il codice per ogni esemplare (avessi a disposizione un Silicon
potrei anche metterci decine di persone... su PC avere troppi oggetti in movimento
porta velocemente a una drastica riduzione del frame rate... con il passare
del tempo questi problemi dovrebbero attenuarsi notevolmente).
Nell'esempio del capitolo
precedente avevamo creato un nodo con il comando PROTO in modo che si potesse
creare facilmente una semplice casa. Anche in questo esempio utilizziamo lo
stesso meccanismo. Avevo pensato di aggiungere due campi alla definizione
del prototipo per la casa: uno per settare la texture della facciata e uno
per la texture del tetto. Alcune prove di velocita' mi hanno indotto ad evitare
ulteriori rallentamenti che si sarebbero avuti mappando textures sulle case.
Per cui la definizione delle case rimane uguale a quelle del precedente esempio.
La parte piu' complicata
riguarda certamente la definizione delle persone che passeggiano nella piazza.
Abbiamo visto che una qualche forma di riutilizzo del codice risulta obbligata.
Possiamo utilizzare semplicemente il comando DEF e poi richiamare con USE?
La risposta e' negativa in quanto vi sono parecchi campi dell'oggetto persona
che vanno cambiati da istanza ad istanza. Per esempio vorrei poter cambiare
il colore degli abiti, ma soprattutto modificare i percorsi che le persone
seguono. Per esempio alcune persone continueranno ad andare avanti e indietro;
altre preferiranno girare in tondo al centro della piazza (non saranno comportamenti
molto intelligenti, ma al momento ci puo' bastare).
Per cui la cosa migliore
da fare e' quella di creare un nuovo nodo tramite il comando PROTO. Il nuovo
nodo 'Human' avra' dei campi che consentono di specificare tutto cio' che
distingue un umano da un altro umano.
Vediamo ora in particolare
che cosa il nodo Human deve fornire.
Per prima cosa dovra'
contenere la rappresentazione fisica dell'oggetto di forma umana. Per motivi
di semplicita' questa e' molto semplificata e mi limito a comporre cilindri,
sfere e box, opportunamente connessi e scalati tra di loro. Sostituire questo
semplice oggetto con un oggetto piu' complesso non pone particolari problemi.
In secondo luogo dovra'
provvedere i meccanismi di animazione. Tutti gli oggetti cammineranno nella
stessa maniera (come stile di camminata; quello che cambia sono i percorsi
che si seguono). Per cui all'interno del nodo Human vengono inseriti i relativi
interpolatori, sensori di tempo e ROUTE che consentono di far muovere gambe
e braccia in modo opportuno. I movimenti sono stati realizzati partendo da
dati contenuti in un libro di biomeccanica; per cui sono decisamente realistici.
Precisato cosa vogliamo
ottenere, possiamo passare ad analizzare le singole parti.
Modellizzazione
fisica
Questa risulta la parte
piu' semplice da comprendere. Rappresenta comunque un aspetto molto importante
anche per quanto riguarda la successiva fase di animazione. Per cui, se vogliamo
modellizzare una gamba, possiamo farlo in questo semplice modo:
Come si puo' vedere,
la gamba destra viene costruita come una sequenza di nodi Transform innestati.
Il giunto 'anca_destra' risulta il piu' esterno. Modificare il suo campo rotation
porta a ruotare tutta la struttura contenuta nel suo campo children. L'altro
giunto e' 'ginocchio_destro' il cui campo rotation ruota solo polpaccio e
piede che sono appunto definiti nel relativo campo children.
L'uso di DEF e' strettamente
necessario solo per i giunti in quanto poi dovro' fare un ROUTE dei valori
degli angoli da ruotare al giunto. La coscia e il polpaccio sono invece tenuti
fissi alla sfera. Se pertanto ruota la sfera che rappresenta il giunto, ruota
anche cio' che gli e' attaccato.
Mi sono soffermato molto
su questo aspetto in quanto e' fondamentale per capire il resto dell'esempio.
Confidando che a questo punto abbiate ben chiaro come procede un singolo movimento,
possiamo passare ad esaminare la struttura complessiva.
Infatti, pensiamo a come realizzare il moto delle gambe mentre una persona
sta camminando: la coscia deve ruotare rispetto al bacino; se la coscia ruota,
ovviamente anche ginocchio, polpaccio e piede dovranno seguire il movimento.
Inoltre dovremo prevedere anche una rotazione al ginocchio. Di conseguenza
polpaccio e piede assumeranno posizioni dipendenti dalle rotazioni dei giunti
all'anca e al ginocchio.
Un modo molto semplice di implementare questi movimenti e' quello di modelizzare
il corpo come una catena cinematica. In altre parole, non facciamo altro che
innestare un nodo Transform dentro ad un altro, in modo che la rotazione imposta
al nodo piu' esterno si ripercuota su tutti i nodi piu' interni. Siccome coscia,
ginocchio, polpaccio e piede saranno innestati all'interno del nodo Transform
che definisce il giunto dell'anca, ruotare questo comporta la rotazione di
tutta la gamba. Se viceversa ruoto il giunto al ginocchio, il nesting dei
Transform e' tale che verranno influenzati solo polpaccio e piede.
DEF anca_destra Transform {
rotation 1 0 0 0
children [
#giunto come sfera
....
DEF coscia_destra Transform {
children [
#coscia come cilindro
...
DEF ginocchio_destro Transform {
rotation 1 0 0 0
children [
#giunto come sfera
...
DEF polpaccio + piede
]
...
Per cui alla fine, ci riduciamo a gestire solo 8 giunti, 2 per ogni gamba e 2 per ogni braccio. Dal punto di vista geometrico, il singolo giunto viene realizzato da una sfera, mentre coscie, polpacci, braccia e altro sono realizzati con dei cilindri. La testa e' stata realizzata con una sfera opportunamente scalata in modo da renderla piu' ovale.
Movimenti
All'interno della definizione
del nodo Human ho inserito anche i movimenti necessari per permettere all'uomo
virtuale di camminare. Questo risulta abbastanza semplice. Non ho fatto altro
che utilizzare degli interpolatori che modificano nel tempo il valore di rotazione
dei giunti.
Dovendo emettere degli eventi di tipo SFRotation ho utilizzato interpolatori
di tipo OrientationInterpolator.
Provvedo poi a fare il routing dei valori esportati dagli interpolatori ai
rispettivi giunti. Si noti che questa parte del movimento e' identica per
tutte le istanze del nodo Human. Il modo di camminare non dipende dalla particolare
persona che abbiamo creato (questo per motivi di semplicita').
Sempre nel nodo Human prevediamo altri due tipi di movimenti che ancora non abbiamo considerato. Tipicamente una persona quando cammina non rimane ferma, ma lo fa per spostarsi da un posto ad un altro... per cui mi pare ovvio che si debba introdurre dei meccanismi che consentano lo spostamento dello oggetto di forma umana. In maniera analoga dovremo rendere disponibile un meccanismo che consente di modificare l'orientamento.
Aggiungiamo dunque alcuni campi al nodo Human che ci consentano di specificare il percorso da seguire e l'orientamento da assumere lungo il percorso. A tal fine considero i seguenti campi:
I campi che piu' ci interessano in questo momento sono i primi quattro. Essi vengono forniti in ingresso come parametri ai due interpolatori definiti nel nodo Human in modo che ogni persona si muova poi secondo un tracciato definito dall'utente.
Se avessimo voluto dotare ogni persona di un proprio stile di camminata avremmo dovuto parametrizzare anche gli interpolatori per i movimenti di braccia e gambe. Quindi avremmo dovuto aggiungere almeno un campo per ogni giunto (assumendo di mettere un solo campo per definire le frazioni di tempo: cosa naturale in quanto solitamente le frazioni sono identiche all'interno di un movimento sincronizzato come quello del cammino).
E con questo direi che non c'e' altro da dire sulla realizzazione del nodo Human. Dovreste ora essere in grado di comprendere il sorgente VRML riportato in seguito.
Ultimo aspetto di questo (non piccolo) esempio e' stata l'introduzione di alcuni alberi. La realizzazione di questi segue il meccanismo che avevamo visto in occasione della lezione sul nodo Billboard. In pratica definisco un poligono quadrato su cui mappo la texture di un albero. Inserendo questo oggetto all'interno di un nodo Billboard otteniamo che l'albero ruoti in modo da apparire sempre rivolto verso la persona che lo guarda.
Vediamo di fare alcune considerazioni conclusive all'esempio.
In questo esercizio abbiamo potuto notare la effettivita' utilita' del comando PROTO. Senza di esso sarebbe stato ben difficile implementare delle persone all'interno del paese.
Inoltre, una volta definito il nodo Human, potremo utilizzare lo stesso oggetto anche in altri contesti senza nessun problema. Per cui aumentiamo di molto il riuso di oggetti.
Dal punto di vista dell'efficienza, questo esempio mostra il rallentamento che si ottiene nel momento in cui si hanno diversi oggetti in movimento. Se avessi aggiunto anche textures alle case il frame rate si sarebbe abbassato decisamente.
Diamo una occhiata al risultato :
Riporto qui di seguito il sorgente completo.
#VRML V2.0 utf8 #semplice esempio di utilizzo del comando PROTO PROTO human [ exposedField SFTime h_start 0 exposedField SFTime duration 30 exposedField MFFloat pos_key [0 1] exposedField MFVec3f pos_keyValue [0 0 0, 0 0 0] exposedField MFFloat or_key [0 1] exposedField MFRotation or_keyValue [0 1 0 0, 0 1 0 0] exposedField SFColor color_shirt 1 1 1 ] { DEF boy Transform { translation 0 0 0 rotation 0 1 0 0 children [ Transform { translation 0 1.4 0 # busto children [ Transform { translation 0 -.25 0 scale 1.2 1 .7 rotation 1 0 0 .03 children [ Transform { scale 1 1 3.6 children [ Shape { appearance Appearance { material Material { emissiveColor 1 1 1 shininess 0 } texture ImageTexture { url "../textures/synth/jeans2.jpg" } } geometry Cylinder { radius .05 height .15 } } ] } Transform { translation 0 .08 0 children [ Shape { appearance Appearance { material Material { emissiveColor .5 .5 .5 shininess 0 } texture ImageTexture { url "../textures/synth/jeans2.jpg" } } geometry Cylinder { radius .2 height .1 } } Transform { translation 0 .12 0 children [ Shape { appearance Appearance { material Material { emissiveColor IS color_shirt shininess 0} } geometry Cylinder { radius .16 height .14 } } Transform { translation 0 .23 0 children [ Shape { appearance Appearance { material Material { emissiveColor IS color_shirt shininess 0 } } geometry Cylinder { radius .2 height .32 } } ] } ] } ] } # testa Transform { translation 0 .63 0 children [ Shape { appearance Appearance { material Material { diffuseColor 1 .8 1 shininess 0 } } geometry Cylinder { radius .07 height .07 } } Transform { translation 0 .14 0 scale 1.1 1.6 1.1 children [ Shape { appearance Appearance { material Material { diffuseColor 1 .8 1 shininess 0 } } geometry Sphere { radius .11 } } ] } ] } ] } # gamba sinistra DEF gambasin Transform { translation -.14 -.25 0 children [ DEF giunto Shape { appearance Appearance { material Material { emissiveColor .5 .5 .5 shininess 0 } texture ImageTexture { url "../textures/synth/jeans2.jpg" } } geometry Sphere { radius .1 } } Transform { translation 0 -.3 0 children [ DEF gamba Shape { appearance Appearance { material Material { emissiveColor 1 0 0 shininess 0 } texture ImageTexture { url "../textures/synth/jeans2.jpg" } } geometry Cylinder { radius .1 height .6 } } DEF ginocchiosin Transform { translation 0 -.3 0 children [ USE giunto Transform { translation 0 -.25 0 children [ Transform { scale 1 .833 1 children [ USE gamba ] } Transform { translation 0 -.25 .075 children [ DEF piede Shape { appearance Appearance { material Material { emissiveColor .5 .5 .5 shininess 0 } } geometry Box { size .15 .08 .3 } } ] } ] } ] } ] } ] } # gamba destra DEF gambades Transform { translation .14 -.25 0 children [ USE giunto Transform { translation 0 -.3 0 children [ USE gamba DEF ginocchiodes Transform { translation 0 -.3 0 children [ USE giunto Transform { translation 0 -.25 0 children [ Transform { scale 1 .833 1 children [ USE gamba ] } Transform { translation 0 -.25 .075 children [ USE piede ] } ] } ] } ] } ] } # braccia Transform { translation .18 .30 0 rotation 0 0 1 .40 children [ DEF spallades Transform { children [ DEF giuntobraccio Shape { appearance Appearance { material Material { emissiveColor IS color_shirt } } geometry Sphere { radius .05 } } Transform { translation 0 -.22 0 children [ DEF pezzobraccio Shape { appearance Appearance { material Material { emissiveColor IS color_shirt } } geometry Cylinder { radius .05 height .44 } } DEF gomitodes Transform { translation 0 -.22 0 children [ USE giuntobraccio Transform { translation 0 -.19 0 children [ DEF pezzobraccio2 Shape { appearance Appearance { material Material { diffuseColor 1 .8 1 } } geometry Cylinder { radius .05 height .38 } } ] } ] } ] } ] } ] } Transform { translation -.18 .30 0 rotation 0 0 1 -.40 children [ DEF spallasin Transform { children [ USE giuntobraccio Transform { translation 0 -.22 0 children [ USE pezzobraccio DEF gomitosin Transform { translation 0 -.22 0 children [ USE giuntobraccio Transform { translation 0 -.19 0 children [ USE pezzobraccio2 ] } ] } ] } ] } ] } ] } ] } DEF time1 TimeSensor { cycleInterval 3 loop TRUE enabled TRUE startTime 1 } DEF time2 TimeSensor { cycleInterval 3 loop TRUE enabled TRUE startTime 2.5 } DEF time3 TimeSensor { cycleInterval IS duration loop TRUE enabled TRUE startTime IS h_start } DEF positionhuman PositionInterpolator { key IS pos_key keyValue IS pos_keyValue } DEF orienthuman OrientationInterpolator { key IS or_key keyValue IS or_keyValue } DEF orientgambades OrientationInterpolator { key [0 .07 .14 .21 .28 .35 .42 .49 .56 .63 .7 .77 .84 .91 1] keyValue [1 0 0 .124, 1 0 0 -.096, 1 0 0 -.322, 1 0 0 -.464, 1 0 0 -.435, 1 0 0 -.363, 1 0 0 -.322, 1 0 0 -.352, 1 0 0 -.185, 1 0 0 -.031, 1 0 0 .062, 1 0 0 .191, 1 0 0 .291, 1 0 0 .261, 1 0 0 .124] } DEF orientginocchiodes OrientationInterpolator { key [0 .07 .14 .21 .28 .35 .42 .49 .56 .63 .7 .77 .84 .91 1] keyValue [1 0 0 .764, 1 0 0 1.008, 1 0 0 1, 1 0 0 .748, 1 0 0 .316, 1 0 0 -.026, 1 0 0 .077, 1 0 0 .304, 1 0 0 .257, 1 0 0 .17, 1 0 0 .154, 1 0 0 .13, 1 0 0 .183, 1 0 0 .425, 1 0 0 .764] } DEF orientspallades OrientationInterpolator { key [0 .35 .9 1] keyValue [1 0 0 .087, 1 0 0 -.292, 1 0 0 .25, 1 0 0 .087] } DEF orientgomitodes OrientationInterpolator { key [0 .35 .9 1] keyValue [1 0 0 -.1, 1 0 0 -.3, 1 0 0 0, 1 0 0 -.1] } DEF orientgambasin OrientationInterpolator { key [0 .07 .14 .21 .28 .35 .42 .49 .56 .63 .7 .77 .84 .91 1] keyValue [1 0 0 .124, 1 0 0 -.096, 1 0 0 -.322, 1 0 0 -.464, 1 0 0 -.435, 1 0 0 -.363, 1 0 0 -.322, 1 0 0 -.352, 1 0 0 -.185, 1 0 0 -.031, 1 0 0 .062, 1 0 0 .191, 1 0 0 .291, 1 0 0 .261, 1 0 0 .124] } DEF orientginocchiosin OrientationInterpolator { key [0 .07 .14 .21 .28 .35 .42 .49 .56 .63 .7 .77 .84 .91 1] keyValue [1 0 0 .764, 1 0 0 1.008, 1 0 0 1, 1 0 0 .748, 1 0 0 .316, 1 0 0 -.026, 1 0 0 .077, 1 0 0 .304, 1 0 0 .257, 1 0 0 .17, 1 0 0 .154, 1 0 0 .13, 1 0 0 .183, 1 0 0 .425, 1 0 0 .764] } DEF orientspallasin OrientationInterpolator { key [0 .35 .9 1] keyValue [1 0 0 .087, 1 0 0 -.292, 1 0 0 .25, 1 0 0 .087] } DEF orientgomitosin OrientationInterpolator { key [0 .35 .9 1] keyValue [1 0 0 -.1, 1 0 0 -.3, 1 0 0 0, 1 0 0 -.1] } ROUTE time1.fraction_changed TO orientgambasin.set_fraction ROUTE orientgambasin.value_changed TO gambasin.set_rotation ROUTE time1.fraction_changed TO orientginocchiosin.set_fraction ROUTE orientginocchiosin.value_changed TO ginocchiosin.set_rotation ROUTE time2.fraction_changed TO orientgambades.set_fraction ROUTE orientgambades.value_changed TO gambades.set_rotation ROUTE time2.fraction_changed TO orientginocchiodes.set_fraction ROUTE orientginocchiodes.value_changed TO ginocchiodes.set_rotation #moto human ROUTE time3.fraction_changed TO positionhuman.set_fraction ROUTE positionhuman.value_changed TO boy.set_translation #orientamento human ROUTE time3.fraction_changed TO orienthuman.set_fraction ROUTE orienthuman.value_changed TO boy.set_rotation #spalla sin e gomito destro ROUTE time2.fraction_changed TO orientspallasin.set_fraction ROUTE orientspallasin.value_changed TO spallasin.set_rotation ROUTE time2.fraction_changed TO orientgomitosin.set_fraction ROUTE orientgomitosin.value_changed TO gomitodes.set_rotation #spalla des e gomito sin ROUTE time1.fraction_changed TO orientspallades.set_fraction ROUTE orientspallades.value_changed TO spallades.set_rotation ROUTE time1.fraction_changed TO orientgomitodes.set_fraction ROUTE orientgomitodes.value_changed TO gomitosin.set_rotation } #end of human PROTO PROTO House [ exposedField SFColor color_house 1 1 1 exposedField SFColor color_roof 1 0 0 exposedField MFString house_tex "" exposedField MFString roof_tex "" ] { Transform { translation 0 0 3 children [ Shape { appearance Appearance { material Material { diffuseColor IS color_house } texture ImageTexture { url IS house_tex } } geometry Box { size 6 6 6 } } Transform { translation 0 3 0 children [ Shape { appearance Appearance { material Material { diffuseColor IS color_roof } textureTransform TextureTransform { scale 16 16 } texture ImageTexture { url IS roof_tex } } geometry IndexedFaceSet { coord Coordinate { point [ -3 0 3, 3 0 3, 3 0 -3, -3 0 -3, 0 3 0 ] } coordIndex [ 0,3,2,1,-1, 0,1,4,-1, 1,2,4,-1, 2,3,4,-1, 3,0,4,-1, ] } } # end of Shape ] } # end of roof ] } } # END OF PROTO # adesso posso liberamente utilizzare il nuovo nodo. DirectionalLight { direction 0 -1 -1 intensity .6 } DirectionalLight { direction -1 0 0 intensity .6 } DirectionalLight { direction 1 0 0 intensity .6 } NavigationInfo { headlight FALSE avatarSize [.5 1.8 .5] } Viewpoint { description "centro" position 0 2 23 #risolve problemi #con Collision Detection } Background { skyAngle [1.3 1.57 3.14] skyColor [.6 .6 1, .5 .5 1, .8 0 0, .5 .5 1] groundAngle [1 1.57] groundColor [.3 .3 1, .4 .4 1, .5 .5 1] } #definisco un piano su cui mettere le varie case. Transform { translation 0 -5 0 children [ Shape { appearance Appearance { material Material { diffuseColor .5 .5 .5 } } geometry Box { size 100 10 100 } } ] } #prima casa! Transform { translation -40 0 -40 children [ House { color_house 0 1 0 color_roof 1 0 0 } ] } #seconda casa Transform { translation -30 0 0 scale 1 1 2 # per ottenere case di dimensioni diverse children [ House { color_house 1 1 1 color_roof .5 0 0 } ] } Transform { translation -40 0 -30 scale 1 2 2 children [ House { color_house .5 .5 0 color_roof .8 .8 .8 } ] } Transform { translation 0 0 -30 scale 3 2 1 children [ House { color_house 1 0 0 color_roof .4 .4 .4 } ] } Transform { translation 40 0 -30 scale 1.5 5 2 children [ House { color_house 1 1 1 color_roof .8 0 0 } ] } Transform { translation 30 0 0 scale 1 2 3 children [ House { color_house 0 .4 .1 color_roof .5 0 .1 } ] } #ultima casa! Transform { translation 0 0 30 scale 3 1.5 1 children [ House { color_house .6 0 0 color_roof .4 .4 .4 } ] } #metto alcuni alberi DEF albero Transform { children [ Billboard { axisOfRotation 0 1 0 children [ Shape { appearance Appearance { material Material { diffuseColor 0 1 0 } texture ImageTexture { url "../textures/plant2.gif" } } geometry IndexedFaceSet { coord Coordinate { point [ -2 0 0, 2 0 0, 2 5 0, -2 5 0 ] } coordIndex [0,1,2,3,-1] texCoord TextureCoordinate { point [ 0 0, 1 0, 1 1, 0 1 ] } texCoordIndex [0,1,2,3,-1] } } ] } ] } Transform { translation -20 0 -10 children [ USE albero ] } Transform { translation 20 0 -10 children [ USE albero ] } Transform { translation -10 0 10 children [ USE albero ] } Transform { translation -13 0 13 children [ USE albero ] } Transform { translation 16 0 15 children [ USE albero ] } Transform { translation 30 0 40 children [ USE albero ] } Transform { translation -30 0 40 children [ USE albero ] } #metto l'insegna del paese Transform { translation 5 .75 0 children [ DEF sostegno Shape { appearance Appearance { material Material { diffuseColor .5 .5 .5 } } geometry Cylinder { radius .04 height 1.5 } } Transform { translation 3 0 0 children [ USE sostegno ] } Transform { translation 1.5 1.25 0 children [ Shape { appearance Appearance { material Material { diffuseColor 1 1 1 } texture ImageTexture { url "../textures/nomecit.gif" } } geometry Box { size 3.5 1 .04 } } ] } ] } #giusto un paio di giardinetti Transform { translation 0 0 27 children [ DEF giardino Shape { appearance Appearance { material Material { diffuseColor 0 .7 0 } } geometry Box { size 18 .2 6 } } ] } Transform { translation 40 0 -23.5 scale 1 1 3 children [ USE giardino ] } #inserisco qui alcuni abitanti del paese! DEF uomo1 Transform { translation 0 0 0 children [ human { color_shirt 1 1 1 h_start 1 duration 120 pos_key [0 .45 .5 .55 .9 .95 1] pos_keyValue [-17 0 -21, 17 0 -21, 20 0 -20, 17 0 -19, -17 0 -19, -20 0 -20, -17 0 -21] or_key [0 .48 .5 .52 .96 .98 1] or_keyValue [0 1 0 -4.71, 0 1 0 -4.71, 0 1 0 0, 0 1 0 -1.57, 0 1 0 -1.57, 0 1 0 -3.14, 0 1 0 -4.71] } ] } Transform { translation 0 0 0 children [ human { color_shirt 1 0 0 h_start 1 duration 120 pos_key [0 .45 .5 .55 .9 .95 1] pos_keyValue [-17 0 -20, 17 0 -20, 20 0 -19, 17 0 -18, -17 0 -18, -20 0 -19, -17 0 -20] or_key [0 .48 .5 .52 .96 .98 1] or_keyValue [0 1 0 -4.71, 0 1 0 -4.71, 0 1 0 0, 0 1 0 -1.57, 0 1 0 -1.57, 0 1 0 -3.14, 0 1 0 -4.71] } ] } Transform { translation 0 0 40 children [ USE uomo1 ] } Transform { rotation 0 1 0 1.57 translation 0 0 10 children [ USE uomo1 ] } Transform { rotation 0 1 0 1.57 translation 40 0 10 children [ USE uomo1 ] } DEF uomo2 Transform { children [ human { color_shirt 0 0 1 h_start 1 duration 100 pos_key [0 .2 .25 .45 .5 .7 .75 .95 1] pos_keyValue [10 0 -7, 10 0 7, 7 0 10, -7 0 10, -10 0 7, -10 0 -7, -7 0 -10, 7 0 -10, 10 0 -7] or_key [0 .2 .25 .45 .5 .7 .75 .95 1] or_keyValue [0 1 0 0, 0 1 0 0, 0 1 0 -1.57, 0 1 0 -1.57, 0 1 0 -3.14, 0 1 0 -3.14, 0 1 0 -4.71, 0 1 0 -4.71, 0 1 0 0] } ] }