Anuncios Google

Curso aplicado de GRRLIB - 11º entrega

Tutoriales Avanzados Homebrewes

Ya está aquí la entrega número 11 de nuestro ya habitual curso de programación en GRRLIB. Este capítulo tiene como objetivo el aprendizaje del cálculo del movimiento a través de nuestro virtual mundo en 3D. ¿A qué esperas? Entra ya y descubre como programar vistas en primera persona apoyándote en un espectacular ejemplo que nos transportará en una nave espacial a través de nuestro sistema solar.

Introducción

En lo que llevamos visto hasta ahora, hemos modelado figuras predefinidas y hemos creado nuestras propias figuras con texturas. Hemos visto también como realizar transformaciones de translación, rotación y escalado, y como combinarlas en distinto orden para modificar nuestro modelo. Adicionalmente hemos visto como especificar nuestra posición y punto de vista de una manera estática, pero lo que no hemos hecho hasta ahora es modificar dinámicamente nuestra posición en la escena

Modificar nuestra posición y el punto al que miramos nos permite, por ejemplo, hacer juegos con vista de primera persona tipo "Doom".

Si la posición del observador es fija, modificar el punto de observación no es muy complicado, y si el punto a observar siempre es el mismo, tampoco resulta complicado modificar la posición del observador, ya que, en ambos casos, podemos limitarnos a incrementar/decrementar sus coordenadas x, y, z.

Sin embargo, si lo que queremos es ir variando una de ellas en función de cómo varía la otra, la cosa se complica un poco y tendremos que echar mano de cálculos matriciales.

Desgraciadamente, si buscamos desarrollar un juego en primera persona, nos encontramos ante el caso citado en el párrafo anterior. Nos movemos en nuestro mundo y el punto de vista se mueve con nosotros.

Afortunadamente Devkitpro viene con una biblioteca específica para hacer cálculos vectoriales y matriciales llamada gu.h que nos evitará tener que hacer estos cálculos por nosotros mismos.

Explorando nuestro mundo virtual

A la hora de situar a nuestro personaje en la escena y permitirle que bucee por nuestro mundo artificial, hay varios parámetros que son importantes y que definen nuestro punto de vista de la escena.

Dichos parámetros son:

  • La posición de la cámara, es decir el punto desde el que miramos
  • El punto al que enfoca la cámara, es decir el punto de la escena al que dirigimos la mirada.
  • El vector "Arriba", es decir la línea perpendicular a nuestra mirada que indica cual es la parte de arriba de la escena.

Aunque los dos primeros parámetros son evidentes quizás el último parámetro cueste un poco más de entender, pero sólo hay que imaginarse que no es lo mismo lo que vemos cuando estamos de pie que cuando estamos colgando boca abajo.

En ambos casos podemos estar mirando el mismo punto, pero el resultado de lo que vemos no es el mismo ya que en el segundo caso vemos la misma imagen pero girada 180º.

El vector arriba es simplemente un vector unitario (es decir de longitud 1) que nos indica cual es la parte de arriba según nuestra perspectiva. En el caso de que estemos de pie el vector arriba siempre será (0,1,0) es decir una unidad positiva en dirección del eje y.

Recordemos que la función de GRRLIB que nos permitía definir los parámetros de la cámara era GRRLIB_Camera3DSettings. Utilizaremos pues dicha función para definir los tres vectores anteriormente citados.

void GRRLIB Camera3dSettings ( f32 posx, f32 posy, f32 posz, f32 upx, f32 upy, f32 upz, f32 lookx, f32 looky, f32 lookz )
 
posx,posy,posz		: Posición del observador, o sea la nuestra.
upx,upy,upz		: Vector arriba.
lookx,looky,lookz	: Punto al que observamos. 

Una vez tenemos claro cómo definir cada uno de los parámetros que definen el punto de vista vamos a plantearnos como modificar dinámicamente dichos parámetros.

Mirando a nuestro alrededor.

Lo primero que vamos a plantear es como girar nuestro punto de vista sin movernos del punto de origen y para facilitar las cosas giraremos sólo sobre el eje Y, es decir mirando al frente y sin mover la mirada arriba y abajo.

En este caso lo único que se modifica es el punto al que miramos, ya que la posición del observador no varía y tampoco lo hace el vector "arriba".  

Lo primero que tenemos que tener en cuenta a la hora de simular los cambios desde nuestro punto de vista es que para nosotros el origen de coordenadas se sitúa en el punto donde nos encontramos, por lo que todas las transformaciones que realicemos deberán hacerse tratando nuestra posición como el origen de coordenadas.

Es decir, para llegar del "punto 1" al "punto 2" debemos girar el "punto 1" 65º tomando como eje de giro el eje Y que pasa por nuestra posición.

Si girásemos alrededor del eje X manteniendo todo lo demás constante, el proceso sería similar, pero esta vez la coordenada X se mantendría constante y el punto de observación variaría sobre el plano YZ.

Vamos a complicar un poco más el proceso girando primero sobre el eje Y y luego sobre el eje X. El problema aquí viene dado porque el sistema de referencia cambia en el momento en que giramos la primera vez. Como podéis ver en el gráfico, si nosotros giramos también debemos girar el sistema de referencia, por lo que para calcular la posición final tendremos que seguir los siguientes pasos:

  1. Mover el origen de coordenadas a nuestra posición
  2. Girar el punto de observación respecto del eje Y
  3. Calcular el nuevo sistema de referencia aplicándole a cada eje el mismo giro que le hemos aplicado al punto de observación.
  4. Girar de nuevo el punto de observación respecto del nuevo eje X que nos ha resultado del cálculo anterior.

Cómo veis, se trata de un proceso algo complicado de ver en un principio pero que se puede resumir en que debemos de aplicar las mismas transformaciones a los ejes de coordenadas que realizamos a nuestro punto de vista antes de poder hacer la siguiente transformación. De tal forma que al final se trata de un proceso reiterativo.

Moviendo nuestro personaje

A la hora de mover nuestro personaje, tenemos que tener en cuenta de nuevo la dirección a la que miramos.

Como nuestro punto de vista no es fijo, mover hacia delante no es algo tan evidente como incrementar la coordenada Z.

Al igual que hicimos en el caso de las rotaciones, para saber de qué manera debemos incrementar o decrementar nuestro vector posición debemos calcular nuestros nuevos ejes de coordenadas. Una vez calculados estos ejes (normalizados) el cálculo del movimiento es sencillo para cada dirección:

  • Adelante: Vector Posición + Nuevo Eje Z * Velocidad
  • Atrás: Vector Posición - Nuevo Eje Z * Velocidad
  • Izquierda: Vector Posición - Nuevo Eje X * Velocidad
  • Derecha: Vector Posición + Nuevo Eje X * Velocidad
  • Arriba
    Vector Posición + Nuevo Eje Y * Velocidad
  • Abajo
    Vector Posición - Nuevo Eje Y * Velocidad

Como veis, la forma más sencilla de aplicar las fórmulas anteriores es operar con vectores ya que una sola operación es suficiente para manejar las tres coordenadas.

Calculando las transformaciones por el método trigonométricos

Quizás el método más inmediato que se nos viene a la cabeza a la hora de realizar una transformación de este tipo es utilizar operaciones trigonométricas.

Para el caso de giro sobre un eje de coordenadas que no haya sufrido ninguna transformación es relativamente sencillo, y basta con recordar algunas fórmulas de las que estudiamos en el colegio. En el ejemplo siguiente muestro como calcular la nueva posición cuando aplicamos un giro sobre el eje Y manteniendo constante el resto de parámetros:

y' = y
 
z' = z*cos(angulo)-x*sen(angulo)
 
x' = z*sen(angulo)+x*cos(angulo)

Y para el caso de girar sobre el eje X las fórmulas serían similares.

x' = x
 
y' = y*cos(angulo)-z*sen(angulo)
 
z' = y*sen(angulo)+x*cos(angulo)

El problema viene cuando intentamos realizar la segunda operación ya que los ejes ya transformados no coinciden con los ejes originales de coordenadas.

El resultado es un montón de complicadas fórmulas que ralentizan y complican en exceso el programa.

Afortunadamente libogc nos provee de una biblioteca que implementa una serie de funciones que hace todos esos cálculos por nosotros utilizando para ello cálculo matricial.

Las funciones de cálculo matricial incluidas en "gu"

Aunque no las utilizaremos todas, vamos a hacer un repaso rápido de las funciones de cálculo matricial que vienen incluidas en la biblioteca gu.

Los dos tipos de datos principales que se utilizan en esta biblioteca son el tipo vector y el tipo matriz de 3x4.

El tipo vector tiene la siguiente definición:

typedef struct _vecf {
	f32 x,y,z;
} guVector;

Aunque cuando se utiliza en operaciones mixtas con matrices se trata como una matriz columna de 3 elementos.

La definición del tipo matriz es la siguiente:

typedef f32     Mtx[3][4];

Tenemos dos grupos principales, las operaciones sobre vectores y las operaciones sobre matrices.

He obviado deliberadamente todas las operaciones que incluye sobre "Quaternas" y sobre matrices de 4x4.

Operaciones sobre matrices

- void guMtxIdentity(Mtx mt);

Carga en mt la matriz identidad. Esta matriz es para las operaciones con matrices como el número 1 para la multiplicación de enteros. Es decir no produce ningún efecto si concatenamos esta matriz con otra. Se utiliza normalmente para inicializar una matriz antes de empezar a operar.

- void guMtxCopy(Mtx src,Mtx dst);

Copia la matriz src en la matriz dst.

- void guMtxConcat(Mtx a,Mtx b,Mtx ab);

Concatena la matriz a con la b y carga el resultado en ab. Esta es la manera que tenemos de combinar dos operaciones con matrices. Veremos cómo se utiliza en el ejemplo. 

- void guMtxScale(Mtx mt,f32 xS,f32 yS,f32 zS);

Inicializa la matriz mt con los factores de escala xS, yS, zS. Se utiliza normalmente para escalar objetos. Hay que tener en cuenta que esta función no multiplica los factores por los valores que tuviera previamente mt, sino que crea una matriz desde cero con los valores de escala. Si queremos escalar una matriz deberemos primero obtener la matriz de escala con esta función y luego concatenarla con la matriz que queremos escalar utilizando guMtxConcat. 

- void guMtxScaleApply(Mtx src,Mtx dst,f32 xS,f32 yS,f32 zS);

Esta operación es equivalente a aplicar la operación de escalado anterior sobre una matriz ya existente. Con esta función nos ahorramos tener que concatenar las matrices. 

- void guMtxTrans(Mtx mt,f32 xT,f32 yT,f32 zT);

Inicializa la matriz mt con los valores de traslación xT,yT,zT

- void guMtxTransApply(Mtx src,Mtx dst,f32 xT,f32 yT,f32 zT);

Aplica los valores de traslación xT,yT,zT a la matriz src y guarda el resultado en dst.

- u32 guMtxInverse(Mtx src,Mtx inv);

Obtiene la matriz inversa. Esta función sirve principalmente para ajustar las matrices que se aplican en el apartado de texturas, y luces. No la utilizaremos en este tutorial.

- void guMtxTranspose(Mtx src,Mtx xPose);

Esta operación haya la matriz traspuesta, es decir intercambia filas por columnas. Nosotros no la utilizaremos en el tutorial.

- void guMtxRotRad(Mtx mt,const char axis,f32 rad);

Devuelve la matriz de rotación sobre el eje axis siendo rad el ángulo en radianes.

axis puede tener los siguientes valores:

  • 'x': rotación sobre el eje X.
  • 'y': rotación sobre el eje Y.
  • 'z': rotación sobre el eje Z.

- void guMtxRotDeg(Mtx mt,const char axis,f32 deg);

Esta función es idéntica a la anterior pero utiliza grados sexagesimales para especificar el ángulo.

- void guMtxRotAxisRad(Mtx mt,guVector *axis,f32 rad);

La función guMtxRotAxisRad nos permite aplicar una rotación sobre un eje arbitrario distinto de los ejes de coordenadas. El eje de rotación lo especificaremos en axis y el ángulo de rotación en radianes.

- void guMtxRotAxisDeg(Mtx mt,guVector *axis,f32 deg);

Función equivalente para trabajar en grados sexagesimales.

Operaciones sobre vectores

- void guVecHalfAngle(guVector *a,guVector *b,guVector *half);

Calcula el vector que se encuentra a medio camino entre a y b.

- void guVecAdd(guVector *a,guVector *b,guVector *ab);

Suma los vectores a y b y devuelve el resultado en ab.

- void guVecSub(guVector *a,guVector *b,guVector *ab);

Le resta b del vector a y lo devuelve en ab. (ab = a-b).

- void guVecScale(guVector *src,guVector *dst,f32 scale);

Multiplica cada componente del vector src por scale y devuelve el resultado en dst.

- void guVecNormalize(guVector *v);

Normaliza el vector v. Es decir devuelve un vector de longitud 1 con la misma dirección y sentido que v.

- void guVecMultiply(Mtx mt,guVector *src,guVector *dst);

Multiplica el vector src por la matriz y lo devuelve en dst.

Esto se puede utilizar para posicionar puntos en el espacio con el fin de detectar colisiones, por ejemplo, partiendo de las matrices que posiciona el objeto en el espacio

- void guVecCross(guVector *a,guVector *b,guVector *axb);

Calcula el producto vectorial de a y b y devuelve el resultado en axb.

- f32 guVecDotProduct(guVector *a,guVector *b);

Calcula el producto escalar (o producto punto) de a por b y lo devuelve como resultado de la función.

Aplicando todo lo aprendido

Pues después de todo el rollo teórico vamos a entrar en materia y pasar a la práctica. Como os adelanté al principio, en esta ocasión vamos a construir nuestro propio sistema solar y aprenderemos a calcular los valores de los parámetros necesarios para poder navegar por su interior.

Lo primero incluiremos las bibliotecas y declararemos las texturas de los 9 planetas del sistema solar:

#include <grrlib.h>
 
#include <stdlib.h>
#include <math.h>
#include <malloc.h>
#include <wiiuse/wpad.h>
 
 
#include "font_png.h"
#include "estrellas_png.h"
#include "venus_png.h"
#include "mercurio_png.h"
#include "tierra_png.h"
#include "marte_png.h"
#include "jupiter_png.h"
#include "saturno_png.h"
#include "urano_png.h"
#include "neptuno_png.h"
#include "pluton_png.h"
#include "luna_png.h"
#include "sol_png.h"
#include "anillo_png.h"
 
GRRLIB_texImg *tex_estrellas;
GRRLIB_texImg *tex_font;
GRRLIB_texImg *tex_mercurio;
GRRLIB_texImg *tex_venus;
GRRLIB_texImg *tex_tierra;
GRRLIB_texImg *tex_marte;
GRRLIB_texImg *tex_jupiter;
GRRLIB_texImg *tex_saturno;
GRRLIB_texImg *tex_anillo;
GRRLIB_texImg *tex_urano;
GRRLIB_texImg *tex_neptuno;
GRRLIB_texImg *tex_pluton;
GRRLIB_texImg *tex_luna;
GRRLIB_texImg *tex_sol;

Declararemos algunas variables que usaremos en el programa:

float ac=1000;
 
f32 vel=0;
guVector player_up = { 0, 1, 0};
guVector player_right={1,0,0};
guVector player_front={0,0,1};
 
guVector lookat = { 0, 0, 5000};
guVector player_position = { 0, 0, -180};
guVector stepdir={0,0,0};

Explicaremos brevemente el uso de las variables anteriores:

  • ac: Esta variable se usara como variable de tiempo. La posición de un planeta en su órbita dependerá de su velocidad angular y del valor de esta variable.
  • vel: Cuanto mayor sea esta variable más deprisa se desplazará nuestra nave. Empezamos con nave parada.
  • player_up: Vector "arriba" del jugador. (También equivale al eje Y normalizado desde el punto de vista del observador).
  • player_right: Vector "derecha" del jugador. (También equivale al eje X normalizado desde el punto de vista del observador)
  • player_front: Vector "adelante" del jugador. (También equivale al eje Z normalizado desde el punto de vista del observador).
  • lookat: Punto al que mira la cámara.
  • player_position: posición en la que se sitúa la cámara.
  • stepdir: Corresponde al vector "adelante" multiplicado por la variable vel. Es decir el vector que tenemos que sumar al vector posición en cada unidad de tiempo.

Seguidamente definiremos una función que nos permitirá dibujar un cubo con textura. Esta función la usaremos para dibujar un gigantesco cubo estrellado que representará el firmamento. La función es idéntica a la que ya definimos en la entrega anterior, por lo que no la explicaré de nuevo.

void DrawTexturedCube(u32 col){
 
float ftex = 1.0f;
bool rep = false;
 
	GRRLIB_SetTexture(tex_estrellas,rep);
	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(ftex,0.0f);
		GX_Position3f32(+0.5f,-0.5f,-0.5f);
		GX_Normal3f32(+0.0,+0.0,-1.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(ftex,ftex);
		GX_Position3f32(+0.5f,+0.5f,-0.5f);
		GX_Normal3f32(+0.0,+0.0,-1.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(0.0f,ftex);
	GX_End();
 
	GRRLIB_SetTexture(tex_estrellas,rep);
	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(ftex,0.0f);
		GX_Position3f32(+0.5f,-0.5f,+0.5f);
		GX_Normal3f32(+0.0,+0.0,+1.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(ftex,ftex);
		GX_Position3f32(-0.5f,-0.5f,+0.5f);
		GX_Normal3f32(+0.0,+0.0,+1.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(0.0f,ftex);
	GX_End();
 
	GRRLIB_SetTexture(tex_estrellas,rep);
	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(ftex,0.0f);
		GX_Position3f32(+0.5f,+0.5f,-0.5f);
		GX_Normal3f32(+0.0,+1.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(ftex,ftex);
		GX_Position3f32(-0.5f,+0.5f,-0.5f);
		GX_Normal3f32(+0.0,+1.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(0.0f,ftex);
	GX_End();
 
	GRRLIB_SetTexture(tex_estrellas,rep);
	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(ftex,0.0f);
		GX_Position3f32(+0.5f,-0.5f,-0.5f);
		GX_Normal3f32(+0.0,-1.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(ftex,ftex);
		GX_Position3f32(-0.5f,-0.5f,-0.5f);
		GX_Normal3f32(+0.0,-1.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(0.0f,ftex);
	GX_End();
 
	GRRLIB_SetTexture(tex_estrellas,rep);
	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(ftex,0.0f);
		GX_Position3f32(-0.5f,-0.5f,-0.5f);
		GX_Normal3f32(-1.0,+0.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(ftex,ftex);
		GX_Position3f32(-0.5f,-0.5f,+0.5f);
		GX_Normal3f32(-1.0,+0.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(0.0f,ftex);
	GX_End();
 
	GRRLIB_SetTexture(tex_estrellas,rep);
	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(ftex,0.0f);
		GX_Position3f32(+0.5f,-0.5f,-0.5f);
		GX_Normal3f32(+1.0,+0.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(ftex,ftex);
		GX_Position3f32(+0.5f,-0.5f,+0.5f);
		GX_Normal3f32(+1.0,+0.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(0.0f,ftex);
	GX_End();
}

La siguiente función es algo más complicada. Se trata de una esfera con textura que nos permitirá modelar todos los planetas además del sol y la luna.

Para la definición de dicha función he partido de la que viene en el fuente de GRRLIB para modelar una esfera sin texturas y le he añadido el mapeado de las texturas.

También he modificado el tipo de primitiva utilizado, ya que en el original se usaba la primitiva "TRIANGLE_STRIP" y para la aplicación de la textura me ha resultado mejor la utilización de la primitiva "QUADS".

void GRRLIB_DrawTexturedSphere(f32 r, int lats, int longs, bool filled, u32 col) {
	int i, j;
	f32 lat0, z0, zr0,
		lat1, z1, zr1,
		lng1, x1, y1,
		lng2, x2, y2;
 
	for(i = 0; i <= lats; i++) {
		lat0 = M_PI * (-0.5F + (f32) (i - 1) / lats);
		z0  = sin(lat0);
		zr0 = cos(lat0);
 
		lat1 = M_PI * (-0.5F + (f32) i / lats);
		z1 = sin(lat1);
		zr1 = cos(lat1);
		GX_Begin(GX_QUADS, GX_VTXFMT0, 4*(longs+1));
		for(j = 0; j <= longs; j++) {
			lng1 = 2 * M_PI * (f32) (j - 1) / longs;
			lng2 = 2 * M_PI * (f32) (j) / longs;
			x1 = cos(lng1);
			y1 = sin(lng1);
 
			x2 = cos(lng2);
			y2 = sin(lng2);
 
			GX_Position3f32(x1 * zr0 * r, y1 * zr0 * r, z0 * r);
			GX_Normal3f32(x1 * zr0 * r, y1 * zr0 * r, z0 * r);
			GX_Color1u32(col);
			GX_TexCoord2f32(1.0f-(float) (j-1) / (float) longs, (float) (i-1) / (float) lats);
 
			GX_Position3f32(x1 * zr1 * r, y1 * zr1 * r, z1 * r);
			GX_Normal3f32(x1 * zr1 * r, y1 * zr1 * r, z1 * r);
			GX_Color1u32(col);
			GX_TexCoord2f32(1.0f-(float) (j-1) / (float) longs, (float) i / (float) lats);
 
			GX_Position3f32(x2 * zr1 * r, y2 * zr1 * r, z1 * r);
			GX_Normal3f32(x2 * zr1 * r, y2 * zr1 * r, z1 * r);
			GX_Color1u32(col);
			GX_TexCoord2f32(1.0f-(float) j / (float) longs, (float) i / (float) lats);
 
			GX_Position3f32(x2 * zr0  * r, y2 * zr0 * r, z0 * r);
			GX_Normal3f32(x2 * zr0 * r, y2 * zr0 * r, z0 * r);
			GX_Color1u32(col);
			GX_TexCoord2f32(1.0f-(float) j / (float) longs, (float) (i-1) / (float) lats);
		}
		GX_End();
	}
}

Básicamente la función hace un recorrido por dos bucles anidados, correspondientes a las latitudes y las longitudes especificadas. Para cada vuelta de bucle calculamos las cuatro posiciones del QUAD en el espacio y su correspondiente trozo de textura en coordenadas de textura.

Hay que tener en cuenta que aunque el tamaño de los Quads en el espacio tridimensional es más grande cuanto más cerca del ecuador nos encontramos, en el caso de la textura todos los Quads son del mismo tamaño.

A continuación definiremos una función que nos permitirá situar un planeta en el espacio de una manera genérica, tanto si se trata de un planeta propiamente dicho como si es un satélite.

La idea de la función es que podemos calcular la posición de un planeta/satélite y dibujarlo si sabemos lo siguiente:

  • Su tamaño (planet_size).
  • La distancia al planeta alrededor del que orbita (planet_distance = 0 para planetas y distinto de 0 para satélites). 
  • La velocidad angular de rotación sobre su eje. (w)
  • La velocidad de rotación sobre el planeta alrededor del que orbita (o1wx, o1wy, o1wz).
  • La distancia al sol. (sun_distance).
  • La velocidad angular de rotación alrededor del sol (o2wx, o2wy, o2wz).
  • Su textura. (texture)
  • Su color. (color)

Para que las transformaciones resulten tal y como esperamos, deberemos hacerlas desde las más locales al planeta hasta las más generales, de forma que:

  1. Aplicamos el giro sobre su propio eje.
  2. Trasladamos a la distancia especificada del planeta madre.
  3. Giramos alrededor del planeta madre.
  4. Trasladamos a la órbita correspondiente alrededor del sol.
  5. Aplicamos el giro correspondiente a la órbita alrededor del sol. 

void Draw_planet(f32 planetsize, f32 w,
				f32 planet_distance, f32 o1wx, f32 o1wy, f32 o1wz, 
				f32 sun_distance, f32 o2wx, f32 o2wy, f32 o2wz, 
				GRRLIB_texImg *texture, u32 color) {
	GRRLIB_ObjectViewBegin();
		GRRLIB_ObjectViewRotate(90, ac*w,90); // giro sobre su eje
		GRRLIB_ObjectViewTrans(planet_distance, 0, 0); // radio de giro sobre el planeta (solo satélites)
		GRRLIB_ObjectViewRotate(ac*o1wx, ac*o1wy,ac*o1wz); // giro sobre el planeta (solo satélites)
 
		GRRLIB_ObjectViewTrans(sun_distance,0,0);					// radio de giro sobre el sol
		GRRLIB_ObjectViewRotate(ac*o2wx, ac*o2wy,ac*o2wz); // giro sobre el sol
	GRRLIB_ObjectViewEnd();
	GRRLIB_SetTexture(texture,0);
	GRRLIB_DrawTexturedSphere(planetsize, 30, 30, true, color);
}

La siguiente función es un caso particular para el planeta Saturno de la función anterior en la que, además de llamar a Draw_planet dibujaremos el anillo característico del planeta, aplicándole exactamente las mismas transformaciones que al planeta más una pequeña inclinación sobre su eje.

Para dibujar el anillo usaremos un plano con una textura png de tal forma que todo lo que no sea el anillo será totalmente transparente.

void Draw_saturno(){
	u32 col = 0xFFFFFFFF;
	f32 sun_distance = 100.0f;
	Draw_planet(4.8f, 3.5, 0.0, 0.0,0.0,0.0, sun_distance, 0.0,0,0.0, tex_saturno,col);
 
	// Dibujamos el anillo que consiste en un Quad con textura
	GRRLIB_ObjectViewBegin();
		GRRLIB_ObjectViewScale(20, 1, 20);// escalamos lo necesario para que rodee al planeta
		GRRLIB_ObjectViewRotate(30,0,30); // giro sobre su eje
		GRRLIB_ObjectViewTrans(sun_distance,0,0);// radio de giro sobre el sol
		GRRLIB_ObjectViewRotate(0, ac*0,0); // giro sobre el sol
	GRRLIB_ObjectViewEnd();
	GRRLIB_SetTexture(tex_anillo,0);
	GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
		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();
}

La siguiente función sirve para dibujar el firmamento. Draw_space hace uso de DrawTexturedCube trasladando la posición del cubo de tal manera que el jugador siempre se encontrará situado en su centro. De esta manera simulamos que el espacio es infinito y nunca alcanzamos las estrellas más lejanas.

void Draw_space(){
	GRRLIB_SetLightOff();
	GRRLIB_SetLightAmbient(0x222222FF);
	GRRLIB_ObjectViewBegin();
		GRRLIB_ObjectViewScale(1000,1000,1000);
		GRRLIB_ObjectViewTrans(player_position.x, player_position.y, player_position.z);
	GRRLIB_ObjectViewEnd();
	DrawTexturedCube(0x8888FFFF);
}

La unción "gira" aplica un giro de ángulo ang alrededor del eje indicado. Aplicará el giro sobre los ejes de coordenadas locales del jugador (player_front, player_right, player_up) y sobre el punto hacia donde mira la cámara (lookat).

Para ello primero calcula la matriz de giro sobre dicho eje y la guarda en m, tras lo cual solo debe multiplicar dicha matriz por cada uno de los vectores nombrados anteriormente.

void gira(guVector* eje, f32 ang){
	Mtx m;
	guMtxRotAxisDeg(m, eje, ang);
	guVecMultiply(m, &player_front, &player_front);
	guVecMultiply(m, &player_right, &player_right);
	guVecMultiply(m, &player_up, &player_up);
	guVecMultiply(m, &lookat, &lookat);
}

A partir de aquí ya entramos en el main, realizando las típicas tareas de inicialización de bibliotecas, carga de texturas y fuentes de letras. También pondremos el fondo negro por defecto y pondremos a "true" la opción antialias para suavizar el renderizado de la escena y eliminar dientes de sierra.

int main() {
	struct expansion_t exp;
 
	GRRLIB_Init();
	WPAD_Init();
 
	tex_sol= GRRLIB_LoadTexture(sol_png);
	tex_mercurio= GRRLIB_LoadTexture(mercurio_png);
	tex_venus= GRRLIB_LoadTexture(venus_png);
	tex_tierra= GRRLIB_LoadTexture(tierra_png);
	tex_marte= GRRLIB_LoadTexture(marte_png);
	tex_jupiter= GRRLIB_LoadTexture(jupiter_png);
	tex_saturno= GRRLIB_LoadTexture(saturno_png);
	tex_anillo= GRRLIB_LoadTexture(anillo_png);
	tex_urano= GRRLIB_LoadTexture(urano_png);
	tex_neptuno= GRRLIB_LoadTexture(neptuno_png);
	tex_pluton= GRRLIB_LoadTexture(pluton_png);
	tex_luna= GRRLIB_LoadTexture(luna_png);
	tex_estrellas= GRRLIB_LoadTexture(estrellas_png);
 
	tex_font = GRRLIB_LoadTexture(font_png);
 
	GRRLIB_InitTileSet(tex_font, 16, 16, 32);
 
 
	GRRLIB_Settings.antialias = true;
 
	GRRLIB_SetBackgroundColour(0x00, 0x00, 0x00, 0x00);
 

Una vez entramos en el bucle while leemos el valor del wiimote 0 y de su nunchuck, además de comprobar si pulsamos el botón home para volver al HBC.

while(1) {
	GRRLIB_2dMode();
	WPAD_ScanPads();
	WPAD_Expansion(0, &exp);
	if(WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) exit(0);

En la siguiente condición comprobamos si tenemos pulsado el botón izquierdo de la cruceta o tenemos el joystick del nunchuck hacia la izquierda. Si es así giramos horizontalmente (es decir sobre el eje Y) hacia la izquierda y también ligeramente sobre el eje Z para conseguir un efecto de inclinación de la nave.

if((WPAD_ButtonsHeld(0) & WPAD_BUTTON_LEFT) ||
	((exp.nunchuk.js.mag > 0.3) && 
	 (exp.nunchuk.js.ang > 205 && exp.nunchuk.js.ang < 335))){
	gira(&player_front, -0.25); // giro lateral izquierda
	gira(&player_up, +0.50); // giro horizontal izquierda
}

Hacia la derecha es exactamente igual pero en sentido contrario.

if((WPAD_ButtonsHeld(0) & WPAD_BUTTON_RIGHT) ||
	((exp.nunchuk.js.mag > 0.3) && 
	(exp.nunchuk.js.ang > 25 && exp.nunchuk.js.ang < 155))){
	gira(&player_front, +0.25); // giro lateral derecha
	gira(&player_up, -0.50); // giro horizontal derecha
}

Hacia arriba y hacia abajo solo giraremos sobre el eje X.

if((WPAD_ButtonsHeld(0) & WPAD_BUTTON_UP) ||
	((exp.nunchuk.js.mag > 0.3) && 
	(exp.nunchuk.js.ang <= 65 || exp.nunchuk.js.ang >= 295))){
	gira(&player_right, +0.25);
}
 
if((WPAD_ButtonsHeld(0) & WPAD_BUTTON_DOWN) ||
	((exp.nunchuk.js.mag > 0.3) && 
	(exp.nunchuk.js.ang >= 115 && exp.nunchuk.js.ang <= 245))){
	gira(&player_right, -0.25);
}

Lo siguiente será gestionar los botones de aceleración (A) y de frenado (B) que aumentaran o reducirán la variable vel respectivamente:

if(WPAD_ButtonsHeld(0) & WPAD_BUTTON_A) {
	vel+=0.01;
}
if(WPAD_ButtonsHeld(0) & WPAD_BUTTON_B) {
	vel-=0.01;
}

Una vez calculada la dirección de navegación debemos avanzar hacia adelante dependiendo de la velocidad actual de la nave. Para ello multiplicaremos el vector player_front por la velocidad (vel) y el resultado lo sumaremos a player_position.

guVecScale(&player_front, &stepdir, vel);
guVecAdd(&player_position, &stepdir, &player_position);

Tras esto pasaremos ya a la representación gráfica, para lo cual empezaremos inicializando los parámetros de la cámara y la posición del observador a partir de los vectores anteriormente calculados:

GRRLIB_Camera3dSettings(player_position.x,player_position.y,player_position.z, 
	player_up.x,player_up.y,player_up.z, 
	lookat.x,lookat.y,lookat.z);

Después pasaremos a poner el hardware en modo 3D y a dibujar los objetos del escenario que no deben recibir luz direccional. Es decir el firmamento y el sol.

Sobre el firmamento debemos evitar la luz direccional para que la sensación sea de que está iluminado por igual miremos a donde miremos y también evitar que se noten los planos del cubo.

En cuanto al sol, será la fuente de luz en sí misma y debe de verse iluminado por todas partes.

Una vez dibujados los dos elementos anteriores, ya podemos pasar a definir la luz ambiente y la luz difusa, esta última con origen en (0,0,0) que es donde está situado el sol.

El efecto será que todos los planetas estarán iluminados solo en la mitad que mira en dirección al sol. La otra mitad solo recibirá la luz ambiente.

Draw_planet(1.0f, 3.5, 0.0, 0.0,0.0,0.0, 10.0f, 0.0,2.5,0.0, tex_mercurio,0xFFFFFFFF);
Draw_planet(1.0f, 3.5, 0.0, 0.0,0.0,0.0, 15.0f, 0.0,2.5,0.0, tex_venus,0xFFFFFFFF);
Draw_planet(1.0f, 0.0, 5.0, 0.0,0.0,3.5, 30.0f, 0.0,2.3,0.0, tex_luna,0xFFFFFFFF);
Draw_planet(2.8f, 3.5, 0.0, 0.0,0.0,0.0, 30.0f, 0.0,2.3,0.0, tex_tierra,0xFFFFFFFF);
Draw_planet(2.5f, 3.5, 0.0, 0.0,0.0,0.0, 45.0f, 0.0,1.8,0.0, tex_marte,0xFFFFFFFF);
Draw_planet(6.0f, 3.5, 0.0, 0.0,0.0,0.0, 70.0f, 0.0,1.6,0.0, tex_jupiter,0xFFFFFFFF);
Draw_saturno();
Draw_planet(3.8f, 3.5, 0.0, 0.0,0.0,0.0, 140.0f, 0.0,1.1,0.0, tex_urano,0xFFFFFFFF);
Draw_planet(4.2f, 3.5, 0.0, 0.0,0.0,0.0, 160.0f, 0.0,0.5,0.0, tex_neptuno,0xFFFFFFFF);
Draw_planet(0.9f, 3.5, 0.0, 0.0,0.0,0.0, 180.0f, 0.0,0.4,0.0, tex_pluton,0xFFFFFFFF);		

Después incrementaremos la variable temporal:

ac+=0.05f;

y finalmente cambiaremos a modo 2D para visualizar el texto de la parte superior:

// Cambia a modo 2D para visualizar el texto
GRRLIB_2dMode();
 
GRRLIB_Printf((640-(16*24))/2, 20, tex_font, 0xFFFFFFFF, 1, "CURSO DE GRRLIB PARTE 11");
GRRLIB_Printf((640-(16*31))/2, 40, tex_font, 0xFFFFFFFF, 1, "JOY NUNCHUK O CRUCETA = MOVERSE");
GRRLIB_Printf((640-(16*25))/2, 60, tex_font, 0xFFFFFFFF, 1, "A = ACELERAR - B = FRENAR");
GRRLIB_Render();

Ya fuera del bucle liberamos memoria y salimos del programa.

GRRLIB_Exit(); // Be a good boy, clear the memory allocated by GRRLIB
 
GRRLIB_FreeTexture(tex_mercurio);
GRRLIB_FreeTexture(tex_venus);
GRRLIB_FreeTexture(tex_tierra);
GRRLIB_FreeTexture(tex_marte);
GRRLIB_FreeTexture(tex_jupiter);
GRRLIB_FreeTexture(tex_saturno);
GRRLIB_FreeTexture(tex_urano);
GRRLIB_FreeTexture(tex_neptuno);
GRRLIB_FreeTexture(tex_pluton);
GRRLIB_FreeTexture(tex_luna);
GRRLIB_FreeTexture(tex_sol);
GRRLIB_FreeTexture(tex_estrellas);
GRRLIB_FreeTexture(tex_font);
 
exit(0);

El código final quedaría como sigue:

#include <grrlib.h>
 
#include <stdlib.h>
#include <math.h>
#include <malloc.h>
#include <wiiuse/wpad.h>
 
 
#include "font_png.h"
#include "estrellas_png.h"
#include "venus_png.h"
#include "mercurio_png.h"
#include "tierra_png.h"
#include "marte_png.h"
#include "jupiter_png.h"
#include "saturno_png.h"
#include "urano_png.h"
#include "neptuno_png.h"
#include "pluton_png.h"
#include "luna_png.h"
#include "sol_png.h"
#include "anillo_png.h"
 
GRRLIB_texImg *tex_estrellas;
GRRLIB_texImg *tex_font;
GRRLIB_texImg *tex_mercurio;
GRRLIB_texImg *tex_venus;
GRRLIB_texImg *tex_tierra;
GRRLIB_texImg *tex_marte;
GRRLIB_texImg *tex_jupiter;
GRRLIB_texImg *tex_saturno;
GRRLIB_texImg *tex_anillo;
GRRLIB_texImg *tex_urano;
GRRLIB_texImg *tex_neptuno;
GRRLIB_texImg *tex_pluton;
GRRLIB_texImg *tex_luna;
GRRLIB_texImg *tex_sol;
 
float ac=1000;
 
f32 vel=0;
guVector player_up = { 0, 1, 0};
guVector player_right={1,0,0};
guVector player_front={0,0,1};
 
guVector lookat = { 0, 0, 5000};
guVector player_position = { 0, 0, -180};
guVector stepdir={0,0,0};
 
 
void DrawTexturedCube(u32 col){
 
float ftex = 1.0f;
bool rep = false;
 
	GRRLIB_SetTexture(tex_estrellas,rep);
	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(ftex,0.0f);
		GX_Position3f32(+0.5f,-0.5f,-0.5f);
		GX_Normal3f32(+0.0,+0.0,-1.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(ftex,ftex);
		GX_Position3f32(+0.5f,+0.5f,-0.5f);
		GX_Normal3f32(+0.0,+0.0,-1.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(0.0f,ftex);
	GX_End();
 
	GRRLIB_SetTexture(tex_estrellas,rep);
	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(ftex,0.0f);
		GX_Position3f32(+0.5f,-0.5f,+0.5f);
		GX_Normal3f32(+0.0,+0.0,+1.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(ftex,ftex);
		GX_Position3f32(-0.5f,-0.5f,+0.5f);
		GX_Normal3f32(+0.0,+0.0,+1.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(0.0f,ftex);
	GX_End();
 
	GRRLIB_SetTexture(tex_estrellas,rep);
	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(ftex,0.0f);
		GX_Position3f32(+0.5f,+0.5f,-0.5f);
		GX_Normal3f32(+0.0,+1.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(ftex,ftex);
		GX_Position3f32(-0.5f,+0.5f,-0.5f);
		GX_Normal3f32(+0.0,+1.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(0.0f,ftex);
	GX_End();
 
	GRRLIB_SetTexture(tex_estrellas,rep);
	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(ftex,0.0f);
		GX_Position3f32(+0.5f,-0.5f,-0.5f);
		GX_Normal3f32(+0.0,-1.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(ftex,ftex);
		GX_Position3f32(-0.5f,-0.5f,-0.5f);
		GX_Normal3f32(+0.0,-1.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(0.0f,ftex);
	GX_End();
 
	GRRLIB_SetTexture(tex_estrellas,rep);
	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(ftex,0.0f);
		GX_Position3f32(-0.5f,-0.5f,-0.5f);
		GX_Normal3f32(-1.0,+0.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(ftex,ftex);
		GX_Position3f32(-0.5f,-0.5f,+0.5f);
		GX_Normal3f32(-1.0,+0.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(0.0f,ftex);
	GX_End();
 
	GRRLIB_SetTexture(tex_estrellas,rep);
	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(ftex,0.0f);
		GX_Position3f32(+0.5f,-0.5f,-0.5f);
		GX_Normal3f32(+1.0,+0.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(ftex,ftex);
		GX_Position3f32(+0.5f,-0.5f,+0.5f);
		GX_Normal3f32(+1.0,+0.0,+0.0);
		GX_Color1u32(col);
		GX_TexCoord2f32(0.0f,ftex);
	GX_End();
}
 
void GRRLIB_DrawTexturedSphere(f32 r, int lats, int longs, bool filled, u32 col) {
	int i, j;
	f32 lat0, z0, zr0,
		lat1, z1, zr1,
		lng1, x1, y1,
		lng2, x2, y2;
 
	for(i = 0; i <= lats; i++) {
		lat0 = M_PI * (-0.5F + (f32) (i - 1) / lats);
		z0  = sin(lat0);
		zr0 = cos(lat0);
 
		lat1 = M_PI * (-0.5F + (f32) i / lats);
		z1 = sin(lat1);
		zr1 = cos(lat1);
		GX_Begin(GX_QUADS, GX_VTXFMT0, 4*(longs+1));
		for(j = 0; j <= longs; j++) {
			lng1 = 2 * M_PI * (f32) (j - 1) / longs;
			lng2 = 2 * M_PI * (f32) (j) / longs;
			x1 = cos(lng1);
			y1 = sin(lng1);
 
			x2 = cos(lng2);
			y2 = sin(lng2);
 
			GX_Position3f32(x1 * zr0 * r, y1 * zr0 * r, z0 * r);
			GX_Normal3f32(x1 * zr0 * r, y1 * zr0 * r, z0 * r);
			GX_Color1u32(col);
			GX_TexCoord2f32(1.0f-(float) (j-1) / (float) longs, (float) (i-1) / (float) lats);
 
			GX_Position3f32(x1 * zr1 * r, y1 * zr1 * r, z1 * r);
			GX_Normal3f32(x1 * zr1 * r, y1 * zr1 * r, z1 * r);
			GX_Color1u32(col);
			GX_TexCoord2f32(1.0f-(float) (j-1) / (float) longs, (float) i / (float) lats);
 
			GX_Position3f32(x2 * zr1 * r, y2 * zr1 * r, z1 * r);
			GX_Normal3f32(x2 * zr1 * r, y2 * zr1 * r, z1 * r);
			GX_Color1u32(col);
			GX_TexCoord2f32(1.0f-(float) j / (float) longs, (float) i / (float) lats);
 
			GX_Position3f32(x2 * zr0  * r, y2 * zr0 * r, z0 * r);
			GX_Normal3f32(x2 * zr0 * r, y2 * zr0 * r, z0 * r);
			GX_Color1u32(col);
			GX_TexCoord2f32(1.0f-(float) j / (float) longs, (float) (i-1) / (float) lats);
		}
		GX_End();
	}
}
 
void Draw_planet(f32 planetsize, f32 w,
				f32 planet_distance, f32 o1wx, f32 o1wy, f32 o1wz, 
				f32 sun_distance, f32 o2wx, f32 o2wy, f32 o2wz, 
				GRRLIB_texImg *texture, u32 color) {
	GRRLIB_ObjectViewBegin();
		GRRLIB_ObjectViewRotate(90, ac*w,90); // giro sobre su eje
		GRRLIB_ObjectViewTrans(planet_distance, 0, 0); // radio de giro sobre el planeta (solo satélites)
		GRRLIB_ObjectViewRotate(ac*o1wx, ac*o1wy,ac*o1wz); // giro sobre el planeta (solo satélites)
 
		GRRLIB_ObjectViewTrans(sun_distance,0,0);					// radio de giro sobre el sol
		GRRLIB_ObjectViewRotate(ac*o2wx, ac*o2wy,ac*o2wz); // giro sobre el sol
	GRRLIB_ObjectViewEnd();
	GRRLIB_SetTexture(texture,0);
	GRRLIB_DrawTexturedSphere(planetsize, 30, 30, true, color);
}
 
void Draw_saturno(){
	u32 col = 0xFFFFFFFF;
	f32 sun_distance = 100.0f;
	Draw_planet(4.8f, 3.5, 0.0, 0.0,0.0,0.0, sun_distance, 0.0,0,0.0, tex_saturno,col);
 
	// Dibujamos el anillo que consiste en un Quad con textura
	GRRLIB_ObjectViewBegin();
		GRRLIB_ObjectViewScale(20, 1, 20);// escalamos lo necesario para que rodee al planeta
		GRRLIB_ObjectViewRotate(30,0,30); // giro sobre su eje
		GRRLIB_ObjectViewTrans(sun_distance,0,0);// radio de giro sobre el sol
		GRRLIB_ObjectViewRotate(0, ac*0,0); // giro sobre el sol
	GRRLIB_ObjectViewEnd();
	GRRLIB_SetTexture(tex_anillo,0);
	GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
		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();
}
 
void Draw_space(){
	GRRLIB_SetLightOff();
	GRRLIB_SetLightAmbient(0x222222FF);
	GRRLIB_ObjectViewBegin();
		GRRLIB_ObjectViewScale(1000,1000,1000);
		GRRLIB_ObjectViewTrans(player_position.x, player_position.y, player_position.z);
	GRRLIB_ObjectViewEnd();
	DrawTexturedCube(0x8888FFFF);
}
 
void gira(guVector* eje, f32 ang){
	Mtx m;
	guMtxRotAxisDeg(m, eje, ang);
	guVecMultiply(m, &player_front, &player_front);
	guVecMultiply(m, &player_right, &player_right);
	guVecMultiply(m, &player_up, &player_up);
	guVecMultiply(m, &lookat, &lookat);
}
 
int main() {
	struct expansion_t exp;
 
	GRRLIB_Init();
	WPAD_Init();
 
	tex_sol= GRRLIB_LoadTexture(sol_png);
	tex_mercurio= GRRLIB_LoadTexture(mercurio_png);
	tex_venus= GRRLIB_LoadTexture(venus_png);
	tex_tierra= GRRLIB_LoadTexture(tierra_png);
	tex_marte= GRRLIB_LoadTexture(marte_png);
	tex_jupiter= GRRLIB_LoadTexture(jupiter_png);
	tex_saturno= GRRLIB_LoadTexture(saturno_png);
	tex_anillo= GRRLIB_LoadTexture(anillo_png);
	tex_urano= GRRLIB_LoadTexture(urano_png);
	tex_neptuno= GRRLIB_LoadTexture(neptuno_png);
	tex_pluton= GRRLIB_LoadTexture(pluton_png);
	tex_luna= GRRLIB_LoadTexture(luna_png);
	tex_estrellas= GRRLIB_LoadTexture(estrellas_png);
 
	tex_font = GRRLIB_LoadTexture(font_png);
 
	GRRLIB_InitTileSet(tex_font, 16, 16, 32);
 
 
	GRRLIB_Settings.antialias = true;
 
	GRRLIB_SetBackgroundColour(0x00, 0x00, 0x00, 0x00);
 
	while(1) {
		GRRLIB_2dMode();
		WPAD_ScanPads();
		WPAD_Expansion(0, &exp);
		if(WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) exit(0);
 
		if((WPAD_ButtonsHeld(0) & WPAD_BUTTON_LEFT) ||
			((exp.nunchuk.js.mag > 0.3) && 
			 (exp.nunchuk.js.ang > 205 && exp.nunchuk.js.ang < 335))){
			gira(&player_front, -0.25); // giro lateral izquierda
			gira(&player_up, +0.50); // giro horizontal izquierda
		}
 
		if((WPAD_ButtonsHeld(0) & WPAD_BUTTON_RIGHT) ||
			((exp.nunchuk.js.mag > 0.3) && 
			(exp.nunchuk.js.ang > 25 && exp.nunchuk.js.ang < 155))){
			gira(&player_front, +0.25); // giro lateral derecha
			gira(&player_up, -0.50); // giro horizontal derecha
		}
 
		if((WPAD_ButtonsHeld(0) & WPAD_BUTTON_UP) ||
			((exp.nunchuk.js.mag > 0.3) && 
			(exp.nunchuk.js.ang <= 65 || exp.nunchuk.js.ang >= 295))){
			gira(&player_right, +0.25);
		}
 
		if((WPAD_ButtonsHeld(0) & WPAD_BUTTON_DOWN) ||
			((exp.nunchuk.js.mag > 0.3) && 
			(exp.nunchuk.js.ang >= 115 && exp.nunchuk.js.ang <= 245))){
			gira(&player_right, -0.25);
		}
 
		if(WPAD_ButtonsHeld(0) & WPAD_BUTTON_A) {
			vel+=0.01;
		}
		if(WPAD_ButtonsHeld(0) & WPAD_BUTTON_B) {
			vel-=0.01;
		}
 
		guVecScale(&player_front, &stepdir, vel);
		guVecAdd(&player_position, &stepdir, &player_position);
 
		GRRLIB_Camera3dSettings(player_position.x,player_position.y,player_position.z, 
			player_up.x,player_up.y,player_up.z, 
			lookat.x,lookat.y,lookat.z);
 
 
		GRRLIB_3dMode(0.1,1000,45,1,1);
 
		Draw_space();
 
		Draw_planet(8.0f, 0.0f, 0.0, 0,0,0, 0.0f, 0,0,0, tex_sol, 0xFFAA00FF);
 
		GRRLIB_SetLightAmbient(0x222222FF);
		GRRLIB_SetLightDiff(0,(guVector){0.0f,0.0f,0},20.0f,1.0f,0xFFFFFFFF);
 
		Draw_planet(1.0f, 3.5, 0.0, 0.0,0.0,0.0, 10.0f, 0.0,2.5,0.0, tex_mercurio,0xFFFFFFFF);
		Draw_planet(1.0f, 3.5, 0.0, 0.0,0.0,0.0, 15.0f, 0.0,2.5,0.0, tex_venus,0xFFFFFFFF);
		Draw_planet(1.0f, 0.0, 5.0, 0.0,0.0,3.5, 30.0f, 0.0,2.3,0.0, tex_luna,0xFFFFFFFF);
		Draw_planet(2.8f, 3.5, 0.0, 0.0,0.0,0.0, 30.0f, 0.0,2.3,0.0, tex_tierra,0xFFFFFFFF);
		Draw_planet(2.5f, 3.5, 0.0, 0.0,0.0,0.0, 45.0f, 0.0,1.8,0.0, tex_marte,0xFFFFFFFF);
		Draw_planet(6.0f, 3.5, 0.0, 0.0,0.0,0.0, 70.0f, 0.0,1.6,0.0, tex_jupiter,0xFFFFFFFF);
		Draw_saturno();
		Draw_planet(3.8f, 3.5, 0.0, 0.0,0.0,0.0, 140.0f, 0.0,1.1,0.0, tex_urano,0xFFFFFFFF);
		Draw_planet(4.2f, 3.5, 0.0, 0.0,0.0,0.0, 160.0f, 0.0,0.5,0.0, tex_neptuno,0xFFFFFFFF);
		Draw_planet(0.9f, 3.5, 0.0, 0.0,0.0,0.0, 180.0f, 0.0,0.4,0.0, tex_pluton,0xFFFFFFFF);		
		ac+=0.05f;
 
		// Cambia a modo 2D para visualizar el texto
		GRRLIB_2dMode();
 
		GRRLIB_Printf((640-(16*24))/2, 20, tex_font, 0xFFFFFFFF, 1, "CURSO DE GRRLIB PARTE 11");
		GRRLIB_Printf((640-(16*31))/2, 40, tex_font, 0xFFFFFFFF, 1, "JOY NUNCHUK O CRUCETA = MOVERSE");
		GRRLIB_Printf((640-(16*25))/2, 60, tex_font, 0xFFFFFFFF, 1, "A = ACELERAR - B = FRENAR");
		GRRLIB_Render();
	}
 
	GRRLIB_Exit(); // Be a good boy, clear the memory allocated by GRRLIB
 
	GRRLIB_FreeTexture(tex_mercurio);
	GRRLIB_FreeTexture(tex_venus);
	GRRLIB_FreeTexture(tex_tierra);
	GRRLIB_FreeTexture(tex_marte);
	GRRLIB_FreeTexture(tex_jupiter);
	GRRLIB_FreeTexture(tex_saturno);
	GRRLIB_FreeTexture(tex_urano);
	GRRLIB_FreeTexture(tex_neptuno);
	GRRLIB_FreeTexture(tex_pluton);
	GRRLIB_FreeTexture(tex_luna);
	GRRLIB_FreeTexture(tex_sol);
	GRRLIB_FreeTexture(tex_estrellas);
	GRRLIB_FreeTexture(tex_font);
 
	exit(0);
}

 

4.375
Tu voto: Ninguno Votos totales: 4.4 (24 votos)

Anuncios Google

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

Una duda acerca de los archivos .dol

Hola a todos:

He comenzado este tutorial con gran pasion, aunque estoy algo pegado en lenguaje C++,aun asi estoy siguiendo el curso que me parece de lo mas genial,ya que es facil de entender incluso para mi ,que ya es decir bastante.

Sin mas la duda que me lleva de cabeza es que si suponemos que tengo varios archivos .dol (ya compilados),para que cada uno realize una operacion especifica o sea uno para la intro (intro.dol),otro para testear los botones (buttontester.dol) y asi varios mas, ¿¿Como puedo hacer que mi programa principal (boot.dol) pueda ejecutar estos (como si de subprogramas se tratara) conforme yo lo requiera y me devolviesen valores para saber si todo a ido bien o no??,sin la necesidad de incluirlos todos dentro del boot.dol.

Gracias por la paciencia ya que creo que esta pregunta sera de lo mas novata seguro.

 

Felicitaciones

Solo queria felicitarte por tu grandioso tutorial, y de paso agradecerte ya que antes no sabia nada sobre programar, ahora poco a poco vengo aprendiendo...

 

Nada mas que hay algunas cosas que me gustaria hacer: ¿Como puedo hacer un menu?, Me gustaria saber como hago para poner un menu con lo basico, ya sabes: Empezar partida, Guardar partida, Opciones, etc... Haber si me ayudas con una idea :D.

 

Saludos!!


D A M V

Imagen de martinezcanto

INTENTO MEJORAR

Buenas

Intento mejorar la calidad de mis texturas... añado una imagen como ejemplo...

Me gustaria darle a mis texturas la apariencia de pintura "metalizada", asi que mis dudas...¿ es una caracteristica propia de la textura? o ¿ hay que utilizar otros efectos? no se luz, brillo...

Por otro lado, si aplico un color a la textura en el que predomina el blanco (asi sale como la veo como imagen), toda la textura se ve afectada por dicho color, me explico, el color blanco de la textura se convierte completamente en el color elegido, pero si en ella tengo por ejemplo el color rojo, este adquiere un color de combinacion entre el rojo y el color que le halla aplicado... ¿es posible realizar algo parecido a la imagen aplicandole color a la textura? es decir, si le digo que la textura es amarilla solo el espacio amarillo se coloree?

Como siempre experando tus indicaciones y agradeciendote tu interes

 

saludos

 

Imagen de wilco2009

Esos efectos se consiguen con

Esos efectos se consiguen con luces, pero lamentablemente en Wii no se pueden reproducir los efectos de la foto, al menos yo no sé hacerlo. Haría falta un hardware gráfico con Raytracing.

Podrías simular el efecto del reflejo. con una luz especular, pero el aspecto metalizado es más complicado.

Por otro lado la aplicación selectiva de un color la podrías hacer dibujando una esfera dentro de otra. Le das color blanco a la esfera interior y a la exterior le aplicas una textura png con sólo la banda y el resto de la textura recortada (sin color de fondo) para que se vea transparente y deje verse la otra esfera.

 


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

Imagen de martinezcanto

COMO SIEMPRE

Buenas

 Como siempre aplico tus indicaciones para mejorar el programa, he aplicado lo de las dos bolas, y el efecto me parece muy bueno, ya que de momento consigo ahorrar la carga de texturas atendiendo al color de la bola.

Intentando aplicar otra de tus indicaciones, la relativa a usar fuentes BITMAP, me surgen dudas, como lo que estoy creando es una bola que gira en la que se repite la numeracion por cada cuarto de vuelta, y la carga de la fuente seria la eleccion de una parte de un TILESET, no veo como cargar la misma parte de la fuente cada cuarto de vuelta... seguro que es facil, pero? no veo como aplicarlo como una textura que ocupa parte de una bola...o tendria que hacerlo como  la carga de una textura en un cubo (de cuatro caras iguales) que gira tangente exteriormente a la bola?

Como siempre agradecido

Imagen de wilco2009

Con un cubo no te iba a

Con un cubo no te iba a quedar bien, ya que te quedaría como una etiqueta despegada.

En el caso que me indicas no se puede aplicar mi recomendación. Más bien deberías crear la textura componiendola con las funciones GRRLIB_CompoStart y GRRLIB_CompoEnd, que no he explicado pero que básicamente crean una textura capturando lo que has dibujado en una posición concreta de la pantalla. Cuando utilizas CompoEnd se borra la pantalla antes de mostrarse por lo que no interferirá en el resultado final.

Escribes los cuatro números espaciados para que te queden equidistantes en una textura de 640x320. El resultado de la textura lo aplicas sobre la esfera completa.

Debería quedarte algo como lo de abajo, aunque debes repasarlo porque no lo he probado.

GRRLIB_texImg *tex_font = GRRLIB_LoadTexture(font);
GRRLIB_InitTileSet(tex_font, 64, 64, 32);
GRRLIB_SetHandle (tex_font, tex_font->tilew/2, tex_font->tileh+circsize);
 
GRRLIB_texImg *tex_screen;
tex_screen = GRRLIB_CreateEmptyTexture(640,320);
 
while (1) {
.....
	GRRLIB_CompoStart();
		GRRLIB_DrawTile(32,160, tex_font, 0, 1, 1, 0xFFFFFFFF, '4'-32);
		GRRLIB_DrawTile(182,160, tex_font, 0, 1, 1, 0xFFFFFFFF, '4'-32);
		GRRLIB_DrawTile(362,160, tex_font, 0, 1, 1, 0xFFFFFFFF, '4'-32);
		GRRLIB_DrawTile(412,160, tex_font, 0, 1, 1, 0xFFFFFFFF, '4'-32);
	GRRLIB_CompoEnd(0, 0, tex_screen);
.....
.....
	GRRLIB_Render();
 
}


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

Imagen de martinezcanto

ESTO INDICA

Buenas

Esto indica "Cuando utilizas CompoEnd se borra la pantalla antes de mostrarse por lo que no interferirá en el resultado final.", que como la accion es tan rapida no llegara a verse que se ha imprimido la textura para copiar?

Saludos

 

 

Imagen de wilco2009

Realmente cuando habla de

Realmente cuando habla de pantalla se refiere solo al buffer de visualización, ya que a la pantalla solo llega cuando se hace el Render al final del While


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

Imagen de martinezcanto

ESPERO QUE

Buenas

Espero que un peñazo como yo no te haga desistir de hacer mas tutos....

He puesto en practica esto ultimo que me has recomendado de la siguiente forma

1º) He creado el png de los numeros mediante el wiibuilder y lo he llamado numeros.png, el tamaño del tile 16x28, el tamaño del tileset 160x28 y el elmento de partida 48 el elemento final 58.

2º) he colocado esto al inicio del programa

#include "numeros_png.h"

GRRLIB_texImg *tex_numeros;

3º) He colocado esto en el main

GRRLIB_texImg *tex_numeros = GRRLIB_LoadTexture(numeros_png);
 GRRLIB_InitTileSet(tex_numeros, 160, 28, 48);
 GRRLIB_SetHandle (tex_numeros, tex_numeros->tilew/2, tex_numeros->tileh+0.4f); (esto no lo entiendo bien, el 0.4f es el diametro de la bola)
 
 GRRLIB_texImg *tex_numerosbolas;
 tex_numerosbolas = GRRLIB_CreateEmptyTexture(640,320);

4º) he colocado esto en el while (inicial)

GRRLIB_CompoStart();
  GRRLIB_DrawTile(32,160, tex_numeros, 0, 1, 1, 0xFFFFFFFF, '4'-48);  (entiendo que se elige el tile 4 a partir del 48)
  GRRLIB_DrawTile(182,160, tex_numeros, 0, 1, 1, 0xFFFFFFFF, '4'-48);
  GRRLIB_DrawTile(362,160, tex_numeros, 0, 1, 1, 0xFFFFFFFF, '4'-48);
  GRRLIB_DrawTile(412,160, tex_numeros, 0, 1, 1, 0xFFFFFFFF, '4'-48);
 GRRLIB_CompoEnd(0, 0, tex_numerosbolas);

......

Draw_CARGANDO (0.4F, 0,0,0,tex_numerosbolas,0xFF0000FF);

.....

Me compila bien, no me da errores, pero pantallazo negro.....

Aprovecho para preguntarte si el numero de frame a mostrar (contenido en una variable A) se puede sustituir por %A...

 

Gracias por tu paciencia

saludos

 

 

 

Imagen de wilco2009

Disculpa la tardanza en

Disculpa la tardanza en contestar, pero he estado fuera. Me lo tengo que mirar, ya que tus preguntas empiezan a ser para nota. ;-)

¿Puedes enviarme un email con el fuente y los gráficos y así me costará menos de depurar?


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

Imagen de martinezcanto

DICHO Y HECHO

Buenas

Dicho y hecho, te lo mando...

Perdoname el corregirte, pero la nota es la del maestro, en ese caso tu, y por lo que ayudas...

 

Sin duda....MATRICULA DE HONOR...

 

Agradecidos saludos

Imagen de wilco2009

Creo que tu problema era más

Creo que tu problema era más sencillo de lo que podría parecer a primera vista.

La funciones Drawtile son funciones 2D por lo que al intentar utilizarlas después de cambiar a 3D se cuelga el programa.

Pasa el bloque compo inmediatamente después GRRLIB_2DMode y todo pasará a funcionar sin más.

Por cierto, muy bonito el programa. Te está quedando genial. Espero que te presentes al Scenery del 2012.

 


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

Imagen de martinezcanto

NO DOY

Buenas

No doy con la tecla... he puesto el 2D de varias maneras y ahora la pantalla me sale azul, se pierde el fondo, sale la bola, pero como si fuese una plastilina de colores entremezclados....

Si puedes y deseas mandame por otro medio la correccion, para seguir captando el tema...

Abusando de tu paciencia y compromiso con el curso, mirame el tema del fallo de los numeros que te comente (el 1 no aparece y el 2 carga cosas raras).

De nuevo, gracias

 

Imagen de wilco2009

Al fin he encontrado un rato

Al fin he encontrado un rato para ver tu problema.

Tenías un error en el InitTileSet. Esta función espera el tamaño de un tile individual y no de la imagen completa. La llamada correcta a la función sería:

        GRRLIB_InitTileSet(tex_numeros, 16, 28, 48);

Por otro lado el compo debe ir al principio del while para no interferir con el resto de las funciones de dibujo:

Sonidos();	
 
GRRLIB_2dMode();	
GRRLIB_CompoStart();
		GRRLIB_DrawTile(72,170, tex_numeros, 0, 3, 3, 0xFFFFFFFF, '4'-48);
		GRRLIB_DrawTile(232,170, tex_numeros, 0, 3, 3, 0xFFFFFFFF, '4'-48);
		GRRLIB_DrawTile(392,170, tex_numeros, 0, 3, 3, 0xFFFFFFFF, '4'-48);
		GRRLIB_DrawTile(552,170, tex_numeros, 0, 3, 3, 0xFFFFFFFF, '4'-48);
GRRLIB_CompoEnd(0, 0, tex_numerosbolas);

En cuanto al problema que tienes con el 1 y el 2 no lo entiendo ya que a mi me funciona bien. Quizás deberías hacer un clean (Alt+2) para que te haga un build completo desde cero.

Si eso no funciona prueba a reinstalar devkitpro y GRRLIB.

Te envio la solución por email.


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

Imagen de martinezcanto

REPASARE

Buenas

Repasare lo que me has mandado, ya que cuando lo ejecuto me siguen saliendo cosas raras..

Respecto al tema del 1 y el 2, he eliminado la carpeta build completamente, he ejecutado el makefile y me la ha vuelto a crear. Antes de esto reinstale el DEVkitpro y GRRLIB. Pero me sigue saliendo el mismo fallo.

Aun no lo he probrado, pero como te comente es valido realizar esto (siendo A una variable entera)

GRRLIB_DrawTile(72,170, tex_numeros, 0, 3, 3, 0xFFFFFFFF, %A-48);

 Agradecido, saludos

 

Imagen de wilco2009

El tema de utilizar una

El tema de utilizar una variable entera en el drawtile es perfectamente válido pero tienes que tener en cuenta que cuando hacíamos '1'-48 realmente estábamos conviertiendo un código ASCII en el correspondiente número de orden dentro de la secuencia de tiles.

Es decir, el código ASCII del número '0' es el 48, por lo que '0'-48 dará exactamente 0, osea el primer tile de la secuencia.

Sí utilizas directamente una variable entera, los valores válidos serán de 0 a 9 y no deberás restarle nada. DrawTile no te permitiría mostrar el número 45 a no ser que utilizara dos instrucciones DrawTile (una para el 4 y otra para el 5).

La sugerencia de usar DrawTile en el bloque compo en lugar de GRRLIB_Printf, era para que te permitiera girar el número si hiciera falta, pero en el caso de no ser necesario es mucho más sencillo usar la segunda, ya que te permitirá imprimir el número sin más conversiones adicionales.

En cuanto al tema de que te salgan cosas raras, te enviaré el .dol para que lo pruebes ya que aparenta ser un problema de instalación, ya que a mi me funciona bien.


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

Imagen de martinezcanto

SOY UN POCO

Buenas

Soy un poco cabezón, es uno de mis defectos, e intentaré conseguir lo del drawtile..... aunque como tu, estoy empezando a pensar que mi ordenador tiene un problema, ya que aunque desinstale e instale sigue saliendo la bola de plastilina...

Por otro lado, por si esto puede con mi paciencia... y como te he comentado en otras ocasiones, lo que estoy preparando depende de un numero aleatorio, he visto que tu recomendacion

void GRRLIB_Printf ( const f32 xpos, const f32 ypos, const GRRLIB_texImg *tex, const u32 color, const f32 zoom, const char *text, ... ) Mostrar una cadena de textoParametros xpos: Posición de la coordenada x de la esquina superior izquierda. ypos: Posición de la coordenada y de la esquina superior izquierda. tex: La textura que contiene el juego de caracteres. color: Color en formato RGBA. zoom Factor de aumento o decremento del tamaño del texto. text Texto a mostrar. ... Argumentos opcionales.establece el texto a mostrar, y erre que erre, ese texto puede ser %A?Por cierto, lo de poner el compo al principio del while tambien lo habia probado.Ahora no se porque sale esto asiiiiiiSALUDOS
Imagen de martinezcanto

SIGO PROBANDO COSAS

Buenas

Sigo probando cosas, y pretendo utilizar el GRRLIB_Printf, con intencion de cargar menos texturas, y suavizar el peso del programa que ya va por 8M.

 

Asi que practicando he escrito esto

GRRLIB_Printf(305,65, numero, 0x0000FFFF,3,"500");

y me sale la imagen 1

despues esto


GRRLIB_Printf(305,65, tex_numero, 0x0000FFFF,3,"500");

 y me sale la imagen 2

 y por ultimo esto

GRRLIB_Printf(305,65, tex_numeros, 0x0000FFFF,3,"500");

y me sale la 3

 

No se como colocar las imagenes, me da error, te las mando por otro medio... ( si tu puedes ponlas, asi las veran los demas)

El ultimo caso es cuando he cargado la textura creada con el compo, el caso primero es usando la misma textura que cargo para los textos ( en este caso numeros) mediante el GRRLIB_PrintfTTF...

Por otro lado solo me funciona si pongo un texto, es decir, "texto", si utilizo una variable poniendo el % delante no me la reconoce...

 

Esperando tus consejos, gracias

 

 

 

 



Imagen de wilco2009

GRRLIB_Printf sólo funciona

GRRLIB_Printf sólo funciona con fuentes bitmap, por lo que la fuente TTF no te funcionará aquí. Ese es el motivo de los errores que te da en la imagen 2.

En el caso de la tercera imagen, simplemente te está diciendo que no está declarada la variable tex_numero.

Si quieres imprimir números, puedes usar la función GRRLIB_Printf como si de printf se tratara. Funciona de la misma manera.

Es decir el sexto parámetro siempre debe ser obligatorioramente una cadena, pero dicha cadena puede contener caracteres de formateo e incluir el contenido de las siguientes parámetros que son opcionales.

Ejemplo, para escribir tu número 500 que tienes en la variable entera A, puedes hacerlo de la siguiente manera:

int A;

GRRLIB_Printf(305,65, numero, 0x0000FFFF,3,"Este número es un entero: %d", A);

Aquí tienes un enlace donde explican el formato:

http://www.cplusplus.com/reference/clibrary/cstdio/printf/


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

Imagen de martinezcanto

NO ME HAGAS

Buenas

No me hagas reir por favor, presentar esto en un concurso, pero si es muy muy basico, tanto es asi que es la version 0.0

Agradezco tus piropos, siempre son bien recibidos cuando son del maestro, y eso quieras o no me motiva a mejorarlo... Como dije en otra ocasion paso atras para seguir adelante, lo de la iluminacion me trae de cabeza y aun no consigo aplicarlo como deseo.

Sabia que tu con un simple vistazo verias donde estaba el fallo de la pantalla negra, aun me queda mucho que aprender, y eso que en otras partes del programa si tenia claro el 2D y el 3D.

Pongo en marcha esto y ... seguire trabajando en mis ideas.... y perdoname, pero seguro que tambien te doy trabajo para ayudarme en mis bloqueos.

Se que estas dedicado a otras cosas, pero seguro seguro que tienes mi voto en lo proximo que te presentes...

Agradecido como siempre, el Peñazo.

 

 

Imagen de martinezcanto

SE QUE NO TIENE NADA QUE VER

Buenas

Se que no tiene nada que ver con los tutoriales, tampoco se si a alguien mas le ocurre, pero ...

Resulta que desde casi el principio que retome mis deseos de programar para wii, cada vez que inicio el ordenador, e intento compilar alguna modificacion me sale un mensaje que me dice...

Cannot create temporay file in c:\documents and settings\Jose Martinez\configuracion local\temp\: No such file or directory

This application has requested the Rutime to teminate it in an unusual way.

Despues me dice que contacte con el equipo de la aplicacion....

Lo soluciono desistalando el Devkitpro, y volviendolo a instalar, y solucionado...

He mirado el makeador y el makefile, pero no veo ningun sitio donde haga alusion a esa carpeta temp...

Sabes porque me ocurre?

Es que al final me cansa un poco desintalar, y volver a instalar todo..

Se que tus conocimientos y sabiduria me orientaran...

Gracias

Imagen de wilco2009

Parece que tienes un problema

Parece que tienes un problema con la variable de entorno temp.

Esto probablemente será porque excedes la longitud máxima que puede contener.

Reinicia el PC para reproducir el problema, abre una consola de comandos (Inicio\Ejecutar\cmd) y teclea el comando "set > c:\devkitpro\set.txt".

Te creará un archivo llamado set.txt en la carpeta c:\devkitpro. Abrela con el bloc de notas y pega el contenido del archivo aquí en el foro para que pueda identificar lo que ocurre.


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

Imagen de martinezcanto

ESTO ES LO QUE

Buenas

Esto es lo que pone

ALLUSERSPROFILE=C:\Documents and Settings\All Users
APPDATA=C:\Documents and Settings\Jos‚ Mart¡nez\Datos de programa
asl.log=Destination=file;OnFirstLog=command,environment
CLASSPATH=.;C:\Archivos de programa\Java\jre6\lib\ext\QTJava.zip
CLIENTNAME=Console
CommonProgramFiles=C:\Archivos de programa\Archivos comunes
COMPUTERNAME=JOSE
ComSpec=C:\WINDOWS\system32\cmd.exe
DEVKITARM=/c/devkitPro/devkitARM
DEVKITPPC=/c/devkitPro/devkitPPC
DEVKITPRO=/c/devkitPro
DEVKITPSP=/c/devkitPro/devkitPSP
FP_NO_HOST_CHECK=NO
HOMEDRIVE=C:
HOMEPATH=\Documents and Settings\Jos‚ Mart¡nez
LOGONSERVER=\\JOSE
NewEnvironment1=C:\Archivos de programa\ATI Technologies\ATI.ACE\
NUMBER_OF_PROCESSORS=2
OS=Windows_NT
Path=c:\devkitPro\msys\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\Archivos de programa\QuickTime\QTSystem\;C:\Archivos de programa\Pinnacle\Shared Files;C:\Archivos de programa\Pinnacle\Shared Files\Filter
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH
PROCESSOR_ARCHITECTURE=x86
PROCESSOR_IDENTIFIER=x86 Family 6 Model 15 Stepping 11, GenuineIntel
PROCESSOR_LEVEL=6
PROCESSOR_REVISION=0f0b
ProgramFiles=C:\Archivos de programa
PROMPT=$P$G
QTJAVA=C:\Archivos de programa\Java\jre6\lib\ext\QTJava.zip
SESSIONNAME=Console
SystemDrive=C:
SystemRoot=C:\WINDOWS
TEMP=C:\Documents and Settings\Jos‚ Mart¡nez\Configuraci¢n local\Temp
TMP=C:\Documents and Settings\Jos‚ Mart¡nez\Configuraci¢n local\Temp
USERDOMAIN=JOSE
USERNAME=Jos‚ Mart¡nez
USERPROFILE=C:\Documents and Settings\Jos‚ Mart¡nez
VS100COMNTOOLS=C:\Archivos de programa\Microsoft Visual Studio 10.0\Common7\Tools\
WIILOAD=tcp:10.10.1.34
windir=C:\WINDOWS
__COMPAT_LAYER=EnableNXShowUI

 

Ni idea que quiere decir, seguro que tu ves algo...

Gracias

 

Imagen de wilco2009

Pienso que el problema está

Pienso que el problema está en que a devkitpro no le gustan los acentos de tu nombre. Intenta cambiando la variable de entorno Temp de la siguiente manera:

  1. crea una carpeta c:\temp en caso de que no exista.
  2. Haz clic con el botón derecho del ratón sobre el icono de "Mi PC" y selecciona propiedades.
  3. En la pestaña opciones avanzadas pincha en el botón variables de entorno.
  4. Pincha sobre la variable TEMP y pulsa editar.
  5. Donde pone valor de la variable borralo todo y pon c:\temp

Y después prueba a volver a compilar.


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

Imagen de martinezcanto

SIN PALABRAS

Buenas

Me dejas sin palabras, no solo nos enseñas a programar, nos orientas, nos guias...

Ademas nos resuelves los problemas

Agradecido, segui paso a paso las indicaciones y a la primera, solucionado el tema del TEMP

Nuevamente, gracias

 

Imagen de martinezcanto

EL VOID

Habia escrito una pregunta que ya te habia hecho, por eso la he borrado

 

Disculpa 

Imagen de martinezcanto

Sigo trabajando

Buenas

Bueno el tutorial me tiene enganchado, ya paso a paso he llegado al 11º, aunque algunos elementos no los he aplicado...

Quizas por eso mi pregunta de hoy..

He creado una esfera, y dentro de la misma otras moviendose...

Para la esfera esterior he creado un png, pero cuando le aplico la trasparencia se difumina su color, pero no consigo que se vean las esferas de dentro...

 

¿ es necesario aplicar caraceristicas de iluminacion para darle trasparencia a la bola exterior? he probado a cambiar los valores (dos ultimos digitos del color) pero la bola exterior parece desaparecer y no ser transparente...

 Como te decia no he aplicado estos conceptos del foco apuntando etc

Saludos

 

Imagen de wilco2009

La respuesta es sí. Necesitas

La respuesta es sí. Necesitas utilizar luces para hacer lo que quieres hacer.

Debe ser suficiente si aplicas una luz ambiente y una difusa:

        GRRLIB_SetLightAmbient(0x333333FF);
        GRRLIB_SetLightDiff(0,(guVector){0.0f,0.0f,0.0f},20.0f,1.0f,0x00FFFFFF);
 

Puedes utilizar como referencia el ejemplo que viene con GRRLIB (3d_light2)

Si lo quisieras que sólo algunas partes fueran completamente transparentes mientras que otras fueran opacas podrías utilizar un png como textura de la esfera exterior.

En el código fuente de este tutorial tienes un ejemplo de esto último en el anillo de saturno.

 


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

Imagen de martinezcanto

yo y mis preguntas o mis preguntas y yo

Gracias

sabia que saltandome cosas los efectos no serian lo mismo..

Otra pregunta...

Cuando usas void ..... entiendo que es (espero no equivocarme) la llamada a una subrutina...

Pensando asi, esta es mi pregunta... poniendo void loquesea y sus parametros puedo hacer una subrutina del tipo que sea, por ejemplo, la eleccion de un numero aleatorio...( claro esta con la libreria correpondiente)..

 

Se que eres sabio y me sacaras de dudas

Como siempre, agradecido de antemano

 

Imagen de wilco2009

A diferencia de otros

A diferencia de otros lenguajes, en C no hay distinción entre funciones y procedimientos (o subrutinas en Basic). En C todo son funciones.

La sintaxis de una función en C es:

tipo nombredefunción(lista de parametros){

.....

.....

}

Cuando quieres definir una función en C que no devuelve nada, tienes que decirle que es de tipo void.

Ejemplo:

void PrintHola(){

printf("Hola");

}

En el caso de que quieras que la función devuelva un valor de un tipo concreto, debes declarar el tipo que debe devolver en lugar del void y en algún punto dentro de la función llamar a la sentencia return para devolver el valor.

Ejemplo:

int suma(int a, int b){

    return a+b;

}

En el primer caso la llamada a la función no devuelve valores por lo que no puede ser asignada en ninguna expresión:

PrintHola();

En el segundo caso podemos utilizar el resultado de la función en una expresión cualquiera:

c = suma(a,b);


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

Wire3D

genial los tutos wilco ;)

de casualidad ya viste el demo de "Wire3D" en el Hombrew browser?

es de lo mejor que he visto en la scene.

espero que pronto estuvieras trabajando con enstas librerias.

la verdad me dejo sorprendido :)

Imagen de wilco2009

La verdad es que wire3d

La verdad es que wire3d  tiene muy buena pinta. No tardare en probarlo.

gracias por seguir el curso.

GRRLIB

Buenas noches wilco.

 

En primer lugar darte las gracias por tan magnífico curso que estas realizando, no tiene desperdicio alguno y decirte que he corrido la noticia y he informado sobre el curso entre personas que conozco y el número ha aumentado considerablemente.

Me han comentado que te manifieste su agradecimiento a través de mí y así lo hago.

El curso es genial, merece la pena la versión impresa que te comenté que estabamos haciendo y algún profano tamién se ha interesado.

También decirte que con el descanso que te vas a tomar, vas ha dejar huérfanos a más de los que tú pensabas, pero hacieno uso comprensible, entendían que bien merecías un descanso, pero me han incidido en que fuera pequeño ¿eh? y que no te escaparás que seguirían esperando para cuando volvierás con ganas de terminar.

También comentarte, siendo un temas a parte, que bajo mi punto de vista cuando el curso lo acabarás le enriqueceria mucho el hecho de programar como final un juego completo haciendo uso de toda la teoría vista en el, un juego completo no quiere decir que sea un juego comercial o brutal graficamente hablando etc, pero si un juego en el que se viese todo el trabajo desde el punto de vista del lector del curso.

Y por último y disculpa por el pedazo texto que me ha salido.

Hacer quiza un pequeño faq de posibles problemas que creas conveniente destacar durante el desarrollo de un juego que se pueda tener.

Y de verdad es un placer poder seguir el curso, aún habiendome saltado las lecciones 10 y 11 , que aún no he podido(gran tirón de orejas para mí), pero que ahora por las noches es cuando disfruto leyendote tranquilamente con el ordenador.

Además algunas cosas las estoy adaptando a ensamblador y también algunos modulos de GRRLIB que creo pueden serme útiles.

Un saludo.

 

cjv99.

 

Imagen de wilco2009

La verdad es que no tienes

La verdad es que no tienes que disculparte por la longitud del texto, ya que siempre es grato leer tus palabras de ánimo.

Después del resultado del último tutorial, quería aprovechar para añadir algunos modelos, como diferentes tipos de naves espaciales, y efectos de disparo, para poder completar algo parecido a un juego.

Hace algunos meses hice una biblioteca que leia .obj y con ella empecé (nunca terminé) un juego cuyo protagonista principal era Samus de Metroid. Quizás era un poco complejo ya que los personajes tipo humanoide necesitan movimientos complejos, y con Samus más o menos los terminé, pero habían muchos personajes por desarrollar. Hay que descomponer los personajes en trozos que puedan moverse de manera individual y luego programar los movimientos permitidos.

Quería aprovechar ese código para incorporar las naves espaciales (más sencillas de implementar), y desarrollar algunos efectos como rayos laser y explosiones.

Si me encuentro con ánimo, puedo ir haciendo algún capítulo más según vaya desarrollando el juego, aunque había pensado intentar acabarlo para presentarlo al scenery 2012. 


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

Imagen de JAVIenigma

Wilco, sin palabras. Vaya

Wilco, sin palabras.

Vaya currada de tutoriales que te estás pegando.

Un saludo!

Imagen de wilco2009

Gracias Javi. De todas

Gracias Javi.

De todas formas, creo que voy a descansar de tutoriales una temporada para ver si hago algún juego.

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.