Sistemi multimediali Massimiliano Piscozzi – [email protected] Prototipi (3) • La definizione di un prototipo può essere contenuta in un file diverso da quello in cui vengono create le istanze File A File X3D-XML contenente la dichiarazione di uno o più prototipi File B File X3D-XML contenente la dichiarazione di uno o più prototipi File C File X3D-XML contenente la dichiarazione di uno o più prototipi File D File X3D-XML che utilizza istanze di prototipi definiti esternamente Maggiore riusabilità dei nodi definiti dall’utente Semplificazione nella scrittura dei file X3D-XML ExternProtoDeclare • La dichiarazione di un prototipo definito esternamente avviene attraverso il tag ExternProtoDeclare <ExternProtoDeclare name=“...” url=“...”/> Indirizzo del file in cui è definito il prototipo + nome del prototipo: nomeFile#nomePrototipo Nome utilizzato localmente per la creazione di istanze del prototipo • Esempio FileA.x3d ... <ProtoDeclare name=“oggetto”> <ProtoInterface> ... </ProtoInterface> <ProtoBody> ... </ProtoBody> </ProtoDeclare> ... FileB.x3d ... <ExternProtoDeclare name=“oggettoEsterno” url=“FileA.x3d#oggetto”/> ... <ProtoInstance name=“oggettoEsterno”> ... </ProtoInstance> ... Scripting • Uno script: – E’ un nodo facente parte dello scene graph – Contiene un “comportamento” definito attraverso funzioni scritte in ECMAScript – Può generare eventi • L’utilizzo di scripts permette di: – Programmare comportamenti più evoluti rispetto a quelli ottenibili tramite i nodi predefiniti (ex: pulsanti a più stati) – Definire matematicamente delle traiettore (ex: parabole, ellissi) – Automatizzare comportamenti (ex: creazione di un gran numero di istanze di un oggetto) – Convertire eventi (ex: rollOver button) – Modificare dinamicamente lo scene graph e il behaviour graph – ... Nodo Script <Script> ... Interfaccia costituita da campi <field name=“...” type=“...” accessType=“...” value=“...”/> <![CDATA[ ... ]]> </Script> Funzioni definite in ECMAScript <![CDATA[ ecmascript: ...lista funzioni... ]]> • Per gestire gli eventi in input occorre definire delle funzioni associate ai campi InputOnly o InputOutput • Per spedire degli eventi associati ai campi OutputOnly o InputOutput è sufficiente modificarne i valori all’interno delle funzioni ECMAScript (1) • Variabili – Linguaggio debolmente tipizzato – Tipi predefiniti: boolean, number, string, object, ... var a; a = 5; print(a); a = ‘Hello World!’ print(a); • Operatori – – – – Unari: ! (NOT), -, ++ (incremento), -- (decremento) Binari: +, - ,* ,/ ,% (modulo) Relazionali: <, >, <=, >=, ==, != Logici: && (AND), || (OR) • Strutture di controllo – Condizionali: if..else, switch – Iterative: for, while, do..while if (condition) { statements } else { statements } switch (expr) { case label: statements break; ... default: statements } for (expr1; epr2; expr3) { statements } while (condition) { statements } do { statements } while (condition) ECMAScript (2) • Funzioni – Non occorre specificare il tipo del valore restituito function myFunction(parameters) { ... } function somma(a,b) { return (a+b); } • Oggetto Array – Array dinamico costituito da elementi di qualsiasi tipo – Proprietà: length var a = new Array(); a[0] = 4; a[1] = ‘BlaBlaBla’; for (i=0; i < a.length; i++) { print(a[i]); } • Oggetto Math – Oggetto globale: non va istanziato tramite new – Costanti matematiche (proprietà): PI, E, SQRT2, LN2, LN10, ... – Funzioni matematiche (metodi): max(), min(), pow(), sqrt(), sin(), cos(), tan(), random() ECMAScript (3) • ECMAScript e nodi “Script” (X3D) – I tipi di dato booleani (SFBool), scalari (SFFloat, SFDouble, SFInt32, SFTime) e le stringhe (SFString) sono rappresentati attraverso i tipi base di ECMAScript (boolean, number, string) – I restanti tipi di dato “singoli” (SFColor, SFVec3f, ...) sono rappresentati attraverso degli oggetti • Ex: verde = new SFColor(0,1,0); • Ex: origine = new SFVec3f(0,0,0); – I tipi di dato “multipli” (MFInt32, MFVec3f, ...) sono rappresentati attraverso degli array • Ex: v = new MFInt32(2,5,1,-3); ECMAScript (4) • ECMAScript e nodi “Script” (X3D) – Per ogni campo di tipo InputOnly e InputOutput bisogna definire una funzione con lo stesso nome – E’ possibile definire due funzioni richiamate quando lo script è caricato o eliminato dal Browser: initialize() e shutdown() – La funzione print() permette di scrivere sulla console (debug) – Tramite l’oggetto globale Browser() è possibile modificare dinamicamente lo scene graph e il behaviour graph • Browser.createFromVRMLString(stringVRML) • Browser.addRoute(fromNode,fromField,toNode,toField) • Browser.deleteRoute(fromNode,fromField,toNode,toField) Script: esempio • Movimento sinusoidale di un cubo TimeSensor Script Transform cycleInterval set_fraction translation loop value_changed ·· · fraction_changed ·· · ·· · <Script DEF="MyScript"> <field name="set_fraction" type="SFFloat" accessType="inputOnly"/> <field name="value_changed" type="SFVec3f" accessType="outputOnly"/> <![CDATA[ ecmascript: function initialize() { print(‘Script inizializzato!’); } set_fraction(value) oppure set_fraction(value, timestamp) function set_fraction(value) { value_changed[0] = -3 + 6 * value; value_changed[1] = 2 * Math.Sin(value*6.28); value_changed[2] = 0; } ]]> </Script> Esempio (1) • Creazione di un prototipo di un pulsante a due stati – Lo stato del pulsante deve essere rappresentato dal suo colore (variazione del diffuseColor) – Il pulsante deve illuminarsi al passaggio del mouse (variazione dell’emissiveColor) – Il pulsante può avere forme diverse Button Transform ·· · Shape ·· · geometry TouchSensor Appearance ·· · isOver Script touchTime isOver ·· · touchTime diffuseColor emissiveColor Sphere ·· · Material diffuseColor emissiveColor ·· · ·· · Esempio (2) • Dichiarazione del prototipo • Connessione del campo geometry del prototipo <ProtoDeclare name=“Button”> <ProtoInterface> <field name="geometry" type="SFNode" accessType="inputOnly"> <Sphere/> </field> </ProtoInterface> Campo costituito da un nodo: <ProtoBody> il valore di default non viene ... specificato nell’attributo value, </ProtoBody> ma attraverso un tag innestato </ProtoDeclare> ... <Shape> <Appearance> <Material DEF=“materialButton”> </Appearance> <IS> <connect nodeField="geometry" protoField="geometry"/> </IS> </Shape> ... Esempio (3) <Script DEF="MyScript"> • Nodo Script <field name="isOver" type="SFBool" accessType="inputOnly"/> <field name="touchTime" type="SFTime" accessType="inputOnly"/> <field name="state" type="SFBool" accessType="inputOutput"/> <field name="emissiveColor_changed" type="SFColor" accessType="outputOnly"/> <field name="diffuseColor_changed" type="SFColor" accessType="outputOnly"/> <![CDATA[ ecmascript: ... ]]> </Script> Esempio (4) function state(value) {} • Funzioni definite all’interno del nodo Script function touchTime(value) { state = !state; if (state) { diffuseColor_changed = new SFColor(1, 0.2, 0.5); } else { diffuseColor_changed = new SFColor(0.2, 1, 0.5); } } function isOver(value) { if (value) { emissiveColor_changed = new SFColor(0.2, 0.2, 0.2); } else { emissiveColor_changed = new SFColor(0, 0, 0); } } Esempio (5) • Routing degli eventi all’interno di <ProtoBody> • Creazione di istanze del prototipo <ROUTE fromNode="Sensor" fromField="isOver" toNode="MyScript" toField="isOver"/> <ROUTE fromNode="Sensor" fromField="touchTime" toNode="MyScript" toField="touchTime"/> <ROUTE fromNode="MyScript" fromField="emissiveColor_changed" toNode="MaterialButton" toField="set_emissiveColor"/> <ROUTE fromNode="MyScript" fromField="diffuseColor_changed" toNode="MaterialButton" toField="set_diffuseColor"/> <ProtoInstance name="button"> <fieldValue name="geometry"> <Box/> </fieldValue> </ProtoInstance> Campo costituito da un nodo: il valore di default non viene specificato nell’attributo value, ma attraverso un tag innestato ProximitySensor • Rileva quando l’avatar entra (ed esce) in una zona rappresentata da un parallelepipedo • Quando l’utente si trova all’interno del parallelepipedo il sensore traccia la sua posizione e orientazione ProximitySensor : X3DEnvironmentalSensorNode { SFBool [in,out] enabled TRUE SFVec3f [in,out] center 000 SFVec3f [in,out] size 000 SFTime [out] enterTime SFTime [out] exitTime SFRotation [out] orientation_changed SFVec3f [out] position_changed SFBool [out] isActive ... } Permette di far iniziare (terminare) delle animazioni quando l’avatar si avvicina (allontana) ad un particolare oggetto Permette di muovere un oggetto in modo da replicare il movimento dell’avatar (utile per la creazione di interfacce) Esempio (1) • Animazione di un parallelepipedo in base alla vicinanza dell’avatar ProximitySensor enterTime TimeSensor PositionInterp. startTime set_fraction fraction_changed value_changed Transform ·· · ·· · scale ·· · exitTime ·· · TimeSensor PositionInterp. startTime set_fraction fraction_changed value_changed ·· · ·· · Shape ·· · Appearance ·· · Box ·· · Material ·· · Esempio (2) • Posizionamento di un oggetto in base alla posizione dell’avatar ProximitySensor Transform position_changed translation orientation_changed rotation ·· · ·· · La grandezza del sensore (campo size) deve essere tale da racchiudere tutta la scena virtuale Occorre disabilitare il rilevamento delle collisioni: <Collision collide=“false”> ... </Collision> Sistema di coordinate locali corrispondente alla posizione dell’avatar Collision ·· · Transform ·· · VisibilitySensor • Rileva quando un’area rappresentata da un parallelepipedo è visibile all’avatar VisibilitySensor : X3DEnvironmentalSensorNode { SFBool [in,out] enabled TRUE SFVec3f [in,out] center 000 SFVec3f [in,out] size 000 SFTime [out] enterTime SFTime [out] exitTime ... } Permette di far iniziare (terminare) delle animazioni quando l’avatar può o meno vedere un particolare oggetto (utile per attirare l’attenzione dell’utente)