| IntroducciónEste artículo continúa nuestras series sobre GLUT, la biblioteca de 
          herramientas GL escrita por Mark Kilgard para aplicaciones OpenGL.
          Tal y como mencionamos en nuestro artículo anterior (Ventanas y Animaciones) GLUT
          es una biblioteca muy interesante y útil para cualquier
          programador OpenGL, ya que permite escribir código portable. GLUT 
          esconde al programador los detalles complicados del gestor de
          ventanas y del interfaz GUI. GLUT se divide en varios  subAPIs. En este número
          describiremos el subAPI de Gestión de Ventanas. 
          Como su nombre indica, éste se ocupa de tareas relacionadas
          con las ventanas usadas por tu aplicación OpenGL:
          crear, cerrar, minimizar una ventana; poner delante, detrás
          esconder, mover; y poner títulos, posiciones, etc... El Sub-Api de Gestión de VentanasAquí está la lista completa de funciones soportadas por la gestión de ventanas
          en GLUT (En la versión 3.6): 
	  
	  | int glutCreateWindow(char *name) | Crea una nueva ventana top-level |  
	  | int glutCreateSubWindow(int win, int x, int y, int width, int height)
 | Crea una sub-ventana |  
	  | void glutSetWindow(int winId) | Establece la ventana con ID winId como ventana actual |  
	  | int glutGetWindow(void) | Solicita el identificador de la ventana actual |  
	  | void glutDestroyWindow(int winId) | Cierra la ventana especificada por winId |  
	  | void glutPostRedisplay(void) | Le dice al procesador de eventos de GLUT que la ventana
               actual necesita ser redibujada |  
	  | void glutSwapBuffers(void) | Intercambia los buffers de la ventana actual |  
	  | void glutPositionWindow(int x, int y) | Solicita un cambio en la posición de la ventana |  
	  | void glutReshapeWindow(int width, int height) | Solicita un cambio en el tamaño de la ventana |  
	  | void glutFullScreen() | Solicita que la ventana actual cambie a full screen |  
	  | void glutPopWindow(void) void glutPushWindow(void)
 | Hace un Push o un Pop de la ventana actual relativo a las otras en
          la pila |  
	  | void glutShowWindow(void) void glutHideWindow(void)
 void glutIconifyWindow(void)
 | Muestra, esconde o minimiza la ventana actual |  
	  | void glutSetWindowTitle(char *name) void glutSetIconTitle(char *name)
 | Pone la barra de título en la ventana o en la ventana minimizada |  Usar Sub-ventanasEl uso de la mayoría de las funciones anteriores es
         muy simple.  Algunas fueron analizadas en nuestro primer
         artículo: glutPostRedisplay, glutCreateWindow,
         glutPositionWindow, glutSwapBuffers,..etc. Otras, aunque
         nuevas, tienen un uso trivial y la descripción
         anterior explica muy bien lo que hacen, como
         glutSetIconTitle, glutFullScreen, ...etc. Sin
         embargo el uso de sub-ventanas no es tan simple, por lo que
         hemos decidido poner a continuación un ejemplo simple
         y explicar brevemente los detalles de su
         implementación. Aquí está el código fuente de la
         pequeña demo OpenGL-GLUT (../../common/March1998/example1.c, ../../common/March1998/Makefile). Su proposito es
         enseñarte: (a) Cómo manejar una sub-ventana,
         (b) Cómo usar el teclado para interaccionar con tu
         aplicación OpenGL (c) Cómo trazar texto en una
         ventana OpenGL..(Por favor, imprime o ten a mano el
         código fuente del ../../common/March1998/example1.c a mano mientras seguimos
         con la explicación.) 
 
 ![[Vista del example1 ejecutándose]](../../common/March1998/glut2.jpg) 
 Primero vamos a echar un vistazo a la función
         main().  Ésta empieza, como cualquier otra aplicación
         GLUT, con inicializaciones: analizar las opciones de la
         línea de comandos, seleccionar el modo de pantalla y
         establecer la posición y el tamaño de la
         ventana inicial. Como nuestra aplicación va a manejar
         más de una ventana, es necesario guardar el entero ID
         de la ventana devuelto por la expresión
         glutCreateWindow. La variable winIdMain es un
         manejador de ventana.  Es ahora el momento de establecer las
         funciones callback para los eventos asociados a la ventana
         winIdMain; algunas funciones callback están
         definidas y todas realizan una tarea en la ventana principal:
         una función de pantalla (mainDisplay) que
         dibuja la escena, una función de cambio de forma
         (mainReshape) que maneja cualquier
         transformación del marco de la ventana, otra llamada
         keyboard que maneja las acciones disparadas por el teclado y
         otra llamada idle para manejar la animación cuando no
         hay otros eventos pendientes (ver  Animaciones y
         Ventanas para una descripción más detallada
         de la función idle;  La primera cosa importante a saber es que en una
         aplicación GLUT sólo puede haber una
         función callback idle. La función idle es
         global para todas las ventanas de la aplicación. Ten
         esto en cuenta cuando diseñes las funciones
         idle(), ellas deben tener cuidado de refrescar todas
         las ventanas y subventanas de la aplicación. Después, en el código, viene la
         creación de una subventana (winIDSub).  Para
         crear una sub-ventana debes proporcionar el ID de la ventana
         de nivel superior, en el presente caso winIDMain, las
         coordenadas de x e y en pixels para la
         subventana, relativas a las coordenadas internas de
         winIDMain, y el ancho y el alto en pixels de la
         ventana solicitada. Después de crear la subventana
         GLUT devuelve un manejador a nuestro programa y, entonces, ya
         estamos listos para monar las funciones callback apropiadas
         para winIDSub. En nuestra dema hemos establecido dos
         funciones callback: una display (subDisplay) y otra reshape
         (subReshape).  Cuando GLUT abre una subventana le proporciona un contexto
         OpenGL completo. Hay pues una pérdida de rendimiento
         al usar subventanas, ya que el driver de la tarjeta de video
         tiene que refrescar el área de memmoria para cada una de las
         ventanas en pasadas separadas. Gracias a tener un contexto
         OpenGL independiente cada ventana tiene su propio sistema de
         coordenadas, por ejemplo. En ../../common/March1998/example1.c los sistemas de
         coordenadas se ponen en mainDisplay() y
         subDisplay() respectivamente. Ahora ve y examina
         esas dos funciones de pantalla, son bastante simples y si has
         seguido nuestro artículo de Enero sobre OpenGL ("Trazado de
         Polígonos Simples") no tendrás problemas
         para entenderlas. La función mainDisplay() dibuja un
         triángulo con vértices Verde, Rojo y
         Azul. OpenGL interpola los colores entre los tres
         vértices para llenar el polígono. Antes de
         trazar el triángulo hemos añadido un
         glRotate que gira el triángulo alrededor del
         eje z (perpendicular a la pantalla), el ángulo de
         rotación (spin) se incrementa lentamente en
         idle() para dar la ilusión de que la figura
         está girando. También la función de pantalla asociada con
         winIdSub está bastante clara. Primero pinta el
         fondo con un color gris, luego dibuja un borde verde
         alrededor de la subventana y, finalmente, traza un
         texto. Más tarde explicaremos como funciona el trazado
         de texto bajo GLUT. Por el momento basta señalar que
         glRasterPos2f(x,y) establece la posición
         dónde se va a dibujar el texto, y que las coordenadas
         x e y usadas son relativas a las
         coordenadas del sistema de la subventana (definida en
         subReshape()).  La subventana actúa como un tablero de texto para
         los datos provenientes de la animación. Es una
         aplicación tonta, bastaría con haber dibujado
         el tablero de texto en la ventana principal para conseguir el
         mismo resultado (incluso de forma más eficiente). Sin
         embargo, bajo algunas circunstancias, tiene sentido abrir una
         ventana para la salida de texto. Por ejemplo cuando la
         animación es en 3D con luces y efectos ambientales y
         no quieres deformar tu tablero de texto con luces molestas,
         efectos de perspectiva, sombras o niebla, etc.  Bajo esas
         circunstancias una subventana es muy útil ya que
         está completamente aislada de la animación
         3D. Hay una diferencia crucial entre la función
         callback de cambio de tamaño para la ventana de
         más alto nivel o para una subventana. Cuando un evento
         de cambio de tamaño se dispara, sólo se invoca
         la función callback de cambio de tamaño de la
         ventana de más alto nivel, en nuestro ejemplo
         mainReshape(). Hay que llamar a la función
         callback de cambio de tamaño subReshape desde
         dentro de mainReshape. Esto tiene sentido ya que la
         localización y la forma de las subventanas está
         condicionado al tamaño y la forma de su ventana
         principal. De esta forma, si lees ahora el código de
         mainReshape()verás que primero damos valor a
         la matriz de proyección para la ventana de más
         alto nivel, entonces cambiamos a la subeventana
         winIDsub y vamos invocando secuencialmente la
         función de cambio de tamañode la subventana con
         el ancho y el alto relativos a winIDMain que queremos
         usar. Antes se mencionó que la función callback
         idle() debe actualizar todas ventanas principales y
         subventanas en la aplicación OpenGL. En nuestro
         ejemplo idle() actualiza primero las variables de
         estado de la animación (time y spin) y
         luego pide a la ventana principal y la subventana que se
         revisualicen. El tecladoHe añadido dos teclas activas al programa. Pulsando
         la tecla "i" puedes activar o desactivar el tablero de texto
         y con la tecla "q" puedes salir de la
         aplicación. Pruébalas :) En el momento que pulses una tecla en tu teclado, el
         driver de proceso de eventos de GLUT registra un evento de
         teclado. Estos eventos son manejados por las funciones
         callback de teclado. En principio cada ventana tiene su
         propia función callback. Cuando el ratón
         está en la posición (x, y) dentro de una
         ventana (o subventana) y se dispara un evento de teclado
         entonces la función callback de teclado asociada con
         esa ventana es invocada. Esta función callback toma
         como argumentos el código ASCII unsigned char
         asociado con la tecla y la posición x, y del cursor en
         ese momento. En ../../common/March1998/example2.c no hay ningún uso para x, y
         pero estoy seguro de que se te ocurriran aplicaciones donde
         puedas tomar ventaja de esta interesante
         característica.  Únicamente la ventana de más alto nivel en nuestra
         demo tiene una función callback. Si pruebas a pulsar
         las teclas "i" o "q" mientras el cursor está dentro de
         la subventana verás que no pasa nada. Por defecto
         cuando se crea una ventana y no se registra ningún
         callback de teclado entonces todas las pulsaciones de teclado
         se ignoran. Ten esto en cuenta en el futuro si usas múltiples
         ventanas y quieres que el teclado esté activo. Finalmente mencionar que la generación de callbacks
         de teclado se puede deshabilitar pasando NULL a
         glutKeyBoardFunc(). Trazar textoRenderizar texto en OpenGL y GLUT es un
         coñazo!. Perdón por decirlo pero es la
         verdad. No estoy seguro de por qué el renderizado de
         texto ha sido desatendido en la librería OpenGL. La
         vieja librería GL de SGI tenía unas pocas
         funciones de alto nivel para manejar el trazado de texto en
         el modo gráfico y había una librería
         auxiliar adicional para cambiar fuentes. OpenGL sólo
         proporciona directivas muy primitivas para el trazado de
         bitmaps, y eso significa que te tienes que hacer tu propia
         librería de bitmaps para cada carácter, tener
         en cuenta la resolución, el escalado de
         fuentes... todo lo necesario!.  GLUT resuelve un poco el dilema de usar texto en OpenGL.
         Proporciona glutBitmapCharacter, que traza un
         único carácter en la posiciçón
         especificada por glRasterPos. He añadido unas
         pocas funciones, drawString(), y
         drawStringBig() que hacen la vida un poco mejor al
         trazar cadenas de caracteres. ConclusiónAquí concluye una introducción muy simple al
         uso de subventanas de GLUT. En este punto tengo que mencionar
         que, aunque es bueno experimentar con él bajo varias
         plataformas, el soporte de subventanas de GLUT no es
         completamente funcional en todos los entornos. Usuarios con
         tarjetas basadas en 3Dfx comprobarán que las
         subventanas aún no son funcionales debido a
         limitaciones hardware. También he encontrado una gran
         penalización en las prestaciones al usar subventanas
         en algunas plataformas. Por ejemplo, bajo mi Linux Alpha con
         una Matrox Millenium con 2Mb, una subventana hace que la
         aplicación vaya el doble de lento, probablemente
         porque desafortunadamente el servidor X para Alpha
         todavía no soporta ninguna aceleración
         hardware. Por el otro lado, la misma aplicación en
         windows 95 y una ATI RageII con 2Mb con el driver de SGI para
         OpenGL va de maravilla. Como el desarrollo de Linux se mueve tan rápido es
         posible que en un futuro cercano alguno de los problemas de
         rendimiento e incompatibilidades desaparezcan. Por el momento
         simplemente tener en cuenta que existen, por lo que usa
         múltiples ventanas con cautela. Por supuesto, usuarios avanzados pueden encontrar
         siempre una forma alternativa de usar subventanas jugando con
         la pila matriz, pero como todavía no hemos estudiado
         esto, perdóname si lo dejo para más
         tarde.. ;)).  |