En la entrega anterior comenzamos a dar nuestros primeros pasos en la programación en 3D para Wii. Vimos como usar las figuras predefinidas de GRRLIB y como usar transformaciones de translación, escalado y rotación sobre las mismas, pero seguro que nos quedamos con ganas de más. Nos faltaban las texturas. Usando texturas podemos llegar a obtener resultados profesionales y convertir nuestro modesto Homebrew en algo cercano a un juego comercial. Combinando texturas con efectos de luz conseguiremos resultados espectaculares y haciendo uso del canal alfa podremos dar a nuestros objetos aspecto de cristal. Entrad, aprended y no os arrepentiréis.
Las formas predefinidas de GRRLIB que vimos en el capítulo anterior son muy fáciles de utilizar, pero si nos limitamos a usar estas formas, no seremos capaces de obtener escenarios en 3D realistas. Por un lado dichas formas no permiten utilizar texturas y por otro no nos permiten representar formas complejas diferentes a las pre configuradas.
Empecemos pues por ver cómo configurar nuestras propias formas geométricas para después aplicar texturas sobre las mismas. Podéis ver en el video el resultado del ejercicio sobre el que trabajaremos en esta entrega.
En la entrega anterior hablamos de que los objetos se formaban a partir de QUADS o de triángulos pero todos los objetos que utilizábamos estaban predefinidos, por lo que no pudimos experimentar lo que esto significaba. En esta entrega vamos a modelar nuestros objetos a base de QUADS.
Un QUAD (o cuadrilátero) es una superficie plana que está compuesta de cuatro vértices. Cada uno de esos vértices que forma un Quad posee una serie de atributos. El atributo más evidente es la posición espacial de ese vértice en coordenadas x,y,z, pero existen otra serie de atributos que completan la información de éste, como son el color, la normal al plano y las coordenadas de textura.
A la hora de dibujar polígonos de una forma manual deberemos describir los atributos de cada unos de los vértices de la figura a dibujar comenzando con GX_Begin() seguido de una lista de atributos de vértices y por último GX_End();.
void GX_Begin(u8 primitve, u8 vtxfmt, u16 vtxcnt); primitve: es una de las posibles que hemos visto en el apartado de arriba, desde GX_POINT, pasando por GX_TRIANGLES, hasta GX_QUADS vtxfmt: selecciona la lista de formatos. Para nosotros será siempre GX_VTXFMT0 vtxcnt: número de vértices totales. Recuerda que deben ser tres por cada triángulo en caso de ser GX_TRIANGLES, o 4 por cada QUAD y que debe haber la cantidad exacta, con el número de atributos por vértice (que estén activos) correcto como posición, normal, color... lo que proceda.
El primer parámetro de GX_Begin es el tipo de polígono que describiremos a continuación y aunque principalmente se utilizarán Quads para casi todo (o quizás triángulos en algunos casos), en Wii se pueden dibujar los siguientes tipos de polígonos:
GX_POINTS: Lista de puntos sueltos.
GX_LINES: Lista de líneas. Usa dos vértices por línea.
GX_LINESTRIP: Lista de líneas concatenadas donde la primera línea usa dos vértices y las siguientes usan el vértice previo y el que se especifique nuevo para formar la línea
GX_TRIANGLES: Lista de triángulos. Tres vértices por triángulo.
GX_TRIANGLESTRIP: Lista de triángulos enlazados de forma que el primero utiliza tres vértices pero los siguientes utilizan los dos últimos vértices del triángulo anterior más el nuevo especificado. Ideal para objetos tipo torno.
GX_TRIANGLEFAN: Lista de triángulos donde el primer vértice se toma como vértice central y se especifican dos vértices nuevos por triángulo. Ideal para hacer superficies de disco o cónicas.
GX_QUADS: Lista de superficies de cuatro puntos. Ideal para sprites y superficies rectangulares o trapezoidales.
El número de vértices que situemos a continuación debe ir indicado en el tercer parámetro de GX_Begin y hay que tener en cuenta que debe ser coherente con el tipo de primitiva elegido. Es decir si estamos dibujando Quads el número de vértices debe ser divisible por 4. Hay que tener mucho cuidado con esto, ya que en programación gráfica para Wii la mayoría de las ocasiones que se nos cuelgue nuestra Wii será porque el número de vértices es incorrecto o porque hemos indicado tipos de atributo de vértices erróneos.
Entre en GX_Begin y el GX_End deberemos describir los vértices del polígono que queremos dibujar, y para ello describiremos los atributos de vértice para cada uno de ellos. Los posibles atributos de un vértice son los siguientes:
- Vector posición: posición X,Y,Z para representación espacial.
- Vector normal: Es un vector 3D (X,Y,Z) de tipo unitario que representa la normal al plano. Empleado para aplicar iluminación difusa o especular.
- Color: Color del vértice. Si se utilizan texturas este atributo puede ser utilizado para cambiar el tono de la textura o darle transparencia a la misma.
- Coordenadas de textura: especificadas como un vector bidimensional que representan un punto de la textura o texel (pixel de textura). Las coordenadas de textura se refieren a la textura que hayamos seleccionado con GRRLIB_SetTexture y correspondiendo el (0.0,0.0) a la esquina superior izquierda y el (1.0, 1.0) a la esquina inferior derecha.
Posición
void GX_Position3f32(f32 x,f32 y,f32 z); //X,Y y Z como flotantes
Normal
void GX_Normal3f32(f32 nx,f32 ny,f32 nz); // x,y,z como floats
Color
void GX_Color4u8(u8 r,u8 g,u8 b,u8 a); // color R, G, B, A 8bits void GX_Color1u32(u32 clr); // color RGBA (A menos peso)
Coordenadas de textura
void GX_TexCoord2f32(f32 s,f32 t); // coordenadas S,T, como floats
Como ejemplo de todo lo anterior, para representar un punto tendremos que definir sus cuatro atributos de vértice utilizando las funciones anteriormente citadas y para dibujar la cara de un cubo con texturas deberíamos definir 4 de dichos vértices:
GX_Begin(GX_QUADS, GX_VTXFMT0, 4); // Vertice 1 GX_Position3f32(-0.5f,+0.5f,0.0f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,0.0f); // Vertice 2 GX_Position3f32(+0.5f,+0.5f,0.0f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,0.0f); // Vertice 3 GX_Position3f32(+0.5f,-0.5f,0.0f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,1.0f); // Vertice 4 GX_Position3f32(-0.5f,-0.5f,0.0f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,1.0f); GX_End();
Como podéis ver, esto es algo más tedioso que en el capítulo anterior, pero no tendremos más remedio que acostumbrarnos si queremos trabajar con texturas. Además, como os dije antes, hay que tener mucho cuidado en no olvidar alguno de los atributos de vértice ya que el compilador no avisará y el resultado será un bloqueo de nuestra Wii.
Aunque el caso citado es el caso más completo, no siempre será necesario describir los cuatro atributos de vértice. Esto dependerá de si queremos trabajar con texturas y/o normales, pero dichos atributos deben ser coherentes con lo que hayamos seleccionado en la función GRRLIB_3dMode:
void GRRLIB_3dMode ( f32 minDist, f32 maxDist, f32 fov, bool texturemode, bool normalmode)
Los dos últimos parámetros de GRRLIB_3dMode nos sirven para indicar si utilizaremos texturas y normales respectivamente.
Si hemos indicado que vamos a usar texturas deberemos especificarlo para cada vértice con GX_TexCoord2f32 y si hemos dicho que utilizaremos normales hay que utilizar GX_Normal3f32 en cada uno de los vértices.
Si no coincide lo que hemos dicho en GRRLIB_3dMode con los descriptores de atributos que hemos utilizado el programa se bloqueará.
Hay que recordar que si queremos utilizar luces especulares o difusas serán necesarias las normales.
Vista la teoría, vamos a pasar a la práctica. Como programa de ejemplo para esta entrega he planteado y cubo con aspecto cristalino con una textura diferente en cada una de sus caras que deja ver una esfera en su interior.
Además dibujaremos dos planos texturados correspondientes al suelo y a la pared y que permitirán apreciar el efecto de las luces.
El programa permitirá encender o apagar sus dos luces con los botones 1 y 2. Utilizaremos dos tipos de luces; La luz 1 será una luz difusa (con su correspondiente componente de luz ambiente) y la luz número 2 será una luz tipo foco o spot.
Para el efecto de transparencia utilizaremos el canal alfa. Como vimos en la programación en 2D, el último componente del formato de color RGBA es el canal alfa. Si el valor del canal alfa es 0xFF el objeto será totalmente opaco, pero si reducimos ese valor conseguiremos un efecto de transparencia. Cuanto más bajo más transparente.
Empezaremos el código de nuestro programa como es habitual incluyendo las bibliotecas necesarias y declarando las variables de textura necesarias.
#include "font_png.h" #include "cara1_png.h" #include "cara2_png.h" #include "cara3_png.h" #include "cara4_png.h" #include "cara5_png.h" #include "cara6_png.h" #include "wii_png.h" GRRLIB_texImg *tex_cara[6]; GRRLIB_texImg *tex_font; GRRLIB_texImg *tex_wii;
Necesitaremos dibujar cuatro elementos, la pared, el suelo, el cubo transparente y la bola interior del cubo. La bola la haremos con la función predefinida GRRLIB_DrawSphere, pero para el resto, como llevarán texturas, utilizaremos el método de los Quads. Para facilitar el trabajo y clarificar el programa definiremos una función, que será llamada desde el main, para cada objeto.
Empezaremos con el dibujo de la pared:
void DrawWall(){ GRRLIB_ObjectView(0,0,-5, 0,0,0,20,20,1); u32 col = 0xFFFFFFFF; GRRLIB_SetTexture(tex_wii,0); GX_Begin(GX_QUADS, GX_VTXFMT0, 4); // cara delantera // coordenada x de textura se aplica sobre la coordenada x del poligono // coordenada y de textura se aplica sobre la coordenada y del poligono // Vertice 1 GX_Position3f32(-0.5f,+0.5f,0.0f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,0.0f); // Vertice 2 GX_Position3f32(+0.5f,+0.5f,0.0f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,0.0f); // Vertice 3 GX_Position3f32(+0.5f,-0.5f,0.0f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,1.0f); // Vertice 4 GX_Position3f32(-0.5f,-0.5f,0.0f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,1.0f); GX_End(); }
La pared consiste en un simple plano vertical que utiliza una textura con las letras de Wii.
Como norma general dibujaremos todos los objetos en 0,0,0 y con un tamaño unitario, utilizando la función GRRLIB_ObjectView para escalarlo al tamaño deseado y moverlo o girarlo donde queramos. En nuestro caso simplemente lo ampliaremos en 20x20 sobre los ejes x e y, y lo alejaremos de nosotros 5 unidades sobre el eje Z. Recordad el orden en que se efectúan las transformaciones.
GRRLIB_ObjectView(0,0,-5, 0,0,0,20,20,1);
Después pasaremos a dibujar la pared, y como es un simple plano nos bastará con dibujar un Quad texturado para conseguir nuestro objetivo.
Empezaremos por tanto con la función GX_Begin:
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
Como es un sólo Quad necesitaremos definir 4 vértices, tal y como hemos indicado como último parámetro de GX_Begin.
Como va a ser un objeto con texturas y normales tendremos que utilizar las cuatro funciones de definición de atributos de vértice para cada uno de los vértices del Quad:
GX_Position3f32(-0.5f,+0.5f,0.0f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,0.0f); // Vertice 2 GX_Position3f32(+0.5f,+0.5f,0.0f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,0.0f); // Vertice 3 GX_Position3f32(+0.5f,-0.5f,0.0f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,1.0f); // Vertice 4 GX_Position3f32(-0.5f,-0.5f,0.0f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,1.0f);
Al tratarse de una pared opaca y para que el color de la textura no se modifique hemos utilizado 0xFFFFFFFF como atributo de color.
Terminando con GX_End().
La siguiente figura es el suelo que realizaremos exactamente de la misma forma que la pared pero sobre el plano horizontal y moviéndolo 5 unidades hacia abajo para hacerlo coincidir con la parte inferior de la pared que hemos dibujado.
void DrawFloor(){ GRRLIB_ObjectView(0,-5,0, 0,0,0,20,1,10); u32 col = 0xFFFFFFFF; GRRLIB_SetTexture(tex_wii,0); GX_Begin(GX_QUADS, GX_VTXFMT0, 4); // cara superior // coordenada x de textura se aplica sobre la coordenada x del poligono // coordenada y de textura se aplica sobre la coordenada z del poligono GX_Position3f32(-0.5f,0,+0.5f); GX_Normal3f32(+0.0,+1.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,0.0f); GX_Position3f32(+0.5f,0,+0.5f); GX_Normal3f32(+0.0,+1.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,0.0f); GX_Position3f32(+0.5f,0,-0.5f); GX_Normal3f32(+0.0,+1.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,1.0f); GX_Position3f32(-0.5f,0,-0.5f); GX_Normal3f32(+0.0,+1.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,1.0f); GX_End(); }
La definición del cubo transparente es un poco más larga ya que consta de 6 caras. Si todas las caras tuvieran la misma textura podríamos utilizan un sólo bloque GX_Begin/GX_End de 24 vértices, pero como queremos asignar una textura para cada cara utilizaremos 6 bloques de 4 vértices cada uno.
void DrawTexturedCube(u32 col){ GRRLIB_SetTexture(tex_cara[0],0); GX_Begin(GX_QUADS, GX_VTXFMT0, 4); // cara trasera // coordenada x de textura se aplica sobre la coordenada x del poligono // coordenada y de textura se aplica sobre la coordenada y del poligono GX_Position3f32(-0.5f,+0.5f,-0.5f); GX_Normal3f32(+0.0,+0.0,-1.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,0.0f); GX_Position3f32(-0.5f,-0.5f,-0.5f); GX_Normal3f32(+0.0,+0.0,-1.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,0.0f); GX_Position3f32(+0.5f,-0.5f,-0.5f); GX_Normal3f32(+0.0,+0.0,-1.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,1.0f); GX_Position3f32(+0.5f,+0.5f,-0.5f); GX_Normal3f32(+0.0,+0.0,-1.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,1.0f); GX_End(); GRRLIB_SetTexture(tex_cara[1],0); GX_Begin(GX_QUADS, GX_VTXFMT0, 4); // cara delantera // coordenada x de textura se aplica sobre la coordenada x del poligono // coordenada y de textura se aplica sobre la coordenada y del poligono GX_Position3f32(-0.5f,+0.5f,+0.5f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,0.0f); GX_Position3f32(+0.5f,+0.5f,+0.5f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,0.0f); GX_Position3f32(+0.5f,-0.5f,+0.5f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,1.0f); GX_Position3f32(-0.5f,-0.5f,+0.5f); GX_Normal3f32(+0.0,+0.0,+1.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,1.0f); GX_End(); GRRLIB_SetTexture(tex_cara[2],0); GX_Begin(GX_QUADS, GX_VTXFMT0, 4); // cara superior // coordenada x de textura se aplica sobre la coordenada x del poligono // coordenada y de textura se aplica sobre la coordenada z del poligono GX_Position3f32(-0.5f,+0.5f,+0.5f); GX_Normal3f32(+0.0,+1.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,0.0f); GX_Position3f32(+0.5f,+0.5f,+0.5f); GX_Normal3f32(+0.0,+1.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,0.0f); GX_Position3f32(+0.5f,+0.5f,-0.5f); GX_Normal3f32(+0.0,+1.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,1.0f); GX_Position3f32(-0.5f,+0.5f,-0.5f); GX_Normal3f32(+0.0,+1.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,1.0f); GX_End(); GRRLIB_SetTexture(tex_cara[3],0); GX_Begin(GX_QUADS, GX_VTXFMT0, 4); // cara inferior // coordenada x de textura se aplica sobre la coordenada x del poligono // coordenada y de textura se aplica sobre la coordenada z del poligono GX_Position3f32(-0.5f,-0.5f,+0.5f); GX_Normal3f32(+0.0,-1.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,0.0f); GX_Position3f32(+0.5f,-0.5f,+0.5f); GX_Normal3f32(+0.0,-1.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,0.0f); GX_Position3f32(+0.5f,-0.5f,-0.5f); GX_Normal3f32(+0.0,-1.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,1.0f); GX_Position3f32(-0.5f,-0.5f,-0.5f); GX_Normal3f32(+0.0,-1.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,1.0f); GX_End(); GRRLIB_SetTexture(tex_cara[4],0); GX_Begin(GX_QUADS, GX_VTXFMT0, 4); // cara izquierda // coordenada x de textura se aplica sobre la coordenada z del poligono // coordenada y de textura se aplica sobre la coordenada y del poligono GX_Position3f32(-0.5f,+0.5f,+0.5f); GX_Normal3f32(-1.0,+0.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,0.0f); GX_Position3f32(-0.5f,+0.5f,-0.5f); GX_Normal3f32(-1.0,+0.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,0.0f); GX_Position3f32(-0.5f,-0.5f,-0.5f); GX_Normal3f32(-1.0,+0.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,1.0f); GX_Position3f32(-0.5f,-0.5f,+0.5f); GX_Normal3f32(-1.0,+0.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,1.0f); GX_End(); GRRLIB_SetTexture(tex_cara[5],0); GX_Begin(GX_QUADS, GX_VTXFMT0, 4); // cara derecha // coordenada x de textura se aplica sobre la coordenada z del poligono // coordenada y de textura se aplica sobre la coordenada y del poligono GX_Position3f32(+0.5f,+0.5f,+0.5f); GX_Normal3f32(+1.0,+0.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,0.0f); GX_Position3f32(+0.5f,+0.5f,-0.5f); GX_Normal3f32(+1.0,+0.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,0.0f); GX_Position3f32(+0.5f,-0.5f,-0.5f); GX_Normal3f32(+1.0,+0.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(1.0f,1.0f); GX_Position3f32(+0.5f,-0.5f,+0.5f); GX_Normal3f32(+1.0,+0.0,+0.0); GX_Color1u32(col); GX_TexCoord2f32(0.0f,1.0f); GX_End(); }
Como queremos un cubo transparente asignaremos el valor inferior a 0xFF al canal alfa del color de los vértices, pero para hacer un poco más flexible la función pasaremos el valor como parámetro, asignando el color desde el main.
Utilizaremos la función GRRLIB_SetTexture delante de cada bloque de cuatro vértices para asignar la textura correspondiente a cada cara.
En este caso dejaremos las transformaciones de vista para el main, ya que queremos conseguir un cubo en movimiento.
Una vez hemos terminado con las funciones de dibujo de los objeto comenzaremos por el main inicializando todas las variables de control que después usaremos en el programa.
float ac=0;
int cubeZ=0;
f32 lightx=-0.96f;
f32 lighty=-0.48f;
f32 lightz=-2.6f;
f32 a = -0.253f;
f32 b = 0.133f;
f32 c = 1.452f;
f32 d = 0.7080f;
f32 e = 0.0f;
f32 f = 0.0f;
u8 i;
bool luzencendida = false;
bool focoencendido = false;
Posteriormente inicializamos WPAD, GRRLIB y las texturas de los objetos.
GRRLIB_Init(); WPAD_Init(); tex_wii= GRRLIB_LoadTexture(wii_png); tex_cara[0]= GRRLIB_LoadTexture(cara1_png); tex_cara[1]= GRRLIB_LoadTexture(cara2_png); tex_cara[2]= GRRLIB_LoadTexture(cara3_png); tex_cara[3]= GRRLIB_LoadTexture(cara4_png); tex_cara[4]= GRRLIB_LoadTexture(cara5_png); tex_cara[5]= GRRLIB_LoadTexture(cara6_png); tex_font = GRRLIB_LoadTexture(font_png); GRRLIB_InitTileSet(tex_font, 16, 16, 32); GRRLIB_Settings.antialias = true; GRRLIB_SetBackgroundColour(0x00, 0x00, 0x00, 0xFF); GRRLIB_Camera3dSettings(0.0f,0.0f,13.0f, 0,1,0, 0,0,0);
Entraremos en el bucle while leyendo los mandos y comprobando la pulsación de home:
WPAD_ScanPads(); if(WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) exit(0);
Las siguientes dos condiciones nos servirán para controlar si pulsamos los botones 1 ó 2 para encender o apagar la luz difusa o la de foco.
if(WPAD_ButtonsDown(0) & WPAD_BUTTON_1) luzencendida = !luzencendida; if(WPAD_ButtonsDown(0) & WPAD_BUTTON_2) focoencendido = !focoencendido;
Después incrementaremos la variable ang en 0.3 unidades a cada vuelta de bucle. Utilizaremos como referencia dicha variable para la rotación del cubo sobre varios de sus ejes.
ang+=0.3;
Empezando ya con las luces, definiremos un valor para la luz ambiente. El valor de la luz ambiente es el valor que tendrán las partes del objeto que no queden iluminadas por la luz difusa.
GRRLIB_SetLightAmbient(0x404040FF);
Después, y siempre que tengamos que encender la luz de foco (focoencendido==1), utilizaremos la función GRRLIB_SetLightSpot para encender la luz de foco.
if (focoencendido) GRRLIB_SetLightSpot(2, (guVector){2, 0.8, 5.0 }, (guVector){lightx,lighty,lightz}, a, b, c, d, e, f, 0xFFFF00FF);
Los parámetros de las luces de foco son un poco difíciles de entender y también de explicar.
Los tres primeros parámetros son más evidentes, ya que son el número de luz a usar, la posición del foco y el punto al que apunta dicho foco.
Los siguientes tres parámetros tienen que ver con la atenuación angular de la luz, donde el primer parámetro corresponde a una atenuación aditiva, el siguiente corresponde con un grado de atenuación multiplicativo y el tercero un grado de atenuación cuadrático. Como os he dicho es complicado, pero no hay nada como jugar con los valores.
Los tres parámetros que van a continuación corresponden a la atenuación por distancia. Es decir a la atenuación de la luz que se produce cuando nos alejamos de su origen. De nuevo en este caso son factores aditivos a la distancia, multiplicativos y cuadráticos.
Al final, la atenuación de un determinado punto se calcula dividiendo el factor de atenuación angular (a) por el factor de atenuación de distancia (k). Siendo a la suma de los tres coeficientes de atenuación angular limitado a un valor entre 0 y 1 y k la suma de los valores de atenuación de distancia.
(Siento en el alma esta parrafada pero no sé explicarlo de otra manera)
El último parámetro de la función es el color de la luz que se define con un valor RGBA.
Una vez definida la luz de foco pasamos al dibujo de los objetos sin textura, en nuestro caso la esfera, para lo cual llamamos a la función GRRLIB_3Mode con el penúltimo parámetro a 0 (sin texturas) y el último a 1 (con normales).
GRRLIB_3dMode(0.1,1000,45,0,1); GRRLIB_ObjectView(0,0,cubeZ, 0,0,0,1,1,1); GRRLIB_DrawSphere(0.5f, 20, 20, true, 0xAAAA22FF);
La siguiente línea detecta cuando tenemos todas las luces apagadas para asignar una mínima luz difusa que ilumine la escena.
if (!luzencendida && !focoencendido) GRRLIB_SetLightDiff(0,(guVector){5.0f,5.0f,5.0f},10.0f,0.1f,0xFFFFFFFF);
En el caso de que tengamos encendida la luz difusa intencionadamente, el parámetro de intensidad de luz es 1.0 frente al valor de 0.1 del caso anterior.
if (luzencendida) GRRLIB_SetLightDiff(0,(guVector){5.0f,5.0f,5.0f},10.0f,1.0f,0xFFFFFFFF);
Inmediatamente después ponemos el sistema en modo 3d con texturas y normales para dibujar los objetos texturados.
GRRLIB_3dMode(0.1,1000,45,1,1);
Y dibujamos el suelo y la pared llamando a las funciones previamente definidas.
DrawFloor(); DrawWall();
Tras esto pasamos a dibujar el cubo transparente llamando a la función DrawTexturedCube que definimos más arriba, pero previamente debemos aplicar los giros necesarios dependientes de la variable ac:
GRRLIB_ObjectView(0,0,cubeZ, ac,ac*2,ac*3,3,3,3); DrawTexturedCube(0xFFFFFF88);
Incrementamos la variable a para el efecto de animación:
ac+=0.5f;
Y finalmente cambiamos a modo 2D para mostrar el texto:
// Cambia a modo 2D para visualizar el texto GRRLIB_2dMode(); GRRLIB_Printf((640-(16*24))/2, 20, tex_font, 0x000000FF, 1, "CURSO DE GRRLIB PARTE 10"); GRRLIB_Printf((640-(16*27))/2, 40, tex_font, 0x0000FFFF, 1, "BOTON 1 = LUZ DIFUSA ON/OFF"); GRRLIB_Printf((640-(16*28))/2, 60, tex_font, 0x0000FFFF, 1, "BOTON 2 = LUZ DE FOCO ON/OFF");
El resto no necesita explicación ya que se limita a llamar a GRRLIB_Render y liberar las texturas usadas.
Y así llegamos al final de este capítulo.
¡¡Hasta la próxima amigos!!.
#include <grrlib.h>
#include <stdlib.h>
#include <math.h>
#include <malloc.h>
#include <wiiuse/wpad.h>
#include "font_png.h"
#include "cara1_png.h"
#include "cara2_png.h"
#include "cara3_png.h"
#include "cara4_png.h"
#include "cara5_png.h"
#include "cara6_png.h"
#include "wii_png.h"
GRRLIB_texImg *tex_cara[6];
GRRLIB_texImg *tex_font;
GRRLIB_texImg *tex_wii;
void DrawWall(){
GRRLIB_ObjectView(0,0,-5, 0,0,0,20,20,1);
u32 col = 0xFFFFFFFF;
GRRLIB_SetTexture(tex_wii,0);
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
// cara delantera
// coordenada x de textura se aplica sobre la coordenada x del poligono
// coordenada y de textura se aplica sobre la coordenada y del poligono
// Vertice 1
GX_Position3f32(-0.5f,+0.5f,0.0f);
GX_Normal3f32(+0.0,+0.0,+1.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,0.0f);
// Vertice 2
GX_Position3f32(+0.5f,+0.5f,0.0f);
GX_Normal3f32(+0.0,+0.0,+1.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,0.0f);
// Vertice 3
GX_Position3f32(+0.5f,-0.5f,0.0f);
GX_Normal3f32(+0.0,+0.0,+1.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,1.0f);
// Vertice 4
GX_Position3f32(-0.5f,-0.5f,0.0f);
GX_Normal3f32(+0.0,+0.0,+1.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,1.0f);
GX_End();
}
void DrawFloor(){
GRRLIB_ObjectView(0,-5,0, 0,0,0,20,1,10);
u32 col = 0xFFFFFFFF;
GRRLIB_SetTexture(tex_wii,0);
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
// cara superior
// coordenada x de textura se aplica sobre la coordenada x del poligono
// coordenada y de textura se aplica sobre la coordenada z del poligono
GX_Position3f32(-0.5f,0,+0.5f);
GX_Normal3f32(+0.0,+1.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,0.0f);
GX_Position3f32(+0.5f,0,+0.5f);
GX_Normal3f32(+0.0,+1.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,0.0f);
GX_Position3f32(+0.5f,0,-0.5f);
GX_Normal3f32(+0.0,+1.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,1.0f);
GX_Position3f32(-0.5f,0,-0.5f);
GX_Normal3f32(+0.0,+1.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,1.0f);
GX_End();
}
void DrawTexturedCube(u32 col){
GRRLIB_SetTexture(tex_cara[0],0);
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
// cara trasera
// coordenada x de textura se aplica sobre la coordenada x del poligono
// coordenada y de textura se aplica sobre la coordenada y del poligono
GX_Position3f32(-0.5f,+0.5f,-0.5f);
GX_Normal3f32(+0.0,+0.0,-1.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,0.0f);
GX_Position3f32(-0.5f,-0.5f,-0.5f);
GX_Normal3f32(+0.0,+0.0,-1.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,0.0f);
GX_Position3f32(+0.5f,-0.5f,-0.5f);
GX_Normal3f32(+0.0,+0.0,-1.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,1.0f);
GX_Position3f32(+0.5f,+0.5f,-0.5f);
GX_Normal3f32(+0.0,+0.0,-1.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,1.0f);
GX_End();
GRRLIB_SetTexture(tex_cara[1],0);
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
// cara delantera
// coordenada x de textura se aplica sobre la coordenada x del poligono
// coordenada y de textura se aplica sobre la coordenada y del poligono
GX_Position3f32(-0.5f,+0.5f,+0.5f);
GX_Normal3f32(+0.0,+0.0,+1.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,0.0f);
GX_Position3f32(+0.5f,+0.5f,+0.5f);
GX_Normal3f32(+0.0,+0.0,+1.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,0.0f);
GX_Position3f32(+0.5f,-0.5f,+0.5f);
GX_Normal3f32(+0.0,+0.0,+1.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,1.0f);
GX_Position3f32(-0.5f,-0.5f,+0.5f);
GX_Normal3f32(+0.0,+0.0,+1.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,1.0f);
GX_End();
GRRLIB_SetTexture(tex_cara[2],0);
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
// cara superior
// coordenada x de textura se aplica sobre la coordenada x del poligono
// coordenada y de textura se aplica sobre la coordenada z del poligono
GX_Position3f32(-0.5f,+0.5f,+0.5f);
GX_Normal3f32(+0.0,+1.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,0.0f);
GX_Position3f32(+0.5f,+0.5f,+0.5f);
GX_Normal3f32(+0.0,+1.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,0.0f);
GX_Position3f32(+0.5f,+0.5f,-0.5f);
GX_Normal3f32(+0.0,+1.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,1.0f);
GX_Position3f32(-0.5f,+0.5f,-0.5f);
GX_Normal3f32(+0.0,+1.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,1.0f);
GX_End();
GRRLIB_SetTexture(tex_cara[3],0);
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
// cara inferior
// coordenada x de textura se aplica sobre la coordenada x del poligono
// coordenada y de textura se aplica sobre la coordenada z del poligono
GX_Position3f32(-0.5f,-0.5f,+0.5f);
GX_Normal3f32(+0.0,-1.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,0.0f);
GX_Position3f32(+0.5f,-0.5f,+0.5f);
GX_Normal3f32(+0.0,-1.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,0.0f);
GX_Position3f32(+0.5f,-0.5f,-0.5f);
GX_Normal3f32(+0.0,-1.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,1.0f);
GX_Position3f32(-0.5f,-0.5f,-0.5f);
GX_Normal3f32(+0.0,-1.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,1.0f);
GX_End();
GRRLIB_SetTexture(tex_cara[4],0);
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
// cara izquierda
// coordenada x de textura se aplica sobre la coordenada z del poligono
// coordenada y de textura se aplica sobre la coordenada y del poligono
GX_Position3f32(-0.5f,+0.5f,+0.5f);
GX_Normal3f32(-1.0,+0.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,0.0f);
GX_Position3f32(-0.5f,+0.5f,-0.5f);
GX_Normal3f32(-1.0,+0.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,0.0f);
GX_Position3f32(-0.5f,-0.5f,-0.5f);
GX_Normal3f32(-1.0,+0.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,1.0f);
GX_Position3f32(-0.5f,-0.5f,+0.5f);
GX_Normal3f32(-1.0,+0.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,1.0f);
GX_End();
GRRLIB_SetTexture(tex_cara[5],0);
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
// cara derecha
// coordenada x de textura se aplica sobre la coordenada z del poligono
// coordenada y de textura se aplica sobre la coordenada y del poligono
GX_Position3f32(+0.5f,+0.5f,+0.5f);
GX_Normal3f32(+1.0,+0.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,0.0f);
GX_Position3f32(+0.5f,+0.5f,-0.5f);
GX_Normal3f32(+1.0,+0.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,0.0f);
GX_Position3f32(+0.5f,-0.5f,-0.5f);
GX_Normal3f32(+1.0,+0.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(1.0f,1.0f);
GX_Position3f32(+0.5f,-0.5f,+0.5f);
GX_Normal3f32(+1.0,+0.0,+0.0);
GX_Color1u32(col);
GX_TexCoord2f32(0.0f,1.0f);
GX_End();
}
int main() {
float ac=0;
int cubeZ=0;
f32 lightx=-0.96f;
f32 lighty=-0.48f;
f32 lightz=-2.6f;
f32 a = -0.253f;
f32 b = 0.133f;
f32 c = 1.452f;
f32 d = 0.7080f;
f32 e = 0.0f;
f32 f = 0.0f;
u8 i;
bool luzencendida = false;
bool focoencendido = false;
GRRLIB_Init();
WPAD_Init();
tex_wii= GRRLIB_LoadTexture(wii_png);
tex_cara[0]= GRRLIB_LoadTexture(cara1_png);
tex_cara[1]= GRRLIB_LoadTexture(cara2_png);
tex_cara[2]= GRRLIB_LoadTexture(cara3_png);
tex_cara[3]= GRRLIB_LoadTexture(cara4_png);
tex_cara[4]= GRRLIB_LoadTexture(cara5_png);
tex_cara[5]= GRRLIB_LoadTexture(cara6_png);
tex_font = GRRLIB_LoadTexture(font_png);
GRRLIB_InitTileSet(tex_font, 16, 16, 32);
GRRLIB_Settings.antialias = true;
GRRLIB_SetBackgroundColour(0x00, 0x00, 0x00, 0xFF);
GRRLIB_Camera3dSettings(0.0f,0.0f,13.0f, 0,1,0, 0,0,0);
while(1) {
GRRLIB_2dMode();
WPAD_ScanPads();
if(WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) exit(0);
if(WPAD_ButtonsDown(0) & WPAD_BUTTON_1) luzencendida = !luzencendida;
if(WPAD_ButtonsDown(0) & WPAD_BUTTON_2) focoencendido = !focoencendido;
GRRLIB_SetLightAmbient(0x404040FF);
if (focoencendido)
GRRLIB_SetLightSpot(2, (guVector){2, 0.8, 5.0 }, (guVector){lightx,lighty,lightz}, a, b, c, d, e, f, 0xFFFF00FF);
GRRLIB_3dMode(0.1,1000,45,0,1);
GRRLIB_ObjectView(0,0,cubeZ, 0,0,0,1,1,1);
GRRLIB_DrawSphere(0.5f, 20, 20, true, 0xAAAA22FF);
if (!luzencendida && !focoencendido)
GRRLIB_SetLightDiff(0,(guVector){5.0f,5.0f,5.0f},10.0f,0.1f,0xFFFFFFFF);
if (luzencendida)
GRRLIB_SetLightDiff(0,(guVector){5.0f,5.0f,5.0f},10.0f,1.0f,0xFFFFFFFF);
GRRLIB_3dMode(0.1,1000,45,1,1);
DrawFloor();
DrawWall();
GRRLIB_ObjectView(0,0,cubeZ, ac,ac*2,ac*3,3,3,3);
DrawTexturedCube(0xFFFFFF88);
ac+=0.5f;
// Cambia a modo 2D para visualizar el texto
GRRLIB_2dMode();
GRRLIB_Printf((640-(16*24))/2, 20, tex_font, 0x000000FF, 1, "CURSO DE GRRLIB PARTE 10");
GRRLIB_Printf((640-(16*27))/2, 40, tex_font, 0x0000FFFF, 1, "BOTON 1 = LUZ DIFUSA ON/OFF");
GRRLIB_Printf((640-(16*28))/2, 60, tex_font, 0x0000FFFF, 1, "BOTON 2 = LUZ DE FOCO ON/OFF");
GRRLIB_Render();
}
GRRLIB_Exit(); // Be a good boy, clear the memory allocated by GRRLIB
GRRLIB_FreeTexture(tex_wii);
for (i=0;i<6;i++)
GRRLIB_FreeTexture(tex_cara[i]);
GRRLIB_FreeTexture(tex_font);
exit(0);
}
Comentarios
GX_TexCoord2f32
Buenas
Ahora, que parecia que tenia dominado ciertas cosas, me he encontrado una pelea con GX_TexCoord2f32...
La pelea es la siguiente:
- He creado un cilindro
- He creado una textura de 640x320, algo parecido a un tileset, en el que se ven como 10 botones, para que por cada 1/10 de giro del cilindro quede visible un "tile" (son como botones)..(no se como poner la imagen aqui de la textura)...
pero tengo un problema al usar la funcion, si hago un calculo directo...
GX_TexCoord2f32 (a*0.1, 0.0f); por ejemplo me carga perfectamente la imagen
pero si hago un calculo indirecto
GX_TexCoord2f32 (a*girtextura, 0.0f), donde ante previamente he puesto f32 girtextura=1/10;
el cilindro desaparce, aunque la wii no se cuelga...
he visto en otros casos como en la carga de texturas de la esfera que aparece un (float) entreparentesis antes del calculo, lo he probado pero nada...
En que fallo???
lo digo porque quiero usar la misma funcion para varias cosas, y el giro puede ser 1/10.. 1/12...1/14...
Como siempre, agradecido... saludos
GRACIAS
Buenas
Gracias a la inestimable ayuda de nuestro tutor WILCO, y despues de mucho pelearme con las matematicas y otros menesteres, he conseguido la espiral buscada...
Os dejo la funcion por si es de utilidad
void espiral(u32 colesp, int nQesp, int nCesp, int nEesp, f32 resp, f32 Resp, f32 kesp)
Los significados son
u32 colesp = color de la espiral
nQesp= numero de quads por anillo de espiral, cuanto mas alto sea mas se parecera a una circunferencia (seria lo rojo de la imagen, yo he puesto 12)
nCesp= numero de anillos para conformar la espiral, cuanto mas alto sea mas se parecera a una circunferencia (el grio de la linea verde, yo he puesto 12)
nEesp= numero de tramos de espiral a dibujar ( una espiral por cada giro de 360º)
resp=radio de la circunferencia de la espiral (la de color rojo)
Resp= radio de giro de la espiral (la linea verde)
Kesp= apertura de la espiral (estiramiento)
La funcion completa seria
void espiral(u32 colesp, int nQesp, int nCesp, int nEesp, f32 resp, f32 Resp, f32 kesp){
anguloesp = 2*(M_PI)/nQesp;
anchotesp=1/nQesp;
giraresp=2*(M_PI)/nCesp;
GRRLIB_SetTexture(tex_espiral,0); // activar para usar texturas
GX_Begin(GX_QUADS, GX_VTXFMT0, nEesp*nCesp*nQesp*4);
for (cesp=0;cesp<nEesp;cesp++){
for (besp=0; besp<nCesp; besp++){
for (aesp=0 ; aesp<nQesp; aesp++){
alfaesp = aesp*anguloesp;
alfa1esp= (aesp+1)*anguloesp;
betaesp=besp*giraresp;
beta1esp=(besp+1)*giraresp;
calfaesp=cos(alfaesp);
calfa1esp=cos(alfa1esp);
salfaesp=sin(alfaesp);
salfa1esp=sin(alfa1esp);
cbetaesp=cos(betaesp);
cbeta1esp=cos(beta1esp);
sbetaesp=sin(betaesp);
sbeta1esp=sin(beta1esp);
Ralfaesp=Resp+(resp*calfaesp);
Ralfa1esp=Resp+(resp*calfa1esp);
kbesp=kesp*besp;
kb1esp=kesp*(besp+1);
GX_Position3f32(Ralfaesp*cbetaesp,resp*salfaesp+kbesp+k0esp,Ralfaesp*sbetaesp);
GX_Normal3f32(Ralfaesp*cbetaesp,resp*salfaesp+kbesp+k0esp,Ralfaesp*sbetaesp);
GX_Color1u32(colesp);
GX_TexCoord2f32(aesp*anchotesp ,0.0f); //activar para usar texturas
GX_Position3f32(Ralfa1esp*cbetaesp,resp*salfa1esp+kbesp+k0esp,Ralfa1esp*sbetaesp);
GX_Normal3f32(Ralfa1esp*cbetaesp,resp*salfa1esp+kbesp+k0esp,Ralfa1esp*sbetaesp);
GX_Color1u32(colesp);
GX_TexCoord2f32((aesp+1)*anchotesp,1.0f);//activar para usar texturas
GX_Position3f32(Ralfa1esp*cbeta1esp,resp*salfa1esp+kb1esp+k0esp,Ralfa1esp*sbeta1esp);
GX_Normal3f32(Ralfa1esp*cbeta1esp,resp*salfa1esp+kb1esp+k0esp,Ralfa1esp*sbeta1esp);
GX_Color1u32(colesp);
GX_TexCoord2f32((aesp+1)*anchotesp ,0.0f);//activar para usar texturas
GX_Position3f32(Ralfaesp*cbeta1esp,resp*salfaesp+kb1esp+k0esp,Ralfaesp*sbeta1esp);
GX_Normal3f32(Ralfaesp*cbeta1esp,resp*salfaesp+kb1esp+k0esp,Ralfaesp*sbeta1esp);
GX_Color1u32(colesp);
GX_TexCoord2f32(aesp*anchotesp,1.0f);//activar para usar texturas
}
}
k0esp=cesp*kesp*nCesp;
}
GX_End();
}
Bueno, espero que os se de utilidad, y aprovecho para agradecer las indicaciones de WILCO
Saludos
Muchas gracias por
Muchas gracias por compartirlo con nosotros. La verdad es que era una función complicada.
SE ACABO
Buenas
Se acabo el veranito como aquel que dice y he retomado la programacion...
Estoy probando el tema del torus del comentario anterior, intento girarlo para ver como se transformaria, intentando que se parezca al comienzo de una espiral, pero estoy encontrando, como siempre, problemas...
Estoy intentando usar las modificaciones mediante
GRRLIB_ObjectViewBegin();
GRRLIB_ObjectViewRotate(gx,gy,gz);
GRRLIB_ObjectViewEnd();
Draw_toro(r, R, nsides, rings, filled, col);
donde gx, gy y gz son los giros respecto a los ejes, intento rotarlo mediante el eje x 90 grados, pero al usar esta funcion, no me sale nada en pantalla, todo negro, sin embargo la wii no se cuelga y con el home salgo sin problemas...
Algo estoy haciendo mal, pero no detecto el problema, una ayudita me vendria bien...
Como siempre, agradecido
ESTO ES LO QUE HE PUESTO
buenas
Esto es lo que he puesto en el fuente, he intentado varias cosas, incluso pesaba que podia ser del 3dmode (por el tema normales y texturas), pero no doy con la tecla, con lo simple que me parecia girar una figura....
#include <grrlib.h>
#include <stdlib.h>
#include <math.h>
#include <malloc.h>
#include <wiiuse/wpad.h>
#define GRRLIB_WHITE 0xFFFFFFFF
void Draw_toro(f32 r, f32 R, int nsides, int rings, bool filled, u32 col) {
int i, j;
f32 theta, phi, theta1;
f32 cosTheta, sinTheta;
f32 cosTheta1, sinTheta1;
f32 ringDelta, sideDelta;
f32 cosPhi, sinPhi, dist;
ringDelta = 2.0 * M_PI / rings;
sideDelta = 2.0 * M_PI / nsides;
theta = 0.0;
cosTheta = 1.0;
sinTheta = 0.0;
for (i = rings - 1; i >= 0; i--) {
theta1 = theta + ringDelta;
cosTheta1 = cos(theta1);
sinTheta1 = sin(theta1);
//GRRLIB_ObjectView(1,-5,0, -10.0,0,0,1,1,1);
if(filled) GX_Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, 2*(nsides+1));
else GX_Begin(GX_LINESTRIP, GX_VTXFMT0, 2*(nsides+1));
phi = 0.0;
for (j = nsides; j >= 0; j--) {
phi += sideDelta;
cosPhi = cos(phi);
sinPhi = sin(phi);
dist = R + r * cosPhi;
GX_Position3f32(cosTheta1 * dist, (-sinTheta1 * dist)+0.5, r * sinPhi);
GX_Normal3f32(cosTheta1 * cosPhi, (-sinTheta1 * cosPhi)+0.5, sinPhi);
GX_Color1u32(col);
GX_Position3f32(cosTheta * dist, (-sinTheta * dist)+0.5, r * sinPhi);
GX_Normal3f32(cosTheta * cosPhi, (-sinTheta * cosPhi)+0.5, sinPhi);
GX_Color1u32(col);
}
GX_End();
theta = theta1;
cosTheta = cosTheta1;
sinTheta = sinTheta1;
}
}
//f32 gx,gy,gz;
void Draw_giro(f32 gx, f32 gy, f32 gz, f32 r, f32 R, int nsides, int rings, bool filled, u32 col) {
//GRRLIB_ObjectView (0,0,0,90.0f,0,0,1,1,1);
GRRLIB_ObjectViewBegin();
GRRLIB_ObjectViewRotate(gx,gy,gz);
GRRLIB_ObjectViewEnd();
Draw_toro(r, R, nsides, rings, filled, col);
}
int main() {
GRRLIB_Init();
WPAD_Init();
GRRLIB_Settings.antialias = true;
GRRLIB_SetBackgroundColour(0x00, 0x00, 0x00, 0xFF);
GRRLIB_Camera3dSettings(0.0f,0.0f,0.0f, 0,0,0, 0,0,0);
while(1) {
GRRLIB_2dMode();
WPAD_ScanPads();
if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) break;
GRRLIB_SetLightAmbient(0x404040FF);
GRRLIB_SetLightDiff(0,(guVector){0.0f,0.0f,0.0f},10.0f,1.0f,0xFFFFFFFF);
GRRLIB_3dMode(0.1,1000,45,0,1);
Draw_giro((M_PI)/2,0,0,6.0, 10.0, 12, 12, 0, GRRLIB_WHITE);
GRRLIB_Render();
}
GRRLIB_Exit();
exit(0);
}
espero no sea una tonteria como en otras ocasiones...
saludos y gracias
El problema que tenías es que
El problema que tenías es que no asignabas correctamente los parámetros de la cámara. Es muy importante que asignes estos parámetros con valores correctos ya que si no pueden darse indeterminaciones que hagan que el resultado no sea el esperado. Pasarle todo ceros a esta función no es una opción adecuada.
Ten en cuenta que al menos debes definir el vector arriba como 0,1,0.
Adicionalmente es conveniente que pongas el punto de vista retirado hacia atrás para permitir ver el objeto desde la distancia.
Tu fuente modificado quedaría así:
Curso aplicado de GRRLIB - Parte 1 - Parte 2 - Parte 3 - Parte 4 - Parte 5 - Parte 6 - Parte 7 - Parte 8 - Parte 9 - Parte 10 - Parte 11
Profundizando en los mandos de la Wii - Parte 1 - Parte 2 - Parte 3 - Parte 4 (Balanceboard) - Parte 5 (Miis)
Homebrew - WiiTriis - LifemiiWii
JAMAS HUBIERA
Buenas
Jamas hubiera pensado que el problema estaba en la posicion de vision, ya que, si no intento girar el toro puedo ver la imagen perfectamente, es decir, si elimino de la base el apartado
GRRLIB_ObjectViewBegin();
GRRLIB_ObjectViewRotate(gx,gy,gz);
GRRLIB_ObjectViewEnd();
como siempre
Gracias por tus aportaciones
Saludos
publica aqui el fuentey le
publica aqui el fuentey le echare un vistazo
NO SE SI SERE
Buenas
No se si sere el alumno mas torpe de este tutto, ya que no veo que nadie plantee dudas como yo... Pero sigo trabajando en mi programa para darle un mejor acabado...
Intento hacer algo parecido a la imagen...
Ahora pido consejo, ya que he intendado varias cosas y no me funcionan bien, asi que explico lo que he ido haciendo
1º) He creado un cilindro con QUADS ( wilco te habras reido de mi cuando te decia que inicialmente habia creado un prisma octogonal y realmente lo que se veia era una corona invertida, jjj, eso ya lo he arreglado)
2º) he intentado modificar la posicion del cilindro atendiendo a la variacion de una elipse , usando la ecuacion de la misma
3º) He modificado la posicion de los ejes atendiendo al giro de la elipse...
Lo he intentado primero haciendo una funcion que recogiese todo eso...y nada, despues en el while un bucle que modificase el punto dos y 3 y llamase a la funcion del cilindro... y nada..
Asi que para centrar mis esfuerzos, aqui la pregunta... Cual es la mejor opcion? una funcion completa o una llamada en bucle dentro del while?
Por otro lado, pedir ayuda, alguien conoce donde puedo encontrar las formulas o ecuaciones que definan el cambio del punto, primero atendiendo al diametro de toro y luego a su cambio atendiendo a la variacion elipsoidal?
me estoy partiendo los cuer... y no llego al final del camino.
Como siempre, gracias y saludos
Por supuesto que no eres el
Por supuesto que no eres el más torpe, sino uno de los más aventajados. ;-)
Pasando a tus preguntas, lo mejor que puedes hacer para dibujar un toro es utilizar como modelo la función DrawTorus cuyos fuentes vienen en el fichero "GRRLIB_3D.c":
En esta definición se usan primitivas TRIANGLE_STRIP.
Si lo que quieres es obtener "wireframe" o un tubo relleno (por ejemplo con aspecto de plástico transparente), lo mejor es usar esas primitivas.
Si por el contrario quieres añadirle textura, te recomiendo que utilices QUADS basandote en la función anterior.
Para obtener la espiral descendente solo tienes que ir decrementando progresivamente el eje Y.
Espero que te sirva de ayuda.
Curso aplicado de GRRLIB - Parte 1 - Parte 2 - Parte 3 - Parte 4 - Parte 5 - Parte 6 - Parte 7 - Parte 8 - Parte 9 - Parte 10 - Parte 11
Profundizando en los mandos de la Wii - Parte 1 - Parte 2 - Parte 3 - Parte 4 (Balanceboard) - Parte 5 (Miis)
Homebrew - WiiTriis - LifemiiWii
COMO BUEN T
Buenas
Como buen torero siempre estas al quite... solo puedo decir OLE, tienes recursos para todo...
He intentado ver lo que me aconsejas, para intentar captar el significado de la funcion y aplicar lo que me sirva para mis intenciones, pero me sale un error el de la imagen.. te lo mando por correo aun no he conseguido cargar imagenes de mi disco duro..
Algun detallito se me escapa...
Saludos
PARA PARA .... a veces soy demasiado impulsivo, ya me he dado cuenta del tema...
En tu respuesta me has indicado como hace un toro GRRLIB mediante una funcion propia de la libreria, y que ser llama DrawTorus.... y yo creia que era una funcion que tu habias definido....
Ok, creo que es eso no?
Voy a seguir probando cosas entonces....
Eso es. Es la definicion de
Eso es. Es la definicion de la funcion tal y como viene en los fuentes de la libreria.
De todas formas hasta finales de la semana que viene estoy de viaje, por lo que no te podré ayudar mucho.
Curso aplicado de GRRLIB - Parte 1 - Parte 2 - Parte 3 - Parte 4 - Parte 5 - Parte 6 - Parte 7 - Parte 8 - Parte 9 - Parte 10 - Parte 11
Profundizando en los mandos de la Wii - Parte 1 - Parte 2 - Parte 3 - Parte 4 (Balanceboard) - Parte 5 (Miis)
Homebrew - WiiTriis - LifemiiWii
NORMALES
Buenas
Estoy avanzando en la programación, y ahora me voy a empezar a pelear con los QUADS...
Para empezar poquito a poco, estoy intentando crear simplemente un cilindro en el centro de la pantalla, sin texturas..
El programa me compila bien, tengo puesto en la definicion del 3d GRRLIB_3dMode(0.1,1000,45,0,1);
Por lo que en la definicion de los vertices no uso el GX_TexCoord2f32
No me sale nada en la pantalla, pero la wii no se me cuelga, al principio tenia mal definido el numero de vertices en el GX_Begin pero lo solucione..
Centrandome en la pregunta de hoy, tienes algun tuto donde se explique mas a fondo el uso de las normales...
GX_Normal3f32(+0.0,+0.0,-1.0);
He puesto esto en todos los vertices y creo que es donde esta el fallo
Como siempre, gracias
he cambiado algo pensando que habia dado con la tecla, pero.... sigue igual... te pongo el programilla completo... espero que el fallo no sea una tonteria como la ultima vez...
#include <grrlib.h>
#include <stdlib.h>
#include <math.h>
#include <malloc.h>
#include <wiiuse/wpad.h>
// cargamos librerias
#include "cilindro_png.h"
GRRLIB_texImg *tex_cilindro;
// cargamos textura a utilizar
#define GRRLIB_WHITE 0xFFFFFFFF
u32 col = GRRLIB_WHITE;
int nQ; //numero de quads divisible por 4
f32 h; // altura del cilindro
f32 r; //radio del cilindro
f32 angulo ; //Angulo de giro del punto en function de los quads a dibujar
f32 anchotes; // ancho porcion textura
int a;
int b=0;
void cilindro(col, nQ, h, r){
angulo = (M_PI)/nQ;
anchotes=1/nQ;
//GRRLIB_SetTexture(tex_cilindro,0);
GX_Begin(GX_QUADS, GX_VTXFMT0, nQ*4);
//For (b=0;b=3;b++){
for (a=0 ; a<nQ; a++){
GX_Position3f32((r*cos(angulo*a)), h*(b+1), r*sin(angulo*a));
GX_Normal3f32(+0.0,+0.0,+0.0);
GX_Color1u32(col);
//GX_TexCoord2f32(a*anchotes ,0.0f);
GX_Position3f32((r*cos(angulo*a)), h*b, r*sin(angulo*a));
GX_Normal3f32(+0.0,+0.0,+0.0);
GX_Color1u32(col);
//GX_TexCoord2f32(a*anchotes,1.0f);
GX_Position3f32(r*cos(angulo*(a+1)), h*(b+1), r*sin(angulo*(a+1)));
GX_Normal3f32(+0.0,+0.0,+0.0);
GX_Color1u32(col);
//GX_TexCoord2f32((a+1)*anchotes,1.0f);
GX_Position3f32((r*cos(angulo*(a+1))), h*b, r*sin(angulo*(a+1)));
GX_Normal3f32(+0.0,+0.0,+0.0);
GX_Color1u32(col);
//GX_TexCoord2f32((a+1)*anchotes ,0.0f);
}
//}
GX_End();
}
int main() {
GRRLIB_Init();
WPAD_Init();
tex_cilindro= GRRLIB_LoadTexture(cilindro_png);
GRRLIB_Settings.antialias = true;
GRRLIB_SetBackgroundColour(0x00, 0x00, 0x00, 0xFF);
GRRLIB_Camera3dSettings(0.0f,0.0f,5.0f, 0,1,0, 0,0,0);
while(1) {
GRRLIB_2dMode();
WPAD_ScanPads();
if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) break;
//GRRLIB_FillScreen(0xFFFF00FF);
GRRLIB_3dMode(0.1,1000,45,0,1);
//GRRLIB_ObjectView(0,0,0, 0,0,0,0,0,0);
//GRRLIB_DrawCylinder (10.0f, 20.0f, 12, 0, GRRLIB_WHITE);
GRRLIB_3dMode(0.1,1000,45,0,1);
// GRRLIB_ObjectView(0,0,0, 0,0,0,0,0,0);
cilindro(col, 8, 20.0f, 10.0f);
GRRLIB_Render();
}
GRRLIB_Exit();
GRRLIB_FreeTexture(tex_cilindro);
exit(0);
}
he visto tambien que en la definicion de
GRRLIB_DrawTexturedSphere
se usa la misma deficinion de coordenadas de posicion como de normales es decir
GX_Position3f32(x1 * zr0 * r, y1 * zr0 * r, z0 * r);
GX_Normal3f32(x1 * zr0 * r, y1 * zr0 * r, z0 * r);
por lo que he probado tambien a hacer un cilindro de radio 1 y altura 1 para luego escalarlo poniendo en la deficinion de cada punto el mismo valor de la posicion y normales, es decir,
GX_Position3f32((r*cos(angulo*a)), h*(b+1), r*sin(angulo*a));
GX_Normal3f32((r*cos(angulo*a)), h*(b+1), r*sin(angulo*a));
pero igual....
como ves, sigo partiendome el coco...
saludos
Las normales representan la
Las normales representan la dirección en que se va a reflejar un rayo de luz que llega a nuestra figura cuando definimos una fuente de luz direccional. En el caso de usar sólo luz ambiental omnidireccional, como es tu caso, no vas a notar ninguna diferencia.
Me ha costado algo descubrirlo, pero tu problema es algo totalmente distinto a las normales. Los parametros de la función cilindro no tienen declaración de tipo.
Declarando tu función como:
void cilindro(u32 col, u32 nQ, f32 h, f32 r)
Conseguirás ver algo. De hecho me sorprende mucho que compile.
Adicionalmente moviendo la figura hacia el fondo con:
GRRLIB_ObjectView(0,0,-100, 0,0,0,1,1,1);
Lo podrás observar mejor. De todas formas aún no representas correctamente un cilindro, aunque esto te lo dejo a ti para que lo resuelvas. espero haberte sido de ayudaCurso aplicado de GRRLIB - Parte 1 - Parte 2 - Parte 3 - Parte 4 - Parte 5 - Parte 6 - Parte 7 - Parte 8 - Parte 9 - Parte 10 - Parte 11
Profundizando en los mandos de la Wii - Parte 1 - Parte 2 - Parte 3 - Parte 4 (Balanceboard) - Parte 5 (Miis)
Homebrew - WiiTriis - LifemiiWii
ME QUEDO PLANCHADO
Buenas
Me quedo planchado cuando te molesto para estos pequeños detalles que se me escapan.... seguramente me obsesione con las normales y el problema era mas simple...
Si, sé que no se vera un cilindro, se tiene que ver una prisma de 8 lados, pero para comenzar necesitaba saber si estaba utilizando bien las funciones... para conseguir un cilindro tendre que usar un prisma de lados infinitos, o muchos lados para que se vaya asemejando a una circunferencia....
Como siempre, agradecidisimo, y espero que la proxima vez no te haga perder el tiempo con mis fallitos...
se ve interesante esto de
se ve interesante esto de programar en el Wii, eso de hacerlos en 2D es genial; pero ya en 3D esta super ;)
lo que seria genial es que tubieramos un editor que en modo visual, se pudiera crear el juego, como los editores de los motores "Unity 3D, Unreal Engine o el CryEngine" por mencionar algunos ;)
makefile
Buenas
En primer lugar felicitarte por el tutorial, he encontrado algo que me faltaba para poder seguir pensando en hacer algo en programacion.
Hace tiempo, y con otro tutorial, me puse a intentar hacer un programita para mi wii.
Por falta de tiempo y por dificutades en seguir avanzando, sobre todo por no poder darle vistosidad a lo que comence, lo deje algo parado.
Con el grrlib, creo que he encontrado parte de lo que me faltabda, y he retomado el tema.
Aunque me he incorporado algo tarde, aun voy intentando asimilar el tutorial 4, hay una duda que me bloquea un poco, es el makefile.
si no tengo mal entendido digamos que es el interpretador de las funciones que pongamos, y que tiene las claves para la compilacion del programa.
He mirado los distintos ejemplos que pones en tu fichero, y veo que hay un make file para cada uno.
he intentado pasar mi primer programa con tu makefile, me compila pero al ponerlo en la wii no me hace nada (pantalla negra y se sale); con el makefile que utilizaba anteriormente si me hace cosas.
Centrandome en la pregunta...
cual es makefile que debemos utilizar para que segun vamos avanzando en el tutorial todas las funciones que vamos añadiendo esten comprendidas?
perdona mi ignorancia, pero creo que con estos tutoriales puedo mejorar y mucho lo que ya tengo hecho...
Saludos y de nuevo felicitarte
En principio debes usar el
En principio debes usar el makefile que viene con el archivo ajunto a cada tutorial ya que, se van incluyendo las opciones que son necesarias para cada entrega.
De todas formas el efecto de usar un makefile incorrecto suele ser un error de compilación ya que es el archivo que le dice al compilador que bibliotecas debe utilizar y que acciones debe realizar.
Si te compila y está bien hecho el programa debería funcionarte.
¿Si le pones el makefile antiguo y recompilas te funciona?
Subeme el programa a algún sitio y le echo un vistazo.
Curso aplicado de GRRLIB - Parte 1 - Parte 2 - Parte 3 - Parte 4 - Parte 5 - Parte 6 - Parte 7 - Parte 8 - Parte 9 - Parte 10 - Parte 11
Profundizando en los mandos de la Wii - Parte 1 - Parte 2 - Parte 3 - Parte 4 (Balanceboard) - Parte 5 (Miis)
Homebrew - WiiTriis - LifemiiWii
A eso me referia
A eso me referia, hay que preparar el makefile para que te vaya compilando todo lo que usas.
Mi programa es muy sencillo, con el makefile antiguo me compila bien, con el makefile de tus ejemplos compila(no me da errores) pero al cargarlo en la wii no hace nada.
No se como funciona esto, y no se si por aqui te puedo mandar este sencillo programa (es el inicio)...
Informame como te lo puedo hacer llegar
gracias
Si incluyes imagenes o algun
Si incluyes imagenes o algun archivo auxiliar me lo puedes subir a un servidor de descargas tipo rapidshare o alguno así y me pegas aquí el enlace.
Si solo es el código fuente, lo puedes pegar aquí mismo usando el botón de codigo que tienes abajo a la derecha.
EDITO: Ya he visto cual es tu problema. El makefile que incluyo en mis tutoriales espera que pongas el código fuente (los archivos .c) en la subcarpeta source. Cuando compilas con ese make file poniendolo todo en la misma carpeta no encuentra nada en la subcarpeta source y compila un programa vacio.
De hecho tu programa no compila porque tiene una mezcla de multiusos con GRRLIB. Lo primero que he visto es que no has puesto la extensión .h al incluir GRRLIB (debes poner #include <grrlib.h>).
Además de eso pones #include <verdana_ttf.h> pero no incluyes la fuente verdana.ttf en la subcarpeta data para que make la convierta en los archivos .o y .h.
Además tienes que sustituir todas las referencias a multiusos por sus equivalentes de grrlib, ya que no se pueden usar de forma simultánea.
Curso aplicado de GRRLIB - Parte 1 - Parte 2 - Parte 3 - Parte 4 - Parte 5 - Parte 6 - Parte 7 - Parte 8 - Parte 9 - Parte 10 - Parte 11
Profundizando en los mandos de la Wii - Parte 1 - Parte 2 - Parte 3 - Parte 4 (Balanceboard) - Parte 5 (Miis)
Homebrew - WiiTriis - LifemiiWii
Se nota que te gusta
Buenas
Se nota que te gusta el tema, estas a la que salta, todavia no hemos terminado de preguntar y practicamente tenemos la respuesta; y si me apuras te gusta incluso mas ayudar..
La verdad que me falta tiempo para decidarle a esto, a ver si el fin de semana le puedo dedicar algo y aplicar tus directrices.
Por cierto, donde puedo encontrar las equivalencias entre multiusos y grrlib, para cambiar los comandos.
Aunque sea muy repetitivo, gracias
Me temo que no hay ninguna
Me temo que no hay ninguna tabla de equivalencia que yo sepa.
Tendrás que leerte los tutoriales y preguntar lo que no entiendas.
Aunque no tiene nada que ver...
Buenas
Aunque no tenga nada que ver, seguro que lo que te voy a preguntar es tema de c...
Necesito crear una matriz de a columnas y b filas, para poder guardar colores... como seria su definicion para que el programa entienda que lo que tengo que meter sean colores con el valor, por ejemplo...0x0000FFFF
Siguiendo con lo mismo, y si en vez de colores quiero guardar textos...por ejemplo..."hola".
Gracias de antemano, y saludos
Gracias maestro
Buenas
Sabia que algo asi tenia que ser, no sabia utilizar las definiciones...
Gracias maestro, sigo trabajando, y despues de conseguir hacer algo ya en 3D, esto me anima.
Saludos
En un array de C debes
En un array de C debes indicar el tipo de dato en la declaración. En el caso que tu citas los colores RGBA se almacenan con el tipo u32, por lo que la definición de una matriz de colores de 32x32 sería así:
u32 colores [32][32];
La matriz tendrá valores desde 0 a 31 en cada dimensión.
Para acceder al elemento 10,12 se escribe: colores[10][12].
Las cadenas en C son arrays de chars por lo que la siguiente declaración servirá para almacenar una matriz de 10x20 de cadenas de 30 caracteres de largo:
char textos [10][20][30];
Curso aplicado de GRRLIB - Parte 1 - Parte 2 - Parte 3 - Parte 4 - Parte 5 - Parte 6 - Parte 7 - Parte 8 - Parte 9 - Parte 10 - Parte 11
Profundizando en los mandos de la Wii - Parte 1 - Parte 2 - Parte 3 - Parte 4 (Balanceboard) - Parte 5 (Miis)
Homebrew - WiiTriis - LifemiiWii
NO DOY CON LA TECLA
Buenas
No doy con la tecla...
He creado un array de texto char textonumeros [8][9][2];
Despues intento cargarle datos textosnumeros [0][0]="01";
Pero me da error me dice
error: incompatible types when assigning to type 'char[2] from type 'char *'
Por mucho que busco informacion no doy con la tecla...
Gracias
Desgraciadamente el C no es
Desgraciadamente el C no es como otros lenguajes y no trata las cadenas como a los demas tipos de datos.
Para C una cadena es una array de caracteres y punto, para colmo los arrays son tratados como si fueran punteros.
Si asignas un array con el operador de asignación lo único que estás haciendo es asignarle una dirección de memoria.
Si lo que quieres es copiar el valor de una cadena a otra debes usar el operador strcpy, cuya sintaxis es:
char * strcpy ( char * destination, const char * source );
Curso aplicado de GRRLIB - Parte 1 - Parte 2 - Parte 3 - Parte 4 - Parte 5 - Parte 6 - Parte 7 - Parte 8 - Parte 9 - Parte 10 - Parte 11
Profundizando en los mandos de la Wii - Parte 1 - Parte 2 - Parte 3 - Parte 4 (Balanceboard) - Parte 5 (Miis)
Homebrew - WiiTriis - LifemiiWii
Es posible
Buenas
Es posible generar matrices de texturas y sonidos??
Gracias
Claro que es posible, solo
Claro que es posible, solo debes declararlos como un array de punteros del tipo requerido.
Posteriormente debes reservar la memoria para cada uno de los elementos como lo harías con variables individuales.
En este capítulo tienes un ejemplo en las texturas del cubo que contiene la esfera. Dicho array tiene las texturas de las seis caras del cubo.
GRRLIB_texImg *tex_cara[6];
Curso aplicado de GRRLIB - Parte 1 - Parte 2 - Parte 3 - Parte 4 - Parte 5 - Parte 6 - Parte 7 - Parte 8 - Parte 9 - Parte 10 - Parte 11
Profundizando en los mandos de la Wii - Parte 1 - Parte 2 - Parte 3 - Parte 4 (Balanceboard) - Parte 5 (Miis)
Homebrew - WiiTriis - LifemiiWii
Truly impressive tutorial. I
Truly impressive tutorial.
I think there may be a in the code bug at;
Lightz f32 =- 2.6f ; ang f32 = 45 , //here f32 a = - 0.253f ;
http://newogame.posterous.com
Thankyou owen, It's true, the
Thankyou owen,
It's true, the code inserted in the tutorial do not match with the attached file.
In the attached file, "ang" variable is not used.
I'll correct it as soon as possible.
Gracias Owen,
Es cierto, el código insertado en el tutorial no coincide con el fichero adjunto.
En el fichero adjunto la variable "ang" no se utiliza.
En cuanto pueda lo corrijo.
Curso aplicado de GRRLIB - Parte 1 - Parte 2 - Parte 3 - Parte 4 - Parte 5 - Parte 6 - Parte 7 - Parte 8 - Parte 9 - Parte 10 - Parte 11
Profundizando en los mandos de la Wii - Parte 1 - Parte 2 - Parte 3 - Parte 4 (Balanceboard) - Parte 5 (Miis)
Homebrew - WiiTriis - LifemiiWii
Wilco, no entiendo de donde sacas tiempo para hacerlo
De verdad te lo digo, no lo entiendo xD
Pd: Buen trabajo, mejor dicho, trabajazo.
Gracias. Dicen que el saber
Gracias.
Dicen que el saber no ocupa lugar y es cierto pero sí que ocupa tiempo. De todas formas merece la pena el tiempo invertido.
Curso aplicado de GRRLIB - Parte 1 - Parte 2 - Parte 3 - Parte 4 - Parte 5 - Parte 6 - Parte 7 - Parte 8 - Parte 9 - Parte 10 - Parte 11
Profundizando en los mandos de la Wii - Parte 1 - Parte 2 - Parte 3 - Parte 4 (Balanceboard) - Parte 5 (Miis)
Homebrew - WiiTriis - LifemiiWii
oro puro
Tus cursos son oro puro ojala tuviese mas tiempo para desarrollar toda tu teoria aun asi gracias por compartirlo con todos nosotros.
Saludos
Gracias a vosotros. Una pena
Gracias a vosotros. Una pena que no te animes.