14. Seleccion de objetos con raton |
![]() |
![]() |
Videojuegos - Curso de Programación de juegos | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Escrito por Vicengetorix | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Página 1 de 3 ![]() Esta puede ser de objetos 3D o 2D, en todo caso veremos 2 metodos para hacerlo, el propio de OpenGL y otro marca de la casa (mio). Los metodos pueden ser muchos como ocurre en todos los temas de programacion. Estos dos metodos se pueden variar, mezclar, desechar y usar otro ... a gusto del programador. El primero, como hemos dicho, es el propio de OpenGL. Hay gente que piensa que es anticuado, que ya no deberia soportarlo OpenGL. No se porque, por que a mi me gusta y me parece util y facil. En todo caso tengo entendido que en OpenGL 3.0 ya no es soportado (es declarado, en ingles, "deprecated", obsoleto). Como hasta OpenGL 2.1 (version anterior a la 3) si es soportado, lo veremos. Pasaran muuuuuuuuuchos años antes de que los drivers de las tarjetas no soporten OpenGL 2.1. Aviso de todas formas que en tarjetas graficas INTEL con ciertos drivers, el retorno de las profundidades de los objetos seleccionados, en este metodo, no funciona bien. A mi me ha ocurrido tambien con una ATI (culpa de los fabricantes). Dicho esto empezamos explicando el metodo. OpenGL tiene un modo especial de pintar objetos llamado "modo de seleccion", que como su nombre indica, es para seleccionar objetos, en el cual, aunque pintes objetos no apareceran en pantalla. El proceso sera asi basicamente al hacer click (en este metodo los objetos seleccionables se pintan dos veces el frame del click, una vez para seleccion y otra el pintado normal).
Conviene saber bien cual es la estructura que tiene el buffer que definimos para almacenar los objetos seleccionados. Contiene 4 campos.
Tras la explicacion teorica, sera mejor empezar con la practica y asi verlo en accion. Empezamos con el codigo del capitulo anterior (13. Pantalla completa y cambio de resolucion). Como siempre con las definiciones globales. Primero definimos mas indicadores de los que usamos para limitar cada pulsacion de tecla a una accion unica.
En la estructura que usamos para guardar los datos del raton incluimos un indicador para saber si se pulse el boton derecho.
Una variable para elidentificativo del objeto seleccionado, definimos un numero como el maximo numero de objetos seleccionados (por si implementaramos seleccionar varios) y un indicador para que la leyenda aparezca en pantalla o no (ya va ocupando mucho).
Las funciones GeneraFuente, Escribe, CargaTextura, GeneraLista, ImprimeResoluciones (esta deberiamos quitarla ya) y CambiaPantalla no cambian. En la funcion IniciaGL() incluiremos dos cosas conocidas para quien haya seguido los capitulos 11 y 12 (los Rompe Ladrillos). Indicaremos a OpenGL que pinte solo las caras delanteras del los objetos para ahorrar tiempo y hacemos el ancho de las lineas mayor para ver mejor los objetos seleccionados, que los pondremos en wireframe (pintaremos solo las aristas de los lados).
La funcion ProcesaMensajes(...) cambia para incluir en los eventos correspondientes, el boton derecho del raton.
y
La funcion CreaVentana() no cambia y la funcion principal WinMain(...) tampoco. Ya solo queda la funcion Pinta(), como es habitual, con la mayor parte del trabajo. Lo primero aqui sera cambiar lo que se hacia en anteriores versiones presionando el boton izquierdo (mover la camara) y que pase a hacerse con el derecho para que dejemos libre el izquierdo para seleccionar objetos.
(antes aqui ponia izquierdo dende ahora pone derecho). Despues pasamos a programar lo que pasa cuando se presiona el boton izquierdo (la seleccion). Usamos el metodo habitual con un indicador para que solo se ejecute una vez por pulsacion de la tecla.
A partir de aqui, y hasta nuevo aviso, todo estara dentro de este if. Primero reservamos sitio para nuestro buffer de seleccion y para guardar el viewport, tal y como dijimos al principio.
Luego decimos a OpenGL cual va a ser el buffer de seleccion y su tamaño con la funcion glSelectBuffer(...), con el tamaño del buffer como primer parametro. Lo calculamos en base al numero maximo de objetos que pensamos que necesitamos seleccionar y el tamaño que almacenara por objeto, tal y como vimos al principio, y sabiendo que usaremos un nombre por objeto. El segundo parametro es la direccion del buffer.
Recuperamos luego el viewport con la funcion adecuada de OpenGL (esto tambien es nuevo).
Despues nos disponemos a preparar la matriz de proyeccion para acotar el area de pantalla en el cual consideramos que si un objeto se pinta, esta seleccionado. En el caso de un click de raton sera una zona muy pequeña de pantalla alrededor del puntero del raton. Se podria implementar que dejando el boton pulsado marcaramos un cuadrado dentro del cual todo estaria seleccionado (eso lo dejo a la habilidad y tiempo libre de quien quiera hacerlo).
Ahora es cuando marcamos los limites del cuadrado de seleccion. en nuestro caso y para un solo click sera un cuadradode 2x2 pixels con centro en las coordenadas del raton. Remarco la palabra centro para quien quiera implementar seleccion en un cuadrado mas grande. Para esto usaremos la funcion de la libreria glu gluPickMatrix(...) cuyos parametros son por orden estos.
Tras crear la matriz proyeccion con gluPickMatrix para restringir la zona de seleccion, multiplicamos la matriz proyeccion por la que tenemos en ese momento en el programa. No nos asustemos con lo de multiplicar matrices, tan solo usaremos gluPerspective o cualquier otro metodo que usemos normalmante para establecer la proyeccion y gluLookAt o, glTranslate y glRotate para colocar la camara. el caso es que la proyeccion y la situacion de la camara sea la misma que en este frame del programa. Si no fuera asi el pintado de los objetos de forma normal y en modo selecion no seria el mismo y no funcionaria la selecion.
Deshabilito texturas e iluminacion, que ahora no hacen falta, regreso a la matriz de modelo y la guardo en la pila para luego recuperarla.
Ahora entro en modo seleccion, modo en que OpenGL no pinta pero va rellenando con los datos de los objetos seleccionados el buffer de seleccion.
Inicio la pila de nombres y empujo en ella un primer valor que ire cambiando por el identificativo de los objetos.
Despues voy pintando los objetos, pero antes de pintar cada uno dejo en la pila (cambio el valor que meti de principio) el identificativo del objeto a pintar. Con este identificativo podre luego saber que objetos estan seleccionados al ver lo que hay en el buffer de seleccion.
Terminado el pintado de objetos seleccionables en modo seleccion, dejo vacia la pila de nombres, restablezco la matriz modelo anterior y lo mismo con la matriz proyeccion anterior. Dejo como siempre, la de modelo como la de trabajo.
Ahora salgo del modo seleccion y dejo el modo normal, en el que se pintan las cosas. Al terminar el modo seleccion la funcion retorna los objetos que han sido seleccionados mientras estaba el modo selecion puesto. Si retorna "-1" es que nos habiamos quedado cortos al dar tamaño al buffer de selecion.
Por ultimo, para saber quien esta seleccionado, lo miro en el buffer de seleccion, si es que hay alguno seleccionado. Si no hay ninguno pongo 0 en la variable "ObjetoSel". Si hay alguno recorro todos los objetos seleccionados (1 o mas) y me quedo con el que esta mas cerca. Recordad que cada objeto seleccionado son 4 GLuint's. A nosotros nos interesa el segundo (indice 1 porque el primero es 0), la profundidad minima del z-buffer de OpenGL y el cuarto (indice 3), el nombre o identificativo del objeto. Al final "ObjetoSel" tendra el nombre del objeto seleccionado mas cercano.
Tras esto terminamos el if en el que tenemos el codigo de seleccion al presionar el boton izquierdo del raton.
Despues, lo unico que hay que hacer es pintar distinto el objeto que ha sido seleccionado en caso de haberlo. En la variable "ObjetoSel" esta el nombre eleccionado. Si es 0 pintaremos todo normal, si no pintaremos en wireframe el objeto cuyo identificativo coincida con el de esta variable. Al pintar cada objeto comprobare si esta o no seleccionado y lo pintare como corresponda. Pongo aqui uno como ejemplo. La unica diferencia con otros es que si no se pintaba originalmente con textura, cuando esta seleccionado no hara falta que la deshabilite.
acordaos que cada objeto tiene su identificador (nombre). El del ejemplo es el 1, los demas seran es 2, 3, 4 y 5 en nuestro programa. Tras relizar esto ultimo con cada objeto el programa (todos son seleccionables), ya habriamos terminado. He aprovechado para actualizar la layenda y hacer que aparezca solo si presionamos la tecla "L" (si sigue creciendo, cuando este en pantalla, no se se vera la escena).
Damos asi por terminado el primer metodo de seleccion, el propio de OpenGL. El resultado en pantalla sera asi. ![]() El codigo completo seria: usw14a.cpp ¡Sólo los usuarios registrados pueden escribir comentarios!
3.26 Copyright (C) 2008 Compojoom.com / Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved." |