Como Por Arte De Magia

Hasta ahora en este tutorial hemos cubierto lo básico en programación ECMAscript y cómo se relacciona con VRML. También hemos echado un ojo a los objetos preconstruidos VRML incluidos como standard en los navegadores. Ahora, profundizaremos en un objeto concreto, y veremos como llevar todo esto a su máxima potencia. Es el objeto Browser.

El objeto Browser

El objeto Browser es un objeto ECMAScript que se proporciona dentro del intérprete de ECMAScript de tu navegador VRML. Todos los navegadores deben proporcionarlo, pero esto es sólo un deben. Desgraciadamente, las cuestiones de scripts con respecto al VRML son muy inconsistentes, y generalmente no todos los navegadores soportan lo que deberían. Ésto es una auténtica pena, dado que éste es el aspecto más poderoso del VRML. En cualquier caso, el objeto Browser es un objeto preconstuido por el navegador, como estaba diciendo. Es un objeto estático, lo que significa que no necesitas crear una variable para comenzar a utilizar sus métodos y propiedades. Para usar un método del objeto Browser, debes usar, sencillamente, la sintaxis Browser.metodo (). Esto ejecutará el método que sea del objeto Browser. Este tiene muchas utilidades. Es una fuente de información, y también proporciona varias maneras de cambiar el mundo que estás viendo. En primer lugar, vamos a ver cómo obtener información del navegador del cliente.

Antes de todo esto, sin embargo, recordemos un par de cosas. Recuerdas que el nodo Script tiene dos campos llamado directOutput y mustEvaluate ¿no?. Éstos pueden ser bastante importantes al usar la interface del Browser, por lo que te recordaré levemente lo que hacen.

Vale, entonces vamos allá...

La Tecnología de la información

Bien. Lo primero que vamos a hacer es ver los métodos que permiten recoger información del usuario. Voy a enumerarlos, junto con una descripción corta de cada uno...

Browser.getName ()
Esta función devuelve una cadena de caracteres que contiene el nombre del navegador, por ejemplo "Cosmo Player" Devuelve una cadena vacía si la información no está disponible.
Browser.getVersion ()
Esto devuelve una cadena que contiene la información de la versión del navegador, por ejemplo "2.1." También devuelve una cadena vacía si la información no está disponible.
Browser.getWorldURL ()
Esto, sorprendentemente, devuelve la URL del mundo cargado como una cadena de caracteres.
Browser.getCurrentFrameRate ()
Esto devuelve un valor numérico igual al fps (frames per second, o lo que es lo mismo, "fotogramas por segundo", como en una película).
Browser.getCurrentSpeed ()
Esta función devuelve otro valor numérico, igual a la velocidad de movimiento del usuario en metros por segundo, siendo relativo a las coordenadas actuales del ViewPoint.
Todos son bastante simples, realmente. He hecho un ejemplo rápido dónde puedes ver todo esto en acción, junto con él el código fuente. No hay nada allí que no puedas entender, ya que ahora estamos bastante familiarizados con la materia. El TimeSensor sólo proporciona actualizaciones regulares del texto que devuelve la información.

Puedes preguntarte para qué se usa realmente toda esta información. Bueno, puedes preparar scripts que funcionen en navegadores particulares, o tener versiones diferentes del mismo script según el navegador, o puedes proporcionar un arreglo de nivel de detalle dinámico dónde tu mundo se vuelve menos detallado si la proporción de fps cae demasiado.

Fuera del aire delgado...

Ahora cubriremos material más complejo, claro que mucho más útil también. Esto implica principalmente el cambio del mundo actual, de una manera u otra. De nuevo, cubriremos estas funciones una por una.

Browser.setDescription("cadena de descripción") - necesita mustEvaluate TRUE
Esta función cambia la descripción actual del mundo (determinado en el nodo WorldInfo) por la cadena pasada como parámetro. bonito y simple.
Browser.loadURL(MFString url, MFString parameter) - necesita mustEvaluate TRUE
Esto carga un nuevo mundo de un archivo distinto. El primer parámetro del método es una lista de MFString de URLs que el navegador tratará de cargar. Si uno falla, probará con el siguiente. El siguiete parámetro proporciona parámetros extras, como una ventana destinataria para el mundo (equivalente a TARGET), y así sucesivamente. Estos parámetros son exactamente iguales que los que usarías para el nodo Anchor. De hecho, este método trabaja de la misma manera que el nodo Anchor. Si el nuevo mundo está cargado en la misma ventana, se cerrará el mundo actual y se reemplazará por el nuevo.
Browser.replaceWorld(MFNode nodos) - necesita mustEvaluate TRUE
Esto reemplaza el todo del mundo actualmente cargado con los nodos pasados en el objeto MFNode. Este método nunca devolverá nada, dado que el script que lo ejecuta será reemplazado por el nuevo mundo cargado en el navegador.

Como puedes ver, estos métodos también son bastante básicos, no hacen nada realmente asombroso. ¡Ahora, sin embargo, nos meteremos en la parte que nos permite crear VRML dinámicamente de la nada!

createVrmlFromX ()

Hay dos funciones que permiten crear nuevos fragmentos de código VRML de la nada, conocidas comunmente como las funciones createVrmlFromX. Estos son:

Como sugiere su nombre, este método permite crear VRML a parir de una cadena de texto o de un archivo. Ambos métodos crean un nuevo objeto MFNode que puedes agregar a un nodo Group a través de su eventIn addChildren. Todos los nodos Group tienen este eventIn, por lo que puedes agregar nodos children en el sitio que más te guste.

Ambos métodos funcionan de manera ligeramente distinta, como puedes ver por sus descripciones. Por qué es así, es algo que no sé. Quizás pensabas que funcionarían de la misma manera, pero no es así.createVrmlFromString toma una cadena como parámetro y devuelve un objeto MFNode que contiene el VRML de la cadena. Esta cadena debe ser VRML válido, y es completamente autónomo. Esto significa no puedes emplear USE de cosas cuyo DEF se encuentra fuera de esta cadena de texto, y lo mismo digo para las definiciones de PROTO. Puedes, sin embargo, incluir Routes en la cadena. Esencialmente, la cadena sería como un archivo separado, y como tal seguiría todas sus normas a excepción de la cabecera. Un ejemplo rápido de esto; Supongamos que estamos dentro de un script que tiene un MFNode eventOut llamado newChildren que va al evento addChildren de algun Group

newVRML =  'Shape {';
newVRML += '   appearance Appearance {';
newVRML += '      material Material {';
newVRML += '         diffuseColor 1 0 0';
newVRML += '      }';
newVRML += '   }';
newVRML += '   geometry Box {';
newVRML += '   }';
newVRML += '}';
newChildren = Browser.createVrmlFromString(newVRML);

Esto crea la cadena de texto que vamos a usar (No tienes por qué indentar las líneas, yo solo lo hago por que así me resulta más facil de leer), y entonces crea un nuevo MFNode a partir suyo. Este MFNode se asigna inmediatamente al eventOut newChildren, y se envía al nodo Group. No se reemplazan los children actuales del Group, el nuevo simplemente se agrega a ellos.

Sencillo por el momento, ¿no es así? El otro método de creación, sin embargo, es un poco más complejo. createVrmlFromString () trabaja de una manera muy simple y flexible. Por alguna razón, createVrmlFromURL () es más complejo y menos flexible. Con este método, creas un MFString que contiene una lista de URLs como primer parámetro, que el navegador tratará de cargar sucesivamente. Estos archivos deben contener VRML totalmente válido, obedeciendo todas las reglas normales. El script lee los nodos que hay dentro del archivo y éstos se convierten en los nuevos nodos del archivo original. Hasta aquí, bien, lo malo viene cuando tratas de envíar el nuevo MFNode a un nodo Group. Con un createVrmlFromString obtienes un objeto MFNode simple que puedes manipular como te de la gana, asignando variables como partes de esa cadena con operaciones simples de concatenación. createVrmlFromURL no devuelve nada. En cambio, manda al MFNode solo, desde dentro de la función. Esto es para lo que valen los otros dos parámetros. El primero es una referencia al nodo al que quieres agregarle nos nuevos, y el segundo es el nombre de un eventIn MFNode que recibirán los nuevos nodos. Creo que un ejemplo es lo mejor.

DEF GROUP Group {
}

Script {
   field SFNode group USE GROUP
   url "javascript:
      function initialize() {
         urlString = new MFString('cone.wrl');
         Browser.createVrmlFromURL(urlString,group,'addChildren');
      }
   "
}

Como ves, los parámetros son el nombre del archivo a usar (en un MFString), la referencia del SFNode, y el nombre del eventIn. La función hace el resto, introduciendo los nuevos nodos en el eventIn del addChildren de group. No estoy bastante seguro por qué es de esta manera, pero estoy seguro de que el tipo que lo hizo tenía sus motivos. ¡Te los contaré si los averiguo! Como alternativa, puedes ignorar el createVRMLFromURL() y usar createVRMLFromString() con un nodo inLine que contenga el archivo que quieras cargar, así:

newVRML = 'Inline { url "cone.wrl" }';
newChildren = Browser.createVrmlFromString(newVRML);

Esto ofrece la funcionalidad de createVRMLFromURL() con el modo de empleo típico del CreateVRMLFromString(). Gracias a Eyal Teler por esta sugerencia.

Volviendo al trabajo, echa un ojo a este ejemplo, junto con el código. Como puedes ver, tenemos un bonito juguete con el que poder jugar y hacer bonitas composiciones. Pulsa los botones del fondo para crear un nuevo objeto. Puede arrastrar ese objeto dentro del área gris, construyendo modelos. Esto se hace creando un nuevo PlaneSensor junto con cada nueva Shape, y proporcionando todas las Route apropiadas dentro del string o archivo. Creamos el cubo y esfera de las cadenas de caracteres, mientras que el cono se crea directamente de un archivo VRML. Echa una mirada al código fuente de cone.wrl. Esto explicará cómo se usan estas útiles funciones, creo.

De A a B

Hay dos métodos más en el objeto Browser que sirven para la creación dinámica de mundos; son los métodos addRoute() y deleteRoute (). Curiosamente, agregan o anulan Rutas entre nodos. Son así...

Estos métodos tienen parámetros muy similares a los de createVrmlFromUrl(). El primero y el tercero son referencias SFNode que tienes que declarar previamente de algún modo, y el segundo y cuarto son cadenas de caracteres que identifican los eventos para unirlos. Son bastante simples en su concepto, la dificultad viene cuando intentas obtener las referencias los SFNode. Esto podría isignificar revisar el archivo entero buscando el nodo correcto que quieres modificar. Pueden ser, sin embargo, vitales si quieres añadir nuevas interfaces de usuario, o simplemente comunicar nodos dinámicamente. Por ejemplo, puedes crear nodos con createVrmlFromX (), y entonces unirlos al mundo con el método addRoute().

Vaporizado

Bueno, creo que está explicado todo, o al menos, yo lo veo así. Si quieres más información sobre la interface de scripts del Browser, te sugiero que miras en la sección 4.12.10 de la especificación. Explica todos los conceptos del objeto Browser. La sección C.6.3 explica lo cubierto de este objeto en ECMAScript. También en la página de las especificaciones tienes una referencia rápida de los objetos, propiedades y parámetros de EcmaScript, que pueden resultarte muy útiles.

Bien, creo que esto es todo lo que puedo enseñarte sobre el scripting en ECMAScript, realmente, y como tal es el fin de esta parte del tutorial. En la próxima parte, voy a revisar algo de Java que usted puedes usar para realizar tareas de script más complejas y estructuradas. Sin embargo, es otro lenguaje de programación entero. Sin embargo, creo que estás preparado, así que, ¡nos vemos allí!