Buenash! Pues veréis, modo 3D, yo tengo un sprite plano ALARGADO mirando hacia el eje x positivo. O sea, su normal apunta en ese eje. Está en vertical.
(http://fotos.subefotos.com/c2f36898db5995318fef6e1c8c7d8b21o.png)
La recta AB indica que el sprite está ahí en vertical, con el plano mirando hacia el eje x. El sprite está colocado encima de la recta AB, tocándola al 100%. Entonces yo aplico 2 rotaciones al sprite: primero una rotación en el eje y de "σ" grados, que hará al sprite "agacharse", y luego otra en el eje z de "φ" grados, que hará al sprite rotar y mirar alrededor. Al finalizar tenemos la recta AB alterada, como en el dibujo, con esos 2 ángulos.
Mi objetivo? Rotar el sprite sobre la recta para que se ponga mirando a la cámara. No quiero que el sprite mire al 100% a la cámara, sino que lo haga sin separarse de la recta. O sea, que puede quedar reducido a lo largo de la recta, pero no a lo ancho de ésta.
Para ello, PRIMERO aplicaré una rotación en el eje z y así, después de rotar sobre el eje y y luego sobre el z otra vez, el sprite final estará como quiero. El problema es cómo calcular esa rotación z inicial (si mi planteamiento es correcto). Debería poder calcular esa z inicial a partir de las rotaciones siguientes "σ" y "φ", pero no me sale.
Cabe decir, que la cámara puede estar en cualquier altura, arriba o abajo, y eso se debe tener en cuenta. La idea es que da igual desde dónde mires, SIEMPRE tienes que ver el sprite plano mirar a la cámara, pero sin que se separe de la recta AB. Es decir, rotar el sprite alrededor de su recta AB (o sea, rotarlo en z al inicio, antes de nada) para que al final mire a la cámara
Se me ocurren dos formas para calcular el ángulo:
Una es resolver un triángulo esférico.
(http://www.librosmaravillosos.com/geometrialobachevski/imagenes/001.jpg)
Primero se deberían calcular las coordenadas esféricas de la cámara (Ecuaciones para transformar de Rectangulares a Esféricas (http://www.wikimatematica.org/index.php?title=Coordenadas_Cil%C3%ADndricas_y_Esf%C3%A9ricas#Ecuaciones_para_transformar_de_Rectangulares_a_Esf.C3.A9ricas))
En el diagrama a=theta, b=theta de la cámara y C=diferencia de los ángulos phi, del eje y de la cámara. Con esos datos se pueden obtener los otros tres, el ángulo que buscas es el suplemento del ángulo B.
Tengo un script para resolver el triángulo:
[spoiler]
[gml]
///Triángulo Esférico
//Una vez definidos los lados a y b, y el águlo comprendido C,
//se obtienen los ángulos A y B, y el lado c.
//Los datos de entrada y salida se dan en radianes.
var d,D;
if (C == 0) //en dirección del polo norte
{
if (a < b) {A=0; B=pi; c=b-a;}
else {A=pi; B=0; c=a-b;}
}
else if (C == pi) //en dirección del polo sur
{
if (a+b > pi) {A=pi; B=pi; c=2*pi-a-b;}
else {A=0; B=0; c=a+b;}
}
else if (a == b) //es un triángulo isóceles
{
D = C/2;
d = arcsin( sin(a) * sin(D) );
B = arctan( 1 / cos(a) / tan(D) );
A = -B;
c = abs( 2*d );
}
else //es un triángulo oblicuo
{
d = arctan( cos((a-b)/2) / cos((a+b)/2) / tan(C/2) );
D = arctan( sin((a-b)/2) / sin((a+b)/2) / tan(C/2) );
A = -d - D;
B = d - D;
c = 2*arctan( tan((a-b)/2) * sin(d) / sin(D) );
}
[/gml]
[/spoiler]
El segundo método es más sencillo. Rotar el vector de posición de la cámara en el orden inverso a las rotaciones del plano, es decir, primero rotar sobre z un ángulo -phi y después rotar sobre y un ángulo -theta. Una vez rotado se obtiene el ángulo de la proyección sobre el plano xy. El código sería así:
[gml]
//coordenadas de la camara
Ax = obCamara.x;
Ay = obCamara.y;
Az = obCamara.z;
//rotar sobre z
Bx = lengthdir_x(Ax,-phi) - lengthdir_y(Ay,-phi);
By = lengthdir_x(Ay,-phi) + lengthdir_y(Ax,-phi);
Bz = Az; //este valor no cambia
//rotar sobre y
Cx = lengthdir_x(Bx,-theta) + lengthdir_y(Bz,-theta);
Cz = lengthdir_x(Bz,-theta) - lengthdir_y(Bx,-theta);
Cy = By; //este valor no cambia
//angulo
a = point_direction( 0,0, Cx,Cy );
[/gml]
No estoy completamente seguro de los signos en la rotación sobre y, si no funciona, entonces hay que cambiar + por -
Primero, mil gracias por responder, me has dejado flipando :D
He intentado implementar el segundo que parece más fácil. Me he asegurado que lo he pasado sin errores, y no funciona, en ninguno de los dos casos (alternando los +-)... debería ver el sprite SIEMPRE con el mismo ancho, mientras que el alargado (distancia de la recta AB, o sea, alargado del sprite) no, y no ocurre... me he dado cuenta que Cz no se usa. Pero bueno, quiero entender e implementar al menos el segundo, que lo veo más viable. Supongo que mi idea era correcta, y antes de rotar en "y" y luego en "z", PRIMERO tengo que rotar en z en un valor igual a "a"...
Luego he intentado entenderlos, y simplemente no lo logro. Por qué rotas el vector de la cámara... sé que tiene que usarse la cámara para obtener la rotación z inicial, pero no lo pillo para nada :C Por ahora no hablo del primero, eso ya lo entiendo 14 veces menos
Tal vez no entendí bien. Dejo un ejemplo de cómo lo entiendo
- camina con WASD
- gira la vista con el ratón
- cambia theta con los dígitos 1 y 2
- cambia phi con los dígitos 3 y 4
El ángulo inicial lo he calculado como mostré en el mensaje anterior (en el evento End Step) y se aplica en el evento Draw:
[gml]
d3d_transform_set_rotation_z( alpha );
d3d_transform_add_rotation_y( theta );
d3d_transform_add_rotation_z( phi );
draw_set_color( c_white );
d3d_draw_wall( 0,25,50, 0,-25,0, tx1,1,1 );
draw_set_color( c_yellow );
d3d_primitive_begin( pr_linelist );
d3d_vertex( 0,0,0 );
d3d_vertex( 0,0,99 );
d3d_primitive_end();
d3d_transform_set_identity();
[/gml]
Gracias! El ejemplo es genial :love: Sí funciona, el problema es que yo hago un:
d3d_transform_add_translation( vrX, vrY, vrZ );
Justo después de las 3 transformaciones. O sea, el plano no está en el origen, lo traslado tras transformarlo hacia donde debe estar, y ESO es lo que estropea todo...
he probado cosas como cambiar el point_direction por:
vrAlpha = point_direction( vrX, vrY, vrCx, vrCy );
Y en parte lo arregla, pero creo que me falta algo más... vrZ, la altura aumentada trasladada, no sé cómo tenerla en cuenta, ni sé si lo que he cambiado en el point_direction es correcto. Alguna idea?
Lo único que debes cambiar es calcular la posición relativa de la cámara respecto al eje:
[gml]
//coordenadas relativas de la camara
Ax = x - vrX;
Ay = y - vrY;
Az = z - vrZ;
//rotar sobre z
Bx = lengthdir_x(Ax,-phi) - lengthdir_y(Ay,-phi);
By = lengthdir_x(Ay,-phi) + lengthdir_y(Ax,-phi);
Bz = Az; //este valor no cambia
//rotar sobre y
Cx = lengthdir_x(Bx,-theta) + lengthdir_y(Bz,-theta);
Cy = By; //este valor no cambia
//Cz = lengthdir_x(Bz,-theta) - lengthdir_y(Bx,-theta);
//angulo
alpha = point_direction( 0,0, Cx,Cy );
[/gml]
Dios, se me ha ido la olla xD Justamente había hecho eso como segundo intento, pero no sé si lo había hecho parcialmente y por eso no iba, o si iba pero pensaba que no iba. Tengo el cerebro frito xD
Muchas gracias, resuelto! Me salvaste la vida T_T y me ha gustado mucho el estilo que usas a la hora de llamar a funciones, de hacer...
nombreDeFuncion( Argumento1, Argument2 )
if ( variable ) ...
De poner un espacio en ambos paréntesis para ver mejor los argumentos en las funciones, y las condiciones en los condicionales, ya me copié e intentaré irlo añadiendo a mi estilo de programación :D