Sistemi multimediali Massimiliano Piscozzi – [email protected] Definizione di nuovi nodi • E’ possibile definire nuovi tipi di nodo a partire da nodi esistenti (built-in o definiti dall’utente) Zero o più campi: PROTOTYPE Uno o più nodi: – Nome – Nome – Interfaccia – Corpo – Deve esistere un nodo radice – Tipo – Tipo di accesso (input/output per il routing degli eventi) – Valori di default – Il nodo radice determina quando il prototipo può essere istanziato A differenza di USE il prototipo può possedere dei campi il cui valore può variare per ogni istanza Esempio di prototipo • Prototipo “Colonna” caratterizzato da un colore e un raggio Colonna Transform children ·· · Shape geometry appearance ·· · raggio colore Cylinder Appearance radius material ·· · ·· · Material diffuseColor ·· · Prototipi (1) • Codifica in XML della dichiarazione di prototipi <ProtoDeclare name=“nomePrototipo”> <ProtoInterface> ... </ProtoInteface> <ProtoBody> ... </ProtoBody> </ProtoDeclare> Interfaccia costituita da campi <field name=“...” type=“...” accessType=“...” value=“...”/> Corpo costituito da dichiarazioni di nodi - Il collegamento tra i campi dei nodi e i campi dell’interfaccia avviene attraverso i tag <IS> e <connect> <unNodo> <IS> <connect nodeField=“...” protoField=“...”/> </IS> </unNodo> Prototipi (2) • Codifica in XML della creazione di istanze di un prototipo <ProtoInstance name=“nomePrototipo”> ... </ProtoInstance> Lista dei valori dei campi <fieldValue name=“...” value=“...”/> Se il valore di un campo non viene specificato esso assume il valore di default (specificato nell’interfaccia o in base alle specifiche X3D) Prototipi: esempio <ProtoDeclare name="Colonna"> <ProtoInterface> <field name="raggio" accessType="inputOutput" type="SFFloat" value="0.3"/> <field name="colore" accessType="inputOutput" type="SFColor" value="0.8 0.7 0.3"/> </ProtoInterface> <ProtoBody> <Transform translation="0 1.25 0"> <Shape> <Cylinder height="2.5"> <IS> <connect nodeField="radius" protoField="raggio"/> </IS> </Cylinder> <Appearance> <Material diffuseColor="0.8 0.7 0.3"> <IS> <connect nodeField="diffuseColor" protoField="colore"/> </IS> </Material> </Appearance> </Shape> </Transform> </ProtoBody> </ProtoDeclare> <ProtoInstance name="Colonna"/> <ProtoInstance name="Colonna"> <fieldValue name="colore" value="0 0 2"/> </ProtoInstance> <ProtoInstance name="Colonna"> <fieldValue name="raggio" value="0.1"/> </ProtoInstance> <ProtoInstance name="Colonna"> <fieldValue name="colore" value="0 0 2"/> <fieldValue name="raggio" value="0.1"/> </ProtoInstance> 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 emissiveColor diffusionColor 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