"Ning√ļn juego dura tanto hasta que te lo terminas como el que te programas t√ļ mismo,
ninguno te absorbe tanto y ninguno te produce tanta satisfacción cuando lo has acabado"
Programacion de videojuegos
Inicio Curso de programación de juegos 14. Seleccion de objetos con raton - Segundo metodo de seleccion
Viernes 03 de Abril del 2020

Menu principal
Colaborar (con PayPal)

Para continuar con el trabajo de esta Web y poder pagar el hosting, viene bien la ayuda que sea. Gracias a todos.

Importe: 

Ultimas descargas
19.Jan

Clase que permite dibujar texto en OpenGL con mucha facilidad.Usa FreeType2.Para ver que hace y c...


14. Seleccion de objetos con raton - Segundo metodo de seleccion Imprimir Correo electrónico
Videojuegos - Curso de Programación de juegos
Escrito por Vicengetorix   
√ćndice de Art√≠culos
14. Seleccion de objetos con raton
Otro metodo
Posdata sobre C++
Todas las p√°ginas

Veremos ahora otro metodo para seleccionar objetos con el raton.

Este metodo es uno de tantos como por ejemplo el de pintar los objetos en un color diferente cada uno y ver que color hay en pantalla debajo del raton, en un buffer oculto o en pantalla y luego borrarla antes de cambiar y pintar la escena normal. Tambien se pueden usar las funciones gluProject y gluUnProject, que permiten obtener las coordenadas de un punto en pantalla que corresponde a otro en el espacio y al reves.

En este metodo lo que vamos a hacer es comprobar el valor del buffer de profundidad en el pixel en el que esta el raton y cuando hemos hecho click, antes y despues de pintar los objetos. Si la profundidad antes es mayor que despues de pintar, en el pixel del raton, es que hemos pintado el objeto debajo del raton y por tanto el objeto esta selecionado. Si se pintara despues otro objeto debajo del raton pero detras del de antes no seria seleccionado ya que al tener mas profundidad en el z-buffer, este no sera modificado. De esa manera nos aseguramos de que el objeto seleccionado es el mas cercano.
Luego en el siguiente frame ya pintaremos diferente el objeto seleccionado.

En esta version de la seleccion, en el if de apretar el boton del raton, solo activaremos un indicador de modo seleccion (nuestro, no oficial de OpenGL) y pondremos a 0 la variable que contiene el identificador del objeto seleccionado.
El meollo del tema estara en el lugar del codigo donde se pintan los objetos.

En esta ocasion, lo unico nuevo que veremos de OpenGL es la forma de ver el contenido del z-buffer con la funcion glReadPixels(...). Con esta funcion, en realidad, se puede recuperar el contenido de un rectangulo arbitrario de pixels, para cualquiera de los bufferes de OpenGL. En esta ocasion lo haremos del de profundidad pero podria ser del de color (en el que se pinta) u otro. Admite bastantes posibilidades.

Partiremos del codigo de la parte primera de este capitulo, quitaremos lo que no nos hace falta y pondremos cosas nuevas.

Empezamos con las definiciones globales (¬°Que sorpresa!).

Quitaremos la definicion de MAX_SEL de antes y a√Īadiremos un indicador para avisar al programa de cuando estamos en modo selecion (se ha hecho click con el raton) y dos variables tipo float donde cargaremos el valor del z-buffer antes y despues de pintar los objetos.

1
2
3
4
5
6
7
// Indicador de si estamos en modo seleccion
// o no.
bool Seleccion = false;
// Variables float para cargar en ellas
// la profundidad del buffer de profundidad
// antes y despues de pintar el objeto.
GLfloat z1, z2;

Igual que antes las funciones GeneraFuente, Escribe, CargaTextura, GeneraLista, ImprimeResoluciones, CambiaPantalla,CreaVentana() y WinMain(...) no cambian.
La funcion ProcesaMensajes(...) sera igual que en el metodo anterior, con el uso del boton izquierdo y derecho del raton incluidos.

En la funcion IniciaGL() incluiremos el codigo que ya se vio en el capitulo 12 para intentar que el hardware suavice un poco la escena.

1
2
3
4
5
6
7
8
9
10
11
// Este codigo sirve para que OpenGL pinte 
// las primitivas (punto, linea y poligono)
// sin escalones (con antialiasing).
// Su efectividad depende mucho del
// hardware y su diver.
glHint(GL_POINT_SMOOTH_HINT,GL_NICEST);
glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
glHint(GL_POLYGON_SMOOTH_HINT,GL_NICEST);
glEnable(GL_POINT_SMOOTH);
glEnable (GL_POLYGON_SMOOTH);
glEnable(GL_LINE_SMOOTH);

Solo queda por cambiar la funcion Pinta().

Recordemos que la mayoria del codigo de seleccion, en el metodo anterior, iba incluido dentro de un if que se ejecutaba al hacer click con el raton y una sola vez por pulsacion (con el metodo que venimos usando desde el principio del curso).
Ahora, dentro de este if habra mucho menos codigo, de hecho solo habra dos lineas, una que pone el indicador de seleccion a true para informar al resto de la funcion de que se ha hecho click en este frame y otra linea que pone en la variable para contener el identificativo del objeto seleccionado a 0. Al terminar la funcion en esta variable estara el objeto seleccionado o seguira el 0 como resultado de no haber seleccionado nada.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// En caso de presionar el boton izquierdo del raton 
// comprobaremos si el raton esta sobre algun objeto.
// Usaremos el metodo habitual con un indicador auxiliar
// para que solo se haga una vez por pulsacion.
if(LevantaTeclaRI && Raton.izquierdo)
{ // Si se ha presionado el boton
// derecho del raton pongo el
// indicador de seleccion a true...
Seleccion = true;
// ...y el Objeto seleccionado a 0.
ObjetoSel = 0;
 
// El resto del codigo para control de la
// tecla Raton Izquierdo
LevantaTeclaRI=false;
}
else if( !Raton.izquierdo) LevantaTeclaRI=true;


La siguientes operaciones seran para cada objeto que sea susceptible de ser seleccionado.

Tendremos que ver el contenido del z-buffer en el pixel donde esta el raton (solo en caso de estar en modo seleccion). Esto lo haremos con la funcion glReadPixels(...). Los parametrosde esta funcion son:

  • Los dos primeros las coordenadas de pantalla (Y empezando abajo) de la esquina inferior izquierda del cuadrado que vamos a leer.
  • Las dos segundas son el tama√Īo del cuadrado de pixels que leera.
  • La quinta, que tipo de datos vamos arecuperar (un color, tres colores, alfa, profundidad, ...)
  • La sexta, el tipo de los datos.
  • La septima la direccion del buffer donde dejara los datos.

Nosotros recuperaremos de las coordenadas del raton, un cuadrado de 1x1 (solo un pixel), la componente de profundicad (el z-buffer), tipo float en la variable "z1".

1
2
3
4
5
6
7
8
9
10
// Si estoy en modo seleccion es que hemos 
// hecho clik con el boton izquierdo del raton
// y hay que ver si se selecciona algo.
// Si estoy en modo seleccion...
if(Seleccion)
// ...recupero el valor del buffer de profundidad
// en el pixel donde esta el raton (coordenada Y
// empezando por abajo) en la variable "z1".
glReadPixels( Raton.x,rect.bottom-Raton.y, 1,1,
GL_DEPTH_COMPONENT,GL_FLOAT,&z1);

Tras esto pintamos normalmente el objeto.

1
2
// Pinto el objeto que sea.
glCallList(Cubo);

Si estoy en modo seleccion recupero de nuevo la profundidad en la variale "z2".

1
2
3
4
5
6
7
// Si estoy en modo seleccion...
if(Seleccion)
// ...recupero el valor del buffer de profundidad
// en el pixel donde esta el raton (coordenada Y
// empezando por abajo) en la variable "z2".
glReadPixels( Raton.x,rect.bottom-Raton.y, 1,1,
GL_DEPTH_COMPONENT,GL_FLOAT,&z2);

Ahora, en caso de que este sea el objeto seleccionado (no en este frame, en modo selecion, si no en cualquiera) pintare algo para que en pantalla sepamos cual es. Nosotros vamos a pintar el mismo objeto encima del anterior pero en wireframe de forma que se veran marcadas las aristas del objeto. Ya hemos usado este metodo en el capitulo 12. Rompe ladrillos ahora en 3D.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Si este objeto (el 1) esta seleccionado...
if(ObjetoSel==1)
{ // Marco el objeto como seleccionado pintando
// encima las aristas del mismo.
// Desbilito iluminacion.
glDisable(GL_LIGHTING);
// Desabilito texturas.
glDisable(GL_TEXTURE_2D);
// Indico a OpenGL que pinte solo las
// aristas de los poligonos.
glPolygonMode(GL_FRONT, GL_LINE);
// Pinto el objeto de nuevo pero esta
// vez se vera en "wireframe"
glCallList(Cubo);
// Indico a OpenGL que pinte normal
// de nuevo.
glPolygonMode(GL_FRONT, GL_FILL);
// Habilito texturas otra vez.
glEnable(GL_TEXTURE_2D);
// Habilito iluminacion.
glEnable(GL_LIGHTING);
}

Luego hacemos la comparacion de las dos profundidades que tomamos antes y despues de pintar el objeto. Si la profundidad despues de pintar el objeto, en el pixel del raton, es menor que antes (ha disminuido) es que lo que acabamos de pintar esta debajo del raton y por tanto esta seleccionado, siempre que este en modo seleccion, que haya hecho click este frame).

1
2
3
4
5
6
7
8
9
10
// Si estoy en modo seleccion,compruebo la profundidad
// antes y despues de pintar el objeto. Si son iguales las
// dos profundidades es que no he pintado el objeto
// donde esta el raton.
// Si la profundidad antes de pintar el objeto es mayor que
// despues de pintarlo es que he pintado el objeto debajo
// del raton, por lo tanto lo considero seleccionado,
// pongo su identificativo (1 en este caso) en la variable
// "ObjetoSel".
if(z2<z1 && Seleccion) ObjetoSel=1;

Tras esto paso a hacer lo mismo con los demas objetos (todos en sus posiciones)

Pongo otro de ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// La misma operacion que hicimos con el 
// primer cubo se hace con el tercero.
// Tan solo cambia el identificativo del
// objeto (esta vez sera 3). Tampoco Desbilitamos
// texturas y las habilitamos al pintar la maya
// para marcarlo como seleccionado por que este
// va pintado sin textura.
if(Seleccion)
glReadPixels( Raton.x,rect.bottom-Raton.y, 1,1,
GL_DEPTH_COMPONENT,GL_FLOAT,&z1);
glCallList(Cubo);
if(Seleccion)
glReadPixels( Raton.x,rect.bottom-Raton.y, 1,1,
GL_DEPTH_COMPONENT,GL_FLOAT,&z2);
 
if(ObjetoSel==3)
{ glDisable(GL_LIGHTING);
glPolygonMode(GL_FRONT, GL_LINE);
glCallList(Cubo);
glPolygonMode(GL_FRONT, GL_FILL);
glEnable(GL_LIGHTING);
}
if(z2<z1 && Seleccion) ObjetoSel=3;

Tras terminar con todos los objetos, solo falta que al final del frame terminemos el modo seleccion. Los frames en los que no se ha hecho click no se hara la toma de profundidades ni su comparacion. Para salir del modo seleccion (nuetro particular modo seleccion, no el propio de OpenGL) solo pondremos false al indicador de seleccion.

1
2
3
4
5
// Al terminar el frame en el que se 
// ha hecho click para seleccionar,
// termino con el modo selecion poniendo
// false en el indicador correspondiente.
Seleccion = false;

Observad que en la variable "ObjetoSel" tendermos siempre el objeto mas cercano.
Si tras considerar un objeto seleccionado pintamos otro, tambien seleccionado y mas cerca de la camara, su identificativo sera introducido en la variable. Si se pintase otro despues pero mas lejos, no seria tenido encuenta ya que la profundidad no seria cambiada por OpenGL al haber profundidades menores en el z-buffer.

Hemos terminado el segundo metodo de seleccion.
Se veria asi el resultado.



El codigo completo seria: usw14b.cpp




¡Sólo los usuarios registrados pueden escribir comentarios!
+/- Comentarios
Buscar
janezo   |79.108.43.xxx |28-07-2012 10:18:22
Quillo, lo mejor de lo mejor.

Yo tambien tengo mujer e hijos y no se de donde
co√Īo sacas el tiempo o cuantos platos tienes que fregar a cambio, :)

De verdad
muchas gracias por el esfuerzo porque los tutoriales son faciles de seguir y muy
instructivos.

Un saludo.
Vicengetorix   |87.221.224.xxx |08-08-2012 00:13:33
Gracias por el comentario.
Platos he fregado millones pero cada vez tengo menos
tiempo.
Espero que, por lo menos lo que va hasta ahora, sea √ļtil y os sirva.
UnKnow   |186.68.128.xxx |09-07-2011 03:01:17
Hmmm... Yo uso el dev-c++, y tira muchos errores, los est√°ba eliminando de a
poco, pero me trabé, y me quedé con 13 errores. :'(
ezequielher  - felicitaciones     |190.179.156.xxx |25-01-2010 22:05:43
hola, que tal? lo felicito por sus tutoriales, yo me topé de casualidad con
esta web, y creo que resulta muy √ļtil en mi caso, ya que requiero de c√≥digo
para armar mi proy en opengl para la facultad, veo el gran esfuerzo q hace p/
que los internautas tengamos conocimiento de opengl, y como he estado viendo la
información en este ámbito no abunda en castellano. Gracias por todo!
RICARDO  - SIGUE ASI     |200.24.193.xxx |22-12-2010 01:31:41
Hola,
Yo solo se vb6 asi que me cuesta un poco pasar el código a este, lo hago
porque no hay manuales que ense√Īen a programar en visual basic, la p√°gina de
Paul Dahuach tiene bastantes ejemplos para vb6 pero le entiendo muy poco, en
cambio esta página me ha sido util, despues me internaré a C++. Muchas
gracias.
Vicengetorix   |194.179.126.xxx |22-12-2010 16:24:17
Gracias a vosotros por dar vuestra opinión y aportar vuestras impresiones.
Zaidan  - Gracias por no abandonar.   |62.57.251.xxx |27-07-2009 17:01:15
Acabo de encontrarme con tus tutoriales y tengo que estarte agradecido por
seguir adelante ya que muchos autores de tutoriales lo acaban abandonando(sera
gracias a tu cabezoneria como dices :P).
Acabo de empezar leyendo los primeros
articulos y he dedecir que para mi entender el open gl es algo titanico puesto
que sol ohe visto un poco de visula basic y actionscript, asi que intentare ir
paso a paso lentamente hasta que pueda entenderlo bien.

Muchas gracias de
nuevo por tu aporte.
Un saludo. :D
Pedro  - Pues al final abandon√≥ como todos   |83.43.184.xxx |23-12-2009 17:15:36
Parece que al final el autor ha acabado haciendo lo que hace la mayoría,
abandonar el barco. Cuando la cosa se complica es hora de desertar y es ahí
cuando nos damos cuenta de que todo había sido un capricho más como comprarse
una moto.
Vicengetorix   |85.53.218.xxx |23-12-2009 19:04:02
El autor es aficionado y lo hace por amor al arte, tiene trabajo, mujer e hijos
peque√Īos.
Si cuando te dan algo gratis, tu te quejas de que quieres m√°s o
criticas lo que te dan, solo conseguir√°s que nunca m√°s te den algo gratis.

El
autor (yo) continuar√° cuando tenga tiempo, a ratos perdidos.
De momento el
autor usa esos ratos libres para mejorar la librer√≠a de UnSitioWeb y a√Īadirle
un entorno gr√°fico de usuario para OpenGL (men√ļs, botones, checkboxes,...)y
cuando sea un producto digno de ser compartido lo colgar√° en la p√°gina.
No hay
continuos artículos en esta página porque no me paga nadie por escribir
rese√Īas y no me interesa escribir por escribir. Solo escribo aqu√≠ cuando hay
algo que me parece interesante. Con eso intento conseguir que la p√°gina tenga
contenido utíl, que sea el contenido lo que la haga una buena página (si es
que alg√ļn d√≠a lo consigo).

3.26 Copyright (C) 2008 Compojoom.com / Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved."

 


Banner
Spanish Chinese (Simplified) English French German Japanese Portuguese Russian