Algoritmo 2.1. Procedimiento para desarrollar un programa en Java3D.
Aunque no profundiza demasiado, este algoritmo sí refleja el concepto fundamental de la programación en Java3D: lo más importante consiste en la creación de las distintas ramas del grafo de escena.
Los sistemas de coordenadas en Java3D son, por defecto, "diestros", de tal forma que la parte positiva del eje de ordenadas es el sentido ascendente de la gravedad, la parte positiva del eje de abscisas es horizontal hacia la derecha y la parte positiva del eje Z está dirigido hacia el observador.
La unidad utilizada por default son los metros. En la Figura 2.1 se puede ver gráficamente esta distribución.
Figura 2.2. Sistema de coordenadas Java3D.
2.5.1 Coordenadas De Alta Resolución.
La representación de las coordenadas con valores en punto flotante de doble precisión, de simple precisión o incluso valores de menor precisión es suficiente para representar y mostrar escenas en tres dimensiones de gran calidad. Si nos alejamos doscientos kilómetros desde el origen de coordenadas utilizando una precisión sencilla de punto flotante, los puntos representables aparecen de alguna forma discretizados. Si lo que se pretende es llegar a distancias muy pequeñas, muy cerca del origen, aparece el mismo problema. Las coordenadas de alta resolución de Java3D constan de 3 números de 256 bits de punto fijo, uno para cada eje. El punto fijo se encuentra en el bit 128 y el valor 1.0 se corresponde exactamente con un metro. Este sistema de coordenadas es suficiente para describir un universo tan enorme como varios cientos de billones de años luz o como para definir objetos tan pequeños como un protón.
En la Tabla 2.2 se representan cuántos bits son necesarios por encima o por debajo del punto fijo para representar el rango que nos interese.
2n metros | Unidades |
87.29 | Universo (20 billones de años luz). |
69.68 | Galaxia (100,000 años luz). |
53.07 | Año luz. |
43.43 | Diámetro del sistema solar. |
23.60 | Diámetro de la Tierra. |
10.65 | Milla. |
9.97 | Kilómetro. |
0 | Metro. |
– 19.93 | Micra. |
– 115.57 | Longitud de Plank. |
Tabla 2.2. Escalas de las coordenadas de alta resolución
Los números de punto fijo de 256 bits proporcionan también la ventaja de ser capaces de representar directamente, de forma exacta, cualquier valor de precisión sencilla de punto flotante.
Las coordenadas de alta resolución de Java3D se utilizan sólo para incluir los sistemas de coordenadas tradicionales de punto flotante en un sistema de mucha mayor resolución. De esta forma se pueden crear universos de cualquier tamaño imaginable sin preocuparnos de la precisión.
Las coordenadas de alta resolución se representan como números de 256 bits con signo en complemento a dos. Aunque Java3D mantiene oculta la representación interna de las coordenadas de alta resolución, el usuario debe especificar dichas coordenadas usando matrices de enteros de 8 elementos. Java3D trata el entero en la posición 0 como los bits más significativos y el situado en la posición 7 como los menos significativos de la coordenada de alta resolución. El punto binario se localiza en el bit 128 o entre los enteros de las posiciones 3 y 4.
La forma en que los cargadores de ficheros manejan las coordenadas de alta resolución depende de cada cargador en concreto, ya que Java3D no define directamente ninguna semántica de carga de ficheros. Sin embargo, hay ciertas consideraciones que pueden indicarse.
Para universos pequeños (del orden de cientos de metros), un solo Locale con coordenadas de alta resolución ubicado en la posición (0.0, 0.0, 0.0) como el nodo raíz es suficiente. Un cargador puede construir automáticamente este nodo durante el proceso de carga ya que las coordenadas de alta resolución no necesitan tener una representación directa en el fichero externo.
Universos virtuales de mayor tamaño se suelen construir siguiendo la jerarquía habitual de directorios de cualquier disco duro, es decir, un universo virtual raíz que contiene la mayoría de las referencias a ficheros externos que incluyen universos virtuales. En este caso, el objeto que contiene la referencia al fichero define la localización de los datos que se tienen que leer en el universo virtual en cuestión.
El contenido de los ficheros de datos debe enlazarse al nodo mientras se leen, heredando así las coordenadas de alta resolución del objeto como el nuevo universo virtual origen del grafo de escena embebido. Si ese mismo grafo de escena contiene a su vez coordenadas de alta resolución, tendrán que ser trasladadas en la cantidad que indiquen esas coordenadas y entonces añadirlas al universo virtual mayor como nuevas coordenadas de alta resolución, con todo su contenido por debajo de las mismas.
A la hora de tratar con objetos que se mueven con gran amplitud, hay que considerar que tendrán que cambiar periódicamente su Locale padre de forma que se adapte más adecuadamente a sus nuevas coordenadas. Si no existiera ningún Locale suficientemente apropiado, la aplicación podría crear uno nuevo.
Java3D proporciona tres modos diferentes de renderización: modo inmediato, modo retenido y modo compilado–retenido. Están ordenados de forma creciente según el grado de libertad para optimizar la ejecución de la aplicación. Los dos últimos métodos son los más usados debido a las características de la gran mayoría de las aplicaciones.
Modo inmediato:
El modo inmediato no permite mucha optimización en cuanto al grafo de escena se refiere. En este modo, el nivel de abstracción de Java3D es bastante elevado y acelera el renderizado inmediato de los objetos. Una aplicación debe proporcionar un método de dibujado con un conjunto completo de puntos, líneas o triángulos que son posteriormente renderizados por el generador de imágenes de alta velocidad de Java3D. Por supuesto, la aplicación puede construir estas listas de puntos, líneas o triángulos en cualquier forma que elija. Este modo de renderización permite la mayor flexibilidad y libertad a costa de una reducción en el rendimiento.
En este modo Java3D no dispone de información acerca de los objetos gráficos de la composición. A causa de esto apenas se pueden realizar optimizaciones en la aplicación del programador. Este puede utilizar o no la estructura de grafo de escena heredada del diseño de Java3D; puede elegir entre dibujar la escena directamente o definir el grafo de escena. El modo inmediato puede utilizarse tanto de forma independiente como combinado con los otros dos métodos existentes.
Modo Retenido:
El modo retenido requiere que la aplicación construya un grafo de escena y que especifique qué elementos de dicho grafo de escena pueden cambiar durante la renderización. El grafo de escena describe los objetos del universo virtual, la distribución de dichos objetos y cómo la aplicación los anima. Este modo proporciona gran parte de la flexibilidad del modo inmediato a la vez que un incremento sustancial en la velocidad de renderización. Todos los objetos definidos en el grafo de escena son accesibles y manipulables, como lo es el grafo de escena en sí mismo. El programador puede crear grafos de escena y añadir o eliminar nodos del mismo rápidamente y ver inmediatamente los efectos de dichos cambios.
Este modo permite construir objetos, insertarlos en una base de datos, componer objetos y añadir comportamientos a los objetos. Java3D sabe que el programador ha definido objetos, sabe cómo ha combinado dichos objetos para crear objetos compuestos o grafos de escena y también conoce qué comportamientos o acciones se han añadido a los objetos.
Todo este conocimiento permite a Java3D realizar diferentes optimizaciones; puede construir estructuras de datos especiales que contengan la geometría de los objetos de tal forma que se aumente la velocidad a la que puede renderizarlo. Puede también compilar los comportamientos de los objetos para que se ejecuten a una mayor velocidad cuando sean invocados.
Modo compilado–retenido:
Este modo, al igual que el retenido, requiere que la aplicación construya un grafo de escena y que especifique qué elementos pueden cambiar durante la renderización. Además, la aplicación puede compilar parte o todos los subgrafos que forman el grafo de escena completo. Java3D compila estos grafos en un formato interno. La representación compilada del grafo de escena apenas se parece a la estructura de árbol original proporcionada por la aplicación, sin embargo, es funcionalmente equivalente. Este modo es el que proporciona el mejor rendimiento, permite que la API de Java3D realice una serie de complicadas optimizaciones. Un programador puede solicitar que Java3D compile un objeto o un grafo de escena. Un objeto o un grafo de escena compilado consiste en cualesquiera estructuras que Java3D quiera crear para asegurar que los objetos o grafos de escena se renderizan a la máxima velocidad posible. Como Java3D sabe que la mayor parte de los objetos o grafos de escena compilados no van a cambiar, puede realizar múltiples optimizaciones como pueden ser la fusión de múltiples objetos en un objeto conceptual, modificar un objeto para disponer de él en una geometría comprimida o incluso romper un objeto en diferentes partes y reensamblarlo en nuevos "objetos conceptuales".
Todos los programas de Java3D se forman, al menos parcialmente, uniendo distintos elementos de la jerarquía de clases de Java3D. Esa gran colección de objetos describe un universo virtual, que será posteriormente renderizado. La API define más de cien clases incluidas en el paquete javax.media.j3d. Estas clases son las normalmente denominadas como clases núcleo de Java3D.
Hay cientos de campos y métodos en dichas clases, sin embargo, un universo virtual sencillo que contenga algo de animación se puede construir con sólo unas pocas de ellas.
Además del paquete núcleo, a la hora de desarrollar programas en Java3D se utilizan otros paquetes. Uno de ellos es el paquete normalmente conocido como de utilidad (com.sun.j3d.utils).El paquete núcleo incluye sólo las clases de nivel más bajo necesarias para programar en Java3D.
Las clases de utilidad son extensiones muy prácticas y potentes. Como era de esperar, la utilización de clases de utilidad reduce significativamente el número de líneas de código a la hora de programar.
Por último, también se utilizan clases del paquete java.awt donde se define el Abstract Windowing Toolkit (AWT) que proporciona una ventana para visualizar la renderización y clases javax.vecmath que define clases matemáticas de vectores para puntos, vectores, matrices y otros objetos matemáticos.
3. El Primer Ejemplo: HelloJava3D.java
En este capítulo vamos a iniciar con un ejemplo bastante sencillo que nos ofrecerá un panorama general de las aplicaciones Java3D siguiendo el Algoritmo 2.1 (2.4. Construcción De Un Árbol), simplemente crearemos un cubo en un applet que estará embebido en un Frame. El código se mostrará por partes y al finalizar lo mostraremos completo.
El ejemplo HelloJava3D.java es una clase definida para extender la clase Applet. La clase principal de un programa Java3D normalmente define un método para construir la rama de contenido gráfico. En el ejemplo HelloJava3D.java dicho método está definido como createSceneGraph(). Los pasos del algoritmo de la sección 2.4 se implementan en el constructor. El paso 1, crear un objeto Canvas3D, se observa en la línea 4 del listado 3.1. El paso 2, crear un objeto SimpleUniverse, se hace en la línea 11. El paso 2a, personalizar el objeto SimpleUniverse, se realiza en la línea 15. El paso 3, construir la rama de contenido, se encuentra en la llamada al método createSceneGraph(). El paso 4, compilar la rama de contenido gráfico, se hace en la línea 8. Finalmente el paso 5, insertar la rama de contenido gráfico en el objeto Locale del SimpleUniverse, se completa en la línea 16.
1. public class HelloJava3D extends Applet {
2. public HelloJava3D() {
3. setLayout(new BorderLayout());
4. Canvas3D canvas3D = new Canvas3D(null);
5. add("Center", canvas3D);
6.
7. BranchGroup scene = createSceneGraph();
8. scene.compile();
9.
10. // SimpleUniverse es una clase Convenience Utility
11. SimpleUniverse simpleU = new SimpleUniverse(canvas3D);
12.
13. // Mueve ViewPlatform atrás un bit, así el objeto de 14. //la escena podrá ser visto .
15. simpleU.getViewingPlatform().setNominalViewingTransform();
16.
17. simpleU.addBranchGraph(scene);
18. } // Fin de HelloJava3D (constructor)
Listado 3.1. Fragmento de HelloJava3D.java.
El paso 3 del algoritmo es crear la rama de contenido gráfico. Esta rama se crea en el Listado 3.2. Probablemente sea la rama de contenido gráfico más sencilla posible. Contiene un objeto gráfico estático, un ColorCube. Éste está localizado en el origen del sistema de coordenadas del universo virtual. Con la localización y orientación dadas de la dirección de la vista del cubo, el cubo aparece como un rectángulo cuando es renderizado (ver más adelante).
1. public BranchGroup createSceneGraph() {
2. // Crea la raíz del grafo branch
3. BranchGroup objRoot = new BranchGroup();
4. // Crea un nodo hoja de forma simple, añadiéndolo al grafo de escena
6. // ColorCube es una clase Convenience Utility
7. objRoot.addChild(new ColorCube(0.4));
8.
9. return objRoot;
10. } // Fin del método createSceneGraph de HelloJava3D
Listado 3.2. Método createSceneGraph de la clase HelloJava3D.
La clase HelloJava3D está derivada de Applet pero el programa puede ejecutarse como una aplicación con el uso de la clase MainFrame. Applet se usa como clase base para hacer más fácil la escritura de un programa Java3D que se ejecuta en una ventana.
MainFrame proporciona un marco AWT (ventana) para un applet permitiendo que se ejecute como una aplicación. El tamaño de la ventana resultante se especifica en la construcción de la clase MainFrame. El Listado 3.3 muestra el uso de MainFrame.
MainFrame crea un applet en una aplicación. Una clase derivada de Applet podría tener un método main() que llame al constructor MainFrame. Éste extiende java.awt.Frame e implementa java.lang.Runnable, java.applet.AppletStub, y java.applet.AppletContext.
MainFrame(java.applet.Applet applet, int width, int height) crea un objeto MainFrame que ejecuta un applet como una aplicación. Sus parámetros son los siguientes:
- applet: El constructor de una clase derivada de Applet. MainFrame proporciona un marco AWT para este applet.
- width: La anchura de la ventana en pixeles.
- height: La altura de la ventana en pixeles.
1. // Permite que el programa sea ejecutado tanto como una aplicación o como si
2. // fuera un applet
3.
4. public static void main(String[] args) {
5. Frame frame = new MainFrame(new HelloJava3D(), 256, 256);
6. } // Fin de main
7.
8.} // Fin de la clase HelloJava3D
Listado 3.3. Programa principal para HelloJava3D.
Los tres listados de código anteriores (3.1, 3.2, y 3.3) forman un programa Java3D completo cuando se usan las sentencias import adecuadas, en el Listado 3.4 podemos ver las necesarias para compilar la clase HelloJava3D. Las clases más comúnmente usadas en Java3D se encuentran en los paquetes javax.media.j3d, o javax.vecmath. En este ejemplo, sólo la clase de utilidad ColorCube se encuentra en el paquete com.sun.j3d.utils.geometry.
Consecuentemente, la mayoría de los programas Java3D tienen las sentencias import del Listado 3.4 con la excepción de ColorCube.
1. import java.applet.Applet;
2. import java.awt.BorderLayout;
3. import java.awt.Frame;
4. import java.awt.event.*;
5. import com.sun.j3d.utils.applet.MainFrame;
6. import com.sun.j3d.utils.universe.*;
7. import com.sun.j3d.utils.geometry.ColorCube;
8. import javax.media.j3d.*;
9. import javax.vecmath.*;
Listado 3.4. Sentencias Import para HelloJava3D.
En el programa de ejemplo HelloJava3D.java, sólo se situó un objeto gráfico en una única localización. En la Figura 3.1 podemos ver el escenario gráfico resultante.
Figura 3.1. Escenario gráfico de HelloJava3D.
Compilando y ejecutando el programa obtenemos la imagen de la Figura 3.2.
Figura 3.2. El programa HelloJava3D.java.
La siguiente sección presenta cada una de las clases usadas en el programa.
3.2 Clases Java3D Usadas en HelloJava3D.
Para añadir un poco de entendimiento del API Java3D y el ejemplo HelloJava3D aquí presentamos una sinopsis de las clases usadas en el programa de ejemplo.
Clase BranchGroup:
Los objetos de este tipo se usan para formar escenarios gráficos, son la raíz de los subgráficos, son los únicos que pueden ser hijos de los objetos Locale, pueden tener varios hijos, sus hijos pueden ser otros objetos Group o Leaf. El constructor por defecto es BranchGroup(), estos ejemplares de BranchGroup sirven como raíz para las ramas del escenario gráfico; y son los únicos que pueden insertarse en un conjunto de objetos Locale.
Clase Canvas3D:
La clase Canvas3D deriva de la clase Canvas del AWT. Al menos un objeto Canvas3D debe ser referenciado en la rama de vista gráfica del escenario gráfico. Su constructor es Canvas3D(GraphicsConfiguration graphicsconfiguration), éste construye e inicializa un nuevo objeto Canvas3D que el Java3D puede renderizar dando un objeto GraphicsConfiguration válido.
Clase Transform3D:
Los objetos Transform3D representan transformaciones de geometrías 3D como una traslación o una rotación. Normalmente sólo se usan en la creación de un objeto TransformGroup. Primero, se construye el objeto Transform3D, posiblemente desde una combinación de objetos Transform3D. Luego se construye el objeto TransformGroup usando el objeto Transform3D. Un objeto de transformación generalizado se representa internamente como una matriz de 4×4 de punto flotante. La representación matemática es la mejor forma. Un objeto Transform3D no se usa en un escenario gráfico. Se usa para especificar la transformación de un objeto TransformGroup.
El constructor por defecto es Transform3D() el cual construye un objeto Transform3D que representa la matriz de identidad (no la transformación). Cuando se especifica una rotación, el ángulo se expresa en radianes. Una rotación completa es 2p radianes. Una forma de especificar ángulos es usar la constante Math.PI. Otra forma es especificar los valores directamente. Algunas aproximaciones son: 45º es 0.785, 90º es 1.57, y 180º es 3.14.
Lista Parcial de Métodos de Transform3D:
Transform3D es una de las pocas clases que no se usan directamente en un escenario gráfico. Las transformaciones representadas por objetos de esta clase se usan para crear objetos TransformGroup que si se usan en escenarios gráficos. Algunos de sus métodos son los siguientes:
- void rotX(double angle): Selecciona el valor de esta transformación a una rotación en contra del sentido del reloj sobre el eje X. El ángulo se especifica en radianes.
- void rotY(double angle): Selecciona el valor de esta transformación a una rotación en contra del sentido del reloj sobre el eje Y. El ángulo se especifica en radianes.
- void rotZ(double angle): Selecciona el valor de esta transformación a una rotación en contra del sentido del reloj sobre el eje Z. El ángulo se especifica en radianes.
Clase TransformGroup:
Como una subclase de la clase Group, los ejemplares de TransformGroup se usan en la creación de escenarios gráficos y tienen una colección de objetos nodos como hijos. Los objetos TransformGroup contienen transformaciones geométricas como traslaciones y rotaciones. La transformación normalmente se crea en un objeto Transform3D, que no es un objeto del escenario gráfico. Sus constructores son los siguientes:
- TransformGroup(): Construye e inicializa un TransformGroup usando una identidad de transformación.
- TransformGroup(Transform3D t1): Construye e inicializa un TransformGroup desde un objeto Transform3D pasado como argumento.
La transformación contenida en un objeto Transform3D se copia a un objeto TransformGroup o cuando se crea el TransformGroup, o usando el método setTransform().
El Método void setTransform(Transform3D t1) de TransformGroup selecciona el componente de transformación de este TransformGroup al valor de la transformación t1 pasada como argumento.
Clase Vector3f:
Es una clase matemática que se encuentra en el paquete javax.vecmath para especificar un vector usando tres valores de punto flotante. Los objetos Vector se usan frecuentemente para especificar traslaciones de geometrías. Los objetos Vector3f no se usan directamente en la construcción de un escenario gráfico. Se usan para especificar las traslaciones, superficies normales, u otras cosas. Sus constructores son los siguientes:
- Vector3f(): Construye e inicializa un Vector3f a (0,0,0).
- Vector3f(float x, float y, float z): Construye e inicializa un Vector3f desde las coordenadas X, Y, Z especificadas.
Clase ColorCube:
ColorCube es una clase de utilidad que se encuentra en el paquete com.sun.j3d.utils.geometry que define la geometría y colores de un cubo centrado en el origen y con diferentes colores en cada cara. El objeto ColorCube es un cubo que tiene 2 metros de arista. Si un cubo sin rotar se sitúa en el origen (como en HelloJava3D), se verá la cara roja desde la localización de visión nominal. Los otros colores son azules, magenta, amarillos, verdes y cian. Sus constructores los mencionamos en seguida:
- ColorCube(): Construye un cubo de color y tamaño por defecto. En este caso, una esquina está situada a un metro de cada uno de los ejes desde el origen, resultando un cubo que está centrado en el sistema de coordenadas y tiene 2 metros de alto, de ancho y de profundo.
- ColorCube(double scale): Construye un cubo de color escalado por el valor especificado. El tamaño por defecto es 2 metros de lado. El ColorCube resultante tiene esquinas en (scale, scale, scale) y (-scale, -scale, -scale).
3.3. Listado Completo de HelloJava3D.
Para finalizar, simplemente listamos el código completo.
- import java.applet.Applet;
- import java.awt.BorderLayout;
- import java.awt.Frame;
- import java.awt.event.*;
- import java.awt.GraphicsConfiguration;
- import com.sun.j3d.utils.applet.MainFrame;
- import com.sun.j3d.utils.universe.*;
- import com.sun.j3d.utils.geometry.ColorCube;
- import javax.media.j3d.*;
- import javax.vecmath.*;
- // HelloJava3D dibuja un único cubo que rota.
- public class HelloJava3D extends Applet {
- public HelloJava3D() {
- setLayout(new BorderLayout());
- GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
- Canvas3D canvas3D = new Canvas3D(config);
- add("Center", canvas3D);
- BranchGroup scene = createSceneGraph();
- // SimpleUniverse es una clase Convenience Utility
- SimpleUniverse simpleU = new SimpleUniverse(canvas3D);
- // Mueve ViewPlatform atrás un bit, así el objeto de la escena podrá ser visto
- simpleU.getViewingPlatform().setNominalViewingTransform();
- simpleU.addBranchGraph(scene);
- } // Fin de HelloJava3D (constructor)
- public BranchGroup createSceneGraph() {
- // Crea la raíz del grafo branch
- BranchGroup objRoot = new BranchGroup();
- objRoot.addChild(new ColorCube(0.4));
- return objRoot;
- } // Fin del método CreateSceneGraph de HelloJava3D
- // Permite que el programa sea ejecutado tanto como aplicación como un applet
- public static void main(String[] args) {
- Frame frame = new MainFrame(new HelloJava3D(), 256, 256);
- } // Fin de main (método de HelloJava3D)
- } // Fin de la clase HelloJava3D
Listado 3.5. Código completo de HelloJava3D.
4. Transformaciones.
En el capítulo anterior creamos un cubo, pero cuando lo ejecutamos parecía simplemente un cuadrado rojo, si necesitáramos algo así no tendría caso usar Java3D y bastaría con recurrir a Java2D. En el capítulo presente vamos a usar unos cuantos métodos de transformaciones que harán que ese cubo parezca un verdadero cubo, para ello, modificaremos el código que se presentó en el Listado 3.5.
4.1. Rotación Simple: HelloJava3Da.java.
La simple rotación de un cubo puede ser hecha para mostrar varias de sus caras. El primer paso es crear la transformación deseada usando un objeto del tipo Transform3D.
El listado 4.1 incorpora un objeto TransformGroup en el escenario gráfico para rotar el cubo sobre el eje X. La transformación de rotado es creada en la línea 6. La rotación se especifica usando el método rotX() en la línea 8. El objeto TransformGroup es creado en la línea 10 en base al objeto de rotado que queremos y que se creó en la línea 6.
Dos parámetros especifican la rotación: El eje de rotado y el ángulo de rotación. El eje es seleccionado usando el método apropiado (rotX, rotY, rotZ). El valor del ángulo es dado en el método empleado. Nosotros estamos rotando 45 grados.
El objeto objRotate genera el ColorCube como su hijo (línea 11). Finalmente, el objRoot hace a objRotate su hijo (línea 12).
1. public BranchGroup createSceneGraph() {
2. //Crea la raíz del grafo branch
3. BranchGroup objRoot = new BranchGroup();
5. // Objeto para rotar
6. Transform3D rotate = new Transform3D();
7.
8. rotate.rotX(Math.PI/4.0d);
9.
10. TransformGroup objRotate = new TransformGroup(rotate);
11. objRotate.addChild(new ColorCube(0.4));
12. objRoot.addChild(objRotate);
13. return objRoot;
14. } // Fin createSceneGraph
Listado 4.1. Rotación simple de un cubo.
El grafo resultante del Listado 4.1 puede verse en la Figura 4.1, en ella es más notoria la transformación, tenemos el nodo raíz de nuestro escenario BG, en ese nodo aplicamos el grupo de transformaciones TG, el cual tiene un nodo hijo, ColorCube, que, por tanto, tendrá los mismos cambios que su padre, en nuestro caso una simple rotación de 45º.
Sustituyendo el método createSceneGraph() del Listado 4.1 en HelloJava3D.java, y cambiando el nombre de la clase y del archivo a HelloJava3Da.java obtenemos el resultado de la Figura 4.2 después de compilar y ejecutar, en él ya notamos la cara superior del objeto.
4.2. Combinando Transformaciones: HelloJava3Db.java.
Ahora vamos a aplicar dos transformaciones sobre el cubo de HelloJava3Da, a este ejemplo le llamamos HelloJava3Db, nuevamente debemos modificar el método createSceneGraph(). Para este caso hay dos transformaciones diferentes las cuales pueden ser combinadas en una sola.
Dos rotaciones son combinadas en el Listado 4.2. Hacer estas dos operaciones simultáneas requiere mezclar dos objetos Transform3D de rotación. En el ejemplo se rota el cubo en torno a los ejes X y Y. Los objetos Transform3D, uno para cada rotación, son creados en las líneas 6 y 7. Las rotaciones individuales son especificadas por dos objetos TransformGroup (líneas 9 y 10). Entonces, las rotaciones son combinadas usando el método mul() en la línea 11. Por último, la combinación es colocada en el objeto TransformGroup (línea 12).
El resultado de estas transformaciones lo vemos en la Figura 4.3. Cabe decir que este es un ejemplo muy sencillo donde damos a conocer un poco de transformaciones, sin embargo, hay muchas otras que también se pueden combinar y que es imposible presentar aquí, éstas incluyen trasladado, escalado, etc.
1. public BranchGroup createSceneGraph() {
2. //Crea la raíz del grafo branch
3. BranchGroup objRoot = new BranchGroup();
4.
5. // Objetos para combinar
6. Transform3D rotate = new Transform3D();
7. Transform3D tempRotate = new Transform3D();
8.
9. rotate.rotX(Math.PI/4.0d);
10. tempRotate.rotY(Math.PI/5.0d);
11. rotate.mul(tempRotate);
12. TransformGroup objRotate = new TransformGroup(rotate);
13.
14. objRotate.addChild(new ColorCube(0.4));
15. objRoot.addChild(objRotate);
16. return objRoot;
17.}//Fin del método.
Listado 4.2. Combinando dos rotaciones.
Figura 4.3. Combinando transformaciones.
5. Animación: Behavior.
En Java3D, Behavior es una clase para especificar animaciones o interacción con objetos visuales. El comportamiento puede cambiar virtualmente cualquier atributo de un objeto. Un comportamiento es especificado por un objeto visual, Java3D actualiza la posición, orientación, color u otros atributos.
La diferencia entre animación e interacción es que la primera responde a ciertos eventos con el transcurso del tiempo, en cambio, la segunda responde a las actividades del usuario.
Cada objeto visual en el universo virtual puede tener su propio comportamiento definido. De hecho, pueden tener varios comportamientos. Para especificar un comportamiento de un objeto visual, el programador deberá crear los objetos que especifiquen el comportamiento, agregar el objeto visual al escenario gráfico y hacer las referencias apropiadas entre los objetos del escenario y los objetos de comportamientos.
Un interpolador es un número predefinido de clases y subclases Behavior en el paquete de Java3D. Basado en una función de tiempo, el objeto interpolador manipula los parámetros de los objetos en el escenario gráfico. Por ejemplo, la clase RotationInterpolator, manipula la rotación especificada por un TransformGroup para generar la rotación de los objetos visuales.
Al igual que hicimos en el capítulo 2, damos el Algoritmo 5.1 para guiarnos en la construcción de animaciones con Java3D, sus detalles se aclararán con un ejemplo más adelante:
1. Crear una instancia de TransformGroup.
a. Establecer la capacidad ALLOW_TRANSFORM_WRITE.
2. Crear un objeto Alpha.
a. Especificar los parámetros de tiempo para Alpha.
3. Crear el objeto interpolador.
a. Teniendo una referencia a los objetos Alpha y TransformGroup, optimizar los parámetros de comportamientos.
4. Especificar la región de planeación
a. Establecer la región de planeación para el comportamiento.
5. Hacer al comportamiento un hijo del TransformGroup.
Algoritmo 5.1. Construcción de animaciones en Java3D.
5.1. Especificando El Comportamiento.
Una acción de comportamiento puede ser un cambio en la posición (PositionInterpolator), orientación (RotationInterpolator), tamaño (ScaleInterpolator), color (ColorInterpolator), o transparencia (TransparencyInterpolator) de un objeto visual. Como se mencionó anteriormente, los interpoladores son clases de comportamientos predefinidas. Todo tipo de comportamientos pueden generarse sin interpoladores usando la animación clásica de Java, sin embargo, los interpoladores hacen estas tareas mucho más fáciles. Las clases de interpolación existen para proporcionar otras acciones, incluyendo combinaciones de ellas. La clase RotationInterpolator es usada más adelante como ejemplo.
Clase RotationInterpolator:
Esta clase es usada para especificar una rotación como comportamiento. Un objeto RotationIterpolator cambia un TransformGroup para especificar la rotación en respuesta al valor de un objeto Alpha. Dicho valor puede cambiar con el transcurso del tiempo. Un RotationInterpolator es flexible en su especificación la cual consiste en definir el eje de rotación y los ángulos inicial y final.
5.2. Variaciones de Tiempo Y Región de Planeación.
Para mapear una acción a cierto intervalo de tiempo debemos usar un objeto de la clase Alpha. La especificación de este objeto puede ser compleja, aquí presentamos la información básica para poder hacerlo.
Clase Alpha:
Los objetos de la clase Alpha son usados para crear una función de variación en el tiempo. Esta clase produce un valor entre cero y uno que depende del tiempo y los parámetros del objeto Alpha. Los objetos de esta clase son usados frecuentemente con interpoladores para completar animaciones.
En el paso cuatro del Algoritmo 5.1, se menciona que debemos especificar la región de planeación, esto se hace, en términos prácticos, para mejorar el rendimiento de nuestra animación y no haya demasiados problemas si contamos con una PC poco poderosa. Para conseguir esto nos valemos del método setSchedulingBounds de la clase Behavior.
Hay varias formas de definir una región de planeación, una de ellas es crear un objeto BoundingSphere.
Esta región de planeación nos sirve para que sólo cuando un objeto sea visible pueda mostrar el comportamiento que le fue asignado, no tendría caso gastar tiempo de CPU procesando objetos que el usuario no ve. De esta forma es como Java3D logra mejorar el rendimiento.
5.3. Ejemplo De Animación: HelloJava3Dc.
Nuevamente vamos a trabajar sobre el programa HelloJava3D en su método createSceneGraph(), le llamaremos HelloJava3Dc.java.
El Listado 5.1 muestra un ejemplo del uso de las clases de interpolación para crear una animación. Simplemente se realiza una rotación completa continua durante cuatro segundos. Obsérvese que éste código corresponde al Algoritmo 5.1 dado al inicio del capítulo.
El paso 1 del algoritmo es crear un objeto TransformGroup con la capacidad ALLOW_TRANSFORM_WRITE habilitada. El objeto TransformGroup es llamado objSpin y se crea en la línea 7. La capacidad de objSpin es establecida en la línea 8.
1. public BranchGroup createSceneGraph() {
2. // Crea la raíz del grafo
3. BranchGroup objRoot = new BranchGroup();
4.
5. // Crea e inicializa el grupo de transformación,
6. // lo agrega a el subgrafo
7. TransformGroup objSpin = new TransformGroup();
8. objSpin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
9. objRoot.addChild(objSpin);
10.
11. // Crea una hoja y la agrega al escenario
12. // ColorCube es una clase de utilidad Convenience
13. objSpin.addChild(new ColorCube(0.4));
14.
15. // Crea una funcion de variación en el tiempo
16. Alpha rotationAlpha = new Alpha(-1, 4000);
17.
18. // Crea un Nuevo comportamiento
19. // se especifican la operación y el grupo de transformación
20.
21. RotationInterpolator rotator =
22. new RotationInterpolator(rotationAlpha, objSpin);
23.
24. //Se especifica la región de planeación
25. BoundingSphere bounds = new BoundingSphere();
26. rotator.setSchedulingBounds(bounds);
27. objSpin.addChild(rotator);
28.
29. return objRoot;
30. } // fin del método createSceneGraph
Listado 5.1. Hellojava3Dc.
El paso 2 nos dice que debemos crear un objeto Alpha para manejar el interpolador, en el ejemplo, rotationAlpha es usado para especificar una rotación continua, los parámetros se dan en la línea 16, el primero es el número de iteraciones (- 1 para un ciclo continuo), el segundo para el tiempo de cada ciclo (4000 milisegundos = 4 segundos).
El paso 3 indica la creación del interpolador. El objeto RotationInterpolator para rotar se genera en las líneas 21 y 22. The interpolator must have references to the target transform and alpha objects. Los parámetros de su constructor deben ser el objeto Alpha y el grupo de transformación creados con anterioridad. En este ejemplo se usa el comportamiento predefinido de RotationInterpolator el cual genera una rotación completa alrededor del eje Y.
Especificar la región de planeación se lleva a cabo en el paso 4 del algoritmo, en nuestro código corresponde a las línea 25 y 26. Un objeto BoundingSphere es empleado para este propósito con su constructor por defecto.
El quinto y último paso hace al comportamiento hijo del grupo de transformación, en el Listado 5.1 esto se observa en la línea 27.
HelloJava3Dc crea el grafo de escena mostrado en la Figura 5.1. Por su parte, la Figura 5.2 muestra varias vistas de la animación hecha.
Figura 5.1. Grafo de HelloJava3Dc.
Figura 5.2. HelloJava3Dc ejecutándose.
5.4. Combinación de Transformaciones y Comportamientos: HelloJava3Dd.
Similar a lo que hicimos antes, podemos generar una combinación de transformaciones y comportamientos. En HelloJava3Dd.java veremos cómo. En los ejemplos anteriores utilizamos los objetos objRotate (HelloJava3Db, Listado 4.2) y objSpin (HelloJava3Dc, Listado 5.1), la diferencia entre ellos es que el primero se utiliza para realizar una rotación estática, mientras que el segundo para una rotación continua. El Listado 5.2 muestra el código modificado de createSceneGraph() para hacer una combinación de transformaciones y comportamientos usando los objetos mencionados.
El proceso a seguir es bastante fácil, primero hacemos lo mismo que en el Listado 4.2, combinar las transformaciones para ver varias caras del cubo (líneas 6 a 13), y posteriormente indicamos la rotación continua igual que en el Listado 5.1 (líneas 19 a 43).
Nótese que simplemente fue agregar al Listado 4.2 el código del Listado 5.1.
En la Figura 5.3 se muestra el grafo resultante, y en la Figura 5.4 una serie de imágenes que representan algunos movimientos del cubo (obsérvese la diferencia con la Figura 5.2).
1. public BranchGroup createSceneGraph() {
2. // Crea la raíz del grafo
3. BranchGroup objRoot = new BranchGroup();
4.
5. // Objetos a combinar
6. Transform3D rotate = new Transform3D();
7. Transform3D tempRotate = new Transform3D();
8.
9. rotate.rotX(Math.PI/4.0d);
10. tempRotate.rotY(Math.PI/5.0d);
11. rotate.mul(tempRotate);
12.
13. TransformGroup objRotate = new TransformGroup(rotate);
14.
15. // Crea e inicializa el grupo de transformación,
16.
17. // lo agrega a el subgrafo
18.
19. TransformGroup objSpin = new TransformGroup();
20. objSpin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
21.
22. objRoot.addChild(objRotate);
23. objRotate.addChild(objSpin);
24.
25. // Crea una hoja y la agrega al escenario
26. // ColorCube es una clase de utilidad Convenience
27. objSpin.addChild(new ColorCube(0.4));
28.
29. // Crea un Nuevo comportamiento
20.
31. // se especifican la operación y el grupo de transformación
32.
33. Alpha rotationAlpha = new Alpha(-1, 4000);
34.
35. RotationInterpolator rotator = new
36. RotationInterpolator(rotationAlpha, objSpin);
37.
38.
39. //Se especifica la región de planeación
40.
41. BoundingSphere bounds = new BoundingSphere();
42. rotator.setSchedulingBounds(bounds);
43. objSpin.addChild(rotator);
44.
45. return objRoot;
46. } // Fin de createSceneGraph
Listado 5.2. Código para combinar transformaciones.
Figura 5.3. Grafo para HelloJava3Dd.
Figura 5.4. HelloJava3Dd ejecutándose.
Bibliografía.
- Advanced Animation and Rendering Techniques. Theory and Practices. Alan Watt and Mark Watt. New York, Addison Wesley Pub Company.
- Interactive Control for Physically – Based Animation, in Computer Graphics Proceedings. Joseph Laszlo, Michiel van de Panne, Eugene Fiume. Annual Conference Series, 2000.
- Inbetweening for Computer Animation Utilizing Moving Point Constaints. Reeves, W. T. SIGGRAPH 81, 263-269.
- Adapting Simulated Behaviors For New Characters, en Computer Graphics. Jessica K. Hodgins and Nancy S. Pollard. (SIGGRAPH 97 roceedings), pp. 153-162, Addison Wesley, Agosto de 1997.
- Computer Graphics Principles and Practice, Second Edition. J. D. Foley, A. van Dam, S.K. Feiner and J.F. Hughes. Addison Wesley Pub Company, 1990.
- Bubblesort-Animation. Barbu, A., Dromowicz, M., Gao, X., Koester, M., and Wolf, C. 1998. Disponible en http://www-cg-hci.informatik.unioldenburg.de/˜da/fpsort/Animation.html.
- G. Collection of ANIMAL Animations. R¨oßling, 1999. Disponible en .
- Approaches for Generating Animations for Lectures. R¨oßling, G., and Freisleben, B. Proceedings of the 11th International Conference of the Society for Information Technology and Teacher Education (SITE 2000) (2000), 809–814.
- Experiences In Using Animations in Introductory Computer Science Lectures. R¨oßling, G., and Freisleben, B. SIGCSE 2000 Proceedings (2000). To Appear.
- Using Student-built Algorithm Animations as Learning Aids. Stasko, J. SIGCSE Session Proceedings (February 1997), 25–29.
- Introducción a la Graficación por Computador. Foley, Dam, Feiner, Hughes, Philips. Addisson Wesley Iberoamericana, 1996.
- Graficas por Computadora. Hearn/Baker. Printice Hall, 1995.
- 3D Computer Graphics, Tercera edición. Alan Watt. Addison Wesley, 2000.
- 3D Computer Animation. J. Vince. Addison Wesley, 1992.
- The Animator's Workbook. Editorial Watson-Guptill Publication.
- Animation cartoon. Preston Blair. Editorial Icara.
- Digital Texturing & Lightning. Owen Demers. Editorial New Riders.
- Generación electrónica de imágenes. Levitan, Eli L. Ediciones Bellatera S.A. Barcelona.
- Wikipedia entry about Animation, www.wikipedia.org/wiki/Animation
- The Aardman Book of 3D−Animation. ISBN 0500018812 o ISBN 0810919966.
- Gimp Animation Tutorial. www.jimmac.musichall.cz/tutor.php3.
- Gimp Tutorials. www.gimp.org/tutorials/
- Brickfilms. www.brickfilms.com (dedicado a películas de detenimiento de movimiento con ladrillos y figuras Lego.
- Gráficos con Java 2D. Juan Antonio Palos. http://java.programacion.com/ Versión Original en Ingles en http://java.sun.com
- Java Swing Draw. Alfredo Casado Bernardez. http://creativecommons.org
- Java Programming Language Guide. Bloch, J. Effective, 2001.
- Documentación completa y descarga de Java y Java3D. http://java.sun.com/docs/
- Thinking in Java. Bruce Eckel. http://www.planetpdf.com/, http://www.codecuts.com/, http://www.pdfforum.com/, http://www.pdfstore.com/. Prentice Hall, 2000.
- Aprenda Java como si estuviera en primero. Javier García de Jalón. Campus Tecnológico de la Universidad de Navarra. 2000. www.tecnum.es
- Java desde Cero. www.cybercursos.net
- TutorJava Nivel Básico. Juan Antonio Palos. http://java.programacion.com/ Versión Original en Ingles en http://java.sun.com
- Getting Started with the Java3D™ API. Dennis J Bouvier. www.sun.org
- Visualization of object-oriented design models. Master’s thesis. Sunita Asija. Depaul University, Chicago, USA, December 1999.
- Visualizing object-oriented software in virtual reality. Jonathan I. Maletic. 2001.
- The Java3D API Specification. Henry Sowizral. Addison Wesley, second edition, 2000.
Luis Antonio Fernández Aldana
Parte del Proyecto de Servicio Social:
Evaluación de las Herramientas para el Desarrollo de Software Multimedia.
México.
Enero 2007.
Acerca del Autor: Luis Antonio Fernández Aldana egresó de la Facultad de Ciencias de la Computación de la Benemérita Universidad Autónoma de Puebla, México, en 2006, se ha dedicado en su formación a el área de multimedia, diseño y programación Web, le gusta compartir sus conocimientos lo cual se ve reflejado en los diversos trabajos que ha publicado gratuitamente en Internet.
Contacto: Sugerencias, comentarios y solicitud de la edición original de este y otros trabajos similares a y http://lamar.cs.buap.mx/~lafa
Nota: El software y otros elementos que aquí se describen o mencionan están protegidos por las correspondientes leyes de derechos de autor. Este manual es de libre distribución y se ha hecho como parte del proyecto de servicio social "Evaluación de las herramientas para la realización de software multimedia" de la Facultad de Ciencias de la Computación, Benemérita Universidad Autónoma de Puebla, México. Este trabajo se terminó de elaborar el 31 de Enero del 2007.
Página anterior | Volver al principio del trabajo | Página siguiente |