Este artículo es el tercero de la serie dedicada a
la GL Utility Toolkit (GLUT) escrito por Mark
Kilgard. GLUT es una herramienta muy útil para
desarrolladores con OpenGL porque abstrae en un API simple de
aprender y utilizar la parte dependiente de la arquitectura
de la aplicación OpenGL.
Como se ha mencionado en artículos anteriores, las
razones por razones de desarrollo OpenGL sólo incluye
directivas para la parte independiente de la arquitectura de
un API 3D avanzado. Abrir ventanas, manejo de la entrada de
teclado, etc.. son muy dependientes de la plataforma y
está fuera del alcance de OpenGL. GLUT es la
respuesta a este problema: GLUT ofrece un interfaz
común para múltiples plataformas para el manejo
de cosas como ventanas, buffers, renderización de
texto, entrada por el teclado y menús.
Hoy exploraremos el uso de menus dentro de nuestras
aplicaciones OpenGL. Un menú es una forma más
de comunicación con el usuario por parte de la
aplicación. En nuestro artículo previo (Manejo
de Ventanas) demostramos otra técnica para la
entrada del usuario, teclas rápidas. GLUT captura los
eventos de entrada de teclado tan bien como los eventos de
ratón y nos deja introducir funciones
(callbacks) para manejar estos incidentes. La
desventaja de las "teclas rápidas" es que son
invisibles, requieren que el usuario lea un manual o recuerde
que tecla hace tal cosa. Los menús, por otra parte son
mucho más flexibles porque son gráficos y
muestran las opciones de usuario disponibles
actualmente.
Como ejemplo he aquí el código fuente de un
programa ejemplo (../../common/May1998/example1.tar.gz). Este programa
es una modificación del programa mostrado en el
artículo anterior Manejo
de Ventanas. Obtenga el programa y compilelo utilizando
el Makefile suministrado.
La compilación requiere la librería OpenGL
(Mesa, por ejemplo) y la biblioteca GLUT. Ambas
librerías son fácilmente obtenibles y
están incluidas de una forma u otra en muchas
distribuciones de Linux. Tras la compilación
obtenemos el fichero ejecutable, example1
. Ejecútelo y podrá ver una nueva ventana
con un triángulo en color de vértices rojo,
verde y azul (ver figura de la izquierda).
El área contiene una ventana principal con fondo
negro y un triángulo en el centro y una sub-ventana
con fondo gris con texto escrito en ella. Nuestro ejemplo es
realmente una animación en tiempo real por lo que el
campo de tiempo en la sub-ventana debe ser
incrementado.
Mueva el ratón al área de la ventana
principal, mantenga pulsado el botón izquierdo del
ratón. Este evento es manejado por nuestro ejemplo y
debe abrir el siguiente menú:
El menú contiene tres opciones (rotar, trasladar,
cambiar color) y una final para terminar la
aplicación. Mientras mantenga pulsado el botón
izquierdo del ratón, muévase a cada
opción y pruebe lo que ocurre.
Seleccionando "Rotation On" el triángulo
rotará alrededor de su centro de gravedad, la
opción "Translation On" moverá el
triángulo horizontalmente a través de la
ventana y lo ocultará cuando el final de la ventana
sea alcanzado. La opción "Change Color" lanzará
un sub-menú nuevo con estas nuevas opciones:
- Red Representa el triángulo en rojo
- Blue Representa el triángulo en azul
- Green Representa el triángulo en verde
- RGB Representa el triángulo en rojo, verde y azul
Este es un ejemplo muy simple del uso de menús en
una aplicación OpenGL. La apariencia de menús
y sub-menús es dependiente de la plataforma. El
aspecto mostrado aquí es el del GLUT para Linux,
compilado y ejecutado bajo Windows 95/NT tendrá una
apariencia diferente. Lo que importa es que esta
funcionalidad es portable si utiliza la librería
GLUT.
Note que el menú principal tiene dos tipos
diferentes de entradas. El primer tipo (Rotation On,
Translation On) cambia cuando la acción es
ejecutada. Esto significa que tras lanzar el
triángulo en modo de rotación, la
próxima vez que vea el menú principal
aparecerá "Rotation Off". La otra clase de entrada
en el menú permanece siempre constante, como "Change
Color", "Red", "Blue" y "Green".
Existen unas algunas cosas interesantes que me
gustaría advertir antes de continuar describiendo
como programar estos menús. ¿Recuerda que esta demo
tiene dos teclas rápidas? (Manejo
de Ventanas). Estas teclas son "q" para salir y "i"
para activar o desactivar la información. Intente
utilizarlas de nuevo, todavía funcionan.
Mientras que el mensaje con información
está activado (sub-ventana), mueva el cursor a
cualquier lugar dentro de la región gris y pulse el
botón izquierdo del ratón. ¡Sorpresa!
Verá este otro menú:
Esto demuestra que los menús no sólo son
relacionados con eventos (botón izquierdo del
menú) sino que son utilizados de acuerdo con el
actual contexto OpenGL. También hemos mencionado en
nuestro pasado artículo que una sub-ventana GLUT
incorpora su propio contexto OpenGL, aquí podemos
ver otra consecuencia de este hecho. Dos menús han
sido relacionados con el mismo evento pero en diferentes
contextos. El desarrollador tiene una herramienta potente
para escribir un interfaz de usuario sofisticado para su
aplicaciones.
Todavía más interesante es el hecho que si
desactiva el mensaje informativo utilizando la tecla "i" y
mueve el cursor ala parte superior del área,
será incapaz de volver a la entrada "This is a dummy
entry". ¿Porque?, porque cuando oculta la sub-ventana el
contexto general (incluyendo los menús incorporados)
con ocultados también.
A continuación vamos a empezar a describir el
contenido del programa. Asumo que el lector está
familiarizado con el material GLUT y OpenGL presentado en
los artículos previos. El área
main() contiene diferentes secciones, ¡el orden
es importante!:
- Inicialización de GLUT. Abre una ventana, pasa argumentos de la línea de comandos, fija el modo de visualización y opciones.
- Creación de la Ventana Principal. Crea la ventana principal, fija las funciones (callbacks) principales asociadas con la ventana, define la función idle
- Crear un Sub-menu. Este sub-menú maneja la opción "Change Color", y será incluida dentro del menú principal posteriormente.
- Crear Menú Principal. Construye el menú principal, añade los ítems y el sub-menú previamente definido, finalmente asocia este menú al botón izquierdo del ratón. Nótese que todavía estamos en el contexto de la ventana principal.
- Crear lista de "llamadas GL" . Este es un punto avanzado de OpenGL que no trataremos aquí exhaustivamente. Es suficiente con saber que una lista de llamandas es una forma de construir y encapsular objetos OpenGL en tiempo de ejecución para su posterior invocación. Invocando un elemento "GL call list", en oposición a construir el objeto una y otra vez es mucho más eficiente para invocaciones posteriores. En esta demo hay cuatro elementos de este tipo: un triángulo rojo, uno azul, uno verde y uno RGB.
- Creación de una sub-ventana . Crea una sub-ventana, salva un manejador, asocia las correspondientes funciones. Después que la sub-ventana es creada un nuevo contexto se activa.
- Creación del Menú Principal para la sub-ventana. Exactamente como hicimos anteriormente, creamos un nuevo menú, añadimos entradas y lo asociamos al botón izquierdo del menú. Ahora, el menú estará asociado al contexto de la sub-ventana.
Excepto para la creación del menú, todas
las otras secciones deben ser familiares al lector. Una
rápida inspección del código para la
creación de menús revela cuan simple es. Los
menús son creados por la directiva int
glutCreateMenu( void (f*)(int) ) , la función
f es de tipo callback que toma el
número devuelto por la opción del menú
escogida y realiza la acción pertinente. El valor
devuelto por la función glutCreateMenu es un
identificador entero para la nueva ventana creada.
Las funciones utilizadas para manejo de eventos de
menú son muy simples. Examine void
mainMenu(int) o void
subMenu(int). Básicamente utilizan una sentencia
switch que procesa el ítem del menú
escogido.
Tras crear el menú nuestro código añade
entradas a éste utilizando la función void
glutAddMenuEntry(char *s, int nr), donde "s" es el
texto a escribir en el campo de la entrada en el menú
y "nr" es el identificador de la función a llamar
cuando este ítem sea escogido. Obviamente evite
utilizar identificadores iguales para entradas diferentes en
el mismo menú.
Los Sub-menus son añadidos de una forma similar utilizando
la función void glutAddSubMenu(char *s, int
nr).
Finalmente, tras completar el menú, es asociado a
un evento de entrada (botón izquierdo del
ratón) con la función
glutAttachMenu. Esta función también
puede tomar los valores GLUT_RIGHT_BUTTON,
GLUT_MIDDLE_BUTTON.
Las entradas en los menús pueden ser cambiadas en
cualquier momento gracias al comando void
glutChangeToMenuEntry(int entry, char *s, int value),
que cambia la entrada en el menú "entry" de la ventana
actual por otra que tiene la etiqueta s y devuelve
value a la función la invoca. En nuestro
ejemplo hemos utilizado
glutChangeToMenuEntry(2, "Translation On", 2);
para cambiar la entrada número 2 del menú
principal, y así para los otros casos.
GLUT permite más posibilidades: cambiar una
opción de menú por un sub-menu de tipo cascada
y viceversa, destruir menús, eliminar opciones de un
menú, y más. No las exploraremos porque pienso
que el ejemplo expuesto es suficientemente simple y funcional
para satisfacer las necesidades de muchos usuarios. Si
necesita capacidades extra por favor consulte el manual
más reciente sobre GLUT.
|