[Tutorial] III.6 - Triangulos, cuadrilateros y glutMotionFunc() - Parte 2
Página 1 de 1.
[Tutorial] III.6 - Triangulos, cuadrilateros y glutMotionFunc() - Parte 2
El funcionamiento del programa para el usuario es sencillo, pulsando con el botón derecho del ratón sobre un vértice, lo seleccionas; si mantienes pulsado el botón izquierdo del ratón mientras lo mueves, estarás cambiando de sitio el vértice actualmente seleccionado.
Ahora analizamos el código, las novedades son muchas, el nivel ha subido, asi que, excepto algunas cosas muy básicas, explicaré todo.
La segunda estructura es para guardar, y poder acceder luego, a los datos actuales del ratón, su posición previamente calculada en el espacio vectorial, y si está pulsado o no el botón izquierdo y derecho.
En la siguiente declaramos un estructura para guardar los datos del ratón, no le damos valores, pues aún no los sabemos.
Las siguientes variables son necesarias, ya veremos por qué, la primera guardará el ancho y alto de la ventana, y la siguiente nos dirá que vértice estamos seleccionando.
La última es para guardar el formato, por si se cambian las dimensiones de la ventana, para que todo siga encajando, ya veremos que se necesita sacar el formato de la función de Reshape.
IniciarGLUT() sigue como hasta ahora.
Simplemente añadir que debes manejarte bien con las estructuras, y que finalmente ponemos a dibujar el punto que nos sirve de referencia, con un tamaño de 7 para verlo, porque sino...
A la función ReProyectar solo le hemos añadido esto:
El problema que se presenta es que la función asignada a glutMotionFunc() recibe la posición XY del ratón cada vez que ésta posición cambia. Pero es la posición sobre la pantalla, partiendo de 0,0 en la esquina de arriba a la izquierda, y por ejemplo, el ratón en el centro de la ventana tiene una posición 300,300; pero a nosotros nos interesa saber su posición en el espacio virtual en el que dibujamos, para situar ahí nuestro vértice o comprobar si está ahí alguno, al ser el centro, sabemos que es 0.0f,0.0f,0.0f, pero necesitamos una función que nos "traduzca" éstas posiciones, en realidad es sencillo.
La teoría cómo se "traduce": tenemos que conseguir una proporción. Por ejemplo, si tiene la ventana 100 de ancho, y está en una posición X de 40, sabemos que está a un 40% de la izquierda. Tras saber eso, podemos aplicarlo a nuestro espacio virtual. Si sabemos que va desde el -10 al 10, sabemos que tiene 20 de ancho, pues el 40% de 20 es 8. Ya sabemos que está a 8 unidades virtuales, y como empieza desde -10, la posición de nuestro ratón en el espacio virtual es... -2. Sencillo si lo vemos así. Ahora hay que pasar estas operaciones a una función, y eso he hecho. Analicemos la primera función, después de leer esto la entenderás mucho mejor, vamos operación por operación:
Datos necesarios (los atributos de la función): la posición X sobre pantalla, ancho de la ventana, alto de la ventana, extremo izquierdo de nuestro espacio virtual, extremo derecho, y el formato (por si ha sido deformada la ventana, ajustar bien).
Operaciones:
Lo último es devolver el dato.
La función ObtenerPosPlanoY() tiene solo dos pequeños cambios: se aplica el formato de otra forma, cuando el alto es mayor. Lo segundo y más importante, en tu espacio virtual la zona negativa está hacia abajo, pero las coordenadas que recibes son mas pequeñas, más hacia arriba, asi que hay que invertirlo con un - delante.
No hay nada como experimentar para entender. Seguimos.
Aclaro que, para vertice_sel, de 0 a 2 es el triángulo y 3 a 6 es el cuadrilátero.
Después, si está pulsado en botón izquierdo del ratón, tanto el vértice seleccionado, como el punto de selección, como la posición espacial del ratón son obviamente iguales.
No hay nada más que decir, bastante he explicado ya, experimenta, y ten en cuanta que ahora el nivel será así... hasta que suba más.
Ahora analizamos el código, las novedades son muchas, el nivel ha subido, asi que, excepto algunas cosas muy básicas, explicaré todo.
- Código:
typedef struct {
GLfloat verticeXYZ[3];
GLfloat colorRGB[3];
} Vertice;
typedef struct {
float x,y;
int izda, dcha;
} EstadoRaton;
La segunda estructura es para guardar, y poder acceder luego, a los datos actuales del ratón, su posición previamente calculada en el espacio vectorial, y si está pulsado o no el botón izquierdo y derecho.
- Código:
Vertice cuadrado[4] = {
{{1,0,0},{1,0,0}},
{{5,-4,0},{1,1,0}},
{{9,0,0},{0,1,0}},
{{5,4,0},{0,0,1}}
};
Vertice triangulo[3] = {
{{-9,4,0},{1,0,0}},
{{-5,-4,0},{0,1,0}},
{{-1,4,0},{0,0,1}}
};
- Código:
Vertice selector = {{-9,4,0},{1,1,1}};
EstadoRaton raton;
int ventana[2], vertice_sel;
GLfloat formato_global;
En la siguiente declaramos un estructura para guardar los datos del ratón, no le damos valores, pues aún no los sabemos.
Las siguientes variables son necesarias, ya veremos por qué, la primera guardará el ancho y alto de la ventana, y la siguiente nos dirá que vértice estamos seleccionando.
La última es para guardar el formato, por si se cambian las dimensiones de la ventana, para que todo siga encajando, ya veremos que se necesita sacar el formato de la función de Reshape.
IniciarGLUT() sigue como hasta ahora.
- Código:
void PintarEscena() {
glMatrixMode(GL_MODELVIEW);
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
int i;
glBegin(GL_TRIANGLES);
for (i=0; i<3; i++) {
glColor3fv(triangulo[i].colorRGB);
glVertex3fv(triangulo[i].verticeXYZ);
}
glEnd();
glBegin(GL_QUADS);
for (i=0; i<4; i++) {
glColor3fv(cuadrado[i].colorRGB);
glVertex3fv(cuadrado[i].verticeXYZ);
}
glEnd();
glPointSize(7);
glBegin(GL_POINTS);
glColor3fv(selector.colorRGB);
glVertex3fv(selector.verticeXYZ);
glEnd();
glutSwapBuffers();
}
Simplemente añadir que debes manejarte bien con las estructuras, y que finalmente ponemos a dibujar el punto que nos sirve de referencia, con un tamaño de 7 para verlo, porque sino...
A la función ReProyectar solo le hemos añadido esto:
- Código:
...
ventana[0] = w;
ventana[1] = h;
...
formato_global = formato;
- Código:
float ObtenerPosPlanoX(float x, int ancho_ventana, int alto_ventana, float pos_x_min, float pos_x_max, float format) {
float pos_x_relativa = ((float)x/ancho_ventana); //La posición relativa de 0 a 1 en X
float pos_plano;
if (ancho_ventana<=alto_ventana) pos_plano = (pos_x_min+((pos_x_max-pos_x_min)*pos_x_relativa));
else pos_plano = ((pos_x_min * format)+(((pos_x_max-pos_x_min) * format)*pos_x_relativa));
return pos_plano;
}
float ObtenerPosPlanoY(float y, int ancho_ventana, int alto_ventana, float pos_y_min, float pos_y_max, float format) {
float pos_y_relativa = ((float)y/alto_ventana); //La posición relativa de 0 a 1 en X
float pos_plano;
if (ancho_ventana<=alto_ventana) pos_plano = -((pos_y_min / format)+(((pos_y_max-pos_y_min) / format)*pos_y_relativa));
else pos_plano = -(pos_y_min+((pos_y_max-pos_y_min)*pos_y_relativa));
return pos_plano;
}
El problema que se presenta es que la función asignada a glutMotionFunc() recibe la posición XY del ratón cada vez que ésta posición cambia. Pero es la posición sobre la pantalla, partiendo de 0,0 en la esquina de arriba a la izquierda, y por ejemplo, el ratón en el centro de la ventana tiene una posición 300,300; pero a nosotros nos interesa saber su posición en el espacio virtual en el que dibujamos, para situar ahí nuestro vértice o comprobar si está ahí alguno, al ser el centro, sabemos que es 0.0f,0.0f,0.0f, pero necesitamos una función que nos "traduzca" éstas posiciones, en realidad es sencillo.
La teoría cómo se "traduce": tenemos que conseguir una proporción. Por ejemplo, si tiene la ventana 100 de ancho, y está en una posición X de 40, sabemos que está a un 40% de la izquierda. Tras saber eso, podemos aplicarlo a nuestro espacio virtual. Si sabemos que va desde el -10 al 10, sabemos que tiene 20 de ancho, pues el 40% de 20 es 8. Ya sabemos que está a 8 unidades virtuales, y como empieza desde -10, la posición de nuestro ratón en el espacio virtual es... -2. Sencillo si lo vemos así. Ahora hay que pasar estas operaciones a una función, y eso he hecho. Analicemos la primera función, después de leer esto la entenderás mucho mejor, vamos operación por operación:
Datos necesarios (los atributos de la función): la posición X sobre pantalla, ancho de la ventana, alto de la ventana, extremo izquierdo de nuestro espacio virtual, extremo derecho, y el formato (por si ha sido deformada la ventana, ajustar bien).
Operaciones:
- float pos_x_relativa = ((float)x/ancho_ventana), el porcentaje del que hablabamos antes, la posición relativa es la posición del ancho entre el total posible.
- if (ancho_ventana<=alto_ventana) pos_plano = (pos_x_min+((pos_x_max-pos_x_min)*pos_x_relativa));, como pasa en ReProyectar(), el formato solo se aplica al eje más ancho, la operación es: la posición en X en el esp. virtual es igual al valor más pequeño posible en X mas el ancho total posible por la posición relativa.
- else pos_plano = ((pos_x_min * format)+(((pos_x_max-pos_x_min) * format)*pos_x_relativa))
, lo de antes pero aplicando el formato, porque si hemos llegado al else es que el ancho es mayor
Lo último es devolver el dato.
La función ObtenerPosPlanoY() tiene solo dos pequeños cambios: se aplica el formato de otra forma, cuando el alto es mayor. Lo segundo y más importante, en tu espacio virtual la zona negativa está hacia abajo, pero las coordenadas que recibes son mas pequeñas, más hacia arriba, asi que hay que invertirlo con un - delante.
No hay nada como experimentar para entender. Seguimos.
- Código:
void ControlRaton(int button, int state, int x, int y) {
if (button==GLUT_LEFT_BUTTON) {
if (state==GLUT_DOWN) raton.izda = 1;
else raton.izda = 0;
}
else if (button==GLUT_RIGHT_BUTTON) {
if (state==GLUT_DOWN) {
raton.dcha = 1;
raton.x = ObtenerPosPlanoX(x,ventana[0],ventana[1],-10,10,formato_global);
raton.y = ObtenerPosPlanoY(y,ventana[0],ventana[1],-10,10,formato_global);
int i;
for (i=0; i<3; i++) {
if (raton.x > triangulo[i].verticeXYZ[0] - 0.5f &&
raton.x < triangulo[i].verticeXYZ[0] + 0.5f &&
raton.y > triangulo[i].verticeXYZ[1] - 0.5f &&
raton.y < triangulo[i].verticeXYZ[1] + 0.5f ) {
vertice_sel = i;
}
}
for (i=0; i<4; i++) {
if (raton.x > cuadrado[i].verticeXYZ[0] - 0.5f &&
raton.x < cuadrado[i].verticeXYZ[0] + 0.5f &&
raton.y > cuadrado[i].verticeXYZ[1] - 0.5f &&
raton.y < cuadrado[i].verticeXYZ[1] + 0.5f ) {
vertice_sel = 3 + i;
}
}
}
else raton.dcha = 0;
}
}
Aclaro que, para vertice_sel, de 0 a 2 es el triángulo y 3 a 6 es el cuadrilátero.
- Código:
void MovimRaton(int x, int y) {
raton.x = ObtenerPosPlanoX(x,ventana[0],ventana[1],-10,10,formato_global);
raton.y = ObtenerPosPlanoY(y,ventana[0],ventana[1],-10,10,formato_global);
}
- Código:
void MoverVertice(int value) {
switch (vertice_sel) {
case 0: case 1: case 2:
selector.verticeXYZ[0] = triangulo[vertice_sel].verticeXYZ[0];
selector.verticeXYZ[1] = triangulo[vertice_sel].verticeXYZ[1];
break;
case 3: case 4: case 5: case 6:
selector.verticeXYZ[0] = cuadrado[vertice_sel-3].verticeXYZ[0];
selector.verticeXYZ[1] = cuadrado[vertice_sel-3].verticeXYZ[1];
break;
}
if (raton.izda) {
if (vertice_sel<=2) {
triangulo[vertice_sel].verticeXYZ[0] = selector.verticeXYZ[0] = raton.x;
triangulo[vertice_sel].verticeXYZ[1] = selector.verticeXYZ[1] = raton.y;
}
else {
cuadrado[vertice_sel-3].verticeXYZ[0] = selector.verticeXYZ[0] = raton.x;
cuadrado[vertice_sel-3].verticeXYZ[1] = selector.verticeXYZ[1] = raton.y;
}
}
glutTimerFunc(33,MoverVertice,1);
glutPostRedisplay();
}
Después, si está pulsado en botón izquierdo del ratón, tanto el vértice seleccionado, como el punto de selección, como la posición espacial del ratón son obviamente iguales.
No hay nada más que decir, bastante he explicado ya, experimenta, y ten en cuanta que ahora el nivel será así... hasta que suba más.
Temas similares
» [Tutorial] III.6 - Triangulos, cuadrilateros y glutMotionFunc()
» [Tutorial] III.7 - Las dos caras del polígono, triángulos STRIP y FAN
» [Tutorial] II.4 - glutKeyboardFunc()
» [Tutorial] II.6 - glutMouseFunc()
» [Tutorial] II.3 - glutTimerFunc()
» [Tutorial] III.7 - Las dos caras del polígono, triángulos STRIP y FAN
» [Tutorial] II.4 - glutKeyboardFunc()
» [Tutorial] II.6 - glutMouseFunc()
» [Tutorial] II.3 - glutTimerFunc()
Página 1 de 1.
Permisos de este foro:
No puedes responder a temas en este foro.
|
|