Mi?rcoles, 12 de agosto de 2009
   Hoy empezamos una serie de artículos sobre programación 3D, utilizando la librería LWJGL(Lightweight Java Game Library), que es una implementación en Java del standar openGL. Para obtener toda la información y documentación completa sobre esta librería te remito a su página web oficial, que aunque está en inglés, es la fuente más fiable sobre lwjgl. Esta librería, pese a su gran potencia(prácticamente igual rendimiento que openGL), no posee mucha documentación en español, por no decir nula. En su lugar, y como descendiente directo de lwjgl, encontramos Java Monkey Engine, que es un motor 3D para Java, desarrollado con lwjgl, y que si dispone de amplia documentación en español. Este motor 3D lo analizaremos en próximos artículos.
    Por ahora, al ser LWJGL una base seria para cualquier motor 3D para java, que se quiera desarrollar, y debido su gran rendimiento(prácticamente igual que openGL utilizando lenguaje C++), nos adentraremos explicando una serie de ejemplos sencillos, pero donde ya se podrá ver su gran potencia y versatilidad.

    En este artículo, vamos a mostrar el típico ejemplo del cubo3D, donde en su interior se moverán un grupo de bolas, que van rebotando en sus paredes.

Bolas rebotando dentro de cubo 3D


     Antes que nada, y por ser este el primero artículo sobre lwjgl, vamos a empezar dando una breve explicación de como instalar y configurar el lwjgl. Primero deberás tener instalado en tu sistema el lenguaje java. Mas concretamente el Java JDK(Java Development Kit), el cual puedes descargar desde su página oficial de sun. Con la edición standar(SE) será suficiente. Sigue las instrucciones de la página de sun, para instalarlo y configurarlo en tu sistema.
    Una vez que tienes instalado el java, el siguiente paso, es descargar el lwjgl, desde su página oficial. Tienes varios paquetes a descargar. Descárgalos todos. A continuación podemos empezar la instalación del lwjgl sobre tu sistema. Desde la página oficial de lwjgl, se encuentran también las instrucciones para esto, para cada uno de los sistemas más utilizados. Aunque en esta página no especifica que descomprimas los paquetes de lwjgl, dentro de la carpeta de tu instalación de java, es lo que yo recomiendo. En concreto, yo hice lo siguiente: Cree una carpeta llamada lwjgl, y descomprimi todos los paquetes dentro de esta carpeta. Luego copie esta carpeta dentro de la carpeta donde tengo instalado el java jdk. Por ejemplo, yo que utilizo linux, tengo instalado el java en /usr/local/java y la carpeta lwjgl, la copie ahí dentro.

  Lo siguiente sería hacer el test que indican en la página. En mi sistema por ejemplo, abrí un terminal, me fui hasta la carpeta /usr/local/java/lwjgl/lwjgl-2.1.0 y ejecuté el comando:
java -cp    .:res:jar/lwjgl.jar:jar/lwjgl_test.jar:jar/lwjgl_util.jar:jar/lwjgl_fmod3.jar:jar/lwjgl_devil.jar:jar/jinput.jar: -Djava.library.path=native/linux org.lwjgl.test.WindowCreationTest

Si todo va bien, tedebería aparecer en el terminal algo parecido a:

The following keys areavailable:

ESCAPE: Exit test

ARROW Keys: Move windowwhen in non-fullscreen mode

L: Listselectable display modes

0-8: Selection ofdisplay modes

F: Togglefullscreen

SHIFT-F: Togglefullscreen with Display.destroy()/create() cycle

Found 11 display modes

Problem retrieving mode [email protected]

Problem retrieving mode [email protected]

Problem retrieving mode [email protected]

Problem retrieving mode [email protected]

Problem retrieving mode [email protected]

Problem retrieving mode [email protected]

 

Se abrirá también una ventana gráfica, que te permitirá jugar un poco con los modos gráficos disponibles en tu sistema. Por ejemplo, en mi sistema, pulsando alguna tecla del 0 al 8 la ventana cambiará de modo gráfico. Bien, una vez que ya tenemos correctamente instalado el lwjgl, lo siguiente sería configurarlo en el entorno de desarrollo que utilices. Esto también se explica en la misma página de instalación del lwjgl para varios entornos(Eclipse,netBeans, etc). Lo más importante, es incluir en la compilación las librerías nativas de lwjgl, que si te fijas se han incluido en el comando de test que hemos ejecutado más arriba. Así como incluir en el proyecto la librería lwjgl. Si anteriormente, habías utilizado cualquier otra librería en tu entorno de desarrollo, la forma de incluir lwjgl es igual.
    Por ejemplo, en mi entorno de desarrollo(NetBeans) debo incluir en las propiedades del proyecto, concretamente en las opciones de la máquina virtual, la línea
-Djava.library.path=/usr/local/java/lwjgl/lwjgl-2.1.0/native/linux   y en la opción de librerías, añadir la librería lwjgl.

    Ahora que ya tenemos instalado y configurado correctamente en nuestro sistema y entorno de desarrollo,la librería lwjgl, pasamos sin más preámbulo, a empezar a explicar el código fuente. En primer lugar, mostramos el programa principal,la función main:


public static void main(String[] args){

    if (args.length == 1 &&args[0].equalsIgnoreCase("-f"))

       fullscreen = true;

    Rebotes rebotes = new Rebotes();

    rebotes.setDisplayVideo(fullscreen);

    rebotes.init();

    rebotes.runLoop();

}

    Lo primero que hacemos es verificar si se ha pasado algún parámetro a la aplicación. En este caso, el único aceptable es la opción -f, que para nosotros significará que queremos que nos muestre la animación a pantalla completa.

A continuación inicializamos el modo gráfico mediante el método setDisplayVideo(fullscreen). A continuación mostramos dicho método:

public void setDisplayVideo(booleanfull) {

    DisplayMode chosenMode = null;

    try {

       DisplayMode[] modes =Display.getAvailableDisplayModes();

       for (int i = 0; i <modes.length; i++) {

          if ((modes[i].getWidth() == targetWidth) && (modes[i].getHeight() == targetHeight)) {

             chosenMode = modes[i];

             break;

          }

      }

   } catch (LWJGLException e) {

      Sys.alert("Error","Imposible establecer el modo gráfico.");

      System.exit(0);

   }

    // En este punto si no hay un modo gráfico adecuado mostramos un error y terminamos la ejecución.

   if (chosenMode == null) {

      Sys.alert("Error","Imposible encontrar el modo gráfico.");

      System.exit(0);

   }

    try {  // una vez establecido el modo gráfico, utilizando el paquete Display de lwjgl, activamos el modo gráfico,

            // establecemos el título de la ventana/ y mostramos la ventana con Display.create(). Si hay algún error lo

           // mostramos y salimos.

       Display.setDisplayMode(chosenMode);

       Display.setFullscreen(full);

       Display.setTitle("Cubo 3D");

       Display.create();

    } catch (LWJGLException e) {

      Sys.alert("Error","Imposible crear display.");

      System.exit(0);

   }

}

 

   Este método para inicializar el modo gráfico siempre será prácticamente igual. En nuestro ejemplo, la resolución ya las fijamos como constantes en targetWidht y targetHeight, pero también podríamos optar por pasarlas como parámetros al programa, y luego pasarlas a este método tal como se pasa el parámetros fullscreen. Por lo demás, creo que el código habla por si mismo. Fijaos sobre todo lo fácil que es usar el paquete Display.

    A continuación mostramos el método init, donde inicializamos las matrices de transformación con las que trabaja lwjgl, ajustamos la posición de la cámara e inicializamos las bolas y sus posiciones aleatorias dentro del cubo.

public void init() {

      // activamos el test de profundidad, inicializamos las matrices de proyección y del modelo de vista,
      // ajustamos el ángulo de perspectiva
      GL11.glEnable(GL11.GL_DEPTH_TEST);
      GL11.glClearColor((float) 0.0, (float) 0.0, (float) 0.0, (float) 0.0);
      GL11.glMatrixMode(GL11.GL_PROJECTION);
      GL11.glLoadIdentity();
      GLU.gluPerspective((float) 60.0, (float) 1.0, (float) 1.0, (float) 100.0);

      GL11.glMatrixMode(GL11.GL_MODELVIEW);
      
      // determinamos la posición de la cámara
      GLU.gluLookAt((float) 2.0, (float) 2.0, (float) 3.0, (float) 0, (float) 0, (float) 0, (float) 0, (float) 1, (float) 0);
      
      // inicializamos las bolas, y ...
      sphere = new Tsphere[NUM_BOLAS];
      java.util.Random srand = new java.util.Random();
      srand.setSeed(new java.util.Date().getTime());
      
      // ...las colocamos dentro del cubo con velocidades iniciales aleatorias.
      for (int i = 0; i < NUM_BOLAS; i++) {
         sphere[i] = new Tsphere(1, ((srand.nextFloat() % 10) / (float)20) + (float)0.1);       
      }
   }

 

   La clase Tsphere, es una clase Java donde hemos definido la funcionalidad de una bola, con métodos como modifySpeed o test. El código de Tsphere lo puedes ver aquí. El método más importante de esta clase, realmente es el constructor, donde pasamos como primer parámetro la máxima posición que puede tener una bola, en cualquiera de sus posibles dimensiones(x, y, z). Al estar dentro de un cubo, su límite está en las paredes del cubo, y este límite es 1. Las posiciones serán números decimales entre 0 y 1. El segundo parámetro que se pasa es la velocidad aleatoria que tendrá la bola.

   Ahora ya solo queda dibujar el cubo y las bolas y empezar a mover éstas. Esto siempre se hará en el bucle principal. En este ejemplo, el método que hemos llamado runLoop. A continuación se muestra este método:

public void runLoop() {
   
      boolean parar = false;
      
      while (!parar) {
         display();
         
         // Actualizamos la pantalla. Se renderiza mediante lwjgl
         Display.update();

         // Se comprueba si el usuario ha ordenado cerrar la ventana
         if (Display.isCloseRequested()) {
            parar = true;
            Display.destroy();
            System.exit(0);
         }
      }
   }

    El método más importante aquí es display(), que lo comentaremos a continuación. Observar lo fácil que se renderiza la pantalla con lwjgl. Simplemente con hacer una llamada a Display.update().

public void display() {

      GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
      GL11.glColor3f((float)1.0, (float)1.0, (float)1.0);
      drawCube();
      //inc++;      
      for (int i = 0; i < NUM_BOLAS; i++) {
         GL11.glPushMatrix();
         GL11.glColor3f((float)0.0, (float)0.0, (float)1.0);
                  
         pos = sphere[i].getPosV();
         
         GL11.glTranslatef((float)pos[0], (float)pos[1], (float)pos[2]);         
         
         Sphere bolita = new Sphere();      
         bolita.setDrawStyle(GLU.GLU_FILL);
         bolita.draw((float)0.05, 20, 20);                  
         
         GL11.glPopMatrix();           
      
         sphere[i].test();            
         
      }
      
      try {
         Thread.sleep(30);
      } catch (InterruptedException e) {
         
      }
      
   }

 

    Aquí lo primero que se hace es limpiar el fondo de la pantalla, es decir, volver a ponerlo todo negro. A continuación ponemos el color de dibujo otra vez a blanco con la función GL11.glColor3f.  A continuación dibujamos el cubo y luego entramos en un bucle donde vamos dibujando cada una de las bolas. Cabe destacar las funciones glPushMatrix y glPopMatrix. Como el lwjgl trabaja con matrices(al igual que openGL), cualquier operación modificaría la matriz original, que es la que creamos en la función init, y entonces se modificaría también la perspectiva y la posición de la cámara, con el movimiento de cada bola. Y cada bola se debe de mover de manera independiente a la posición del cubo y la posición de las demás bolas. La función glPushMatrix lo que hace es guardar el estado de la matriz de transformación actual, y luego la recupera con glPopMatrix(). Especial mención tiene el método test() de nuestra clase Tsphere, donde se comprueba, si la bola ha chocado con alguna pared del cubo, y en caso afirmativo, se cambia su dirección. Además, también se incrementa su posición (x,y,z). Fijaos también en la clase Sphere, que viene en el paquete lwjgl.util.glu.Sphere, y que nos permite dibujar una esfera. Observar que donde realmente se realiza el movimiento de la bola es en la línea

    GL11.glTranslate((float)pos[0], (float)pos[1], (float)pos[2]);

    Aqui es donde realmente se hace el movimiento de la bola. Se suma a la matriz actual del modelo de vista(la que tenemos a partir del método init), la matriz de la posición actual de la bola, y la matriz resultante, dará la nueva posición de la bola.

   Bueno, hasta aquí todo lo que quería comentar. Si quereis profundizar más, aquí os dejo los códigos Rebotes.java y Tsphere.java

   En el próximo artículo seguiremos trabajando sobre este ejemplo, aplicando algo de la teoría de luces y sombras, sobre la superficie de las esferas, y alguna sorpresa más. 

Por último os dejo con un pequeño video de la aplicación funcionando en mi sistema.

  .glTranslatef((float)pos[0], (float)pos[1], (float)pos[2]);        


Publicado por antoniopf @ 17:26  | Programaci?n 3D
Comentarios (1)  | Enviar
Comentarios
Hey! funciona perfecto, pero tengo una duda... digamos que en netbeans construyo el ejecutable, pero no se como ejcutarlo tira unos errores ahi... supongo que debe ser por consola, pero no tengo idea.

Ayuda!
Publicado por Icaro
Jueves, 03 de junio de 2010 | 4:04