| IntroducciónPor razones de diseño, la especificación de OpenGL se aisló de 
    cualquier dependencia de un sistema de ventanas concreto. El interface resultante
    es una librería de trazado en 2D y 3D portable y eficiente. Es trabajo del
    sistema de ventanas nativo el abrir y trazar ventanas. La librería de OpenGL
    se comunica con el sistema nativo a través de librerías adicionales
    auxiliares. Por ejemplo, la librería auxiliar GLX describe la 
    interacción entre OpenGL y el sistema X Window System. El kit de utilidades de OpenGL (OpenGL Utility Toolkit - GLUT) es un interface
    de programación con bindings para ANSI C y FORTRAN para escribir programas
    OpenGL independientes del sistema de ventanas. Lo ha escrito Mark J. Kilgard y cubre
    un gran agujero dejado por la especificación de OpenGL. Gracias a GLUT, los 
    desarrolladores pueden usar un inteface común para el sistema de ventanas
    independientemente de la plataforma empleada. Las aplicaciones de OpenGL que usan 
    GLUT se pueden portar fácilmente entre plataformas sin tener que introducir
    muchos cambios en el código fuente. En fin, GLUT simplifica la producción
    de código OpenGL y complementa la librería de OpenGL. GLUT es relativamente pequeño y fácil de aprender. Está bien
    diseñado y su autor ha escrito una buena documentación. Por eso, parece 
    redundante empezar una serie de artículos aquí en 
    LinuxFocus. Recomendamos a cualquier 
    desarrollador serio que lea la documentación de Mark. Nuestro propósito
    escribiendo estas columnas de GLUT es introducir la librería GLUT y su uso
    paso por paso con ejemplos, como complemento a la lectura de la serie de OpenGL de 
    este magazine. Esperamos hacer una contribución útil y como mínimo
    motivar a alguno de vosostros a subiros al vagón de Linux y OpenGL. De cualquier
    modo, obtener vuestra copia de la documentación de Mark como referencia. La API de GLUT es una máquina de estados, como OpenGL. Esto significa que 
    GLUT tiene una serie de variables de estado que duran toda la ejecución de 
    la aplicación. El estado inicial de la máquina de GLUT se ha elegido
    razonablemente para ajustarse a la mayor parte de aplicaciones. El programa puede
    modificar los valores de las varibles de estado para ajustarlas a su gusto. Cuando
    se llama a una función de GLUT, su acción se modifica de acuerdo a los 
    valores de las variables de estado. Las funciones de GLUT son simples, tienen pocos
    parámetros. Nunca devuelven punteros, y los únicos punteros pasados 
    a las funciones de GLUT son punteros a cadenas de caracteres y manejadores de fuentes.
     Las funciones de GLUT se pueden clasificar en varias subAPIs según su 
    funcionalidad: 
    Inicialización
    Inicio del procesado de eventos
    Control de ventanas
    Control de overlay
    Control de menús
    Registro de funciones Callback
    Control del mapa de colores
    Obtención del estado
    Trazado de fuentes
    Trazado de formas geométricas
     Es este artículo exploraremos algunas de las funciones de 
    inicialización, procesado de eventos y control de ventanas necesarias
    para empezar un sencillo programa en OpenGL. InitializationsTodo programa de OpenGL que utilice GLUT debe empezar inicializando el 
    estado de la máquina de estados de GLUT. Las funciones de inicialización
    de GLUT tienen el prefijo glutInit-. La rutina principal de 
    inicialización es glutInit:Uso: glutInit(int **argcp, 
                         char **argv);
 argcp 
    es un puntero a la variable argc de la función main (sin modificar).
    Al acabar la función, el valor apuntado por argcp se actualiza, ya que glutInit
    extrae todas las opciones de la línea de comandos relevantes para la 
    librería GLUT. Por ejemplo: bajo X Window System toda opción relevante
    para la ventana X asociada a la ventana GLUT..
 argv 
     es la variable argv de la función main (sin modificar).
 glutInit se encarga de modificar las variables de estado de GLUT y 
    negociar una sesión con el sistema de ventanas. Hay muy pocas funciones
    que pueden aparecer antes de glutInit; solo aquellas precedidas por 
    glutInit-. Estas rutinas se pueden usar para poner los estados de 
    inicialización por defecto, por ejemplo:Uso: glutInitWindowPosition(int x, 
                         int **y);
 glutInitWindowSize(int width, 
                         int **height);
 x , y 
    posición en la pantalla en píxels de la ventana 
                 (esquina superior izquierda)
 width, height 
     ancho y alto en píxels de la ventana.
 Existe otra rutina de inicialización ominpresente en toda aplicación
    en OpenGL, glutInitDisplayMode():Uso: glutInitDisplayMode(unsigned int mode);
 mode 
    es el modo de display, un OR de los posibles valores de display, que
    son:
 
 
   
    
    | GLUT_RGBA | Selecciona una ventana en modo RGBA. Es el valor por defecto
      si no se indican ni GLUT_RGBA ni GLUT_INDEX. |  
    | GLUT_RGB | Lo mismo que GLUT_RGBA. |  
    | GLUT_INDEX | Seleciona una ventana en modo de índice de colores. 
      Se impone sobre GLUT_RGBA. |  
    | GLUT_SINGLE | Selecciona una ventana en modo buffer simple. Es el valor por
      defecto. |  
    | GLUT_DOUBLE | Selecciona una ventana en modo buffer doble. Se impone sobre
       GLUT_SINGLE. |  
    | GLUT_ACCUM | Selecciona una ventana con un buffer acumulativo. |  
    | GLUT_ALPHA | Selecciona una ventana con una componente alpha del buffer de 
      color. |  
    | GLUT_DEPTH | Selecciona una ventana con un buffer de profundidad. |  
    | GLUT_STENCIL | Selecciona una ventana con un buffer de estarcido. |  
    | GLUT_MULTISAMPLE | Selecciona una ventana con soporte multimuestra. |  
    | GLUT_STEREO | Selecciona una ventana estéreo. |  
    | GLUT_LUMINANCE | Selecciona una ventana con un modelo de color de "luminancia". |  Si alguna de estas propiedades no te es familiar, no te preocupes, más tarde
    o más pronto escribiremos sobre ellas. Veamos un par de ejemplos, primero una
    inicialización simple para una aplicación de trazado en un paso:#include <GL/glut.h> 
 void main(int argcp, char **argv){
 
 /* Poner el tamaño y posición de la ventana */
 glutInitWindowSize(640, 480);
 glutInitWindowPosition(0, 0);
 
 /* Seleccionar el tipo de modo de display:
 Buffer simple y color RGBA */
 glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);
 
 /* Inicializar el estado de GLUT */
 glutInit(&argcp, argv);
 
 .....más código
 
 
 };
 
 Ahora un ejemplo de un programa de animación:#include <GL/glut.h> 
 void main(int argcp, char **argv){
 
 /* Poner el tamaño y posición de la ventana */
 glutInitWindowSize(640, 480);
 glutInitWindowPosition(0, 0);
 
 /* Seleccionar el tipo de modo de display:
 Buffer doble y color RGBA */
 glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
 
 /* Inicializar el estado de GLUT */
 glutInit(&argcp, argv);
 
 .....más código
 
 
 };
 
 Volveremos a estos dos ejemplos a medida que aprendamos más sobre GLUT.
    La principal diferencia es que en el segundo caso el display se inicializa en modo
    de doble buffer, ideal para animaciones porque elimina el parpadeo cuando cambia
    la imagen en la secuencia de animación. Procesado de EventosComo hemos dicho antes, GLUT es una máquina de estados. Ahora veremos
    que también está diseñada como un motor dirigido por eventos. Esto 
    significa que hay un "timer" o bucle continuo que comienza después de la 
    inicialización correspondiente y que procesa uno por uno todos los eventos 
    declarados a GLUT durante la inicialización. Los eventos son: un botón
    del ratón que se ha pulsado, una ventana que se cierra, una ventana que se 
    redimensiona, un cursor que se mueve, unas teclas del teclado que se han pulsado,
    un curioso evento "idle", esto es, no pasa nada. Cada uno de los posibles eventos
    se debe registrar en una de las variables de estado de GLUT para que el "timer"
    o bucle de proceso de eventos de GLUT mire periódicamente si este evento ha
    sido activado por el usuario. 
     Por ejemplo, podemos registrar "pulsar botón del ratón" como
    un evento para GLUT. Los eventos se registran mediante rutinas de registro
    callback. Todas tienen la sintaxis glut[algunEvento]Func, en el caso
    del click del ratón será glutMouseFunc. Un registro de callback
    le dice a la máquina de GLUT que función definida por el usuario se debe
    llamar si el correspondiente evento es activado. Así pues, si escribo mi rutina 
    MyMouse que especifica qué hacer cuando se pulsa el botón 
    izquierdo del ratón, o el botón derecho, etc.. entonces registraré 
    mi función callback después de glutInit() en main() usando
    la sentencia "glutMouseFunc(MyMouse);" . Dejemos para más tarde qué funciones callback y qué eventos
    están permitidos en GLUT. Lo importante ahora es señalar que después de 
    registrar todos los eventos importantes de nuestra aplicación debemos invocar
    la rutina de procesado de eventos de GLUT, que es glutMainLoop(). Esta 
    función nunca vuelve, nuestro programa básicamente comienza un bucle
    infinito. Irá llamando, cuando sea necesario, las funciones callback que hayan
    sido previamente registradas. Toda función main() de una aplicación OpenGL
    debe pues terminar en una sentencia glutMainLoop(). Así pues, en el caso 
    de nuestra plantilla de una animación: #include <GL/glut.h> 
 void main(int argcp, char **argv){
 
 /* Inicializar el estado de GLUT */
 glutInit(&argcp, argv);
 glutInitWindowSize(640, 480);
 glutInitWindowPosition(0, 0);
 
 /* Abrir una ventana */
 glutCreateWindow("My OpenGL Application");
 
 /* Seleccionar el tipo de modo de display:
 Buffer doble y color RGBA */
 glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
 
 
 /* Registrar funciones Callback */
 .....
 
 /* Iniciar el procesado de eventos */
 glutMainLoop();
 };
 
 Notad que he añadido un código que no he mencionado antes. Es una de las
    funciones de control de ventanas de GLUT, glutCreateWindow(char **name). 
    Esto es lo que me gusta de la filosofía de diseño de OpenGL y GLUT, está
    muy claro lo que hace la rutina solo mirando el nombre!. Sí, se encarga de 
    pasar la orden al sistema de ventanas subyacente de abrir una ventana para nuestra
    aplicación de OpenGL. La ventana tendrá el nombre "name", pasado como
    una cadena de caracteres. En el entorno de X Window este nombre se escribe en la esquina
    superior izquierda de la ventana. La sección de control de ventanas de GLUT
    tiene muchas otras funciones que iremos estudiando poco a poco. Por hoy, esta es 
    suficiente. También he reordenado las funciones de inicialización para
    mostrar que se pueden poner después de glutInit() Volvamos a los eventos... En el presente artículo quiero introducir
    dos funciones de registro de callback que son fundamentales en cualquier programa
    de animación. La función glutDisplayFunc, que registra la 
    función de display para la ventana actual y la función 
    glutIdleFunc, que registra la función "idle". Ambas rutinas
    de registro esperan una función del tipo void *(void). Supongamos
    que escribimos dos funciones callback adicionales en nuestra plantilla de 
    animación, void MyDisplay(void) que se encarga de invocar las 
    instrucciones OpenGL que dibujan nuestra escena en la ventana, y 
    void MyIdle(void) que es una función que es llamada cuando no 
    hay entradas del usuario, esto es, cada vez que el procesador de eventos de GLUT
    da una vuelta al bucle infinito (glutMainLoop()) y no encuentra ningún
    nuevo evento activado, procesa MyIdle. ¿Por qué necesito registrar
    una función callback "idle" en un programa de animación? Porque si
    quiero modificar cada una de las imágenes mostradas durante la animación
    independientemente de la entrada del usuario, debe existir una función (la
    función callback "idle") que se llame muy a menudo durante la ejecución 
    del programa y cambie las imágenes antes de que se dibujen en pantalla por 
    Mydisplay(). Animation ExampleFinalmente aquí tenemos una simple plantilla para un programa de 
    animación:#include <GL/glut.h> 
 void MyIdle(void){
 /* Código para modificar las variables que definen la próxima imagen */
 ....
 };
 
 void MyDisplay(void){
 /* Código OpenGL que dibuja una imagen */
 ....
 /* Después de dibujar la imagen, intercambiar los buffers */
 glutSwapBuffers();
 };
 
 void main(int argcp, char **argv){
 
 /* Inicializar el estado de GLUT */
 glutInit(&argcp, argv);
 glutInitWindowSize(640, 480);
 glutInitWindowPosition(0, 0);
 
 /* Abrir una ventana */
 glutCreateWindow("My OpenGL Application");
 
 /* Seleccionar el tipo de modo de display:
 Buffer doble y color RGBA */
 glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
 
 
 /* Registrar funciones Callback */
 glutDisplayFunc(MyDisplay)
 glutIdleFunc(MyIdle)
 
 /* Iniciar el procesado de eventos */
 glutMainLoop();
 };
 
 Nota que al final de MyDisplay he añadido una nueva rutina
    GLUT, glutSwapBuffers(). Es muy útil en animaciones.
    Estamos usando una ventana en modo de DOBLE buffer, uno que se muestra y otro
    oculto. Las instrucciones de dibujo de OpenGL en este caso siempre trazan en el 
    buffer oculto. La llamada a glutSwapBuffers intercambia los buffers, 
    mostrando en la ventana de golpe todo lo que hemos dibujado. Esta técnica 
    es común en animaciones por ordenador porque previene que el ojo humano vea
    como se construye la imagen línea por línea. Ya tenemos suficiente material para empezar a escribir aplicaciones OpenGL,
    lo único que falta son las instrucciones OpenGL en MyDisplay
    que realizan el dibujo... pero esto es otra historia ;-). En el próximo artículo de programación en GLUT 
    exploraremos más profundamente las funcionalidades disponibles en la 
    sección de control de ventanas de GLUT como abrir multiples escenas
    dentro de la misma ventana. También aprenderemos cómo usar menús
    y sus pros y contras en cuanto a portabilidad. |