Principal | Gráficos 3D | Gráficos 2D | Fractales | Math | Códigos | Tutoriales | Links
GLUT es un interfaz de programacion con "C" ANSI y FORTRAN para escribir programas en OpenGL que sean independientes del sistema operativo de ventanas. La utilidad GLUT ofrecen , entre otras cosas, las siguientes prestaciones:
En el siguiente articulo, se explotan sobre todo la segunda y la cuarta
de las prestaciones antes numeradas.
GLUT procesa tres tipos de eventos : los eventos de ventanas, menús y globales.
Los primeros indican cuando es necesario redimensionar o dibujar una ventana,
y cuando se produce un evento de entrada por ventana. Los eventos de menús se
definen mediante la llamada glutCreateMenu(), y los globales procesan
el transcurso del tiempo y el uso de los menús. En el Cuadro 1 se presenta
parte de la función principal del programa, donde se destacan en negritas las
rutinas destinadas a procesar eventos de entrada.
Así por ejemplo, glutDisplayFunc establece cuando se debe redibujar la
ventana. El modo de utilizar esta rutina es el siguiente void glutDisplayFunc(void
(*func)(void));, el argumnto de glutDisplayFunc es un puntero a una
función Display que debe definir el usuario. En nuestro caso es la función redibuja(),
que a su ves llama dibujaBox(), la encargada de dibujar el cubo. Para
cada ventana que se crea se debe establecer la respectiva glutdisplayFunc(),
pues en caso contrario se produce error.
La función glutMouseFunc() define la respuesta del sistema ante un evento
de ratón en la ventana de trabajo. Cuando el usuario pulsa o suelta los botones
del ratón , cada pulsación genera una llamada a glutMouseFunc(). Su sintaxis
se establece de la siguiente forma.
void glutMouseFunc(void(*func) (intbutton, int stute, int x, int y));
El parametro buttom puede tomar los valores GLUT_LEFT_BUTTOM, GLUT_MIDDLE_BUTTOM,
o GLUT_RIGHT_BUTTOM. El parámetro de estado puede ser GLUT_UP o GLUT_DOWN e
indica si el respectivo botón a sido pulsado o liberado. Los parámetros X e
Y la posición del ratón , en coordenadas relativas a la ventana en el momento
en que cambia el estado del mismo. La función que aparece como primer parámetro
debe ser definida por el programador, y en nuestro caso se encarga de guardar
las coordenadas del ratón en el momento que se ha pulsado el botón izquierdo
para el calculo del desplazamiento de este en ambos ejes de la ventana.
El desplazamiento del ratón se utiliza en la función movimiento() para
definir, de acuerdo a la operación que se vaya a realizar, la magnitud del ángulo
a rotar, el desplazamiento del objeto o la magnitud del escaldo. Esta función
la debe definir el programador y es llamada por glutMotionfunc(), otra
de las rutinas dedicadas a procesar eventos de entrada.
La rutina glutMotionFunc() es llamada por el sistema cuando el ratón
se mueve por la ventana de trabajo con uno o mas botones pulsados . De forma
análoga existe la función glutPassiveMotionFunc(), que es llamada cuando
el ratón se mueve dentro de la ventana sin tener ningún botón pulsado. El modo
de empleo de ambas funciones es el siguiente:
void glutMotionFunc(void (*func)(int x, imt y));
void glutPassiveMotionFunc(void (*func)(int x, int y));
Si bien estas funciones nos ayudan a establecer cuando se ah movido el ratón
y la magnitud de su desplazamiento, debemos disponer de un medio para indicar
al sistema la operación que deseamos realizar sobre el objeto en cuestión. Esto
es posible realizarlo por medio del ratón, gracias al sencillo sistema de menú
desplegables que es posible construir con Glut (Figura 1).
Figura 1
La forma de construir un menú desplegable se aprecia en el Cuadro 1 Mediante las funciones glutCreateMenu(), glutAddMenuEntry(), glutAddSubMenu() y glutAttachMenu(), respectivamente. Su forma de empleo se señala a continuación:
Cuadro 1 int main(int argc, char **argv) { int menu_rotar, menu_mover, menu_escalar; glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(400, 400); glutCreateWindow("Transformaciones con el mouse"); glutDisplayFunc(redibuja); glutMouseFunc(mouse); glutMotionFunc(movimiento); menu_rotar=glutCreateMenu(controlTransf); glutAddMenuEntry("eje x", ROT_EJE_X); glutAddMenuEntry("eje y", ROT_EJE_Y); glutAddMenuEntry("eje z", ROT_EJE_Z); menu_mover=glutCreateMenu(controlTransf); glutAddMenuEntry("eje x", MOV_EJE_X); glutAddMenuEntry("eje y", MOV_EJE_Y); glutAddMenuEntry("eje z", MOV_EJE_Z); glutAddMenuEntry("ejes xy", MOV_EJE_XY); menu_escalar=glutCreateMenu(controlTransf); glutAddMenuEntry("eje x", SCAL_EJE_X); glutAddMenuEntry("eje y", SCAL_EJE_Y); glutAddMenuEntry("eje z", SCAL_EJE_Z); glutAddMenuEntry("ejes xyz", SCAL_EJE_XYZ); glutCreateMenu(controlTransf); glutAddSubMenu("Rotar", menu_rotar); glutAddSubMenu("Mover", menu_mover); glutAddSubMenu("Escalar", menu_escalar); glutAttachMenu(GLUT_RIGHT_BUTTON); , , glutMainLoop(); killObject(obj); return 0; } |
El menú se debe crear desde los elementos mas internos, es decir, comenzadon
por cada uno de los submenis . Mediante glutCrateMenu() se crean los
diferentes menús para rotar, mover y escalar. Esta función se llama por el sistema
cuando se selecciona alguna de las entradas del menú (en nuestro caso, cuando
se selecciona alguna de las opciones correspondientes a los diferentes ejes
de coordenadas). La función glutCreateFunc() devuelve un único identificador
entero que comienza en la unidad.
Para cada entrada del menú principal, se establecen las respectivas entradas
o submenus mediante glutAddMenuEntryy(), que añade una nueva entrada.
Cada ves que es seleccionada una de estas entradas, glutAddMenuEntryy()
pasa, mediante el parametro value, la entrada seleccionada.
Al menu principal, que se forma de la misma forma que los submenus, se le adiciona
cada uno de los submenus antes creados mediante glutAddSubMenu(). La
cadena de caracteres "name", que se pone como parámetro, aparecerá cuando
se despliegue el menú principal.
Por ultimo, glutAttchMenu(), establece la aparición y despliegue del
menú cuando se pulse alguno de los botones del ratón, lo que se define mediante
uno de los parámetros, GLUT_LEFT_BUTTON, GLUT_RIGHT_BUTTON o GLUT_MIDDLE_BUTTON.
En nuestro caso concreto, la aparición y despliegue del menú se relaciona con
la pulsación del botón derecho del ratón.
El Codigo lo compile con VisualC++ 6-0, el modelo que utilice fue uno de los que viene con el 3D Studio Max, los de baja resolucion poligonal, exporte el modelo a formato ASCII para luego simplificarlo y asi poder cargarlo desde OpenGL, en realidad podria haber cargado directamente el modelo ASCII pero el codigo para leer este tipo de archivos es algo largo y confuso. Como convertir y utilizar este formato lo podes ver en el articulo Conversion de un archivo ASCII.
Codigo fuente y archivo con el modelo de prueba
/* Programa que demuestra la ejecución de transformaciones del tipo mover, rotar, escalar en 3D de modo interactivo mediante el mouse. Se utiliza un sistema de menú para la selección de las distintas operaciones. */ #include <GL/glut.h> #include <GL/glaux.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> struct Point3f { float x, y, z; }; struct Face { int vertexIndices[3]; int smoothingGroup; Point3f normal[3]; Point3f faceNormal; }; struct Object3D { int nVertices; Point3f *pVertices; int nFaces; Face *pFaces; }; enum {ROT_EJE_X, ROT_EJE_Y, ROT_EJE_Z, MOV_EJE_X,MOV_EJE_Y,MOV_EJE_Z,MOV_EJE_XY, SCAL_EJE_X,SCAL_EJE_Y,SCAL_EJE_Z,SCAL_EJE_XYZ}; Object3D obj; int moving, begin_x,begin_y; int newModel = 1; GLfloat trasl_x, trasl_y; GLfloat escala_x, escala_y; GLfloat angle =0.f; GLboolean ROTAR=FALSE; GLboolean MOVER=FALSE; GLboolean ESCALAR=FALSE; GLfloat EJE_X=0.0; GLfloat EJE_Y=0.0; GLfloat EJE_Z=0.0; float LightPos[] = { 0.0f, 0.0f, 1.0f, 0.0f}; float LightAmb[] = { 0.2f, 0.2f, 0.2f, 1.0f}; float LightDif[] = { 1.0f, 1.0f, 1.0f, 1.0f}; float LightSpc[] = { 0.5f, 0.5f, 0.5f, 1.0f}; void CargarModelo(char *filename, Object3D &object) { FILE *file; char *tempString = new char [80]; char trash[15]; float tempX, tempY, tempZ; int tempA, tempB, tempC; int indexSmoothing; int i; if((file = fopen(filename, "rt"))==NULL) { printf("File Not Found : %s\n",filename); exit(1); } while(strncmp(tempString, "Vertices",8)) { fscanf(file, "%s", tempString); if (feof(file)) { printf("String \"Vertices\" no existe\n"); exit(1); } } fgetc(file); fscanf(file, "%d", &object.nVertices); object.pVertices = new Point3f[object.nVertices]; for (i=0; i<object.nVertices; i++) { fscanf(file, "%f %f %f\n", &tempX, &tempY, &tempZ); object.pVertices[i].x=tempX; object.pVertices[i].y=tempY; object.pVertices[i].z=tempZ; } while(strncmp(tempString, "Faces",5)) { fscanf(file, "%s", tempString); if (feof(file)) { printf("String \"Faces\" no existe\n"); exit(1); } } fgetc(file); fscanf(file, "%d", &object.nFaces); object.pFaces = new Face[object.nFaces]; for (i=0; i<object.nFaces; i++) { fscanf(file, "%d %d %d\n", &tempA, &tempB, &tempC); fscanf(file, "%s %d\n", &trash, &indexSmoothing); object.pFaces[i].vertexIndices[0]=tempA; object.pFaces[i].vertexIndices[1]=tempB; object.pFaces[i].vertexIndices[2]=tempC; object.pFaces[i].smoothingGroup=indexSmoothing; } } void applySmoothingGroups(Object3D &object) { int i,j,k,l,smoothingGroup; float length; for (i=0; i<object.nFaces; i++) { smoothingGroup = object.pFaces[i].smoothingGroup; for (j=i+1; j<object.nFaces; j++) { if (smoothingGroup == object.pFaces[j].smoothingGroup) { for (k=0; k<3; k++) { for (l=0; l<3; l++) { if (object.pFaces[i].vertexIndices[k] == object.pFaces[j].vertexIndices[l]) { object.pFaces[i].normal[k].x += object.pFaces[j].faceNormal.x; object.pFaces[i].normal[k].y += object.pFaces[j].faceNormal.y; object.pFaces[i].normal[k].z += object.pFaces[j].faceNormal.z; object.pFaces[j].normal[l].x += object.pFaces[i].faceNormal.x; object.pFaces[j].normal[l].y += object.pFaces[i].faceNormal.y; object.pFaces[j].normal[l].z += object.pFaces[i].faceNormal.z; } } } } } for (k=0; k<3; k++) { length = sqrt(pow(object.pFaces[i].normal[k].x,2.0) + pow(object.pFaces[i].normal[k].y,2.0) + pow(object.pFaces[i].normal[k].z,2.0)); if (length == 0) { object.pFaces[i].normal[k].x = 1; object.pFaces[i].normal[k].y = 1; object.pFaces[i].normal[k].z = 1; } else { object.pFaces[i].normal[k].x /= length; object.pFaces[i].normal[k].y /= length; object.pFaces[i].normal[k].z /= length; } } } } void CalcularNormales(Object3D &object) { float x1, y1, z1; float x2, y2, z2; float x3, y3, z3; float length; int a, b, c; int i; for (i=0; i<object.nFaces; i++) { Face& face = object.pFaces[i]; a = face.vertexIndices[0]; b = face.vertexIndices[1]; c = face.vertexIndices[2]; x1 = object.pVertices[b].x - object.pVertices[a].x; y1 = object.pVertices[b].y - object.pVertices[a].y; z1 = object.pVertices[b].z - object.pVertices[a].z; x2 = object.pVertices[c].x - object.pVertices[a].x; y2 = object.pVertices[c].y - object.pVertices[a].y; z2 = object.pVertices[c].z - object.pVertices[a].z; z3 = x1*y2 - y1*x2; x3 = y1*z2 - z1*y2; y3 = z1*x2 - x1*z2; length = sqrt(x3*x3 + y3*y3 + z3*z3); if (length == 0) { face.faceNormal.x=1; face.faceNormal.y=1; face.faceNormal.z=1; } else { face.faceNormal.x=x3/length; face.faceNormal.y=y3/length; face.faceNormal.z=z3/length; } face.normal[0].x=face.normal[1].x=face.normal[2].x=face.faceNormal.x; face.normal[0].y=face.normal[1].y=face.normal[2].y=face.faceNormal.y; face.normal[0].z=face.normal[1].z=face.normal[2].z=face.faceNormal.z; } } void killObject(Object3D &object) { delete[] object.pFaces; object.pFaces = NULL; object.nFaces = 0; delete[] object.pVertices; object.pVertices = NULL; object.nVertices = 0; } void mouse(int button, int state, int x, int y) { if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { moving = 1; begin_x = x; begin_y = y; } if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) { moving = 0; } } void movimiento(int x, int y) { //Captura el movimiento del mouse. if (moving) { trasl_x=(GLfloat)(x - begin_x)/100.0; trasl_y=(GLfloat)(y - begin_y)/100.0; angle = (GLfloat)(x - begin_x)/5.0; escala_x=(GLfloat)(x - begin_x)/20.0; if(escala_x < -0.9f) escala_x=-0.9; begin_x = x; begin_y = y; newModel = 1; glutPostRedisplay(); } } void controlTransf(int value) {//Casos del menu de opciones. switch (value) { //printf (" Se ha seleccionado la opcion de rotar en el eje X\n"); case ROT_EJE_X: ROTAR=TRUE; MOVER=FALSE; ESCALAR=FALSE; EJE_X=1.0; EJE_Y=0.0; EJE_Z=0.0; break; //printf (" Se ha seleccionado la opcion de rotar en el eje Y\n"); case ROT_EJE_Y: ROTAR=TRUE; MOVER=FALSE; ESCALAR=FALSE; EJE_X=0.0; EJE_Y=1.0; EJE_Z=0.0; break; //printf (" Se ha seleccionado la opcion de rotar en el eje Z\n"); case ROT_EJE_Z: ROTAR=TRUE; MOVER=FALSE; ESCALAR=FALSE; EJE_X=0.0; EJE_Y=0.0; EJE_Z=1.0; break; //printf (" Se ha seleccionado la opcion de mover en el eje X\n"); case MOV_EJE_X: ROTAR=FALSE; MOVER=TRUE; ESCALAR=FALSE; EJE_X=1.0; EJE_Y=0.0; EJE_Z=0.0; break; //printf (" Se ha seleccionado la opcion de mover en el eje Y\n"); case MOV_EJE_Y: ROTAR=FALSE; MOVER=TRUE; ESCALAR=FALSE; EJE_X=0.0; EJE_Y=1.0; EJE_Z=0.0; break; //printf (" Se ha seleccionado la opcion de mover en el eje Z\n"); case MOV_EJE_Z: ROTAR=FALSE; MOVER=TRUE; ESCALAR=FALSE; EJE_X=0.0; EJE_Y=0.0; EJE_Z=1.0; break; //printf (" Se ha seleccionado la opcion de mover en los ejes XY\n"); case MOV_EJE_XY: ROTAR=FALSE; MOVER=TRUE; ESCALAR=FALSE; EJE_X=1.0; EJE_Y=1.0; EJE_Z=0.0; break; //printf (" Se ha seleccionado la opcion de escalar en el eje X\n"); case SCAL_EJE_X: ROTAR=FALSE; MOVER=FALSE; ESCALAR=TRUE; EJE_X=1.0; EJE_Y=0.0; EJE_Z=0.0; break; //printf (" Se ha seleccionado la opcion de escalar en el eje Y\n"); case SCAL_EJE_Y: ROTAR=FALSE; MOVER=FALSE; ESCALAR=TRUE; EJE_X=0.0; EJE_Y=1.0; EJE_Z=0.0; break; //printf (" Se ha seleccionado la opcion de escalar en el eje Z\n"); case SCAL_EJE_Z: ROTAR=FALSE; MOVER=FALSE; ESCALAR=TRUE; EJE_X=0.0; EJE_Y=0.0; EJE_Z=1.0; break; //printf (" Se ha seleccionado la opcion de escalar en los ejes XYZ\n"); case SCAL_EJE_XYZ: ROTAR=FALSE; MOVER=FALSE; ESCALAR=TRUE; EJE_X=1.0; EJE_Y=1.0; EJE_Z=1.0; break; } glutPostRedisplay(); } void recalcModelView(void) { glPopMatrix(); if(ROTAR) //Transformación de rotacion glRotatef(angle, EJE_X, EJE_Y, EJE_Z); if(MOVER)//Transformación de traslación glTranslatef(trasl_x*EJE_X, -trasl_y*EJE_Y,trasl_x*EJE_Z ); if(ESCALAR)//Transformación de escalado glScaled(1.0 + escala_x*EJE_X, 1.0 +escala_x*EJE_Y, 1.0 + escala_x*EJE_Z); glPushMatrix(); newModel = 0; } void redibuja(void) { GLfloat mat_ambient[] = { 0.02f, 0.16f, 0.16f, 1.0f }; GLfloat mat_diffuse[] = { 0.1f, 0.9f, 1.0f, 1.0f }; GLfloat mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat mat_shininess[] = { 100.0f }; int a, b, c; int i; if (newModel) recalcModelView(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); glBegin(GL_TRIANGLES); for (i=0; i<obj.nFaces; i++) { const Face& face = obj.pFaces[i]; a=face.vertexIndices[0]; b=face.vertexIndices[1]; c=face.vertexIndices[2]; glNormal3f(face.normal[0].x, face.normal[0].y, face.normal[0].z); glVertex3f(obj.pVertices[a].x, obj.pVertices[a].y, obj.pVertices[a].z); glNormal3f(face.normal[1].x, face.normal[1].y, face.normal[1].z); glVertex3f(obj.pVertices[b].x, obj.pVertices[b].y, obj.pVertices[b].z); glNormal3f(face.normal[2].x, face.normal[2].y, face.normal[2].z); glVertex3f(obj.pVertices[c].x, obj.pVertices[c].y, obj.pVertices[c].z); } glEnd(); glPopMatrix(); glFlush(); glutSwapBuffers(); } void init(void) { glClearColor(0.0, 0.0, 0.0, 0.0); glShadeModel(GL_SMOOTH); glCullFace(GL_BACK); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glEnable(GL_DEPTH_TEST); glLightfv(GL_LIGHT0, GL_POSITION, LightPos); glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmb); glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDif); glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpc); glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); CargarModelo("ship.dat", obj); CalcularNormales(obj); applySmoothingGroups(obj); } int main(int argc, char **argv) { int menu_rotar, menu_mover, menu_escalar; glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(400, 400); glutCreateWindow("Transformaciones con el mouse"); glutDisplayFunc(redibuja); glutMouseFunc(mouse); glutMotionFunc(movimiento); menu_rotar=glutCreateMenu(controlTransf); glutAddMenuEntry("eje x", ROT_EJE_X); glutAddMenuEntry("eje y", ROT_EJE_Y); glutAddMenuEntry("eje z", ROT_EJE_Z); menu_mover=glutCreateMenu(controlTransf); glutAddMenuEntry("eje x", MOV_EJE_X); glutAddMenuEntry("eje y", MOV_EJE_Y); glutAddMenuEntry("eje z", MOV_EJE_Z); glutAddMenuEntry("ejes xy", MOV_EJE_XY); menu_escalar=glutCreateMenu(controlTransf); glutAddMenuEntry("eje x", SCAL_EJE_X); glutAddMenuEntry("eje y", SCAL_EJE_Y); glutAddMenuEntry("eje z", SCAL_EJE_Z); glutAddMenuEntry("ejes xyz", SCAL_EJE_XYZ); glutCreateMenu(controlTransf); glutAddSubMenu("Rotar", menu_rotar); glutAddSubMenu("Mover", menu_mover); glutAddSubMenu("Escalar", menu_escalar); glutAttachMenu(GLUT_RIGHT_BUTTON); init(); /* Se establece la vista del cubo.*/ glMatrixMode(GL_PROJECTION); gluPerspective(45.0, 1.0, 1.0, 10.0); glMatrixMode(GL_MODELVIEW); gluLookAt(0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.); glPushMatrix(); glutMainLoop(); killObject(obj); return 0; } |
valcoey@hotmail.com
Ramiro
Buenos Aires, Argentina, 2002
Principal | Gráficos 3D | Gráficos 2D | Fractales | Math | Códigos | Tutoriales | Links