SlideShare una empresa de Scribd logo
1 de 51
Descargar para leer sin conexión
Introducción a Java 3D
Lorenzo Martín Edreira
Rubén Melcón Fariña
1 INTRODUCCIÓN
1.1¿Qué es la API Java 3D API?
La API Java 3D es una jerarquía de clases java que sirven como interfaz a sistemas gráficos de 3
dimensiones de imagen y sonido. Los programadores trabajan con constructores de alto nivel para
crear y manipular objetos 3D. Estos objetos residen en un universo virtual que es dibujado para su
presentación a los usuarios.
Un programa Java 3D crea instancias de objetos Java 3D y los coloca en la estructura de datos de
objetos gráficos de la escena. El gráfico de la escena está organizado en forma de árbol y especifica
completamente el contenido del universo virtual, y cómo este es dibujado.
1.2 La API Java 3D
Todos los programas Java 3D están, por lo menos parcialmente, formados por objetos de la
jerarquía de clases Java 3D. Esta colección de objetos describe un universo virtual, el cual va a ser
dibujado. La API define sobre 100 clases presentadas en el paquete javax.media.j3d. Estas clases
son referidas comúnmente como las clases núcleo de Java 3D
En adicción a las clases núcleo de Java 3D nos encontraremos con las clases de utilidades que
incluyen cargadores de contenido, clases de geometría ... que suponen una adicción conveniente y
potente a las clases de bajo nivel que componen la clases núcleo de Java 3D. Las clases de
utilidades las encontraremos en el paquete com.sun.j3d.utils. Como se muestra en el nombre del
paquete, estas clases no pertenecen a la especificación de la API Java 3D, y son clases propias de la
implementación de referencia realizada por Sun Microsystems.
Además de los paquetes de clases núcleo de Java 3D y las clases de utilidades proporcionadas
por Sun Microsystems, nos encontraremos con clases del paquete java.awt, que nos definen clases
para crear un ventana en la que mostrar el gráfico, y el paquete javax.vecmatch ,que definen clases
matemáticas de puntos de vectores, matrices y otros objetos matemáticos.
La API Java 3D no es un conjunto de paquetes y clases que vengan de forma predeterminada en
la distribución Java normal (J2SE), sino que es una extensión que debemos instalar de forma aparte.
En nuestro caso utilizaremos la versión Java 3D 1.3.1 con interfaz a OpenGL y el J2SE 1.4.2 en una
plataforma Microsoft Windows. A parte del SDK Java3D para MS Windows, también podremos
descargarnos la versión con interfaz a DirectDraw, también para Microsoft Windows y la versión
para Solaris (versión SPARC) de Java 3D con interfaz a OpenGL.
Los archivos que nos encontraremos en nuestra distribución son:
<JREDIR>binJ3D.dll
<JREDIR>binj3daudio.dll
<JREDIR>binJ3DUtils.dll
<JREDIR>libextvecmath.jar
<JREDIR>libextj3dcore.jar
<JREDIR>libextj3daudio.jar
<JREDIR>libextj3dutils.jar
<JDKDIR>j3d-utils-src.jar
<JDKDIR>demojava3d
Una vez instalado el paquete, ya podremos desarrollar nuestras aplicaciones Java3D, pero antes
daremos unas nociones básicas de la arquitectura de construcción de los universos virtuales en Java
3D.
1.3 Estructura de la escena de un gráfico
Un universo virtual Java 3D se crea a partir de un escenario gráfico. Un escenario gráfico se crea
usando instancias de clases Java 3D. El conjunto de objetos gráficos está formado por objetos que
definen la geometría, sonido, luces, localización, orientación y apariencia de objetos de audio y
visuales.
Una definición de un conjunto de objetos gráficos es una estructura de datos compuesto por
nodos y arcos. Un nodo es una elemento de datos y un arco es una relación entre nodos. Los nodos
en el diagrama del escenario son las instancias de las clases Java3D. Los arcos representan los dos
tipos de relaciones (Padre-Hijo y Referencia) entre las instancias Java 3D.
Las relaciones Padre-Hijo forman una relación entre los objetos del escenario, y las relaciones de
referencia asocian los nodos NodeComponent con un nodo del escenario definiendo la geometría y
atributos de la apariencia usados para dibujar los objetos visuales
1.4 Instrucciones para escribir Programas Java 3D
Para escribir programas que utilicen las clases Java 3D es recomendable seguir una secuencia de
pasos lógicos que nos permitirán una mejor estructuración de nuestros programas y el uso de un
método de construcción común en todos los programas. Los pasos para construir un programa Java
3D con siete:
1) Crear un objeto Canvas3D
2) Crear un objeto VirtualUniverse
3) Crear un objeto Locale, atacando al VirtualUniverse
4) Construir un gráfico de la rama de visualización
a) Crear un objeto vista
b) Crear un objeto ViewPlatform
c) Crear un objeto PhysicalBody
d) Crear un objeto PhysicalEnvisonment
e) Atacar ViewPlatform, PhysicalBody, PhysicalEnvironment y Canvas3D al objeto View
5) Construir un gráfico de la rama del contenido
6) Compilar las ramas
7) Insertar las distintas partes que componen el escenario en el Locale
1.4.1 Una receta simple para escribir los programas Java 3D
La mayoría de nuestros programas Java 3D utilizan una estructura en el proceso de visualización
casi idéntica. Debido a esto, Sun Microsystems ha provisto una clase Java que nos creará todo el
universo virtual, y nos ahorrará tiempo de codificación.
Esta clase, llamada SimpleUniverse, es el punto de inicio más fácil para empezar a programar,
porque nos podremos olvidar de las estructuras de visualización y nos podremos concentrar en las
estructuras de contenido. Un inconveniente que tendremos con esta clase es que no podremos tener
múltiples vistas del universo virtual, y lo que no debemos de olvidar, esta clase no forma parte de la
especificación Java 3D, con lo que no tenemos asegurado que se ejecuten nuestros programas en
otras implementaciones.
Con la clase SimpleUniverse nuestro esquema de construcción de programas Java 3D nos
quedará:
1) Crear un objeto Canvas3D
2) Crear un objeto SimpleUniverse que haga referencia al objeto Canvas3D
a) Personalizar SimpleUniverse
3) Construir la rama de contenido (BranchGroup)
4) Compilar el gráfico de la rama de contenido
5) Insertar las estructuras de la rama de contenido en el Locale del SimpleUniverse
Una vez creados los objetos Canvas3D y SimpleUniverse, el siguiente paso es la creación de las
estructuras de la rama de contenido. Después de crear la rama de contenido, esta será insertada con
el método addBranchGraph(BranchGroup) de SimpleUniverse.
En el objeto BranchGroup definiremos todos los objetos Java 3D que queremos que se
encuentren contenidos en nuestro escenario, así como las clases de comportamiento que queremos
que afecten a nuestros objetos. Por ejemplo, el objeto Transform3D nos permitirá rotar, escalar y
trasladar nuestros objetos, y la clase TransformGroup nos permitirá agrupar las clases de
comportamiento. La forma de utilizar estas clases de comportamiento será:
1) Crear el objeto Transform3D
2) Crear los objetos 3D de la escena
3) Añadir los objetos Transform3D a un TransformGroup
4) Añadir los objetos 3D a TransformGroup
5) Añadir TransformGroup al objeto BranchGroup.
1.5 Capacidades y Rendimiento
Una vez que tenemos definida nuestro escenario debemos de compilarlo a través del método
compile() de BranchGroup que nos permitirá realizar optimizaciones de nuestro escenario. En esta
compilación lo que se suele realizar es la agrupación de los objetos Transform3D de un
TransformGroup a una representación interna optimizada.
Pero un efecto lateral de la compilación es que las capacidades del TransformGroup de modificar
su comportamiento se ve limitado. La solución que utilizaremos para evitar este comportamiento
será la de establecer las capacidades del objeto TransformGroup para notificar al compilador de que
queremos permitir ciertos comportamientos en nuestros objetos. Todo esto lo realizaremos a través
del método setCapability(Capability), donde podremos especificar los comportamientos que
queremos permitir. Algunas de las capacidades que tendremos disponibles son:
1) ALLOW_TRANSFORM_READ - Acceso de lectura del objeto TransformGroup
2) ALLOW_TRANSFORM_WRITE - Acceso de escritura del objeto TransformGroup
3) ALLOW_CHILDREN_EXTEND - Se permiten añadir más nodos hijo
4) ALLOW_CHILDREN_READ - Acceso de lectura de los nodos hijo
5) ALLOW_CHILDREN_WRITE - Acceso de escritura de los nodos hijo
1.6 Animación
En Java 3D, Behaviour es una clase para especificar animaciones o interacciones de objetos
visuales, capaz de cambiar virtualmente cualquier atributo de un objeto visual. Un programador
puede usar un número predefinido de comportamientos o especificar uno personalizado. Una vez
que se especifica un comportamiento para un objeto visual, el sistema Java 3D actualiza la posición,
orientación, color u otros atributos del objeto visual automáticamente.
La diferencia entre animación e interacción está en cuando el comportamiento es activado en
respuesta al paso del tiempo o en respuesta a las actividades del usuario.
Para especificar un comportamiento para un objeto visual, el programador crea los objetos que
especifican el comportamiento, añaden al objeto visual a la escena del gráfico, y se construyen las
referencias apropiadas entre los objetos del escenario y los objetos Behaviour.
Un problema que nos encontraremos al utilizar los comportamientos es que tendremos que
especificar una región que haga de límite para el comportamiento. Este límite se llama región de
planificación. Un comportamiento no está activo a menos que el volumen de activación de la
plataforma de visión tenga un punto de intersección con la región de planificación de un objeto
Behavior.
Una de las clases de comportamiento predefinidas que nos podremos encontrar es la clase
Interpolator. Esta clase está basada en una función de tiempo en la que el objeto Interpolator
manipula los parámetros de un objeto del gráfico del escenario. Por ejemplo, RotationInterpolator
manipula la rotación especificada por un TransformGroup para afectar a la rotación de los objetos
visuales hijos del TransformGroup.
Los pasos para especificar una animación con un objeto Interpolator son los siguientes:
1) Crear un TransformGroup con la capacidad ALLOW_TRANSFORM_WRITE
2) Crear un objeto Alpha que especifique los parámetros de tiempo
3) Crear un objeto Interpolator que haga referencia a los objetos Alpha y TransformGroup
5) Especificar una región de planificación
6) Hacer el comportamiento hijo del TransformGroup
1.7 Ejemplo
Para ver el funcionamiento de todas las clases mencionadas anteriormente, veremos un pequeño
programa en el que utilizaremos todos los puntos anteriores. El programa que crearemos creará un
objeto predefinido en el paquete de utilidades que nos define un cubo con un color para cada cara.
Además a la escena le añadiremos un rotación dependiente del tiempo y un límite para el
comportamiento definido por una esfera de radio 100 y centrado en el punto (0,0,0).
package es.udc.fi.gc;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;
public class Hello extends Applet {
private SimpleUniverse u = null;
public BranchGroup createSceneGraph() {
// Crear la raiz de la rama de objetos gráficos
BranchGroup objRoot = new BranchGroup();
// Crear el nodo TransformGroup y engancharlo a la raíz
// Habilitar la capacidad TRANSFORM_WRITE para que se pueda modificar
// en tiempo de ejecución.
TransformGroup objTrans = new TransformGroup();
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
objRoot.addChild(objTrans);
// Crear un objeto Shape3D y añadirlo a la escena.
objTrans.addChild(new ColorCube(0.4));
// Crear un objeto Behavior que realizará las operaciones
// deseadas en la transformada especificada y añadirlo a la escena
Transform3D yAxis = new Transform3D();
Alpha rotationAlpha = new Alpha(-1, 4000);
RotationInterpolator rotator =
new RotationInterpolator(
rotationAlpha,
objTrans,
yAxis,
0.0f,
(float) Math.PI * 2.0f);
BoundingSphere bounds =
new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);
rotator.setSchedulingBounds(bounds);
objRoot.addChild(rotator);
// Compilamos para optimizar
objRoot.compile();
return objRoot;
}
public Hello() {
}
public void init() {
setLayout(new BorderLayout());
GraphicsConfiguration config =
SimpleUniverse.getPreferredConfiguration();
Canvas3D c = new Canvas3D(config);
add("Center", c);
// Creamos una escena y atacarla al universo virtual
BranchGroup scene = createSceneGraph();
u = new SimpleUniverse(c);
// Mueve el punto de vista un poco atrás para ver los objetos
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}
public void destroy() {
u.cleanup();
}
//
// El siguiente código permite a HelloUniverse ejecutarse como una aplicación
// y como un applet
//
public static void main(String[] args) {
new MainFrame(new Hello(), 256, 256);
}
}
2. CREANDO GEOMETRÍAS
En el capítulo 1 exploramos los conceptos básicos de la construcción de un universo virtual Java
3D, concentrándonos en especificar las transformadas y comportamientos, y para demostrar su
construcción y funcionamiento construimos un pequeño programa. Pero en el programa que
construimos sólo había un figura, y era un cubo de colores que ya venía preconstruido en las clases
de utilidades de la distribución de Java 3D, y a la cual no le podíamos especificar ni la apariencia ni
el color.
En este capítulo veremos que hay tres formas de construir los objetos que queremos representar y
que podremos construir a través de objetos ya predefinidos (cubos, conos, esferas ...), a través de
coordenadas de los vértices o a través de cargadores de geometrías.
2.1 Sistemas de coordenadas del Mundo Virtual
En nuestros programas Java 3D una instancia de VirtualUniverse sirve como la raíz del escenario
de los gráfico. El término universo virtual se refiere al espacio virtual de 3 dimensiones donde se
sitúan los objetos 3D. Cada objeto Locale establece un sistema de coordenadas cartesianas en el
universo virtual. Esto es, un objeto Locale sirve como punto de referencia para los objetos visuales
en nuestro universo virtual. Con un Locale en un SimpleUniverse existe un sistema de coordenadas
en el universo virtual.
El sistema de coordenadas de universo virtual Java es de mano derecha. El eje X es positivo a la
derecha, el eje Y es positivo hacia arriba y el eje X es positivo hacia el usuario.
2.2 Definiciones básicas de Objetos visuales
2.2.1 Shape3D
Un nodo Shape3D define un objeto visual genérico. La clase Shape3D no contiene información
sobre el color o la forma de un objeto visual, sino que esta información se almacena en los objetos
NodeComponent referenciados por el objeto Shape3D. Un objeto Shape3D puede referenciar a un
componente del nodo Geometry y/o Appearance.
2.2.2 Componentes Nodo
Los objetos NodeComponent contienen la especificación de los atributos del objeto visual. Cada
una de las subclases de NodeComponent define ciertos atributos visuales como la apariencia,
geometría, material, texturas ...
2.2.3 Definiendo las Clases de objetos visuales
El mismo objeto visual puede encontrarse varias veces en nuestra escena, por lo que tendrá
sentido definir una clase que cree el objeto visual en vez de construir cada objeto visual desde el
principio.
La estructura de la construcción de un objeto visual es la siguiente:
public class VisualObject extends Shape3D {
private Geometry voGeometry;
private Appearance voAppearance;
// Creamos el objeto Shape3D con la geometría y apariencia
public VisualObject(){
voGeometry = createGeometry();
voAppearance = createAppearance();
this.setGeometry(voGeometry);
this.setAppearance(voAppearance);
}
....
}
2.3 Clases de utilidades geométricas
Las distribución que nos ofrece Sun Microsystems viene acompañada de un paquete de utilidades
que nos suministra clases de objetos visuales que representan primitivas de objetos del mundo real.
Estas clases nos proporcionarán una forma sencilla de crear estos objetos y pudiendo especificar la
apariencia deseada.
Las primitivas predefinidas que nos encontraremos las tendremos en el paquete
com.sun.j3d.utils.geometry y corresponden a los objetos que representan a un cubo, cono, cilindros
y esferas:
com.sun.j3d.utils.geometry.Box
com.sun.j3d.utils.geometry.Cone
com.sun.j3d.utils.geometry.Cilinder
com.sun.j3d.utils.geometry.Sphere
La construcción de una escena con uno de estos componentes es muy fácil, sólo tenemos que
cambiar los colores para que nuestra figura no se confunda con el fondo y después seguir los pasos
de la clase Hello del primer capítulo:
package es.udc.fi.gc;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.Cone;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
public class HelloCone extends Applet {
private SimpleUniverse u = null;
public BranchGroup createSceneGraph() {
// Crear la raiz de la rama del gráfico
BranchGroup objRoot = new BranchGroup();
// Crear un objeto Shape3D y añadirlo a la escena.
Appearance apariencia = new Appearance();
apariencia.setColoringAttributes(new ColoringAttributes(0.3f, 1.0f,1.0f,
ColoringAttributes.SHADE_FLAT));
Node node = new Cone(0.5f,0.3f,apariencia);
objRoot.addChild(node);
// Compilar para optimizar
objRoot.compile();
return objRoot;
}
public HelloCone() {
}
public void init() {
setLayout(new BorderLayout());
GraphicsConfiguration config =
SimpleUniverse.getPreferredConfiguration();
Canvas3D c = new Canvas3D(config);
add("Center", c);
// Crear una escena y atacarla al universo virtual
BranchGroup scene = createSceneGraph();
u = new SimpleUniverse(c);
// Mueve el punto de vista un poco atrás para ver los objetos
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}
public void destroy() {
u.cleanup();
}
public static void main(String[] args) {
new MainFrame(new HelloCone(), 256, 256);
}
}
2.4 Clases matemáticas
Para crear objetos visuales necesitaremos a la clase Geometry y a sus subclases. Muchas de las
subclases de Geometry describen primitivas basándose en otras primitivas más sencillas basadas en
la definición a partir de sus vértices, como sucede con los puntos, líneas y polígonos. Pero antes de
meternos con estas clases necesitaremos de otras clases matemáticas que nos ayudarán a definir
nuestros objetos.
Las clases matemáticas que necesitaremos las encontraremos en el paquete javax.vecmatch.* y
las clases que más utilizaremos son aquellas que definen puntos (Pointf, Pointd), colores (Colorf,
Colord), vectores (Vectorf, Vectord), coordenadas de texturas (TexCoordf, TextCoordd) y
cuarteniones (Quadd y Quadf).
2.5 Clases de geometría
En los gráficos en 3D todo está modelado y dibujado con datos basados en conjuntos de vértices.
Para realizar la construcción de objetos complejos, la API Java 3D nos proporciona una clase,
GeometryArray, con el que podremos construir objetos complejos basándonos en sus vértices como
puntos, líneas, triángulos y polígonos en general. Pero además de GeometryArray tendremos otras
clases que extienden de ella y con las que podremos especificar otros parámetros, como la
reutilización de los vértices. Las clases padres en la construcción de objetos son GeometryArray,
GeometryStripArray e IndexedGeometryArray.
Las subclases de GeometryArray y una representación de son comportamiento son:
Las subclases de GeometryStripArray y una representación gráfica de ellas son:
Las subclases de IndexedGeometryArray nos proporcionarán una forma de eliminar los vértices
redundantes, aunque con la penalización de tener que construir arrays de datos que contengan los
índices para las coordenadas, índices, color, texturas y normales. Lo único que deberemos tener en
cuenta es que la matrices de índices podrán tener múltiples referencias al mismo vértice en los
arrays de datos. Las clases que tendremos disponibles son IndexedGeometryArray,
IndexedPointArray, IndexedLineArray, IndexedTriangleArray, IndexedQuadArray,
IndexedGeometryStripArray, IndexedLineStripArray, IndexedTriangleStripArray y
IndexedTriangleFanArray.
EL siguiente ejemplo muestra la construcción de dos rectas y un triángulo con los vértices de
colores distintos en nuestro escenario.
package es.udc.fi.gc;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;
public class BasicFigures extends Applet {
private SimpleUniverse u = null;
public BranchGroup createSceneGraph() {
BranchGroup objRoot = new BranchGroup();
LineArray line1 = new LineArray(2, LineArray.COORDINATES);
line1.setCoordinate(0, new Point3f(0.0f, 0.0f, 0.0f));
line1.setCoordinate(1, new Point3f(0.3f, 0.0f, 0.0f));
LineArray line2 = new LineArray(2, LineArray.COORDINATES);
line2.setCoordinate(0, new Point3f(0.0f, 0.3f, 0.0f));
line2.setCoordinate(1, new Point3f(0.1f, 0.0f, 0.3f));
TriangleArray triangle = new TriangleArray(3, TriangleArray.COORDINATES|
TriangleArray.COLOR_3);
triangle.setCoordinate(0, new Point3f (-0.3f, 0.5f, 0.0f));
triangle.setCoordinate(1, new Point3f (-0.65f, -0.6f, 0.0f));
triangle.setCoordinate(2, new Point3f (0.0f, -0.5f, 0.0f));
triangle.setColor(0, new Color3f(1.0f, 0.0f, 0.0f));
triangle.setColor(1, new Color3f(1.0f, 1.0f, 0.0f));
triangle.setColor(2, new Color3f(1.0f, 0.0f, 1.0f));
objRoot.addChild(new Shape3D(line1));
objRoot.addChild(new Shape3D(line2));
objRoot.addChild(new Shape3D(triangle));
// Compilamos para optimizar
objRoot.compile();
return objRoot;
}
public BasicFigures() {
}
public void init() {
setLayout(new BorderLayout());
GraphicsConfiguration config =
SimpleUniverse.getPreferredConfiguration();
Canvas3D c = new Canvas3D(config);
add("Center", c);
// Crear una escena y atacarla al universo virtual
BranchGroup scene = createSceneGraph();
u = new SimpleUniverse(c);
// Mueve el punto de vista un poco atrás para ver los objetos
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}
public void destroy() {
u.cleanup();
}
public static void main(String[] args) {
new MainFrame(new BasicFigures(), 256, 256);
}
}
2.6 Apariencia y atributos
Los objetos Shape3D pueden hacer referencia a objetos Geometry y Appearance. Tal como
hemos visto, los objetos Geometry especifican la información por vértices de los objetos visuales.
La información de cada vértice también puede especificar la información de color, pero si queremos
efectos más sofisticados tendremos que recurrir al nodo Appearance.
Un objeto Appearance no contiene información de como debería de ser un objeto Shape3D, sino
que la información que contiene es la que dice dónde encontrar los datos de apariencia. Esto es,
dentro del objeto Appearance no encontraremos especificada la información de la apariencia, sino
que tendremos las referencias a los objetos atributos que contienen la información.
Los objetos de atributos que encontraremos en la clase Appearance son:
• PointAttributes
• LineAttributes
• PolygonAttributes
• ColoringAttributes
• TransparencyAttributes
• RenderingAttributes
• Material
• TextureAttributes
• Texture
• TexCoodGeneration
2.7 Cargadores de contenido
Además de las clases de para crear geometrías que hemos visto, también habíamos mencionado
al principio de este capítulo que existían los cargadores de clases. Un cargador de clase son
cargadores de objetos 3D definidos en archivos construidos con herramientas de terceras compañías.
Como ejemplo, mencionaremos que existen cargadores para archivos generados con el 3D-Studio,
AutoCAD, VRML, WaveFrom, LightWare ... que son gratuitos y que podremos descargar a través
de la página de enlaces de Java 3D.
2.8 Otros elementos de contenido
Otros objetos que podremos añadir a nuestro escenario pueden ser texto en 2 dimensiones
(Text2D), texto en 3 dimensiones (Text3D) y colores de fondo (BackGround). Estos objetos son
muy fáciles de utilizar, y nos serán muy útiles de utilizar en nuestras escenas.
Con el objeto Text2D tendremos que especificar el texto que deseamos visualizar junto con los
atributos de color, tamaño y fuente al añadirlo al BranchGroup. Ejemplo:
package es.udc.fi.gc;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.Text2D;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;
public class HelloText2D extends Applet {
private SimpleUniverse u = null;
public BranchGroup createSceneGraph() {
// Crear la raiz de la rama del gráfico
BranchGroup objRoot = new BranchGroup();
Text2D text2D = new Text2D("Texto en 2D", new Color3f(0.8f, 1.0f, 1.0f),
"Helvetica", 21, Font.BOLD);
objRoot.addChild(text2D);
// Compilar para optimizar
objRoot.compile();
return objRoot;
}
public HelloText2D() {
}
public void init() {
setLayout(new BorderLayout());
GraphicsConfiguration config =
SimpleUniverse.getPreferredConfiguration();
Canvas3D c = new Canvas3D(config);
add("Center", c);
// Crear una escena y atacarla al universo virtual
BranchGroup scene = createSceneGraph();
u = new SimpleUniverse(c);
// Mueve el punto de vista un poco atrás para ver los objetos
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}
public void destroy() {
u.cleanup();
}
public static void main(String[] args) {
new MainFrame(new HelloText2D(), 256, 256);
}
}
En el caso del objeto Text3D tendremos que especificar una extrusión a lo largo del eje Z en el
constructor de la fuente (Font3D).
Y con el fondo necesitaremos especificar el color deseado y los límites dentro de los que se
aplicará el nuevo color de fondo. En el siguiente código veremos un ejemplo de estas dos últimas
características juntas.
package es.udc.fi.gc;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;
public class HelloText3D extends Applet {
private SimpleUniverse u = null;
public BranchGroup createSceneGraph() {
// Crear la raiz de la rama del gráfico
BranchGroup objRoot = new BranchGroup();
Background bg= new Background();
bg.setColor(0.3f, 0.6f,0.0f);
bg.setApplicationBounds(new BoundingSphere(new Point3d(),100.0f));
Font3D font3D = new Font3D(new Font("Helvetica", Font.PLAIN, 6), new
FontExtrusion());
Text3D text3D =
new Text3D(font3D,
"Texto en 3D",
new Point3f(-12.0f, 0.8f, -50.0f));
Shape3D textShape = new Shape3D(text3D);
objRoot.addChild(textShape);
objRoot.addChild(bg);
// Compilar para optimizar
objRoot.compile();
return objRoot;
}
public HelloText3D() {
}
public void init() {
setLayout(new BorderLayout());
GraphicsConfiguration config =
SimpleUniverse.getPreferredConfiguration();
Canvas3D c = new Canvas3D(config);
add("Center", c);
// Crear una escena y atacarla al universo virtual
BranchGroup scene = createSceneGraph();
u = new SimpleUniverse(c);
// Mueve el punto de vista un poco atrás para ver los objetos
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}
public void destroy() {
u.cleanup();
}
public static void main(String[] args) {
new MainFrame(new HelloText3D(), 256, 256);
}
}
2.9 Transformaciones básicas
Una vez que tenemos nuestros objetos visuales creados, lo siguiente que desearemos hacer será
aplicarle alguna transformación para colocarlos en el lugar deseado de nuestro escenario. En Java
3D existe una clase de transformaciones, Transform3D, y en ella aplicaremos las operaciones que
deseemos, como rotaciones, escalados y translacciones. Una vez aplicada la operación deseada,
añadiremos el objeto Transform3D a un TransformGroup, que es una objeto que utilizaremos para
agrupar a las transformaciones. Una vez que tenemos el TransformGroup, le añadiremos los objetos
a los que queremos aplicar las transformaciones.
Los métodos para aplicar las transformaciones en Transform3D son rotX(double), rotY(double),
rotZ(double), setTranslation(Vector3*) y setScale(Vector3d). Además de estos métodos también
dispondremos de otros que nos permitirán agrupar en el propio Transform3D las transformaciones,
multiplicando directamente las matrices de transformación, mul(Transform3D), o aplicándole otra
transformación, add(Transform3D).
En el siguiente código crearemos un triángulo de colores y lo rotaremos sobre el eje Y.
package es.udc.fi.gc;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;
public class BasicTransform extends Applet {
private SimpleUniverse u = null;
public BranchGroup createSceneGraph() {
BranchGroup objRoot = new BranchGroup();
TriangleArray triangle = new TriangleArray(3, TriangleArray.COORDINATES|
TriangleArray.COLOR_3);
triangle.setCoordinate(0, new Point3f (-0.3f, 0.5f, 0.0f));
triangle.setCoordinate(1, new Point3f (-0.65f, -0.6f, 0.0f));
triangle.setCoordinate(2, new Point3f (0.0f, -0.5f, 0.0f));
triangle.setColor(0, new Color3f(1.0f, 0.0f, 0.0f));
triangle.setColor(1, new Color3f(1.0f, 1.0f, 0.0f));
triangle.setColor(2, new Color3f(1.0f, 0.0f, 1.0f));
TransformGroup tg = new TransformGroup();
tg.addChild(new Shape3D(triangle));
Transform3D rotation = new Transform3D();
rotation.rotY(0.9);
tg.setTransform(rotation);
objRoot.addChild(tg);
// Compilamos para optimizar
objRoot.compile();
return objRoot;
}
public BasicTransform() {
}
public void init() {
setLayout(new BorderLayout());
GraphicsConfiguration config =
SimpleUniverse.getPreferredConfiguration();
Canvas3D c = new Canvas3D(config);
add("Center", c);
// Crear una escena y atacarla al universo virtual
BranchGroup scene = createSceneGraph();
u = new SimpleUniverse(c);
// Mueve el punto de vista un poco atrás para ver los objetos
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}
public void destroy() {
u.cleanup();
}
public static void main(String[] args) {
new MainFrame(new BasicTransform(), 256, 256);
}
}
3.Interacción
En los capítulos previos hemos hecho varios programas que demuestran las capacidades del API
Java 3D, pero estas capacidades están bastante limitadas, ya que son estáticas. Lo verdaderamente
interesante de los mundos virtuales es la interacción y animación que pueden producir. La
interacción es la respuesta a los eventos producidos por el usuario. La animación está definida por
los cambios de la escena a lo largo del tiempo sin la interacción directa del usuario.
3.1 La clase Behavior
En Java 3D la interacción y la animación son especificados a través de los objetos Behavior. La
clase Behavior es una clase abstracta que nos proporcionará una base para acceder a sus
descendientes que nos proporcionarán el código para responder a los cambios en el universo
virtual.
El propósito de los objetos descendientes de Behavior es cambiar el escenario o los objetos en
respuesta a algún tipo de estímulo. Hay muchos tipos de estímulos, como la acción del usuario,
colisiones entre objetos, el transcurso del tiempo o la localización de la vista. Cada uno de estos
estímulos hará que determinados objetos del escenario se comporten de distinta forma. Por ejemplo,
mientras que la localización de la vista puede apenas provocar cambios posibles sobre un
TransformGroup que contenga un BillBoard, si que puede afectar y de manera muy significativa a
los objetos geométricos en cuanto a su nivel de detalle en la escena.
En los paquetes que tenemos en la distribución tenemos varias clases que implementan Behavior
y que podremos utilizar en nuestros escenarios sin tener que recurrir a construir nuestras propias
clases Behavior. Algunas de las clases que encontraremos son KeyNavigationBehavior,
MouseBehavior y PickMouseBehavior. Estas clases ya tienen comportamientos definidos que
podremos utilizar para navegar por la escena y el mecanismo para utilizar estas clases es:
1) Preparar el escenario
2) Insertar objetos Behavior en el escenario haciendo referencia a los objetos de cambio
3) Especificar un límite de acción de los objetos Behavior
4) Establecer las capacidades de escritura y lectura para el destino.
En el siguiente ejemplo utilizaremos el ejemplo del cono y le añadiremos el objeto
KeyNavigationBehavior para poder navegar sobre nuestro universo virtual. Si ejecutamos el
ejemplo podremos navegar con las teclas de dirección del teclado.
package es.udc.fi.gc;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.behaviors.keyboard.KeyNavigatorBehavior;
import com.sun.j3d.utils.geometry.Cone;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.Point3d;
public class MovingCone extends Applet {
private SimpleUniverse u = null;
public BranchGroup createSceneGraph() {
// Crear la raiz de la rama del gráfico
BranchGroup objRoot = new BranchGroup();
// Crear un objeto Shape3D y añadirlo a la escena.
Appearance apariencia = new Appearance();
apariencia.setColoringAttributes(new ColoringAttributes(0.3f, 1.0f,1.0f,
ColoringAttributes.SHADE_FLAT));
Node node = new Cone(0.5f,0.3f,apariencia);
objRoot.addChild(node);
// Cogemos el TG del universo virtual, ya que queremos que los
// cambios afecten a toda la esdena
TransformGroup tg = u.getViewingPlatform().getViewPlatformTransform();
// Le decimos a nuestro Behavior que aplique los cambios al TG
KeyNavigatorBehavior keyNavBeh = new KeyNavigatorBehavior(tg);
// Establecemos los límites
keyNavBeh.setSchedulingBounds(new BoundingSphere (new Point3d(),100));
// Añadimos el Behavior a la escena
objRoot.addChild(keyNavBeh);
// Compilamos para optimizar
objRoot.compile();
return objRoot;
}
public MovingCone() {
}
public void init() {
setLayout(new BorderLayout());
GraphicsConfiguration config =
SimpleUniverse.getPreferredConfiguration();
Canvas3D c = new Canvas3D(config);
add("Center", c);
// Crear una escena y atacarla al universo virtual
u = new SimpleUniverse(c);
BranchGroup scene = createSceneGraph();
// Mueve el punto de vista un poco atrás para ver los objetos
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}
public void destroy() {
u.cleanup();
}
public static void main(String[] args) {
new MainFrame(new MovingCone(), 256, 256);
}
}
3.2 Construyendo nuestra propia clase Behavior
Para construir nuestra propia clase de respuesta de interacción debemos de implementar a la clase
Behavior, y específicamente a los métodos abstractos initialize () y processStimulus(). En el
constructor de nuestra clase debemos requerir como parámetro un TransformGroup sobre el que
vamos a realizar nuestras respuestas a los estímulos, especificar el evento que lanzará nuestras
modificaciones y reinstalarlo. El siguiente trozo de código muestra un ejemplo en el cual cada vez
que presionamos una tecla rotaremos el TransformGroup.
package es.udc.fi.gc;
import java.awt.event.KeyEvent;
import java.util.Enumeration;
import javax.media.j3d.Behavior;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.WakeupOnAWTEvent;
public class KeyRotationBehavior extends Behavior {
private TransformGroup targetTG;
private Transform3D rotation = new Transform3D();
private double angle = 0.0;
// Constructor con TransformGroup como parámetro
public KeyRotationBehavior(TransformGroup tg) {
this.targetTG = tg;
}
// Establecemos el evento ante el cual desencadenaremos
// nuestras respuestas.
// En este caso, el evento será la presión de una tecla.
public void initialize() {
this.wakeupOn(new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED));
}
// Realizamos la transformación del TransformGroup como respuesta
// al estímulo y volvemos a establecer el estímulo ante el que
// reaccionaremos.
public void processStimulus(Enumeration arg0) {
angle +=0.1;
rotation.rotX(angle);
targetTG.setTransform(rotation);
this.wakeupOn(new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED));
}
}
4.Animación
Ciertos objetos pueden cambiar independientemente de las acciones del usuario, por ejemplo las
manecillas de un reloj deberían de moverse sin tener que depender del estímulo del usuario. Las
manecillas de un reloj son un ejemplo de animación. En este manual consideramos una animación
como los cambios producidos en nuestra escena debido al transcurrir del tiempo y sin necesitar la
interacción del usuario.
Como sucede con las interacciones, las animaciones son implementadas usando los objetos
Behavior, y como hemos visto con la interacción, podemos describir nuestros propios descendientes
de Behavior o utilizar aquellos que ya vienen definidos en la distribución.
Una de las clases de animación que veremos es la clase Interpolator. Un objeto Interpolator, junto
con un objeto Alpha, manipula un objeto visual de la escena para conseguir una animación basada
en el tiempo.
Otro conjunto de clases de animación de objetos visuales son los BillBoard y LOD (Nivel de
detalle) que no responden al paso del tiempo sino al cambio de punto de vista del usuario.
4.1 Objetos Interpolator y Alpha
Un objeto Alpha produce una valor entre 0.0 y 1.0 incluidos que cambian con el tiempo y que
generan una onda y que marcarán los tiempos de nuestra animación y las veces que se repetirá la
onda. La forma de las fases de las que consta nuestra onda son todas parametrizables a través del
objeto Alpha y podremos alterarlas para que, si es lo que deseamos, la onda sólo esté formado por
una fase.
Ahora que ya sabemos qué es un objeto Alpha y para que sirve, veremos como se integra con el
objeto Interpolator. Para conseguir animación con el objeto Interpolator deberemos de seguir los
siguientes pasos:
• crear un objeto al que aplicar la animación con las capacidades apropiadas
• crear un objeto Alpha
• crear el objeto Interpolator haciendo referencia al objeto Alpha y al objeto destino
• añadir los límites para el objeto Interpolator
• añadir el objeto Interpolator a nuestra escena
En el primer ejemplo que mostramos en este tutorial vimos un ejemplo de la aplicación de los
objetos Interpolator y Alpha. El código era muy parecido al siguiente, con la salvedad de que hemos
cambiado el eje de rotación al eje X:
package es.udc.fi.gc;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;
public class RotColorCube extends Applet {
private SimpleUniverse u = null;
public BranchGroup createSceneGraph() {
// Crear la raiz de la rama del gráfico
BranchGroup objRoot = new BranchGroup();
// Crear el nodo TransformGroup y engancharlo a la raíz
// Habilitar la capacidad TRANSFORM_WRITE para que se pueda modificar
// en tiempo de ejecución.
TransformGroup objSpin = new TransformGroup();
objSpin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
// Creamos un objeto Alpha rota indefinidamente y con un
// periodo de 1 minuto
Alpha alpha = new Alpha(-1, 6000);
// Creamos un objeto Interpolator que gire sobre el eje X
Transform3D axisX = new Transform3D();
axisX.setRotation(new Matrix3f(0.0f,1.0f,0.0f,1.0f,0.0f,0.0f,0.0f,0.0f,1.0f));
RotationInterpolator rotX = new RotationInterpolator(alpha, objSpin,axisX,-10,4);
rotX.setSchedulingBounds(new BoundingSphere(new Point3d(),100));
// Construimos nuestra escena
objRoot.addChild(objSpin);
objSpin.addChild(new ColorCube(0.6));
objRoot.addChild(rotX);
// Compilamos para optimizar
objRoot.compile();
return objRoot;
}
public RotColorCube() {
}
public void init() {
setLayout(new BorderLayout());
GraphicsConfiguration config =
SimpleUniverse.getPreferredConfiguration();
Canvas3D c = new Canvas3D(config);
add("Center", c);
// Crear una escena y atacarla al universo virtual
BranchGroup scene = createSceneGraph();
u = new SimpleUniverse(c);
// Mueve el punto de vista un poco atrás para ver los objetos
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}
public void destroy() {
u.cleanup();
}
public static void main(String[] args) {
new MainFrame(new RotColorCube(), 256, 256);
}
}
Además del RotationInterpolator también podremos utilizar los interpoladores ya definidos como
ColorInterpolator, PathInterpolator, PositionInterpolator, ScaleInterpolator y
TransparencyInterpolator, cuyo funcionamiento es muy similar al ya descrito RotationInterpolator.
4.2 Clase BillBoard
El término billboard usado en el contexto de los gráficos en computación se refiere a la técnica
de rotar automáticamente un objeto visual plano para que se encuentre siempre mirando al visor. La
principal motivación para utilizar el billboard es la de disminuir los costes de computación de
objetos complejos. El ejemplo clásico de utilización de billboards es en la representación de árboles
de un paisaje.
4.2.1 Usando un billboard.
El comportamiento billboard funciona bien para los árboles porque estos parecen básicamente
los mismos cuando son vistos desde el frente, atrás o desde cualquier otro ángulo. Como un
billboard hace que un objeto visual parezca el mismo desde todos los ángulos de vista serán
apropiados para representar objetos 3D que son geométricamente simétricos sobre el eje Y.
Usar un objeto billboard es muy similar a un objeto interpolator, excepto que no existe un objeto
Alpha que controle la animación. La animación del billboard se controla por la posición relativa al
visor en el mundo virtual. Los pasos para usar un Billboard son:
1. Crear un TransformGroup con ALLOW_TRANSFORM_WRITE de destino
2. Crear un objeto BillBoard haciendo referencia al TransformGroup de destino.
3. Suministrar unos límites para el objeto BillBoard
4. Construir el escenario.
Un código de ejemplo mostrando su funcionamiento es el siguiente:
public BranchGroup createSceneGraph(SimpleUniverse u) {
BranchGroup objRoot = new BranchGroup();
Vector3f translate = new Vector3f();
Transform3D T3D = new Transform3D();
TransformGroup TGT = new TransformGroup();
TransformGroup TGR = new TransformGroup();
Billboard billboard = null;
BoundingSphere bSphere = new BoundingSphere();
translate.set(new Point3f(1.0f, 1.0f, 0.0f));
T3D.setTranslation(translate);
TGT.setTransform(T3D);
// Establecemos el billboard
TGR.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
billboard = new Billboard(TGR);
billboard.setSchedulingBounds(bSphere);
// Construimos la escena
objRoot.addChild(TGT);
objRoot.addChild(billboard);
TGT.addChild(TGR);
TGR.addChild(crearArboles());
return objRoot;
}
4.3 Animaciones LOD
El término LOD se utiliza para nombrar una técnica que varía la cantidad de detalles de un objeto
virtual basándose en otro valor del mundo virtual. Lo normal es cambiar el LOD basándonos en la
distancia al visor. Tal y como la distancia entre el visor y el objeto aumenta iremos disminuyendo el
nivel de detalle del objeto visual, ganando así tiempo de procesamiento para otros objetos. Otras
aplicaciones en las que se suele utilizar es cuando queremos mantener una velocidad de frames
constante, sin importar la velocidad a la que cambie el mundo virtual.
Cada objeto LOD tiene uno o más objetos Switch como destino, que irá cambiando dependiendo
del valor parámetro que hayamos fijado. En el caso de un objeto DistanceLOD, se cambiará
conforme a la distancia al objeto. Las distancias se especifican con un array que empieza con la
distancia máxima del primer hijo del Switch que será usado. Cuando la distancia desde el objeto
DistanceLOD al visor es mayor que el primer límite, se cambiará al segundo hijo del Switch y así
consecutivamente.
Los pasos a seguir par usar un objeto DistanceLOD son:
1. Crear objetos Switch con la capacidad ALLOW_SWITCH_WRITE
2. Crear una lista de limites de distancias para el objeto DistanceLOD
3. Crear un objeto DistanceLOD usando la lista de límites
4. Establecer el switch de destino para el objeto DistanceLOD
5. Suministrar los límites para el objeto DistanceLOD
6. construir la escena
En el siguiente ejemplo crearemos varias esferas de distinto nivel de detalle y distintos colores
que irán apareciendo dependiendo de la distancia a la que se encuentren del visor.
El código de este ejemplo es el siguiente:
package es.udc.fi.gc;
import java.applet.Applet;
import java.awt.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.behaviors.vp.*;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;
public class LOD extends Applet {
private SimpleUniverse u = null;
public BranchGroup createSceneGraph() {
// Crear la raíz
BranchGroup objRoot = new BranchGroup();
// Creamos el TransformGroup y añadimos las capacidades
// que necesitamos
TransformGroup objTrans = new TransformGroup();
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
objRoot.addChild(objTrans);
// Creamos el switch
Switch sw = new Switch(0);
sw.setCapability(javax.media.j3d.Switch.ALLOW_SWITCH_READ);
sw.setCapability(javax.media.j3d.Switch.ALLOW_SWITCH_WRITE);
// Creamos varios niveles para el switch, con esferas menos
// detalladas y de distintos colores que irán cambiando conforme
// a la distancia del visor
Appearance appearance40 = new Appearance();
appearance40.setColoringAttributes(new ColoringAttributes(0.7f, 1.0f, 0.4f,1));
Sphere sphere40 = new Sphere(0.4f, Sphere.GENERATE_NORMALS, 40);
sphere40.setAppearance(appearance40);
Appearance appearance10 = new Appearance();
appearance10.setColoringAttributes(new ColoringAttributes(0.3f, 1.0f, 1.0f,1));
Sphere sphere10 = new Sphere(0.4f, Sphere.GENERATE_NORMALS, 10);
sphere10.setAppearance(appearance10);
Appearance appearance5 = new Appearance();
appearance5.setColoringAttributes(new ColoringAttributes(1.0f, 0.5f, 1.0f,1));
Sphere sphere5 = new Sphere(0.4f, Sphere.GENERATE_NORMALS, 5);
sphere5.setAppearance(appearance5);
Appearance appearance3 = new Appearance();
appearance3.setColoringAttributes(new ColoringAttributes(1.0f, 1.0f, 0.5f,1));
Sphere sphere3 = new Sphere(0.4f, Sphere.GENERATE_NORMALS, 3);
sphere3.setAppearance(appearance3);
sw.addChild(sphere40);
sw.addChild(sphere10);
sw.addChild(sphere5);
sw.addChild(sphere3);
// Añadimos el switch
objTrans.addChild(sw);
BoundingSphere bounds =
new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
// Establecemos el LOD
float[] distances = new float[3];
distances[0] = 5.0f;
distances[1] = 10.0f;
distances[2] = 25.0f;
DistanceLOD lod = new DistanceLOD(distances);
lod.addSwitch(sw);
lod.setSchedulingBounds(bounds);
objTrans.addChild(lod);
// Optimizaciones
objRoot.compile();
return objRoot;
}
public LOD() {
}
public void init() {
setLayout(new BorderLayout());
GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
Canvas3D c = new Canvas3D(config);
add("Center", c);
// Creamos la escena y la atacamos al universo virtual
BranchGroup scene = createSceneGraph();
u = new SimpleUniverse(c);
// añadimos un zoom a la escena
ViewingPlatform viewingPlatform = u.getViewingPlatform();
viewingPlatform.setNominalViewingTransform();
// Añadimos un OrbitBehavior para el Zoom, pero deshabilitamos
// la rotación y la traslación
OrbitBehavior orbit = new OrbitBehavior(c,
OrbitBehavior.REVERSE_ZOOM |
OrbitBehavior.DISABLE_ROTATE |
OrbitBehavior.DISABLE_TRANSLATE);
BoundingSphere bounds =
new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);
orbit.setSchedulingBounds(bounds);
viewingPlatform.setViewPlatformBehavior(orbit);
u.addBranchGraph(scene);
}
public void destroy() {
u.cleanup();
}
public static void main(String[] args) {
new MainFrame(new LOD(), 512, 512);
}
}
5. Iluminación
En el mundo real, en la visualización de un objeto no sólo se tiene en cuenta el color y la forma
de este, sino que también tenemos en cuenta la cantidad y el ángulo de la luz que recibe y las luces y
sombras de los objetos adyacentes. En este capítulo presentaremos el modelo de iluminación en
Java 3D y veremos cómo crear ambientes y sombras para nuestro universo virtual.
5.1 El modelo de iluminación en Java 3D
Como ya se dijo anteriormente, los colores que recibimos son una combinación de las
propiedades físicas del objeto, las características de las fuentes de luz, la posición relativa de las
fuentes de luz y los ángulos desde los cuales es visto el objeto. Java 3D usa un modelo de
iluminación que intenta simular el ambiente real, pero como los cálculos de luminosidad son muy
costosos en la especificación Java 3D se ha propuesto un modelo de iluminación que no tiene en
cuenta los efectos producidos por el resto de los objetos visuales.
El modelo de iluminación de Java 3D incorpora tres tipos de reflexiones del mundo real:
ambiente, difusa y enfocada. La reflexión ambiente resulta de las luces de ambiente, que son luces
de poca intensidad pero constantes en todo el escenario. La reflexión difusa es la reflexión normal,
de una fuente de luz sobre un objeto visual. Las reflexiones enfocadas son las reflexiones de mucha
intensidad de una fuente de luz sobre un objeto.
Una limitación importante en el modelo de iluminación de Java 3D es que existe un número
máximo de fuentes de luces en un escenario. En estos momentos, el límite está en los 8 focos de
luces, y si intentamos sobrepasar este límite no podremos observar más de 8 focos de luz. Aunque
esto depende de la implementación de la librería, no es recomendable sobrepasar este límite, ya que
está definido como un valor mínimo por la especificación y no podremos asegurar el buen
funcionamiento de nuestra aplicación en otras plataformas si se sobrepasa.
5.2 Receta para los objetos visuales de luz
Para habilitar los objetos visuales de luz en nuestro universo necesitaremos seguir un número de
pasos, que como es habitual en este tutorial, los describiremos en forma de receta de cocina. Antes
de nada, mencionaremos un detalles con cierta importancia: las luces no son objetos visibles, con lo
que no intentes ver un foco en el universo virtual.
Los pasos a seguir son:
1. Especificar la fuente de luz
a) Establecer los límites
b) Añadirlo al escenario
2. Objetos visuales
a) Establecer las normales
b) Establecer las propiedades de los materiales
Si nos olvidamos de cualquiera de estos pasos no podremos observar los efectos de la fuente de
luz. La presencia del objeto Material en el objeto Appearance de un objeto visual es el que nos
habilitará el modelo de luces para este objeto. Sin la presencia de Material el objeto será coloreado,
pero no será iluminado por las fuentes de luz.
En la siguiente figura se muestra una luz débil de ambiente sobre una esfera:
Si a la figura anterior le quitamos la luz de ambiente, la imagen producida será la siguiente:
Si comparamos las dos imágenes veremos una pequeña diferencia de color en las esferas, esta
diferencia será la provocada por la luz de ambiente.
El código del programa que produjo estas imágenes es el siguiente:
package es.udc.fi.gc;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;
public class BasicLight extends Applet {
private SimpleUniverse u = null;
public BranchGroup createSceneGraph() {
BranchGroup objRoot = new BranchGroup();
Background background = new Background(0.2f, 0.4f, 0.5f);
background.setApplicationBounds(new BoundingSphere());
Appearance appearance = new Appearance();
appearance.setMaterial(new Material());
// Generamos la esfera con la una apariencia y generamos las normales
Sphere sphere = new Sphere(0.5f, Sphere.GENERATE_NORMALS ,appearance);
AmbientLight light = new AmbientLight();
light.setInfluencingBounds(new BoundingSphere());
objRoot.addChild(light);
objRoot.addChild(sphere);
objRoot.addChild(background);
// Compilamos para optimizar
objRoot.compile();
return objRoot;
}
public BasicLight() {
}
public void init() {
setLayout(new BorderLayout());
GraphicsConfiguration config =
SimpleUniverse.getPreferredConfiguration();
Canvas3D c = new Canvas3D(config);
add("Center", c);
// Crear una escena y atacarla al universo virtual
BranchGroup scene = createSceneGraph();
u = new SimpleUniverse(c);
// Mueve el punto de vista un poco atrás para ver los objetos
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}
public void destroy() {
u.cleanup();
}
public static void main(String[] args) {
new MainFrame(new BasicLight(), 256, 256);
}
}
En el siguiente ejemplo colocaremos una luz direccional y aplicada a variaciones de la misma
figura geométrica y veremos como la forma de los objetos visuales afecta al tipo de reflexión de la
luz sobre estos.
La única diferencia en el código de estos dos ejemplos radica en el número de vértices de las
figuras. Mientras que en la figura de la derecha la esfera está construida con 20 vértices, la figura de
la izquieda está construida con 40. El código de la luz direccional es el siguiente:
Sphere sphere = new Sphere(0.5f, Sphere.GENERATE_NORMALS ,40,appearance);
DirectionalLight light = new DirectionalLight();
light.setInfluencingBounds(new BoundingSphere());
light.setDirection(-1.0f, -1.0f, 0.0f);
5.3 Clases de Luces
El API Java 3D nos suministra cuatro tipo de luces. Cada una de ellas es hija de la clase Light.
La clase Light suministra los métodos y constantes de las capacidades asociadas para manipular el
estado, color y límites de un objeto Light. Los tipos de luces y sus efectos son los siguientes:
1. La clase javax.media.j3d.AmbientLight nos suministrará una intensidad de luz igual para
todas las localizaciones y en todas las direcciones.
2. La clase javax.media.j3d.DirectionalLight modela fuentes de luz distantes teniendo un vector
de dirección constante.
3. La clase javax.media.j3d.PointLight especifica una fuente de luz atenuada en un lugar
concreto del espacio que radia luz de forma igual en todas las direcciones.
4. La clase javax.media.j3d.SpotLight es una subclase de PointLight con la adicción de la
dirección, ángulo de difusión y atributos de concentración.
5.4 El objeto Material
Las propiedades de un material son especificadas en el objeto Material que está integrado en el
objeto de orden superior Appearance. El objeto Material especifica los colores de ambiente, de
difusión, direccionales y emisivos y el valor de brillo. Cada uno de los tres primeros colores son
usados en el modelo de iluminación para calcula la reflexión correspondiente. El color emisivo
permite a los objetos visuales brillar en la oscuridad y el valor de brillo sólo es usado para calcular
la reflexión direccional. Los métodos para especificar estos parámetros son setAmbientColor
(Color3f), setDiffuseColor(Color3f), setEmissiveColor(Color3f), setShininess(float) y
setSpecularColor(Color3f).
5.5 Normales
Tal y como se dijo en la sección 5.2, las normales son un requisito indispensable para la
realización de sombreados sobre objetos visuales. La normal de una superficie nos indica cual es la
parte de la superficie que se verá afectada por los efectos de iluminación. Por esto es tan importante
el definir este parámetro de forma correcta. Para definr correctamente una normal debemos definir
un vector que apunte hacia las fuentes de luz. Para definir las normales cuando creamos objetos
visuales usando las clases Geometry, podremos usar uno de los métodos setNormal() para
especificar las normales para cada vértice.
6. Texturas
6.1 ¿Qué es una textura?
La apariencia de los objetos del mundo real depende de su textura. La textura de un objeto es
realmente la geometría de menor detalle de la superficie del objeto. Una textura especifica la
complejidad y densidad de la geometría de la superficie.
Si intentáramos modelar una representación de un objeto real en Java 3D con su textura mediante
las primitivas que hemos visto, el modelo sería imposible de representar debido a su alta
complejidad. Lo que se hace para la simulación de texturas es la simulación de estas mediante un
gráfico o dibujo, y asignándolo a la forma geométrica del modelo del objeto visual.
6.2 Construyendo texturas
Para aplicar una textura a un polígono en Java 3D debemos de seguir una serie de pasos que
expresaremos en modo de receta y que son:
1. crear un nodo Appearance
2. cargar el mapa de la textura
3. establecer la textura en el nodo Appearance
4. especificar la localización de las imágenes de textura en la geometría
6.2.1 Preparar la imagen de textura
Este paso no requiere programación alguna y consta de la edición de nuestra textura en una
herramienta que nos permita la edición de una imagen. Algo que tenemos que tener en cuenta aquí
es que la imagen debe de tener una dimensión potencia de 2, para optimizar el rendimiento de la
aplicación, y asegurarnos de que la imagen se graba en un formato de imagen que los cargadores de
Java sean capaces de entender.
6.2.2 Carga de la textura
Ahora que ya hemos preparado nuestro archivo de texturas, podremos cargarlo. Para cargar la
textura utilizaremos una clase de utilidad llamada TextureLoader. Esta clase lo que hace es cargar el
archivo de texturas desde un archivo del sistema de archivos o desde un URL para devolver un
objeto ImageComponent2D. El código para realizar esta operación es el siguiente:
TextureLoader loader = new TextureLoader(“textura.gif”, this);
ImageComponent2 image = loader.getImage();
El argumento “this” en el constructor de TextureLoader es una referencia a un “Observador”, ya
que para cargar la textura se utiliza el paquete java.awt.image y este paquete carga las clases de
forma asíncrona.
6.2.3 Crear el nodo Appearance
Para ser usada como textura la imagen que hemos cargado en el apartado anterior, debemos de
establecerla como textura en un objeto Texture2D. El siguiente código muestra un ejemplo de carga
y asignación de la textura a un nodo Appearance:
TextureLoader loader = new TextureLoader(“textura.jpg”, this);
ImageComponent2D image = loader.getImage();
Texture2D texture = new Texture2D();
texture.setImage(0, image);
Appearance appearance = new Apperance();
appearance.setTexture(texture);
6.2.4 Especificando las TextureCoordinates
Ahora lo único que nos falta es especificar los puntos de colocación de la textura en la geometría
de nuestro objeto visual. Cada coordenada de la textura especifica un punto de la textura a ser
aplicada a un vértice. Con la especificación de algunos de los puntos de la imagen a ser aplicados a
los vértices de la geometría, la imagen será “acondicionada” para que cuadre en ella. Algo que
debemos de tener en cuenta es que las coordenadas de la textura son coordenadas con valores
normalizados. Esto es, cuando establezcamos una coordenada de textura a 1.0f, esto querrá
significar que queremos que una de la esquina de la textura estará en un límite del plano. Veámoslo
con un ejemplo.
En el siguiente trozo de código crearemos un plano y estableceremos en el una textura:
QuadArray plane = new QuadArray(4, GeometryArray.COORDINATES |
GeometryArray.TEXTURE_COORDINATE_2);
Point3f p = new Point3f();
p.set(-1.0f, 1.0f, 0.0f);
plane.setCoordinate(0,p);
p.set(-1.0f, -1.0f, 0.0f);
plane.setCoordinate(1,p);
p.set(1.0f, -1.0f, 0.0f);
plane.setCoordinate(2,p);
p.set(1.0f, 1.0f, 0.0f);
plane.setCoordinate(3,p);
Point2f q = new Point2f();
q.set(0.0f, 1.0f);
plane.setTextureCoordinate(0.q);
q.set(0.0f, 0.0f);
plane.setTextureCoordinate(1,q);
q.set(1.0f, 0.0f);
plane.setTextureCoordinate(2,q);
q.set(1.0f, 1.0f);
plane.setTextureCoordinate(3,q);
En el último ejemplo de este tutorial veremos el código de un programa que carga una textura de
piedra en un cubo que gira. En este ejemplo no será necesario especificar las coordenadas de la
textura debido a que nuestro objeto visual es cuadrado y encajará perfectamente.
package es.udc.fi.gc;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.image.TextureLoader;
import com.sun.j3d.utils.geometry.Box;
import javax.media.j3d.*;
import javax.vecmath.*;
public class TextureExample extends Applet {
private java.net.URL texImage = null;
private SimpleUniverse u = null;
public BranchGroup createSceneGraph() {
BranchGroup objRoot = new BranchGroup();
TransformGroup objTrans = new TransformGroup();
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
objRoot.addChild(objTrans);
Appearance app = new Appearance();
Texture tex = new TextureLoader(texImage, this).getTexture();
app.setTexture(tex);
Box textureCube =
new Box(0.4f, 0.4f, 0.4f, Box.GENERATE_TEXTURE_COORDS, app);
objTrans.addChild(textureCube);
Transform3D yAxis = new Transform3D();
Alpha rotationAlpha =
new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0, 4000, 0, 0, 0, 0, 0);
RotationInterpolator rotator =
new RotationInterpolator(
rotationAlpha,
objTrans,
yAxis,
0.0f,
(float) Math.PI * 2.0f);
BoundingSphere bounds =
new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);
rotator.setSchedulingBounds(bounds);
objTrans.addChild(rotator);
objRoot.compile();
return objRoot;
}
public TextureExample() {
}
public TextureExample(java.net.URL url) {
texImage = url;
}
public void init() {
if (texImage == null) {
// the path to the image for an applet
try {
texImage =new java.net.URL(
getCodeBase().toString() + "../images/stone.jpg");
} catch (java.net.MalformedURLException ex) {
System.out.println(ex.getMessage());
System.exit(1);
}
}
setLayout(new BorderLayout());
GraphicsConfiguration config =
SimpleUniverse.getPreferredConfiguration();
Canvas3D c = new Canvas3D(config);
add("Center", c);
BranchGroup scene = createSceneGraph();
u = new SimpleUniverse(c);
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}
public void destroy() {
u.cleanup();
}
public static void main(String[] args) {
java.net.URL url = null;
if (args.length > 0) {
try {
url = new java.net.URL("file:" + args[0]);
} catch (java.net.MalformedURLException ex) {
System.out.println(ex.getMessage());
System.exit(1);
}
} else {
try {
url = new java.net.URL("file:c:stone.jpg");
} catch (java.net.MalformedURLException ex) {
System.out.println(ex.getMessage());
System.exit(1);
}
}
new MainFrame(new TextureExample(url), 256, 256);
}
}
7. Bibliografía
1. Java 3D Specificacion 1.1. (Sun Microsystems)
2. Java 3D Specificacion 1.3.1 (Sun Microsystems)
3. Getting Started with the Java 3D API v1.X (Sun Microsystems)

Más contenido relacionado

La actualidad más candente

Distributed objects & components of corba
Distributed objects & components of corbaDistributed objects & components of corba
Distributed objects & components of corbaMayuresh Wadekar
 
Administración de procesos y del procesador
Administración de procesos y del procesadorAdministración de procesos y del procesador
Administración de procesos y del procesadorFernando Camacho
 
METODOLOGÍA UWE (UML-BASED WEB ENGINEERING)
METODOLOGÍA UWE (UML-BASED WEB ENGINEERING) METODOLOGÍA UWE (UML-BASED WEB ENGINEERING)
METODOLOGÍA UWE (UML-BASED WEB ENGINEERING) Germán Sánchez
 
Procesos Ligeros: Hilos o Hebras
Procesos Ligeros: Hilos o HebrasProcesos Ligeros: Hilos o Hebras
Procesos Ligeros: Hilos o HebrasJ M
 
Redes Ethernet / IEEE 802.3
Redes Ethernet / IEEE 802.3Redes Ethernet / IEEE 802.3
Redes Ethernet / IEEE 802.3roberticorios
 
Desarrollo SW Basado en Componentes
Desarrollo SW Basado en ComponentesDesarrollo SW Basado en Componentes
Desarrollo SW Basado en Componentestoryneutral
 
Actividad 10: Reporte de polimorfismo, herencia & encapsulamiento
Actividad  10: Reporte de polimorfismo, herencia & encapsulamientoActividad  10: Reporte de polimorfismo, herencia & encapsulamiento
Actividad 10: Reporte de polimorfismo, herencia & encapsulamientograchika
 
Multiprogramacion
MultiprogramacionMultiprogramacion
MultiprogramacionIrisMTF16
 

La actualidad más candente (20)

Servidores web o http
Servidores web o httpServidores web o http
Servidores web o http
 
Diagramas de comportamientos
Diagramas de comportamientosDiagramas de comportamientos
Diagramas de comportamientos
 
ADO.NET
ADO.NETADO.NET
ADO.NET
 
Bases de datos embebidas
Bases de datos embebidasBases de datos embebidas
Bases de datos embebidas
 
Distributed objects & components of corba
Distributed objects & components of corbaDistributed objects & components of corba
Distributed objects & components of corba
 
Administración de procesos y del procesador
Administración de procesos y del procesadorAdministración de procesos y del procesador
Administración de procesos y del procesador
 
Tecnología Orientada a Objetos
Tecnología Orientada a ObjetosTecnología Orientada a Objetos
Tecnología Orientada a Objetos
 
METODOLOGÍA UWE (UML-BASED WEB ENGINEERING)
METODOLOGÍA UWE (UML-BASED WEB ENGINEERING) METODOLOGÍA UWE (UML-BASED WEB ENGINEERING)
METODOLOGÍA UWE (UML-BASED WEB ENGINEERING)
 
Procesos Ligeros: Hilos o Hebras
Procesos Ligeros: Hilos o HebrasProcesos Ligeros: Hilos o Hebras
Procesos Ligeros: Hilos o Hebras
 
Redes Ethernet / IEEE 802.3
Redes Ethernet / IEEE 802.3Redes Ethernet / IEEE 802.3
Redes Ethernet / IEEE 802.3
 
Desarrollo SW Basado en Componentes
Desarrollo SW Basado en ComponentesDesarrollo SW Basado en Componentes
Desarrollo SW Basado en Componentes
 
Redes 2
Redes 2Redes 2
Redes 2
 
Java.sql.*
Java.sql.*Java.sql.*
Java.sql.*
 
Servidor dhcp
Servidor dhcpServidor dhcp
Servidor dhcp
 
deadlok- interbloqueos
deadlok- interbloqueosdeadlok- interbloqueos
deadlok- interbloqueos
 
Unidad i
Unidad iUnidad i
Unidad i
 
Actividad 10: Reporte de polimorfismo, herencia & encapsulamiento
Actividad  10: Reporte de polimorfismo, herencia & encapsulamientoActividad  10: Reporte de polimorfismo, herencia & encapsulamiento
Actividad 10: Reporte de polimorfismo, herencia & encapsulamiento
 
Introduccion a graficos mediante JAVA 2D
Introduccion a graficos mediante JAVA 2DIntroduccion a graficos mediante JAVA 2D
Introduccion a graficos mediante JAVA 2D
 
Multiprogramacion
MultiprogramacionMultiprogramacion
Multiprogramacion
 
Traductor y su estructura
Traductor y su estructuraTraductor y su estructura
Traductor y su estructura
 

Destacado

Análisis de alternativas para el desarrollo de aplicaciones gráficas 3D sobre...
Análisis de alternativas para el desarrollo de aplicaciones gráficas 3D sobre...Análisis de alternativas para el desarrollo de aplicaciones gráficas 3D sobre...
Análisis de alternativas para el desarrollo de aplicaciones gráficas 3D sobre...Rubén Talón Argente
 
Genesisslideshere
GenesisslideshereGenesisslideshere
GenesisslideshereGENEDURAN
 
Java 3d
Java 3dJava 3d
Java 3djtk1
 
Actividades de aprendizaje en Mundos Virtuales 3D. Análisis de casos prácticos.
Actividades de aprendizaje en Mundos Virtuales 3D. Análisis de casos prácticos.Actividades de aprendizaje en Mundos Virtuales 3D. Análisis de casos prácticos.
Actividades de aprendizaje en Mundos Virtuales 3D. Análisis de casos prácticos.Ruth Martínez
 
Algoritmos basicos de dibujo en 2 d
Algoritmos basicos de dibujo en 2 dAlgoritmos basicos de dibujo en 2 d
Algoritmos basicos de dibujo en 2 dUDEC
 
CUERPOS GEOMÉTRICOS O FIGURAS 3D
CUERPOS GEOMÉTRICOS O FIGURAS 3DCUERPOS GEOMÉTRICOS O FIGURAS 3D
CUERPOS GEOMÉTRICOS O FIGURAS 3DSandra Farías
 
8b Curso de POO en java - paso de diagrama clases a java 1
8b Curso de POO en java - paso de diagrama clases a java 18b Curso de POO en java - paso de diagrama clases a java 1
8b Curso de POO en java - paso de diagrama clases a java 1Clara Patricia Avella Ibañez
 
MODELADO DE PERSONAJES 3D
MODELADO DE PERSONAJES 3DMODELADO DE PERSONAJES 3D
MODELADO DE PERSONAJES 3Dguestd52308
 

Destacado (13)

Análisis de alternativas para el desarrollo de aplicaciones gráficas 3D sobre...
Análisis de alternativas para el desarrollo de aplicaciones gráficas 3D sobre...Análisis de alternativas para el desarrollo de aplicaciones gráficas 3D sobre...
Análisis de alternativas para el desarrollo de aplicaciones gráficas 3D sobre...
 
Genesisslideshere
GenesisslideshereGenesisslideshere
Genesisslideshere
 
Java 3d
Java 3dJava 3d
Java 3d
 
Java 3D
Java 3DJava 3D
Java 3D
 
esferas en 3d
esferas en 3desferas en 3d
esferas en 3d
 
Figuras 3 d
Figuras 3 dFiguras 3 d
Figuras 3 d
 
Aplicacion de texturas
Aplicacion de texturasAplicacion de texturas
Aplicacion de texturas
 
Actividades de aprendizaje en Mundos Virtuales 3D. Análisis de casos prácticos.
Actividades de aprendizaje en Mundos Virtuales 3D. Análisis de casos prácticos.Actividades de aprendizaje en Mundos Virtuales 3D. Análisis de casos prácticos.
Actividades de aprendizaje en Mundos Virtuales 3D. Análisis de casos prácticos.
 
Cociente incremental
Cociente incrementalCociente incremental
Cociente incremental
 
Algoritmos basicos de dibujo en 2 d
Algoritmos basicos de dibujo en 2 dAlgoritmos basicos de dibujo en 2 d
Algoritmos basicos de dibujo en 2 d
 
CUERPOS GEOMÉTRICOS O FIGURAS 3D
CUERPOS GEOMÉTRICOS O FIGURAS 3DCUERPOS GEOMÉTRICOS O FIGURAS 3D
CUERPOS GEOMÉTRICOS O FIGURAS 3D
 
8b Curso de POO en java - paso de diagrama clases a java 1
8b Curso de POO en java - paso de diagrama clases a java 18b Curso de POO en java - paso de diagrama clases a java 1
8b Curso de POO en java - paso de diagrama clases a java 1
 
MODELADO DE PERSONAJES 3D
MODELADO DE PERSONAJES 3DMODELADO DE PERSONAJES 3D
MODELADO DE PERSONAJES 3D
 

Similar a Java 3 d manual

Introduccion a java3 d
Introduccion a java3 dIntroduccion a java3 d
Introduccion a java3 dFONCODES
 
Resumen el gran libro de andorid
Resumen el gran libro de andoridResumen el gran libro de andorid
Resumen el gran libro de andoridJilton Delgado
 
IOF Labs Weekend - Android (27082011)
IOF Labs Weekend - Android (27082011)IOF Labs Weekend - Android (27082011)
IOF Labs Weekend - Android (27082011)repcax
 
Como insertar una imagen en eclipse java
Como insertar una imagen en eclipse javaComo insertar una imagen en eclipse java
Como insertar una imagen en eclipse javaJosué Naquid
 
[ES] Conectividad de java a base de datos(jdbc)
[ES] Conectividad de java a base  de datos(jdbc)[ES] Conectividad de java a base  de datos(jdbc)
[ES] Conectividad de java a base de datos(jdbc)Eudris Cabrera
 
Java awt javax swing
Java awt  javax swingJava awt  javax swing
Java awt javax swingdevsco63
 
MOOC de Realidad aumentada (Coursera)
MOOC de Realidad aumentada (Coursera)MOOC de Realidad aumentada (Coursera)
MOOC de Realidad aumentada (Coursera)C Xyon PalaXy
 
Presentacion aplicaciones en java
Presentacion aplicaciones en javaPresentacion aplicaciones en java
Presentacion aplicaciones en javarsalazar16988
 
Como insertar una imagen en eclipse
Como insertar una imagen en eclipseComo insertar una imagen en eclipse
Como insertar una imagen en eclipsejaquiiMc
 
POOABD (POO Aplicada a B Datos) - API JDBC - Parte 2
POOABD (POO Aplicada a B Datos) - API JDBC - Parte 2POOABD (POO Aplicada a B Datos) - API JDBC - Parte 2
POOABD (POO Aplicada a B Datos) - API JDBC - Parte 2Laura Noussan Lettry
 
Apuntes de introduccion a la programación
Apuntes de introduccion a la programaciónApuntes de introduccion a la programación
Apuntes de introduccion a la programaciónvictdiazm
 
Presentacion carlos
Presentacion carlosPresentacion carlos
Presentacion carlosKarlytozdj
 

Similar a Java 3 d manual (20)

Introduccion a java3 d
Introduccion a java3 dIntroduccion a java3 d
Introduccion a java3 d
 
Api 3 d
Api 3 dApi 3 d
Api 3 d
 
Resumen el gran libro de andorid
Resumen el gran libro de andoridResumen el gran libro de andorid
Resumen el gran libro de andorid
 
IOF Labs Weekend - Android (27082011)
IOF Labs Weekend - Android (27082011)IOF Labs Weekend - Android (27082011)
IOF Labs Weekend - Android (27082011)
 
GWT - Una introducción
GWT - Una introducciónGWT - Una introducción
GWT - Una introducción
 
Tarea 05 OP.docx
Tarea 05 OP.docxTarea 05 OP.docx
Tarea 05 OP.docx
 
Como insertar una imagen en eclipse java
Como insertar una imagen en eclipse javaComo insertar una imagen en eclipse java
Como insertar una imagen en eclipse java
 
Modelado por descomposición
Modelado por descomposiciónModelado por descomposición
Modelado por descomposición
 
Modelo por descomposicion
Modelo por descomposicionModelo por descomposicion
Modelo por descomposicion
 
Programacion java basica
Programacion java basicaProgramacion java basica
Programacion java basica
 
[ES] Conectividad de java a base de datos(jdbc)
[ES] Conectividad de java a base  de datos(jdbc)[ES] Conectividad de java a base  de datos(jdbc)
[ES] Conectividad de java a base de datos(jdbc)
 
Java awt javax swing
Java awt  javax swingJava awt  javax swing
Java awt javax swing
 
Clase swing
Clase swingClase swing
Clase swing
 
MOOC de Realidad aumentada (Coursera)
MOOC de Realidad aumentada (Coursera)MOOC de Realidad aumentada (Coursera)
MOOC de Realidad aumentada (Coursera)
 
Programacion en java
Programacion en javaProgramacion en java
Programacion en java
 
Presentacion aplicaciones en java
Presentacion aplicaciones en javaPresentacion aplicaciones en java
Presentacion aplicaciones en java
 
Como insertar una imagen en eclipse
Como insertar una imagen en eclipseComo insertar una imagen en eclipse
Como insertar una imagen en eclipse
 
POOABD (POO Aplicada a B Datos) - API JDBC - Parte 2
POOABD (POO Aplicada a B Datos) - API JDBC - Parte 2POOABD (POO Aplicada a B Datos) - API JDBC - Parte 2
POOABD (POO Aplicada a B Datos) - API JDBC - Parte 2
 
Apuntes de introduccion a la programación
Apuntes de introduccion a la programaciónApuntes de introduccion a la programación
Apuntes de introduccion a la programación
 
Presentacion carlos
Presentacion carlosPresentacion carlos
Presentacion carlos
 

Java 3 d manual

  • 1. Introducción a Java 3D Lorenzo Martín Edreira Rubén Melcón Fariña
  • 2. 1 INTRODUCCIÓN 1.1¿Qué es la API Java 3D API? La API Java 3D es una jerarquía de clases java que sirven como interfaz a sistemas gráficos de 3 dimensiones de imagen y sonido. Los programadores trabajan con constructores de alto nivel para crear y manipular objetos 3D. Estos objetos residen en un universo virtual que es dibujado para su presentación a los usuarios. Un programa Java 3D crea instancias de objetos Java 3D y los coloca en la estructura de datos de objetos gráficos de la escena. El gráfico de la escena está organizado en forma de árbol y especifica completamente el contenido del universo virtual, y cómo este es dibujado. 1.2 La API Java 3D Todos los programas Java 3D están, por lo menos parcialmente, formados por objetos de la jerarquía de clases Java 3D. Esta colección de objetos describe un universo virtual, el cual va a ser dibujado. La API define sobre 100 clases presentadas en el paquete javax.media.j3d. Estas clases son referidas comúnmente como las clases núcleo de Java 3D En adicción a las clases núcleo de Java 3D nos encontraremos con las clases de utilidades que incluyen cargadores de contenido, clases de geometría ... que suponen una adicción conveniente y potente a las clases de bajo nivel que componen la clases núcleo de Java 3D. Las clases de utilidades las encontraremos en el paquete com.sun.j3d.utils. Como se muestra en el nombre del paquete, estas clases no pertenecen a la especificación de la API Java 3D, y son clases propias de la implementación de referencia realizada por Sun Microsystems. Además de los paquetes de clases núcleo de Java 3D y las clases de utilidades proporcionadas por Sun Microsystems, nos encontraremos con clases del paquete java.awt, que nos definen clases para crear un ventana en la que mostrar el gráfico, y el paquete javax.vecmatch ,que definen clases matemáticas de puntos de vectores, matrices y otros objetos matemáticos. La API Java 3D no es un conjunto de paquetes y clases que vengan de forma predeterminada en la distribución Java normal (J2SE), sino que es una extensión que debemos instalar de forma aparte. En nuestro caso utilizaremos la versión Java 3D 1.3.1 con interfaz a OpenGL y el J2SE 1.4.2 en una plataforma Microsoft Windows. A parte del SDK Java3D para MS Windows, también podremos descargarnos la versión con interfaz a DirectDraw, también para Microsoft Windows y la versión para Solaris (versión SPARC) de Java 3D con interfaz a OpenGL. Los archivos que nos encontraremos en nuestra distribución son: <JREDIR>binJ3D.dll <JREDIR>binj3daudio.dll <JREDIR>binJ3DUtils.dll <JREDIR>libextvecmath.jar <JREDIR>libextj3dcore.jar <JREDIR>libextj3daudio.jar
  • 3. <JREDIR>libextj3dutils.jar <JDKDIR>j3d-utils-src.jar <JDKDIR>demojava3d Una vez instalado el paquete, ya podremos desarrollar nuestras aplicaciones Java3D, pero antes daremos unas nociones básicas de la arquitectura de construcción de los universos virtuales en Java 3D. 1.3 Estructura de la escena de un gráfico Un universo virtual Java 3D se crea a partir de un escenario gráfico. Un escenario gráfico se crea usando instancias de clases Java 3D. El conjunto de objetos gráficos está formado por objetos que definen la geometría, sonido, luces, localización, orientación y apariencia de objetos de audio y visuales. Una definición de un conjunto de objetos gráficos es una estructura de datos compuesto por nodos y arcos. Un nodo es una elemento de datos y un arco es una relación entre nodos. Los nodos en el diagrama del escenario son las instancias de las clases Java3D. Los arcos representan los dos tipos de relaciones (Padre-Hijo y Referencia) entre las instancias Java 3D. Las relaciones Padre-Hijo forman una relación entre los objetos del escenario, y las relaciones de referencia asocian los nodos NodeComponent con un nodo del escenario definiendo la geometría y atributos de la apariencia usados para dibujar los objetos visuales 1.4 Instrucciones para escribir Programas Java 3D Para escribir programas que utilicen las clases Java 3D es recomendable seguir una secuencia de pasos lógicos que nos permitirán una mejor estructuración de nuestros programas y el uso de un método de construcción común en todos los programas. Los pasos para construir un programa Java 3D con siete: 1) Crear un objeto Canvas3D 2) Crear un objeto VirtualUniverse 3) Crear un objeto Locale, atacando al VirtualUniverse 4) Construir un gráfico de la rama de visualización a) Crear un objeto vista b) Crear un objeto ViewPlatform c) Crear un objeto PhysicalBody d) Crear un objeto PhysicalEnvisonment e) Atacar ViewPlatform, PhysicalBody, PhysicalEnvironment y Canvas3D al objeto View 5) Construir un gráfico de la rama del contenido 6) Compilar las ramas 7) Insertar las distintas partes que componen el escenario en el Locale
  • 4. 1.4.1 Una receta simple para escribir los programas Java 3D La mayoría de nuestros programas Java 3D utilizan una estructura en el proceso de visualización casi idéntica. Debido a esto, Sun Microsystems ha provisto una clase Java que nos creará todo el universo virtual, y nos ahorrará tiempo de codificación. Esta clase, llamada SimpleUniverse, es el punto de inicio más fácil para empezar a programar, porque nos podremos olvidar de las estructuras de visualización y nos podremos concentrar en las estructuras de contenido. Un inconveniente que tendremos con esta clase es que no podremos tener múltiples vistas del universo virtual, y lo que no debemos de olvidar, esta clase no forma parte de la especificación Java 3D, con lo que no tenemos asegurado que se ejecuten nuestros programas en otras implementaciones. Con la clase SimpleUniverse nuestro esquema de construcción de programas Java 3D nos quedará: 1) Crear un objeto Canvas3D 2) Crear un objeto SimpleUniverse que haga referencia al objeto Canvas3D a) Personalizar SimpleUniverse 3) Construir la rama de contenido (BranchGroup) 4) Compilar el gráfico de la rama de contenido 5) Insertar las estructuras de la rama de contenido en el Locale del SimpleUniverse Una vez creados los objetos Canvas3D y SimpleUniverse, el siguiente paso es la creación de las estructuras de la rama de contenido. Después de crear la rama de contenido, esta será insertada con el método addBranchGraph(BranchGroup) de SimpleUniverse. En el objeto BranchGroup definiremos todos los objetos Java 3D que queremos que se encuentren contenidos en nuestro escenario, así como las clases de comportamiento que queremos que afecten a nuestros objetos. Por ejemplo, el objeto Transform3D nos permitirá rotar, escalar y trasladar nuestros objetos, y la clase TransformGroup nos permitirá agrupar las clases de comportamiento. La forma de utilizar estas clases de comportamiento será: 1) Crear el objeto Transform3D 2) Crear los objetos 3D de la escena 3) Añadir los objetos Transform3D a un TransformGroup 4) Añadir los objetos 3D a TransformGroup 5) Añadir TransformGroup al objeto BranchGroup. 1.5 Capacidades y Rendimiento Una vez que tenemos definida nuestro escenario debemos de compilarlo a través del método compile() de BranchGroup que nos permitirá realizar optimizaciones de nuestro escenario. En esta compilación lo que se suele realizar es la agrupación de los objetos Transform3D de un TransformGroup a una representación interna optimizada.
  • 5. Pero un efecto lateral de la compilación es que las capacidades del TransformGroup de modificar su comportamiento se ve limitado. La solución que utilizaremos para evitar este comportamiento será la de establecer las capacidades del objeto TransformGroup para notificar al compilador de que queremos permitir ciertos comportamientos en nuestros objetos. Todo esto lo realizaremos a través del método setCapability(Capability), donde podremos especificar los comportamientos que queremos permitir. Algunas de las capacidades que tendremos disponibles son: 1) ALLOW_TRANSFORM_READ - Acceso de lectura del objeto TransformGroup 2) ALLOW_TRANSFORM_WRITE - Acceso de escritura del objeto TransformGroup 3) ALLOW_CHILDREN_EXTEND - Se permiten añadir más nodos hijo 4) ALLOW_CHILDREN_READ - Acceso de lectura de los nodos hijo 5) ALLOW_CHILDREN_WRITE - Acceso de escritura de los nodos hijo 1.6 Animación En Java 3D, Behaviour es una clase para especificar animaciones o interacciones de objetos visuales, capaz de cambiar virtualmente cualquier atributo de un objeto visual. Un programador puede usar un número predefinido de comportamientos o especificar uno personalizado. Una vez que se especifica un comportamiento para un objeto visual, el sistema Java 3D actualiza la posición, orientación, color u otros atributos del objeto visual automáticamente. La diferencia entre animación e interacción está en cuando el comportamiento es activado en respuesta al paso del tiempo o en respuesta a las actividades del usuario. Para especificar un comportamiento para un objeto visual, el programador crea los objetos que especifican el comportamiento, añaden al objeto visual a la escena del gráfico, y se construyen las referencias apropiadas entre los objetos del escenario y los objetos Behaviour. Un problema que nos encontraremos al utilizar los comportamientos es que tendremos que especificar una región que haga de límite para el comportamiento. Este límite se llama región de planificación. Un comportamiento no está activo a menos que el volumen de activación de la plataforma de visión tenga un punto de intersección con la región de planificación de un objeto Behavior. Una de las clases de comportamiento predefinidas que nos podremos encontrar es la clase Interpolator. Esta clase está basada en una función de tiempo en la que el objeto Interpolator manipula los parámetros de un objeto del gráfico del escenario. Por ejemplo, RotationInterpolator manipula la rotación especificada por un TransformGroup para afectar a la rotación de los objetos visuales hijos del TransformGroup. Los pasos para especificar una animación con un objeto Interpolator son los siguientes: 1) Crear un TransformGroup con la capacidad ALLOW_TRANSFORM_WRITE 2) Crear un objeto Alpha que especifique los parámetros de tiempo 3) Crear un objeto Interpolator que haga referencia a los objetos Alpha y TransformGroup 5) Especificar una región de planificación
  • 6. 6) Hacer el comportamiento hijo del TransformGroup 1.7 Ejemplo Para ver el funcionamiento de todas las clases mencionadas anteriormente, veremos un pequeño programa en el que utilizaremos todos los puntos anteriores. El programa que crearemos creará un objeto predefinido en el paquete de utilidades que nos define un cubo con un color para cada cara. Además a la escena le añadiremos un rotación dependiente del tiempo y un límite para el comportamiento definido por una esfera de radio 100 y centrado en el punto (0,0,0). package es.udc.fi.gc; import java.applet.Applet; import java.awt.BorderLayout; import java.awt.GraphicsConfiguration; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.ColorCube; import com.sun.j3d.utils.universe.*; import javax.media.j3d.*; import javax.vecmath.*; public class Hello extends Applet { private SimpleUniverse u = null; public BranchGroup createSceneGraph() { // Crear la raiz de la rama de objetos gráficos BranchGroup objRoot = new BranchGroup();
  • 7. // Crear el nodo TransformGroup y engancharlo a la raíz // Habilitar la capacidad TRANSFORM_WRITE para que se pueda modificar // en tiempo de ejecución. TransformGroup objTrans = new TransformGroup(); objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); objRoot.addChild(objTrans); // Crear un objeto Shape3D y añadirlo a la escena. objTrans.addChild(new ColorCube(0.4)); // Crear un objeto Behavior que realizará las operaciones // deseadas en la transformada especificada y añadirlo a la escena Transform3D yAxis = new Transform3D(); Alpha rotationAlpha = new Alpha(-1, 4000); RotationInterpolator rotator = new RotationInterpolator( rotationAlpha, objTrans, yAxis, 0.0f, (float) Math.PI * 2.0f); BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); rotator.setSchedulingBounds(bounds); objRoot.addChild(rotator); // Compilamos para optimizar objRoot.compile(); return objRoot; } public Hello() { } public void init() { setLayout(new BorderLayout()); GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
  • 8. Canvas3D c = new Canvas3D(config); add("Center", c); // Creamos una escena y atacarla al universo virtual BranchGroup scene = createSceneGraph(); u = new SimpleUniverse(c); // Mueve el punto de vista un poco atrás para ver los objetos u.getViewingPlatform().setNominalViewingTransform(); u.addBranchGraph(scene); } public void destroy() { u.cleanup(); } // // El siguiente código permite a HelloUniverse ejecutarse como una aplicación // y como un applet // public static void main(String[] args) { new MainFrame(new Hello(), 256, 256); } }
  • 9. 2. CREANDO GEOMETRÍAS En el capítulo 1 exploramos los conceptos básicos de la construcción de un universo virtual Java 3D, concentrándonos en especificar las transformadas y comportamientos, y para demostrar su construcción y funcionamiento construimos un pequeño programa. Pero en el programa que construimos sólo había un figura, y era un cubo de colores que ya venía preconstruido en las clases de utilidades de la distribución de Java 3D, y a la cual no le podíamos especificar ni la apariencia ni el color. En este capítulo veremos que hay tres formas de construir los objetos que queremos representar y que podremos construir a través de objetos ya predefinidos (cubos, conos, esferas ...), a través de coordenadas de los vértices o a través de cargadores de geometrías. 2.1 Sistemas de coordenadas del Mundo Virtual En nuestros programas Java 3D una instancia de VirtualUniverse sirve como la raíz del escenario de los gráfico. El término universo virtual se refiere al espacio virtual de 3 dimensiones donde se sitúan los objetos 3D. Cada objeto Locale establece un sistema de coordenadas cartesianas en el universo virtual. Esto es, un objeto Locale sirve como punto de referencia para los objetos visuales en nuestro universo virtual. Con un Locale en un SimpleUniverse existe un sistema de coordenadas en el universo virtual. El sistema de coordenadas de universo virtual Java es de mano derecha. El eje X es positivo a la derecha, el eje Y es positivo hacia arriba y el eje X es positivo hacia el usuario. 2.2 Definiciones básicas de Objetos visuales
  • 10. 2.2.1 Shape3D Un nodo Shape3D define un objeto visual genérico. La clase Shape3D no contiene información sobre el color o la forma de un objeto visual, sino que esta información se almacena en los objetos NodeComponent referenciados por el objeto Shape3D. Un objeto Shape3D puede referenciar a un componente del nodo Geometry y/o Appearance. 2.2.2 Componentes Nodo Los objetos NodeComponent contienen la especificación de los atributos del objeto visual. Cada una de las subclases de NodeComponent define ciertos atributos visuales como la apariencia, geometría, material, texturas ... 2.2.3 Definiendo las Clases de objetos visuales El mismo objeto visual puede encontrarse varias veces en nuestra escena, por lo que tendrá sentido definir una clase que cree el objeto visual en vez de construir cada objeto visual desde el principio. La estructura de la construcción de un objeto visual es la siguiente: public class VisualObject extends Shape3D { private Geometry voGeometry; private Appearance voAppearance; // Creamos el objeto Shape3D con la geometría y apariencia public VisualObject(){ voGeometry = createGeometry(); voAppearance = createAppearance(); this.setGeometry(voGeometry); this.setAppearance(voAppearance); } .... }
  • 11. 2.3 Clases de utilidades geométricas Las distribución que nos ofrece Sun Microsystems viene acompañada de un paquete de utilidades que nos suministra clases de objetos visuales que representan primitivas de objetos del mundo real. Estas clases nos proporcionarán una forma sencilla de crear estos objetos y pudiendo especificar la apariencia deseada. Las primitivas predefinidas que nos encontraremos las tendremos en el paquete com.sun.j3d.utils.geometry y corresponden a los objetos que representan a un cubo, cono, cilindros y esferas: com.sun.j3d.utils.geometry.Box com.sun.j3d.utils.geometry.Cone com.sun.j3d.utils.geometry.Cilinder com.sun.j3d.utils.geometry.Sphere La construcción de una escena con uno de estos componentes es muy fácil, sólo tenemos que cambiar los colores para que nuestra figura no se confunda con el fondo y después seguir los pasos de la clase Hello del primer capítulo: package es.udc.fi.gc; import java.applet.Applet; import java.awt.BorderLayout; import java.awt.GraphicsConfiguration; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.Cone; import com.sun.j3d.utils.universe.*;
  • 12. import javax.media.j3d.*; public class HelloCone extends Applet { private SimpleUniverse u = null; public BranchGroup createSceneGraph() { // Crear la raiz de la rama del gráfico BranchGroup objRoot = new BranchGroup(); // Crear un objeto Shape3D y añadirlo a la escena. Appearance apariencia = new Appearance(); apariencia.setColoringAttributes(new ColoringAttributes(0.3f, 1.0f,1.0f, ColoringAttributes.SHADE_FLAT)); Node node = new Cone(0.5f,0.3f,apariencia); objRoot.addChild(node); // Compilar para optimizar objRoot.compile(); return objRoot; } public HelloCone() { } public void init() { setLayout(new BorderLayout()); GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration(); Canvas3D c = new Canvas3D(config); add("Center", c); // Crear una escena y atacarla al universo virtual BranchGroup scene = createSceneGraph(); u = new SimpleUniverse(c); // Mueve el punto de vista un poco atrás para ver los objetos u.getViewingPlatform().setNominalViewingTransform(); u.addBranchGraph(scene);
  • 13. } public void destroy() { u.cleanup(); } public static void main(String[] args) { new MainFrame(new HelloCone(), 256, 256); } } 2.4 Clases matemáticas Para crear objetos visuales necesitaremos a la clase Geometry y a sus subclases. Muchas de las subclases de Geometry describen primitivas basándose en otras primitivas más sencillas basadas en la definición a partir de sus vértices, como sucede con los puntos, líneas y polígonos. Pero antes de meternos con estas clases necesitaremos de otras clases matemáticas que nos ayudarán a definir nuestros objetos. Las clases matemáticas que necesitaremos las encontraremos en el paquete javax.vecmatch.* y las clases que más utilizaremos son aquellas que definen puntos (Pointf, Pointd), colores (Colorf, Colord), vectores (Vectorf, Vectord), coordenadas de texturas (TexCoordf, TextCoordd) y cuarteniones (Quadd y Quadf). 2.5 Clases de geometría En los gráficos en 3D todo está modelado y dibujado con datos basados en conjuntos de vértices. Para realizar la construcción de objetos complejos, la API Java 3D nos proporciona una clase, GeometryArray, con el que podremos construir objetos complejos basándonos en sus vértices como puntos, líneas, triángulos y polígonos en general. Pero además de GeometryArray tendremos otras clases que extienden de ella y con las que podremos especificar otros parámetros, como la reutilización de los vértices. Las clases padres en la construcción de objetos son GeometryArray, GeometryStripArray e IndexedGeometryArray. Las subclases de GeometryArray y una representación de son comportamiento son:
  • 14. Las subclases de GeometryStripArray y una representación gráfica de ellas son: Las subclases de IndexedGeometryArray nos proporcionarán una forma de eliminar los vértices redundantes, aunque con la penalización de tener que construir arrays de datos que contengan los índices para las coordenadas, índices, color, texturas y normales. Lo único que deberemos tener en cuenta es que la matrices de índices podrán tener múltiples referencias al mismo vértice en los arrays de datos. Las clases que tendremos disponibles son IndexedGeometryArray, IndexedPointArray, IndexedLineArray, IndexedTriangleArray, IndexedQuadArray, IndexedGeometryStripArray, IndexedLineStripArray, IndexedTriangleStripArray y IndexedTriangleFanArray. EL siguiente ejemplo muestra la construcción de dos rectas y un triángulo con los vértices de colores distintos en nuestro escenario.
  • 15. package es.udc.fi.gc; import java.applet.Applet; import java.awt.BorderLayout; import java.awt.GraphicsConfiguration; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.universe.*; import javax.media.j3d.*; import javax.vecmath.*; public class BasicFigures extends Applet { private SimpleUniverse u = null; public BranchGroup createSceneGraph() { BranchGroup objRoot = new BranchGroup(); LineArray line1 = new LineArray(2, LineArray.COORDINATES); line1.setCoordinate(0, new Point3f(0.0f, 0.0f, 0.0f)); line1.setCoordinate(1, new Point3f(0.3f, 0.0f, 0.0f)); LineArray line2 = new LineArray(2, LineArray.COORDINATES); line2.setCoordinate(0, new Point3f(0.0f, 0.3f, 0.0f)); line2.setCoordinate(1, new Point3f(0.1f, 0.0f, 0.3f)); TriangleArray triangle = new TriangleArray(3, TriangleArray.COORDINATES| TriangleArray.COLOR_3);
  • 16. triangle.setCoordinate(0, new Point3f (-0.3f, 0.5f, 0.0f)); triangle.setCoordinate(1, new Point3f (-0.65f, -0.6f, 0.0f)); triangle.setCoordinate(2, new Point3f (0.0f, -0.5f, 0.0f)); triangle.setColor(0, new Color3f(1.0f, 0.0f, 0.0f)); triangle.setColor(1, new Color3f(1.0f, 1.0f, 0.0f)); triangle.setColor(2, new Color3f(1.0f, 0.0f, 1.0f)); objRoot.addChild(new Shape3D(line1)); objRoot.addChild(new Shape3D(line2)); objRoot.addChild(new Shape3D(triangle)); // Compilamos para optimizar objRoot.compile(); return objRoot; } public BasicFigures() { } public void init() { setLayout(new BorderLayout()); GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration(); Canvas3D c = new Canvas3D(config); add("Center", c); // Crear una escena y atacarla al universo virtual BranchGroup scene = createSceneGraph(); u = new SimpleUniverse(c); // Mueve el punto de vista un poco atrás para ver los objetos u.getViewingPlatform().setNominalViewingTransform(); u.addBranchGraph(scene); } public void destroy() { u.cleanup(); }
  • 17. public static void main(String[] args) { new MainFrame(new BasicFigures(), 256, 256); } } 2.6 Apariencia y atributos Los objetos Shape3D pueden hacer referencia a objetos Geometry y Appearance. Tal como hemos visto, los objetos Geometry especifican la información por vértices de los objetos visuales. La información de cada vértice también puede especificar la información de color, pero si queremos efectos más sofisticados tendremos que recurrir al nodo Appearance. Un objeto Appearance no contiene información de como debería de ser un objeto Shape3D, sino que la información que contiene es la que dice dónde encontrar los datos de apariencia. Esto es, dentro del objeto Appearance no encontraremos especificada la información de la apariencia, sino que tendremos las referencias a los objetos atributos que contienen la información. Los objetos de atributos que encontraremos en la clase Appearance son: • PointAttributes • LineAttributes • PolygonAttributes • ColoringAttributes • TransparencyAttributes • RenderingAttributes • Material • TextureAttributes • Texture • TexCoodGeneration 2.7 Cargadores de contenido Además de las clases de para crear geometrías que hemos visto, también habíamos mencionado al principio de este capítulo que existían los cargadores de clases. Un cargador de clase son cargadores de objetos 3D definidos en archivos construidos con herramientas de terceras compañías. Como ejemplo, mencionaremos que existen cargadores para archivos generados con el 3D-Studio, AutoCAD, VRML, WaveFrom, LightWare ... que son gratuitos y que podremos descargar a través de la página de enlaces de Java 3D. 2.8 Otros elementos de contenido Otros objetos que podremos añadir a nuestro escenario pueden ser texto en 2 dimensiones (Text2D), texto en 3 dimensiones (Text3D) y colores de fondo (BackGround). Estos objetos son muy fáciles de utilizar, y nos serán muy útiles de utilizar en nuestras escenas.
  • 18. Con el objeto Text2D tendremos que especificar el texto que deseamos visualizar junto con los atributos de color, tamaño y fuente al añadirlo al BranchGroup. Ejemplo: package es.udc.fi.gc; import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Font; import java.awt.GraphicsConfiguration; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.Text2D; import com.sun.j3d.utils.universe.*; import javax.media.j3d.*; import javax.vecmath.*; public class HelloText2D extends Applet { private SimpleUniverse u = null; public BranchGroup createSceneGraph() { // Crear la raiz de la rama del gráfico BranchGroup objRoot = new BranchGroup(); Text2D text2D = new Text2D("Texto en 2D", new Color3f(0.8f, 1.0f, 1.0f), "Helvetica", 21, Font.BOLD); objRoot.addChild(text2D); // Compilar para optimizar objRoot.compile(); return objRoot; }
  • 19. public HelloText2D() { } public void init() { setLayout(new BorderLayout()); GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration(); Canvas3D c = new Canvas3D(config); add("Center", c); // Crear una escena y atacarla al universo virtual BranchGroup scene = createSceneGraph(); u = new SimpleUniverse(c); // Mueve el punto de vista un poco atrás para ver los objetos u.getViewingPlatform().setNominalViewingTransform(); u.addBranchGraph(scene); } public void destroy() { u.cleanup(); } public static void main(String[] args) { new MainFrame(new HelloText2D(), 256, 256); } } En el caso del objeto Text3D tendremos que especificar una extrusión a lo largo del eje Z en el constructor de la fuente (Font3D). Y con el fondo necesitaremos especificar el color deseado y los límites dentro de los que se aplicará el nuevo color de fondo. En el siguiente código veremos un ejemplo de estas dos últimas características juntas.
  • 20. package es.udc.fi.gc; import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Font; import java.awt.GraphicsConfiguration; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.universe.*; import javax.media.j3d.*; import javax.vecmath.*; public class HelloText3D extends Applet { private SimpleUniverse u = null; public BranchGroup createSceneGraph() { // Crear la raiz de la rama del gráfico BranchGroup objRoot = new BranchGroup(); Background bg= new Background(); bg.setColor(0.3f, 0.6f,0.0f); bg.setApplicationBounds(new BoundingSphere(new Point3d(),100.0f)); Font3D font3D = new Font3D(new Font("Helvetica", Font.PLAIN, 6), new FontExtrusion()); Text3D text3D = new Text3D(font3D, "Texto en 3D", new Point3f(-12.0f, 0.8f, -50.0f));
  • 21. Shape3D textShape = new Shape3D(text3D); objRoot.addChild(textShape); objRoot.addChild(bg); // Compilar para optimizar objRoot.compile(); return objRoot; } public HelloText3D() { } public void init() { setLayout(new BorderLayout()); GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration(); Canvas3D c = new Canvas3D(config); add("Center", c); // Crear una escena y atacarla al universo virtual BranchGroup scene = createSceneGraph(); u = new SimpleUniverse(c); // Mueve el punto de vista un poco atrás para ver los objetos u.getViewingPlatform().setNominalViewingTransform(); u.addBranchGraph(scene); } public void destroy() { u.cleanup(); } public static void main(String[] args) { new MainFrame(new HelloText3D(), 256, 256); } }
  • 22. 2.9 Transformaciones básicas Una vez que tenemos nuestros objetos visuales creados, lo siguiente que desearemos hacer será aplicarle alguna transformación para colocarlos en el lugar deseado de nuestro escenario. En Java 3D existe una clase de transformaciones, Transform3D, y en ella aplicaremos las operaciones que deseemos, como rotaciones, escalados y translacciones. Una vez aplicada la operación deseada, añadiremos el objeto Transform3D a un TransformGroup, que es una objeto que utilizaremos para agrupar a las transformaciones. Una vez que tenemos el TransformGroup, le añadiremos los objetos a los que queremos aplicar las transformaciones. Los métodos para aplicar las transformaciones en Transform3D son rotX(double), rotY(double), rotZ(double), setTranslation(Vector3*) y setScale(Vector3d). Además de estos métodos también dispondremos de otros que nos permitirán agrupar en el propio Transform3D las transformaciones, multiplicando directamente las matrices de transformación, mul(Transform3D), o aplicándole otra transformación, add(Transform3D). En el siguiente código crearemos un triángulo de colores y lo rotaremos sobre el eje Y. package es.udc.fi.gc; import java.applet.Applet; import java.awt.BorderLayout; import java.awt.GraphicsConfiguration; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.universe.*; import javax.media.j3d.*; import javax.vecmath.*; public class BasicTransform extends Applet { private SimpleUniverse u = null;
  • 23. public BranchGroup createSceneGraph() { BranchGroup objRoot = new BranchGroup(); TriangleArray triangle = new TriangleArray(3, TriangleArray.COORDINATES| TriangleArray.COLOR_3); triangle.setCoordinate(0, new Point3f (-0.3f, 0.5f, 0.0f)); triangle.setCoordinate(1, new Point3f (-0.65f, -0.6f, 0.0f)); triangle.setCoordinate(2, new Point3f (0.0f, -0.5f, 0.0f)); triangle.setColor(0, new Color3f(1.0f, 0.0f, 0.0f)); triangle.setColor(1, new Color3f(1.0f, 1.0f, 0.0f)); triangle.setColor(2, new Color3f(1.0f, 0.0f, 1.0f)); TransformGroup tg = new TransformGroup(); tg.addChild(new Shape3D(triangle)); Transform3D rotation = new Transform3D(); rotation.rotY(0.9); tg.setTransform(rotation); objRoot.addChild(tg); // Compilamos para optimizar objRoot.compile(); return objRoot; } public BasicTransform() { } public void init() { setLayout(new BorderLayout()); GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration(); Canvas3D c = new Canvas3D(config); add("Center", c); // Crear una escena y atacarla al universo virtual BranchGroup scene = createSceneGraph();
  • 24. u = new SimpleUniverse(c); // Mueve el punto de vista un poco atrás para ver los objetos u.getViewingPlatform().setNominalViewingTransform(); u.addBranchGraph(scene); } public void destroy() { u.cleanup(); } public static void main(String[] args) { new MainFrame(new BasicTransform(), 256, 256); } }
  • 25. 3.Interacción En los capítulos previos hemos hecho varios programas que demuestran las capacidades del API Java 3D, pero estas capacidades están bastante limitadas, ya que son estáticas. Lo verdaderamente interesante de los mundos virtuales es la interacción y animación que pueden producir. La interacción es la respuesta a los eventos producidos por el usuario. La animación está definida por los cambios de la escena a lo largo del tiempo sin la interacción directa del usuario. 3.1 La clase Behavior En Java 3D la interacción y la animación son especificados a través de los objetos Behavior. La clase Behavior es una clase abstracta que nos proporcionará una base para acceder a sus descendientes que nos proporcionarán el código para responder a los cambios en el universo virtual. El propósito de los objetos descendientes de Behavior es cambiar el escenario o los objetos en respuesta a algún tipo de estímulo. Hay muchos tipos de estímulos, como la acción del usuario, colisiones entre objetos, el transcurso del tiempo o la localización de la vista. Cada uno de estos estímulos hará que determinados objetos del escenario se comporten de distinta forma. Por ejemplo, mientras que la localización de la vista puede apenas provocar cambios posibles sobre un TransformGroup que contenga un BillBoard, si que puede afectar y de manera muy significativa a los objetos geométricos en cuanto a su nivel de detalle en la escena. En los paquetes que tenemos en la distribución tenemos varias clases que implementan Behavior y que podremos utilizar en nuestros escenarios sin tener que recurrir a construir nuestras propias clases Behavior. Algunas de las clases que encontraremos son KeyNavigationBehavior, MouseBehavior y PickMouseBehavior. Estas clases ya tienen comportamientos definidos que podremos utilizar para navegar por la escena y el mecanismo para utilizar estas clases es: 1) Preparar el escenario 2) Insertar objetos Behavior en el escenario haciendo referencia a los objetos de cambio 3) Especificar un límite de acción de los objetos Behavior 4) Establecer las capacidades de escritura y lectura para el destino. En el siguiente ejemplo utilizaremos el ejemplo del cono y le añadiremos el objeto KeyNavigationBehavior para poder navegar sobre nuestro universo virtual. Si ejecutamos el ejemplo podremos navegar con las teclas de dirección del teclado.
  • 26. package es.udc.fi.gc; import java.applet.Applet; import java.awt.BorderLayout; import java.awt.GraphicsConfiguration; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.behaviors.keyboard.KeyNavigatorBehavior; import com.sun.j3d.utils.geometry.Cone; import com.sun.j3d.utils.universe.*; import javax.media.j3d.*; import javax.vecmath.Point3d; public class MovingCone extends Applet {
  • 27. private SimpleUniverse u = null; public BranchGroup createSceneGraph() { // Crear la raiz de la rama del gráfico BranchGroup objRoot = new BranchGroup(); // Crear un objeto Shape3D y añadirlo a la escena. Appearance apariencia = new Appearance(); apariencia.setColoringAttributes(new ColoringAttributes(0.3f, 1.0f,1.0f, ColoringAttributes.SHADE_FLAT)); Node node = new Cone(0.5f,0.3f,apariencia); objRoot.addChild(node); // Cogemos el TG del universo virtual, ya que queremos que los // cambios afecten a toda la esdena TransformGroup tg = u.getViewingPlatform().getViewPlatformTransform(); // Le decimos a nuestro Behavior que aplique los cambios al TG KeyNavigatorBehavior keyNavBeh = new KeyNavigatorBehavior(tg); // Establecemos los límites keyNavBeh.setSchedulingBounds(new BoundingSphere (new Point3d(),100)); // Añadimos el Behavior a la escena objRoot.addChild(keyNavBeh); // Compilamos para optimizar objRoot.compile(); return objRoot; } public MovingCone() { } public void init() { setLayout(new BorderLayout()); GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
  • 28. Canvas3D c = new Canvas3D(config); add("Center", c); // Crear una escena y atacarla al universo virtual u = new SimpleUniverse(c); BranchGroup scene = createSceneGraph(); // Mueve el punto de vista un poco atrás para ver los objetos u.getViewingPlatform().setNominalViewingTransform(); u.addBranchGraph(scene); } public void destroy() { u.cleanup(); } public static void main(String[] args) { new MainFrame(new MovingCone(), 256, 256); } } 3.2 Construyendo nuestra propia clase Behavior Para construir nuestra propia clase de respuesta de interacción debemos de implementar a la clase Behavior, y específicamente a los métodos abstractos initialize () y processStimulus(). En el constructor de nuestra clase debemos requerir como parámetro un TransformGroup sobre el que vamos a realizar nuestras respuestas a los estímulos, especificar el evento que lanzará nuestras modificaciones y reinstalarlo. El siguiente trozo de código muestra un ejemplo en el cual cada vez que presionamos una tecla rotaremos el TransformGroup. package es.udc.fi.gc; import java.awt.event.KeyEvent; import java.util.Enumeration; import javax.media.j3d.Behavior; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.WakeupOnAWTEvent; public class KeyRotationBehavior extends Behavior { private TransformGroup targetTG; private Transform3D rotation = new Transform3D();
  • 29. private double angle = 0.0; // Constructor con TransformGroup como parámetro public KeyRotationBehavior(TransformGroup tg) { this.targetTG = tg; } // Establecemos el evento ante el cual desencadenaremos // nuestras respuestas. // En este caso, el evento será la presión de una tecla. public void initialize() { this.wakeupOn(new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED)); } // Realizamos la transformación del TransformGroup como respuesta // al estímulo y volvemos a establecer el estímulo ante el que // reaccionaremos. public void processStimulus(Enumeration arg0) { angle +=0.1; rotation.rotX(angle); targetTG.setTransform(rotation); this.wakeupOn(new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED)); } }
  • 30. 4.Animación Ciertos objetos pueden cambiar independientemente de las acciones del usuario, por ejemplo las manecillas de un reloj deberían de moverse sin tener que depender del estímulo del usuario. Las manecillas de un reloj son un ejemplo de animación. En este manual consideramos una animación como los cambios producidos en nuestra escena debido al transcurrir del tiempo y sin necesitar la interacción del usuario. Como sucede con las interacciones, las animaciones son implementadas usando los objetos Behavior, y como hemos visto con la interacción, podemos describir nuestros propios descendientes de Behavior o utilizar aquellos que ya vienen definidos en la distribución. Una de las clases de animación que veremos es la clase Interpolator. Un objeto Interpolator, junto con un objeto Alpha, manipula un objeto visual de la escena para conseguir una animación basada en el tiempo. Otro conjunto de clases de animación de objetos visuales son los BillBoard y LOD (Nivel de detalle) que no responden al paso del tiempo sino al cambio de punto de vista del usuario. 4.1 Objetos Interpolator y Alpha Un objeto Alpha produce una valor entre 0.0 y 1.0 incluidos que cambian con el tiempo y que generan una onda y que marcarán los tiempos de nuestra animación y las veces que se repetirá la onda. La forma de las fases de las que consta nuestra onda son todas parametrizables a través del objeto Alpha y podremos alterarlas para que, si es lo que deseamos, la onda sólo esté formado por una fase. Ahora que ya sabemos qué es un objeto Alpha y para que sirve, veremos como se integra con el objeto Interpolator. Para conseguir animación con el objeto Interpolator deberemos de seguir los siguientes pasos: • crear un objeto al que aplicar la animación con las capacidades apropiadas • crear un objeto Alpha • crear el objeto Interpolator haciendo referencia al objeto Alpha y al objeto destino • añadir los límites para el objeto Interpolator • añadir el objeto Interpolator a nuestra escena En el primer ejemplo que mostramos en este tutorial vimos un ejemplo de la aplicación de los objetos Interpolator y Alpha. El código era muy parecido al siguiente, con la salvedad de que hemos cambiado el eje de rotación al eje X:
  • 31. package es.udc.fi.gc; import java.applet.Applet; import java.awt.BorderLayout; import java.awt.GraphicsConfiguration; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.ColorCube; import com.sun.j3d.utils.universe.*; import javax.media.j3d.*; import javax.vecmath.*; public class RotColorCube extends Applet { private SimpleUniverse u = null; public BranchGroup createSceneGraph() { // Crear la raiz de la rama del gráfico BranchGroup objRoot = new BranchGroup(); // Crear el nodo TransformGroup y engancharlo a la raíz // Habilitar la capacidad TRANSFORM_WRITE para que se pueda modificar // en tiempo de ejecución. TransformGroup objSpin = new TransformGroup(); objSpin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); // Creamos un objeto Alpha rota indefinidamente y con un // periodo de 1 minuto Alpha alpha = new Alpha(-1, 6000);
  • 32. // Creamos un objeto Interpolator que gire sobre el eje X Transform3D axisX = new Transform3D(); axisX.setRotation(new Matrix3f(0.0f,1.0f,0.0f,1.0f,0.0f,0.0f,0.0f,0.0f,1.0f)); RotationInterpolator rotX = new RotationInterpolator(alpha, objSpin,axisX,-10,4); rotX.setSchedulingBounds(new BoundingSphere(new Point3d(),100)); // Construimos nuestra escena objRoot.addChild(objSpin); objSpin.addChild(new ColorCube(0.6)); objRoot.addChild(rotX); // Compilamos para optimizar objRoot.compile(); return objRoot; } public RotColorCube() { } public void init() { setLayout(new BorderLayout()); GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration(); Canvas3D c = new Canvas3D(config); add("Center", c); // Crear una escena y atacarla al universo virtual BranchGroup scene = createSceneGraph(); u = new SimpleUniverse(c); // Mueve el punto de vista un poco atrás para ver los objetos u.getViewingPlatform().setNominalViewingTransform(); u.addBranchGraph(scene); } public void destroy() { u.cleanup(); } public static void main(String[] args) {
  • 33. new MainFrame(new RotColorCube(), 256, 256); } } Además del RotationInterpolator también podremos utilizar los interpoladores ya definidos como ColorInterpolator, PathInterpolator, PositionInterpolator, ScaleInterpolator y TransparencyInterpolator, cuyo funcionamiento es muy similar al ya descrito RotationInterpolator. 4.2 Clase BillBoard El término billboard usado en el contexto de los gráficos en computación se refiere a la técnica de rotar automáticamente un objeto visual plano para que se encuentre siempre mirando al visor. La principal motivación para utilizar el billboard es la de disminuir los costes de computación de objetos complejos. El ejemplo clásico de utilización de billboards es en la representación de árboles de un paisaje. 4.2.1 Usando un billboard. El comportamiento billboard funciona bien para los árboles porque estos parecen básicamente los mismos cuando son vistos desde el frente, atrás o desde cualquier otro ángulo. Como un billboard hace que un objeto visual parezca el mismo desde todos los ángulos de vista serán apropiados para representar objetos 3D que son geométricamente simétricos sobre el eje Y. Usar un objeto billboard es muy similar a un objeto interpolator, excepto que no existe un objeto Alpha que controle la animación. La animación del billboard se controla por la posición relativa al visor en el mundo virtual. Los pasos para usar un Billboard son: 1. Crear un TransformGroup con ALLOW_TRANSFORM_WRITE de destino 2. Crear un objeto BillBoard haciendo referencia al TransformGroup de destino. 3. Suministrar unos límites para el objeto BillBoard 4. Construir el escenario. Un código de ejemplo mostrando su funcionamiento es el siguiente: public BranchGroup createSceneGraph(SimpleUniverse u) { BranchGroup objRoot = new BranchGroup(); Vector3f translate = new Vector3f(); Transform3D T3D = new Transform3D(); TransformGroup TGT = new TransformGroup(); TransformGroup TGR = new TransformGroup(); Billboard billboard = null; BoundingSphere bSphere = new BoundingSphere();
  • 34. translate.set(new Point3f(1.0f, 1.0f, 0.0f)); T3D.setTranslation(translate); TGT.setTransform(T3D); // Establecemos el billboard TGR.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); billboard = new Billboard(TGR); billboard.setSchedulingBounds(bSphere); // Construimos la escena objRoot.addChild(TGT); objRoot.addChild(billboard); TGT.addChild(TGR); TGR.addChild(crearArboles()); return objRoot; } 4.3 Animaciones LOD El término LOD se utiliza para nombrar una técnica que varía la cantidad de detalles de un objeto virtual basándose en otro valor del mundo virtual. Lo normal es cambiar el LOD basándonos en la distancia al visor. Tal y como la distancia entre el visor y el objeto aumenta iremos disminuyendo el nivel de detalle del objeto visual, ganando así tiempo de procesamiento para otros objetos. Otras aplicaciones en las que se suele utilizar es cuando queremos mantener una velocidad de frames constante, sin importar la velocidad a la que cambie el mundo virtual. Cada objeto LOD tiene uno o más objetos Switch como destino, que irá cambiando dependiendo del valor parámetro que hayamos fijado. En el caso de un objeto DistanceLOD, se cambiará conforme a la distancia al objeto. Las distancias se especifican con un array que empieza con la distancia máxima del primer hijo del Switch que será usado. Cuando la distancia desde el objeto DistanceLOD al visor es mayor que el primer límite, se cambiará al segundo hijo del Switch y así consecutivamente. Los pasos a seguir par usar un objeto DistanceLOD son: 1. Crear objetos Switch con la capacidad ALLOW_SWITCH_WRITE 2. Crear una lista de limites de distancias para el objeto DistanceLOD 3. Crear un objeto DistanceLOD usando la lista de límites 4. Establecer el switch de destino para el objeto DistanceLOD 5. Suministrar los límites para el objeto DistanceLOD 6. construir la escena
  • 35. En el siguiente ejemplo crearemos varias esferas de distinto nivel de detalle y distintos colores que irán apareciendo dependiendo de la distancia a la que se encuentren del visor.
  • 36. El código de este ejemplo es el siguiente: package es.udc.fi.gc; import java.applet.Applet; import java.awt.*; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.*; import com.sun.j3d.utils.behaviors.vp.*; import com.sun.j3d.utils.universe.*; import javax.media.j3d.*; import javax.vecmath.*; public class LOD extends Applet { private SimpleUniverse u = null; public BranchGroup createSceneGraph() { // Crear la raíz BranchGroup objRoot = new BranchGroup(); // Creamos el TransformGroup y añadimos las capacidades // que necesitamos TransformGroup objTrans = new TransformGroup(); objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); objRoot.addChild(objTrans); // Creamos el switch Switch sw = new Switch(0); sw.setCapability(javax.media.j3d.Switch.ALLOW_SWITCH_READ); sw.setCapability(javax.media.j3d.Switch.ALLOW_SWITCH_WRITE); // Creamos varios niveles para el switch, con esferas menos // detalladas y de distintos colores que irán cambiando conforme // a la distancia del visor Appearance appearance40 = new Appearance(); appearance40.setColoringAttributes(new ColoringAttributes(0.7f, 1.0f, 0.4f,1)); Sphere sphere40 = new Sphere(0.4f, Sphere.GENERATE_NORMALS, 40); sphere40.setAppearance(appearance40);
  • 37. Appearance appearance10 = new Appearance(); appearance10.setColoringAttributes(new ColoringAttributes(0.3f, 1.0f, 1.0f,1)); Sphere sphere10 = new Sphere(0.4f, Sphere.GENERATE_NORMALS, 10); sphere10.setAppearance(appearance10); Appearance appearance5 = new Appearance(); appearance5.setColoringAttributes(new ColoringAttributes(1.0f, 0.5f, 1.0f,1)); Sphere sphere5 = new Sphere(0.4f, Sphere.GENERATE_NORMALS, 5); sphere5.setAppearance(appearance5); Appearance appearance3 = new Appearance(); appearance3.setColoringAttributes(new ColoringAttributes(1.0f, 1.0f, 0.5f,1)); Sphere sphere3 = new Sphere(0.4f, Sphere.GENERATE_NORMALS, 3); sphere3.setAppearance(appearance3); sw.addChild(sphere40); sw.addChild(sphere10); sw.addChild(sphere5); sw.addChild(sphere3); // Añadimos el switch objTrans.addChild(sw); BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0); // Establecemos el LOD float[] distances = new float[3]; distances[0] = 5.0f; distances[1] = 10.0f; distances[2] = 25.0f; DistanceLOD lod = new DistanceLOD(distances); lod.addSwitch(sw); lod.setSchedulingBounds(bounds); objTrans.addChild(lod); // Optimizaciones objRoot.compile(); return objRoot; }
  • 38. public LOD() { } public void init() { setLayout(new BorderLayout()); GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration(); Canvas3D c = new Canvas3D(config); add("Center", c); // Creamos la escena y la atacamos al universo virtual BranchGroup scene = createSceneGraph(); u = new SimpleUniverse(c); // añadimos un zoom a la escena ViewingPlatform viewingPlatform = u.getViewingPlatform(); viewingPlatform.setNominalViewingTransform(); // Añadimos un OrbitBehavior para el Zoom, pero deshabilitamos // la rotación y la traslación OrbitBehavior orbit = new OrbitBehavior(c, OrbitBehavior.REVERSE_ZOOM | OrbitBehavior.DISABLE_ROTATE | OrbitBehavior.DISABLE_TRANSLATE); BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); orbit.setSchedulingBounds(bounds); viewingPlatform.setViewPlatformBehavior(orbit); u.addBranchGraph(scene); } public void destroy() { u.cleanup(); } public static void main(String[] args) { new MainFrame(new LOD(), 512, 512); }
  • 39. }
  • 40. 5. Iluminación En el mundo real, en la visualización de un objeto no sólo se tiene en cuenta el color y la forma de este, sino que también tenemos en cuenta la cantidad y el ángulo de la luz que recibe y las luces y sombras de los objetos adyacentes. En este capítulo presentaremos el modelo de iluminación en Java 3D y veremos cómo crear ambientes y sombras para nuestro universo virtual. 5.1 El modelo de iluminación en Java 3D Como ya se dijo anteriormente, los colores que recibimos son una combinación de las propiedades físicas del objeto, las características de las fuentes de luz, la posición relativa de las fuentes de luz y los ángulos desde los cuales es visto el objeto. Java 3D usa un modelo de iluminación que intenta simular el ambiente real, pero como los cálculos de luminosidad son muy costosos en la especificación Java 3D se ha propuesto un modelo de iluminación que no tiene en cuenta los efectos producidos por el resto de los objetos visuales. El modelo de iluminación de Java 3D incorpora tres tipos de reflexiones del mundo real: ambiente, difusa y enfocada. La reflexión ambiente resulta de las luces de ambiente, que son luces de poca intensidad pero constantes en todo el escenario. La reflexión difusa es la reflexión normal, de una fuente de luz sobre un objeto visual. Las reflexiones enfocadas son las reflexiones de mucha intensidad de una fuente de luz sobre un objeto. Una limitación importante en el modelo de iluminación de Java 3D es que existe un número máximo de fuentes de luces en un escenario. En estos momentos, el límite está en los 8 focos de luces, y si intentamos sobrepasar este límite no podremos observar más de 8 focos de luz. Aunque esto depende de la implementación de la librería, no es recomendable sobrepasar este límite, ya que está definido como un valor mínimo por la especificación y no podremos asegurar el buen funcionamiento de nuestra aplicación en otras plataformas si se sobrepasa. 5.2 Receta para los objetos visuales de luz Para habilitar los objetos visuales de luz en nuestro universo necesitaremos seguir un número de pasos, que como es habitual en este tutorial, los describiremos en forma de receta de cocina. Antes de nada, mencionaremos un detalles con cierta importancia: las luces no son objetos visibles, con lo que no intentes ver un foco en el universo virtual. Los pasos a seguir son: 1. Especificar la fuente de luz a) Establecer los límites b) Añadirlo al escenario 2. Objetos visuales a) Establecer las normales b) Establecer las propiedades de los materiales Si nos olvidamos de cualquiera de estos pasos no podremos observar los efectos de la fuente de
  • 41. luz. La presencia del objeto Material en el objeto Appearance de un objeto visual es el que nos habilitará el modelo de luces para este objeto. Sin la presencia de Material el objeto será coloreado, pero no será iluminado por las fuentes de luz. En la siguiente figura se muestra una luz débil de ambiente sobre una esfera: Si a la figura anterior le quitamos la luz de ambiente, la imagen producida será la siguiente: Si comparamos las dos imágenes veremos una pequeña diferencia de color en las esferas, esta diferencia será la provocada por la luz de ambiente. El código del programa que produjo estas imágenes es el siguiente: package es.udc.fi.gc; import java.applet.Applet;
  • 42. import java.awt.BorderLayout; import java.awt.GraphicsConfiguration; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.universe.*; import javax.media.j3d.*; import javax.vecmath.*; public class BasicLight extends Applet { private SimpleUniverse u = null; public BranchGroup createSceneGraph() { BranchGroup objRoot = new BranchGroup(); Background background = new Background(0.2f, 0.4f, 0.5f); background.setApplicationBounds(new BoundingSphere()); Appearance appearance = new Appearance(); appearance.setMaterial(new Material()); // Generamos la esfera con la una apariencia y generamos las normales Sphere sphere = new Sphere(0.5f, Sphere.GENERATE_NORMALS ,appearance); AmbientLight light = new AmbientLight(); light.setInfluencingBounds(new BoundingSphere()); objRoot.addChild(light); objRoot.addChild(sphere); objRoot.addChild(background); // Compilamos para optimizar objRoot.compile(); return objRoot; } public BasicLight() { } public void init() { setLayout(new BorderLayout());
  • 43. GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration(); Canvas3D c = new Canvas3D(config); add("Center", c); // Crear una escena y atacarla al universo virtual BranchGroup scene = createSceneGraph(); u = new SimpleUniverse(c); // Mueve el punto de vista un poco atrás para ver los objetos u.getViewingPlatform().setNominalViewingTransform(); u.addBranchGraph(scene); } public void destroy() { u.cleanup(); } public static void main(String[] args) { new MainFrame(new BasicLight(), 256, 256); } } En el siguiente ejemplo colocaremos una luz direccional y aplicada a variaciones de la misma figura geométrica y veremos como la forma de los objetos visuales afecta al tipo de reflexión de la luz sobre estos.
  • 44. La única diferencia en el código de estos dos ejemplos radica en el número de vértices de las figuras. Mientras que en la figura de la derecha la esfera está construida con 20 vértices, la figura de la izquieda está construida con 40. El código de la luz direccional es el siguiente: Sphere sphere = new Sphere(0.5f, Sphere.GENERATE_NORMALS ,40,appearance); DirectionalLight light = new DirectionalLight(); light.setInfluencingBounds(new BoundingSphere()); light.setDirection(-1.0f, -1.0f, 0.0f); 5.3 Clases de Luces El API Java 3D nos suministra cuatro tipo de luces. Cada una de ellas es hija de la clase Light. La clase Light suministra los métodos y constantes de las capacidades asociadas para manipular el estado, color y límites de un objeto Light. Los tipos de luces y sus efectos son los siguientes: 1. La clase javax.media.j3d.AmbientLight nos suministrará una intensidad de luz igual para todas las localizaciones y en todas las direcciones. 2. La clase javax.media.j3d.DirectionalLight modela fuentes de luz distantes teniendo un vector de dirección constante. 3. La clase javax.media.j3d.PointLight especifica una fuente de luz atenuada en un lugar concreto del espacio que radia luz de forma igual en todas las direcciones. 4. La clase javax.media.j3d.SpotLight es una subclase de PointLight con la adicción de la dirección, ángulo de difusión y atributos de concentración. 5.4 El objeto Material Las propiedades de un material son especificadas en el objeto Material que está integrado en el objeto de orden superior Appearance. El objeto Material especifica los colores de ambiente, de difusión, direccionales y emisivos y el valor de brillo. Cada uno de los tres primeros colores son usados en el modelo de iluminación para calcula la reflexión correspondiente. El color emisivo permite a los objetos visuales brillar en la oscuridad y el valor de brillo sólo es usado para calcular la reflexión direccional. Los métodos para especificar estos parámetros son setAmbientColor (Color3f), setDiffuseColor(Color3f), setEmissiveColor(Color3f), setShininess(float) y setSpecularColor(Color3f). 5.5 Normales Tal y como se dijo en la sección 5.2, las normales son un requisito indispensable para la realización de sombreados sobre objetos visuales. La normal de una superficie nos indica cual es la parte de la superficie que se verá afectada por los efectos de iluminación. Por esto es tan importante el definir este parámetro de forma correcta. Para definr correctamente una normal debemos definir un vector que apunte hacia las fuentes de luz. Para definir las normales cuando creamos objetos visuales usando las clases Geometry, podremos usar uno de los métodos setNormal() para especificar las normales para cada vértice.
  • 45. 6. Texturas 6.1 ¿Qué es una textura? La apariencia de los objetos del mundo real depende de su textura. La textura de un objeto es realmente la geometría de menor detalle de la superficie del objeto. Una textura especifica la complejidad y densidad de la geometría de la superficie. Si intentáramos modelar una representación de un objeto real en Java 3D con su textura mediante las primitivas que hemos visto, el modelo sería imposible de representar debido a su alta complejidad. Lo que se hace para la simulación de texturas es la simulación de estas mediante un gráfico o dibujo, y asignándolo a la forma geométrica del modelo del objeto visual. 6.2 Construyendo texturas Para aplicar una textura a un polígono en Java 3D debemos de seguir una serie de pasos que expresaremos en modo de receta y que son: 1. crear un nodo Appearance 2. cargar el mapa de la textura 3. establecer la textura en el nodo Appearance 4. especificar la localización de las imágenes de textura en la geometría 6.2.1 Preparar la imagen de textura Este paso no requiere programación alguna y consta de la edición de nuestra textura en una herramienta que nos permita la edición de una imagen. Algo que tenemos que tener en cuenta aquí es que la imagen debe de tener una dimensión potencia de 2, para optimizar el rendimiento de la aplicación, y asegurarnos de que la imagen se graba en un formato de imagen que los cargadores de Java sean capaces de entender. 6.2.2 Carga de la textura Ahora que ya hemos preparado nuestro archivo de texturas, podremos cargarlo. Para cargar la textura utilizaremos una clase de utilidad llamada TextureLoader. Esta clase lo que hace es cargar el archivo de texturas desde un archivo del sistema de archivos o desde un URL para devolver un objeto ImageComponent2D. El código para realizar esta operación es el siguiente: TextureLoader loader = new TextureLoader(“textura.gif”, this); ImageComponent2 image = loader.getImage(); El argumento “this” en el constructor de TextureLoader es una referencia a un “Observador”, ya que para cargar la textura se utiliza el paquete java.awt.image y este paquete carga las clases de forma asíncrona.
  • 46. 6.2.3 Crear el nodo Appearance Para ser usada como textura la imagen que hemos cargado en el apartado anterior, debemos de establecerla como textura en un objeto Texture2D. El siguiente código muestra un ejemplo de carga y asignación de la textura a un nodo Appearance: TextureLoader loader = new TextureLoader(“textura.jpg”, this); ImageComponent2D image = loader.getImage(); Texture2D texture = new Texture2D(); texture.setImage(0, image); Appearance appearance = new Apperance(); appearance.setTexture(texture); 6.2.4 Especificando las TextureCoordinates Ahora lo único que nos falta es especificar los puntos de colocación de la textura en la geometría de nuestro objeto visual. Cada coordenada de la textura especifica un punto de la textura a ser aplicada a un vértice. Con la especificación de algunos de los puntos de la imagen a ser aplicados a los vértices de la geometría, la imagen será “acondicionada” para que cuadre en ella. Algo que debemos de tener en cuenta es que las coordenadas de la textura son coordenadas con valores normalizados. Esto es, cuando establezcamos una coordenada de textura a 1.0f, esto querrá significar que queremos que una de la esquina de la textura estará en un límite del plano. Veámoslo con un ejemplo. En el siguiente trozo de código crearemos un plano y estableceremos en el una textura: QuadArray plane = new QuadArray(4, GeometryArray.COORDINATES | GeometryArray.TEXTURE_COORDINATE_2); Point3f p = new Point3f(); p.set(-1.0f, 1.0f, 0.0f); plane.setCoordinate(0,p); p.set(-1.0f, -1.0f, 0.0f); plane.setCoordinate(1,p); p.set(1.0f, -1.0f, 0.0f); plane.setCoordinate(2,p); p.set(1.0f, 1.0f, 0.0f); plane.setCoordinate(3,p); Point2f q = new Point2f(); q.set(0.0f, 1.0f); plane.setTextureCoordinate(0.q); q.set(0.0f, 0.0f); plane.setTextureCoordinate(1,q); q.set(1.0f, 0.0f); plane.setTextureCoordinate(2,q);
  • 47. q.set(1.0f, 1.0f); plane.setTextureCoordinate(3,q); En el último ejemplo de este tutorial veremos el código de un programa que carga una textura de piedra en un cubo que gira. En este ejemplo no será necesario especificar las coordenadas de la textura debido a que nuestro objeto visual es cuadrado y encajará perfectamente. package es.udc.fi.gc; import java.applet.Applet; import java.awt.*; import java.awt.event.*; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.universe.*; import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.geometry.Box; import javax.media.j3d.*; import javax.vecmath.*; public class TextureExample extends Applet { private java.net.URL texImage = null; private SimpleUniverse u = null; public BranchGroup createSceneGraph() { BranchGroup objRoot = new BranchGroup();
  • 48. TransformGroup objTrans = new TransformGroup(); objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); objRoot.addChild(objTrans); Appearance app = new Appearance(); Texture tex = new TextureLoader(texImage, this).getTexture(); app.setTexture(tex); Box textureCube = new Box(0.4f, 0.4f, 0.4f, Box.GENERATE_TEXTURE_COORDS, app); objTrans.addChild(textureCube); Transform3D yAxis = new Transform3D(); Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0, 4000, 0, 0, 0, 0, 0); RotationInterpolator rotator = new RotationInterpolator( rotationAlpha, objTrans, yAxis, 0.0f, (float) Math.PI * 2.0f); BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); rotator.setSchedulingBounds(bounds); objTrans.addChild(rotator); objRoot.compile(); return objRoot; } public TextureExample() { } public TextureExample(java.net.URL url) { texImage = url; }
  • 49. public void init() { if (texImage == null) { // the path to the image for an applet try { texImage =new java.net.URL( getCodeBase().toString() + "../images/stone.jpg"); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } } setLayout(new BorderLayout()); GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration(); Canvas3D c = new Canvas3D(config); add("Center", c); BranchGroup scene = createSceneGraph(); u = new SimpleUniverse(c); u.getViewingPlatform().setNominalViewingTransform(); u.addBranchGraph(scene); } public void destroy() { u.cleanup(); } public static void main(String[] args) { java.net.URL url = null; if (args.length > 0) { try { url = new java.net.URL("file:" + args[0]); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } } else { try { url = new java.net.URL("file:c:stone.jpg");
  • 50. } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } } new MainFrame(new TextureExample(url), 256, 256); } }
  • 51. 7. Bibliografía 1. Java 3D Specificacion 1.1. (Sun Microsystems) 2. Java 3D Specificacion 1.3.1 (Sun Microsystems) 3. Getting Started with the Java 3D API v1.X (Sun Microsystems)