Está en la página 1de 42

Curso de Programacin en Wii By Hermes

NOTA: Este curso originalmente fue iniciado en entuwii.net hace ya bastante y utiliza una librera libogc antigua, por lo que algunos mtodos podran cambiar (por ejemplo, el acceso a libfat ha cambiado) Sin embargo, siempre que useis el instalador que recomiendo y las libreras precompiladas, no deberais tener problemas para poder usarlo "No importa si sabes mucho o sabes poco. Lo nico que me importa es si eres capaz de asimilar lo suficiente para conseguir tu objetivo" - Hermes

Introduccin
Este es un curso de programacin dedicado obviamente a la Wii. La idea es que una persona con conocimientos de C mas o menos vago, pueda programar para dicha consola, facilitando en lo posible la entrada a este mundillo, con un poco de voluntad por vuestra parte. Si, amigo, si has entrado aqu pensando que yo te voy a ensear a programar para Wii, te equivocas: eres t el que tiene que hacer el esfuerzo de aprender y yo solo puedo allanarte el camino en lo posible, pero quiz yo peque de estar demasiado metido en la materia y pueda dar cosas por obvias, incluso un exceso de informacin, mientras que t puede que te rindas a las primeras de cambio o te animes mucho al principio, pero luego dejes el tema apartado debido a que la programacin no es un camino de Rosas o mejor dicho, es como una Rosa con espinas, donde te vas pinchando todo el rato con el tallo hasta acceder a la flor. Curiosamente, conozco muchos programadores que opinan que para hacer determinadas tareas, C no es el lenguaje mas conveniente y que no tienen pegas en aprender diferentes lenguajes de programacin si hace falta, pero sin embargo ponen muchas pegas a aprender cosas de una determinada maquina que es muy diferente a lo que ellos suelen estar acostumbrados. Vamos que los sacas de Windows y se cagan, porque ya no es que todo sea nuevo, si no que no estn acostumbrados a adaptarse al hardware y eso es un hndicap. Desde un punto de vista prctico, todo lo que yo te pueda ofrecer aqu seguramente no lo puedas utilizar profesionalmente de una forma directa... pero no os olvideis que la programacin es un proceso creativo y que requiere una buena capacidad de adaptacin. Por tanto Que mejor aprendizaje que tratar con maquinas como las Consolas, que disponen de un Hardware relativamente limitado y tan diferente con los ordenadores de hoy da? Divertirse mientras aprendemos y hacemos algo diferente? Yo creo que no tiene precio e incluso si eres un programador profesional (yo soy un aficionado! ) a lo

mejor te sirve para recuperar aquello que te motiv a elegir esta profesin y que has perdido con la rutina. En fin, que la idea es proporcionarte una base para que puedas entretenerte haciendo programas en Wii, pero el uso que tu quieras hacer de ello, depende de t

Comenzando por el Principio


Lo primero que debes hacer es descargarte DevkitPro: http://sourceforge.net/project/showfile ... _id=160396 Descargaros la version 1.4.7 del Instalador. Si estas utilizando otras plataformas, mirate esto: http://wiki.devkitpro.org/index.php/Getting_Started NOTA: El instalador est desactualizado (y el curso tambien, por culpa de libfat) y os crear conflictos. Mira ste hilo que te ayudar a conseguir lo ficheros necesarios: viewtopic.php?f=165&t=1257837&p=1716884329#p1716884329 Bajo Linux marcan os puede echar una mano y seguramente otros usuarios del foro: Yo utilizo Windows XP como base de trabajo y ya soy muy viejo para cambiar Bien, una vez instalado DevkitPro (instalalo entero, yo lo tengo as en c:/devkitPro), tendrs un entorno basado en MinGW que te permitir programar para diversas consolas y podrs usar el famoso "make" (luego explico algo mas sobre este tema). Adems, se incluye una utilidad por si necesitas un Editor, llamada Programmer Notepad o algo similar, que tal vez os resulte util. Ahora necesitas mis parches: http://mods.elotrolado.net/~hermes/curs ... Hermes.rar Esto deberis copiarlo para sustituir las libreras y los ficheros de cabecera de libogc donde tengis instalado DevkitPro, sobrescribiendo todo lo que sobre escriba. Se tratan de las libreras ya compiladas con lo ultimo del git de hackmii y que incluye cambios que no encontrareis en ninguna parte. Y por ultimo, os he subido algunos ejemplos hechos por mi: http://mods.elotrolado.net/~hermes/curs ... amples.rar Esto lo podis instalar en devkitPro/examples/wii/hermes por ejemplo, para que tengis una referencia. Algunos entenderis bastantes cosas vindolos, porque no hay mejor maestro que disponer de un cdigo que muestre como se hacen las cosas. Ah se incluye los fuentes de la screenlib, que es la base grfica que vamos a utilizar en este cursillo, ejemplos que muestran el uso de las fuentes de letra o el ultimo ejemplo que muestra como simular

una batera usando el Nunchuk y el Wiimotes como baquetas Por cierto, en este ultimo ejemplo, quiz os preguntis Como diablos se controlan los acelermetros? Pues macho, yo tengo la misma idea que tu sobre eso, pero si sacas los valores a pantalla, puedes ver que si pones el mando en Vertical hacia arriba, te da unos valores distintos, que si los bajas y hay cosas que se sobreentienden como "gforce" (fuerza G). No debes caer en el error de pensar que yo lo se todo, porque no es verdad: puede que yo haya avanzado ms que t y eso me permita poder explicarte mis experiencias y servirte de gua, pero la experimentacin es algo que vas a tener que usar por norma. Por ejemplo, preparando este ejemplo, pude darme cuenta de que hay un fallo en la librera que hace que no pueda usar los valores gforce u orientacion (orient) del Nunchuk, si no nicamente, los valores de aceleracin en RAW. Asi que me toc hacer una apa para que el Nunchuk fuera operativo y mi batera no se quedara coja (otro se hubiera rendido en vez de adaptarse a lo que hay :evil:) Por ultimo y en el apartado de las descargas, quiz os interese contar con: SpriteGen: editor de Sprites y mapas de Sprites Es una utilidad de Windows pero rula desde Wine, segn me contaron (al menos yo le puse empeo ) Creador de fuentes Convierte fuentes ttf tratando de adaptar el tamao para exportalas al formato que usa la screenlib Audacity Yo lo uso a menudo para exportar y preparar efectos de sonido a formato RAW 8 bit con signo, con la ASNDLIB Dentro de los ejemplos que he subido, os he incluido un par de utilidades: filetochar que es una potente utilidad que convertir archivos binarios a C usando varios tipos de variables y aadiendo alineacin, etc y Wiiload, la utilidad complemento del HBC para poder subir los ejecutables mediante WIFI. Tambin tenis un directorio de recursos con sonidos y algn grfico (mirar resources). Hay un ejecutable .bat que os muestra el uso de filetochar de modo prctico.

Cosas que debes saber del entorno de programacin


Los Makefile: Si vienes de plataformas MSDOS/Windows, sabrs lo que es un .bat. Pues el Makefile es como uno de esos viejos archivos de procesamiento por lotes, pero mas a lo bestia, por as decir . Lo que aqu tienes que saber, es que dispones de una serie de plantillas de ejemplo y no importa que no conozcas todos los detalles: solo importa que sepas lo suficiente para poder aprovecharlo a tu favor. Por defecto los Makefiles que vienen en DevkitPro, tratan de compilar todos los archivos C que estn dentro del subdirectorio "source" y el ejecutable toma el nombre del directorio en el que se encuentra el Makefile.

Esto es, si tenemos un directorio llamado examples y dentro de el, el Makefile y un subdirectorio source conteniendo distintos archivos C, si desde linea de comandos hicieramos "make" el resultado sera que se compilaran todos los archivos dentro de source y obtendriamos un archivo examples.dol otro llamado examples.elf (que no deberamos incluir en la release) y nos creara un subdirectorio build conteniendo los objetos de dicha compilacin. Estos objetos dentro de build se utilizan para compilar rpidamente, evitando recompilar los archivos que no hemos cambiado. Por lo tanto, si eliminamos el subdirectorio build podremos recompilar limpiamente todo de nuevo. A veces es conveniente hacerlo para evitar ciertos problemas curiosos. Sin embargo, en los Makefile suele existir una orden especifica que nos permite limpiar de objetos de una forma rpida. Si en linea de comandos ponemos "make clean" se ejecutar una seccin de cdigo (si existe!) que tratar de borrar todos esos objetos As pues: Cdigo: Seleccionar todo
make -> se utiliza para compilar make clean -> se utiliza para borrar el resultado de las compilaciones

Una forma de acelerar esto desde Windows, sera crear un fichero bat como los que yo uso en mis ejemplos: MakeIt.bat make pause

"pause" se utiliza para que la ventana no se cierre automticamente y quede a la espera de que se pulse una tecla. Por lo general, es mejor idea utilizar un IDE que sea capaz de hacer "make" y capturar el resultado a una ventana del programa. Esto suele tener la ventaja aadida de que si se producen muchos errores, bajo linea de comandos se suele perder parte del listado y que algunos IDE permiten ir directamente a la linea causante del problema pinchando en el error. En mi caso, me resulta mas cmodo trabajar con ficheros .bat pues no suelo cometer errores que desaconsejen mucho esta practica y por otro lado, cuando llevas demasiadas cosas a la vez puede ser mas engorro preparar un proyecto que tirar de un .bat "warro" Interioridades de un Makefile: Nos adentramos en tierras Marcianas nuestros Makefiles. para aprender a controlar algunas cosillas de

Si miris algunos de mis ejemplos, quiz habris notado que mis Makefile compilan solo los ficheros que necesito y que incluso se permite compilar ficheros que estn fuera del subdirectorio source tomndolos desde el directorio resources Como diablos se consigue eso? Pues, si miramos en las "tripas" del Makefile, vemos cosas como: Cdigo: Seleccionar todo
TARGET := $(notdir $(CURDIR)) BUILD := build SOURCES := source RESOURCES := ../../resources DATA := data INCLUDES := include ../resources

Esto son como las variables de entorno que se pueden usar en Windows y en este caso estn indicando: - TARGET: Este es el nombre del fichero que queremos obtener, el codigo "raro" que le sigue, le est diciendo que pille el nombre de actual directorio pero sin ruta. Esa es la razn de que si compilo en example1, obtengo un example1.dol - BUILD y SOURCES son la razn de que se compile lo que hay dentro del subdirectorio source y que los objetos se almacenen en el subdirectorio build - RESOURCES : Es una variable que creo yo para poder compilar los fuentes externos. El .. implica subir un directorio y dado que al coger los fuentes, el compilador estar en el directorio source ../../ nos sita fuera del directorio donde estamos compilando y ../../resources nos mete dentro del directorio resources de mis ejemplos. - DATA: Por lo general devkitPro utiliza este subdirectorio (que deberiais crear vosotros) para tratar con ciertos tipos de datos. Yo no lo uso - INCLUDE: esta variable os permite especificar varios destinos donde encontrar ficheros .h necesarios para vuestra aplicacin. En este caso, sealan a un hipottico subdirectorio include y a ../resources, para que encuentre los ficheros .h de los datos que tenemos en resources Bien, bajando un poco vemos: Cdigo: Seleccionar todo
LIBS := logc -lm -lfat -lasnd -lmodplay -lwiiuse -lscreen -lbte -lz -

Aqu es donde especificamos las libreras que vamos a utilizar: -l indica una librera con una denominacin estndar que vamos a encontrar en este caso en devkitPro/libogc/lib/wii . Asi -lfat en realidad hace referencia a libfat.a y -lasnd a libasnd.a.

Tambin podemos pasar directamente libreras que se sitan en otra parte. Por ejemplo: Cdigo: Seleccionar todo
LIBS := ../../tremor/libtremor.a -lfat -lasnd -lmodplay lwiiuse -lscreen -lbte -lz -logc -lm

Eso hara referencia a la libreria libtremor.a que se encuentra en el directorio "tremor" que es "hermano" del directorio de la aplicacin que estamos compilando. Un detalle importante: El orden de colocacin de las libreras, es importante. Hay libreras que usan funciones que proceden de otras libreras y que si no estn correctamente ordenadas, se produce un error. La forma de ordenarla, es la siguiente: aqu el ultimo elemento, es el primero Es decir, que para construir nuestro ejecutable, libm.a (-lm) se utiliza antes que libtremor.a. Bueno, aclarado esta parte, bajemos un poco ms Cdigo: Seleccionar todo
#CFILES := $(dir)/*.c))) CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard main.c $(RESOURCES)/econmy.c

Aqu hay varios detalles importantes: Dentro del Makefile, el smbolo # se utiliza para comentar, por lo que el resto de la lnea es ignorado. Si echis un ojo a esa linea "rara" se ve que es la responsable de que se compilaran todos los ficheros .c presentes en el subdirectorio source En lugar de eso, la he reemplazado por los ficheros a compilar. El primero es main.c que est dentro de source y el segundo emplea el contenido de la variable RESOURCES para componer la ruta donde encontrar "econmy.c" que es el fichero MOD que usa el primero de los ejemplos que os he pasado. Como veis, la forma de usar el contenido de la variable RESOURCES es $(RESOURCES). Y si quiero aadir mas ficheros .c que es lo que tengo que hacer?. Pues aadirlos a la lnea, como estis viendo. Esta forma de trabajar es conveniente, por que por ejemplo, si usara la forma "automatica" que utilizan los ejemplos de DevkitPro, aadira todos los ficheros C presente en RESOURCES, cuando solo necesitamos uno. Bueno, hemos visto cosas bastante importantes, pero como puedo hacer que el Makefile interprete otras ordenes? Por ejemplo, en algunos de mis programas utilizo el Makefile para ejecutar el programa

haciendo "Make run" Pues bien imaginate que al principio aades una variable como esta: export WIILOAD =tcp:192.168.2.14

Wiiload necesita una variable de entorno llamada WIILOAD con la IP de la Wii para poder pasar el ejecutable al HBC, al aadir la palabra "export" el make se ocupar de ello, haciendo visible esa variable para todos los programas Ahora nos vamos mas abajo en el Makefile y encontramos este cdigo: run: wiiload $(OUTPUT).dol

Es decir: que mi Makefile tiene la posibilidad de ejecutar "make run" pero necesitis dos cosas: aadir la variable de entorno WIILOAD tal y como os explico (cambiando la IP por la de vuestra Wii obviamente) y por otro lado, teneis que aadir wiiload.exe en alguna ruta sealada por el PATH. Un buen sitio sera C:\devkitPro\msys\bin Tambien teniendo en cuenta que est en la carpeta "util" que es un directorio "hermano" a la aplicacin, podrias utilizar esta frmula: run: ../util/wiiload $(OUTPUT).dol

En mis ejemplo, uso ejecutar.bat que directamente, crea la variable de entorno y pasa un parmetro para evitar que mis aplicaciones, al no ver ningn parmetro, interpreten que no se puede volver al cargador y al hacer reset, se vuelva al men del sistema. Si vosotros quereis aadir otros usos. pues por ejemplo, podes aadir: convertir: [tab]../util/filetochar <loquesea>

Ntese que [tab] representa al pulsar el tabulador para separar el comando. Luego la cosa sera hacer "make convertir" para que hicera esa tarea en concreto

Cosas que debes saber de la programacin bajo Wii


"Programar en C, no es programar en BASIC" Suena de perogrullo, pero esto tiene sus implicaciones. El Endian (de los coj....)

Lo primero que debes saber sobre Wii es que utiliza "Big Endian" para almacenar los datos. Los procesadores Intel usan justo lo contrario "Litte Endian" Eso tiene que ver con la forma de almacenar el dato. Cuando un dato ocupa mas de un byte, existen dos formas de organizarlo de mayor a menor peso, que eso se conoce como Big Endian y de menor a mayor peso, que se conoce como Little Endian. En plataformas Intel se utiliza la ordenacin de menor a mayor (Little), asi por ejemplo, para almacenar en memoria el valor 256 en decimal, el primer byte sera 0 y el segundo 1 (1*256+0= 256). Sin embargo, en la Wii sera al reves, primero el 1 y despues el 0!. Con lo cual si por ejemplo, quieres importar un BMP, o un WAV, pues te toca cambiar el orden de los bytes de los datos. Esa es una de las razones por las que es util exportar los datos a C en RAW, puesto que si importas una lista de numeros de 16 bits, no se ve afectada por la forma de almacenar el numero en memoria (es decir, 32767 es el mismo numero en ambos sistemas, pero cambia la forma en que ese numero se almacena en memoria o dentro de un fichero, por ejemplo). Siempre que importes datos desde una plataforma Intel, tendrs que tener en cuenta el tipo de Endian que usan los datos. Para convertir el Endian, puedes usar estas funciones: Cdigo: Seleccionar todo
unsigned short inline swap_16(unsigned short a) // para numeros de 16 bits { return( (a<<8) | (a>>8)); } unsigned inline swap_32(unsigned a) // para numeros de 32 bits { return( (a<<24) | (a>>24) | ((a<<8) & 0xff0000) | ((a>>8) & 0xff00)); }

Los tipos de datos En Wii se usan los tipos de siempre, pero ademas aade otros tipos que son abreviaciones, de forma usual: char -> s8 -> entero de 8 bits con signo: rango -128 a 127 unsigned char -> u8 -> entero de 8 bits sin signo: rango 0 a 255 short -> s16 -> entero de 16 bits con signo: rango -32768 a 32767 unsigned short -> u16 -> entero de 16 bits sin signo: rango 0 a 65535 int -> s32 -> entero de 32 bits con signo: rango -0x80000000 a 0x7fffffff

unsigned int -> u32 -> entero de 32 bits sin signo: rango 0 a 0xffffffff long -> s32 -> lo mismo que int unsigned long -> u32 -> lo mismo que unsigned int long long -> s64 -> entero de 64 bits con signo: rango -0x8000000000000000 a 0x7fffffffffffffff (peacho numero ) unsigned long long -> u64 -> entero de 64 bits sin signo: rango 0 a 0xffffffffffffffff (peacho numero x2 ) float -> f32 -> Numero con coma flotante de 32 bits double -> f64 -> Numero con coma flotante de 64 bits

Tambien podeis ver formas como vu16 que se refieren a un tipo "volatile" y otros tipos predefinidos en devkitPro/libogc/include/gctypes.h La alineacin de los datos Esto es algo que tenis que, necesariamente, tener en cuenta en maquinas como Wii y es que su procesador, adems de manejar los tipos que he listado arriba, necesita almacenarlos en memoria alineados con respecto a su tamao. Es decir, si estoy manejando un int el procesador solo puede leer ints o almacenar ints en direcciones alineadas a 4 bytes: 0, 4, 8, 12, 16..... En plataforma Intel estas acostumbrado a que un int, lo puedes guardar en direcciones desalineadas. y esto es algo que tienes que tener, muy pero que muy en cuenta. Por ejemplo, si defines un struct: struct { char lie; int repido; } manue;

Esa estructura no ser igual compilando en Windows que en Wii Por que? Pues porque en Windows la alineacin del dato no es problema, por lo que "lie" ocupar un byte y "repido" 4 bytes. Sin embargo, en Wii el compilador adems de eso, aadir 3 bytes de padding despues de "lie" para que asegurar que "repido" est alineado a 4 bytes. Esto es algo que tendris que tener en cuenta y usar inteligentemente las estructuras y gestionar las variables de distinto tamao adecuadamente: acostumbrarse a alinear los datos y paddearlos de forma adecuada, puede redundar en una mayor velocidad e incluso menor espacio.

Que sucede si el procesador lee un dato de forma desalineada al tamao del dato? Pues que se caga en vuestras muelas, asi de sencillo y vosotros os volveris locos para encontrar el bug seguramente. La alineacin de datos que usen DMA y el tratamiento de la memoria Seguramente conceptos como memoria cacheada o no cacheada os suene, asi que no me voy a extender. El tema es que el procesador trabaja sobre memoria cacheada normalmente, mientras que las DMA (perifricos que hacen uso) no. Por lo tanto, debemos tener en cuenta dos cosas: que los perifricos suelen acceder a memoria usando una determinada alineacin, al igual que pasa con los datos normales y que existe un mecanismo que se encarga de leer/escribir de la memoria para refrescar la cach. La alineacin utilizada es de 32 bytes. Para obtener memoria con esa alineacin, podemos recurrir a usar memalign(32, x) en lugar de malloc(x) para asignar memoria, pero tambien podemos declarar una variable global de forma que tenga esa alineacin: u8 buffer[1024] __attribute__ ((aligned (32))); u8 buffer[1024] __attribute__ ((aligned (32)))={0}; // si quereis fijar algun dato. Las lneas de cache usan 32 bytes, por lo tanto, es conveniente alnear el dato a 32 y asi mismo asegurar el padding (bytes de relleno): #define MAX_BYTES 17 u8 buffer[MAX_BYTES+32] __attribute__ ((aligned (32))); // forma rapida de aplicar un padding de seguridad Por ejemplo, si quieres subir una textura o un sonido con ASNDLIB, es preciso que la memoria pasada est alineada (fundamental) y con el correspondiente padding (conveniente). Si no se tienen en cuenta esas reglas, es posible que el flush de la cache, MACHAQUE datos de la pila (me ha pasado) o de otras variables (me ha pasado++). El mecanismo de la cache Existen una serie de funciones encargadas del refresco de la cache y de su escritura en memoria. Prestad atencin porque no comprender el uso de estas funciones, suele acarrear muchos problemas. Os adelanto que dentro de screenlib o de la ASNDLIB, no precisis utilizarlas, pues ya se usan de forma automtica, pero os ser til para otras cosas: -DCFlushRange(void *startaddress,u32 len); Esta funcin recibe la direccin de memoria (debera estar alineada a 32) y la longitud en bytes (debera estar paddeada, pero ya lo hace ella) de la memoria que queremos "flushear". Es decir: lo que hace es escribir los datos desde la cach a la memoria, si en la cache hay cambios que no se han reflejado en la memoria en todo ese rango. Por eso es til llamarla antes de hacer una

DMA - DCInvalidateRange(void *startaddress,u32 len); Esta funcin recibe la direccin de memoria (debera estar alineada a 32) y la longitud en bytes (debera estar paddeada, pero ya lo hace ella) de la memoria que queremos "invalidar" en la cach. Es decir, lo que hace es leer desde la memoria todo ese rango, perdiendo todos los datos almacenados en la cach. Resulta til si un perifrico nos suministra datos en memoria mediante DMA, para que la cach se refresque y al leer desde el procesador, obtengamos los datos correctos Y hasta aqu creo que tenis todo lo que necesitis saber con respecto a este tema de modo prctico

El truco del almendruco


Las excepciones son una putada, cada vez que el programa la casca, la hemos jodido, porque de donde coo viene el problema? En la libreras que os he pasado, tenis una modificacin particular ma del cdigo encargado de gestionar excepciones: En lugar de pegar el pantallazo y quedarse muerto en espera de pulses un boton del PAD de Gamecube, aqu a los 8 segundos tratar de volver al cargador. Pero tambien tiene otro truco: Existe una variable de cadena que podeis cambiar de forma externa, para que os muestre un texto al ocurrir la excepcion: extern char *debug_str; Si en una funcion aades por ejemplo: void funcion() { debug_str="funcion()"; ...... ...... debug_str="Indefinido"; }

Si dentro de funcin se produce una excepcion, se mostrar la cadena "funcion()" y sabris que la excepcin ha ocurrido en ese punto. Tambin existe un truco para conocer exactamente, en que funcin ha ocurrido la excepcin. Con debug_str tal vez puedas saber en que funcin global haya ocurrido, pero de forma imprecisa: mas bien os puede servir para acorralar el problema, si es reiterativo, pero como digo, existe otro truco: Cuando sale la pantalla de excepcin, aparece un listado de los registros y justo abajo, aparece una serie de direcciones de memoria que son la traza de donde ocurri la

excepcin. Pues bien, existe una utilidad que permite a partir de la informacin de debug que proporciona el elf, conocer a que funcin pertenece esa direccin de memoria: set path=%path%;C:\devkitPro\devkitPPC\bin powerpc-gekko-addr2line.exe -e Guitarfun.elf -f 0x805d5400 pause

Ah podeis ver un uso especfico que le tuve que dar yo de estar lleno de heridas en combate )

(uno aprende a curarse a base

********************************************************************** *****************************

La screenlib (prolegmenos)
Bueno, toca meterse en materia directa, as que primero nos alejaremos un poco para ver las cosas en perspectiva. Mi librera grfica, como todas, se basa en las GX (podis descargar un interesante documento sobre el tema aqui: http://www.entuwii.net/foro/viewtopic.p ... &sk=t&sd=a). Nuestra consola utiliza un mecanismo para pasarle los datos al GX, llamado FIFO (First In, First Out) que tiene como una de sus cualidades que permite pasar datos desalineados con su tamao (es decir, aqu es posible enviarle un int sin que est alineado a 4 con la memoria empleada en el FIFO) y que en definitiva, marca la capacidad total de instrucciones/ datos/ vrtices que podeis enviar al GX. El tamao empleado por defecto en screenlib es fijado desde FIFO_SIZE a 1MB exacto. Evidentemente, es posible usar otros tamaos mayores si es necesario, pero 1MB es un tamao mas que suficiente para muchos usos e incluso se puede provocar un salto para que el GX utilice otra memoria fuera del FIFO para desarrollar sus tareas (mi programa Guitarfun utiliza esa tcnica). Pero bueno, no vamos a perdernos en divagaciones: basta con saber que en cada frame de vdeo, disponemos de un tope de 1MB para enviar datos al GX, pero que el GX va machacando instrucciones al mismo tiempo, por lo que va dejando espacio libre para nuevas instrucciones / datos (trabaja en anillo) y que posiblemente, es algo de lo que no te tengas que preocupar. Por otro lado, conviene que sepas que las GX utilizan por hardware dos matrices de transformacin de polgonos: una de Proyeccin y otra Mundial. En screenlib la Mundial no se utiliza (se le pasa una matriz unitaria, que no provoca cambios) y la de Proyeccin es fijada para usar el mtodo Ortogrfico que es mas adecuado para usos 2D. En dicha matriz se establece un rango X/Y ajustado a la resolucin de pantalla y con un

ligero reajuste en el caso de resolucin NTSC, puesto que en mi pantalla (que es compatible con ambos formatos), aprecio un estiramiento vertical excesivo de la seal de vdeo en el modo NTSC (y de momento, nadie que tenga pantalla NTSC se me ha quejado de que se produzca una franja negra vertical, por lo que asumo que no es un problema de mi TV). Para la Z, utilizo un rango de 1000. Externamente, podis conocer el ancho y el alto de la resolucin empleada gracias a las variables SCR_WIDTH y SCR_HEIGHT. Si la resolucin es <=480 es obvio que se trata de una seal progresiva o NTSC que refrescan a 60Hz (Aqui la resolucin sera de 640x480). En modo PAL la resolucin es de 640x528 y el refresco de 50Hz. El uso de Z, como ya he comentado utiliza un rango de 1000. De forma interna, el rango sera de 0 a -999, siendo ese -999 el punto mas alejado. En mi librera puesto que tratamos con 2D, utilizo el trmino de layer (capa) puesto que en 2D se suelen utilizar varias capas de grficos y para hacer mas amigable el tema, niego el valor para hacer que el rango sea de 0 a 999 y evitar el uso de nmeros negativos. Asi pues, podemos hacer uso de 1000 capas y la capa 999 se podra considerar como el fondo de pantalla, mientras que layer= 0 es poner los graficos en primer plano. Al utilizar una proyeccin Ortogrfica, no se produce un escalado con la distancia para disminuir el tamao, tenedlo en cuenta. Transparencias y Translucidez Screenlib es inicializada para utilizar colores con Alfa por debajo de 8 (en un rango de 0 a 255) como colores transparentes, tanto en texturas como en color directo a los polgonos. Esto significa que no se debe hacer uso de los modos de color que no empleen Alpha (modo 16bits RGB565) dado que dichos colores no sern dibujados en pantalla. Gracias a esto podemos dibujar Sprites sin que su color de fondo, se superponga al fondo (evidentemente, siempre que el color usado de fondo, sea considerado transparente). Si utilizis mi programa Spritegen y queris exportar los sprites a color directo de 16 bits, en "Export to C", desmarcar "Use Palette" y en "Direct Color" selecciona 15 bits. Si queris convertir directamente esos sprites en tiles, podis fijar la alineacin a 32 bytes ah. De esta forma exportareis los sprites como RGB5A1 y si el fondo est utilizando el color 0 (que por defecto tiene Alpha=0 en Spritegen) pues ya lo tenis listo para funcionar. Solo os faltar crear la textura a partir de los datos del sprite para tenerlo funcionando. En el ejemplo 3 que os he puesto, podis ver que se ha hecho una exportacin de datos desde Spritegen a 8 bits con paleta ("Export to c", marcar "Alpha Enable" y "Use Palette". "Direct Color/ Palette Color" a 15 bits). En dicho ejemplo, asigno memoria alineada para alojar los tiles de los sprites y conservo el mapa de color original. Eso os permite no tener que alinear los datos exportados, pero la ventaja principal, es que sabiendo que el indice de color transparente es 0, podis utilizar el mapa original para detectar colisiones de forma mas precisa: un color en el mapa original distinto a 0 representa algo "solido". Como podis observar, si exporto color directo o con paleta a 15 bits (+ 1 de Alpha! y Wii solo admite paletas de 16 bits...) en realidad, ese Alpha solo va a servir para

determinar si un color es transparente o no (si se dibuja o no se dibuja, vamos): pero Wii puede usar un rango mas amplio que 16 bits (y realmente, Wii trata de forma especial los colores con el bit Alpha=0, puesto que usa un modo de color denominado RGB5A3 y por eso a RGB5A1 es conveniente que el valor global sea 0 para Alpha=0 para evitar cosas raras). En realidad, vosotros podis especificar tambin un color en formato RGBA8 (8 bits por componente) para dibujar superficies, o cajas desde screenlib y eso os permite hacer una especie de filtro para los usos con textura o sin textura. De esta forma, entramos en el terreno de la translucidez. La translucidez es un fenmeno que consiste en poder ver objetos que estn detrs de otros objetos a travs de ellos. As por ejemplo, un cristal es un material translcido, pues permite ver lo que hay detrs aunque le cause una interferencia (por ejemplo, si ves a travs de un cristal verdoso, los objetos de detrs tomarn dicha tonalidad). La diferencia real con la transparencia es que esta ltima, realmente no dibuja los pxeles por lo que no ocupan espacio en la ordenacin Z (el layer). As pues, para dibujar objetos translcidos de forma correcta, debemos dibujar PRIMERO los objetos que queden detrs, pues si el objeto translcido usa layer 0, la ordenacin Z no permitir que se dibuje nada detrs (por ejemplo, en layer 100). Y las partes transparentes, en realidad son "agujeros" en el objeto La forma de interpretar esto, mas o menos es asi: En un rango de 0 a 255: Alpha <8 -> Transparente, Alpha>=8 ->Nivel de translucidez, Alpha==255 -> Color Slido Si hablamos de color RGB5A1 seria Alpha==0 -> Transparente, Alpha==1 -> Color Solido

La screenlib (ahora s)
Anun Brev escribi:Alargue su pene pulsando Aqu

Inicializar Para inicializar la librera, se usa esta funcin: InitScreen();

Esto es todo lo que tienes que hacer en tu main() para que el video est operativo usando la configuracin que tengas en tu Wii. Al hacerlo, SCR_WIDTH y SCR_HEIGHT te informar de la resolucin que estas empleando y como expliqu en el anterior captulo, puedes determinar si el refresco es a

60Hz porque el alto sera de 480 pxeles. Sin embargo, conviene recordar que la lbrera trabajar pensando en 4:3 y no en 16:9. En ese caso, te tocar a ti determinar el formato de la pantalla, ya que la resolucin de salida seguira siendo la misma! y aplicar los factores de escala correspondiente. Si quieres conocer si estas usando una configuracin de 16:9 puedes inicializar as: Cdigo: Seleccionar todo
int is_16_9=0; CONF_Init(); is_16_9=CONF_GetAspectRatio(); InitScreen();

Y a partir de ah, hacer los ajustes pertinentes Cambiar el Viewport (nivel: usuario avanzado) Para usos especiales, puedes cambiar el Viewport. Por ejemplo, podras virtualizar la pantalla para tratar los 16:9 de forma proporcionada directamente, cambiando el ancho, pero yo no te lo aconsejo porque por ejemplo, si ajustases para trabajar a 854x480, realmente, estaras condensando 1,333 pxeles en 1... para ciertos grficos quiz no sea mucho problema, pero para letras o pequeos detalles, es mejor desproporcionar un poco a que te falte resolucin. En mi ejemplo 3 muestro como utilizar un Viewport de 480x360 para "inventarme" una resolucin de la que carece Wii de esta forma: ChangeProjection(0, -10*(SCR_HEIGHT<=480), 479, 360+10*(SCR_HEIGHT<=480));

Como se puede apreciar, aplico una correccin para video NTSC (<=480) y espero que entiendas que esos valores son experimentales: en mi TV se ve as perfecto, pero no puedo saber como se ver en la tuya Si por alguna razn quisieras volver a los valores originales, es tan facil como usar sta funcin: Cdigo: Seleccionar todo
RestoreProjection();

En el ejemplo 3 podis observar como cambio el valor de una variable: xclip . Esta variable se utiliza para limitar por la derecha el texto visible (funcin s_printf, que veremos mas adelante) Finalizando el frame

La funcin Screen_flip(); se encarga de esperar a que se dibujen todos los grficos, sincronizar con el video vertical, intercambiar el buffer de la pantalla y borrar el nuevo, asi como el buffer Z. Es la ltima funcin que debes llamar para completar el frame de video y mientras no sea llamada, no podrs apreciar los cambios efectuados. Dibujando caracteres La librera dispone internamente, de dos sets de caracteres ANSI, los cuales son facilmente seleccionables con la funcin SelectFontTexture SelectFontTexture(0); // -> selecciona el font por defecto SelectFontTexture(1); // -> selecciona el font extra/externo

(Nivel Avanzado) La librera permite reemplazar el segundo juego de caracteres por otro que haya capturado el usuario utilizando la utilidad ttf2raw (ver utilidades recomendadas al principio). Para ello se dispone de la funcin: UploadFontTextureExt(unsigned char *punt);

Donde punt sera el lugar de la memoria donde tenemos alojado el nuevo font. Se puede restablecer el font original usando UploadFontTextureExt(INTERNAL_EXTFONT); Si quieres cambiar el font por defecto, pues te tocara modificar screenlib a tu gusto (fin de Nivel avanzado) Las variables PX y PY, son las encargadas de proporcionar las coordenadas de inicio del texto (en pxeles) que imprimiremos con la funcion s_printf color Se encarga de suministrar el color de las letras en formato RGBA8 (ten cuidado de no utilizar esta variable para otra cosa ) bkcolor Proporciona el color del fondo tambien en formato RGBA8. Por defecto es 0 autocenter Est pensada para centrar en horizontal el texto si la pones a 1. Por defecto est a 0 (desactivada) sizeletter Controla los diferentes tamaos de letra, pero para t ser mejor usar la funcin letter_size() letter_size(tamx, tamy); Ajusta el tamao de las letras a tamx, tamy. El font original es

a 16x32 pero puedes ajustarlo a diferentes tamaos as. xclip (avanzado) Esta variable controla el lmite derecho de las letras para su visualizacin. Si un caracter supera ese limite, el resto del texto ser ignorado, pero nota que esto no equivale a hacer un recorte exacto en ese punto y si un carcter excede por ancho ese lmite, ser totalmente visible si es posible s_printf( char *format, ...); Funciona de forma similar a la funcin estndar printf, que como es de sobra conocida, pues no necesita mucha explicacin. La nica diferencia, es que si un texto se sale de la pantalla, no se proceder a un cambio de lnea automtico, cosa lgica si piensas que aqu el tamao de letra es variable. Por cierto, las letras desde aqu, se escriben en layer 0. print_str(u32 x, u32 y, u32 z, u32 color, char *string,u16 tamx, u16 tamy); Si lo que quieres es imprimir una simple cadena, sin formato, etc, puedes utilizar sta funcin que como puedes observar, necesita de coordenadas x,y,z (la z aqui si se aleja en sentido negativo!) color (para el fondo usa bkcolor), la cadena de texto y el ancho y alto de los caracteres. ********************************************************************** *****************************

La screenlib - Texturas
Las texturas son un asunto que merecen un captulo aparte dentro de sta documentacin. La screenlib ofrece soporte para texturas con paleta (soportando las 16 paletas que provee las GX) o sin paleta y tambin para modos de color que no son directamente soportados por las GX, pero con una pequea adaptacin son posibles. Antes de nada, tienes que saber que Wii aloja sus texturas en la memoria "normal": es decir, en otras mquinas tienes que subirlas a la VRAM, mientras que aqu bastar con que esa memoria est alineada con 32 (recuerda memalign, que lo mencionamos al principio) y que hayamos flusheado los datos, puesto que la textura se trabaja fuera de cach por las GX. Como la screenlib no pretende ser un sustituto de las GX, si no un complemento de ellas, para trabajar, necesitars usar los mismo objetos para manejar texturas: GXTexObj text; // -> esta es la forma de definir un objeto de textura GXTlutObj palette; //-> esta es la forma de definir un objeto de paleta

Las texturas se organizan internamente en tiles, generalmente de 4x4, pero hay otras organizaciones de datos. Por precaucin, sera conveniente que tanto el ancho, como el alto de la textura, fuera divisible entre 8 para estar seguro de que nuestra textura no parte tiles (que yo no recuerdo el lmite exacto y en todo caso, es un margen mucho mas generoso que usar texturas en base 2 que precisan otros sistemas) Las dimensiones mximas a usar en textura son de 1024x1024, lo que implica un tamao empleado de 1,2 o 4 MB (asi que si te parece pequeo, ya ves que no lo es tanto )

Creando Paletas Internamente, screenlib utiliza una funcin llamada ctlut() para crear una paleta partiendo de los datos proporcionados de forma externa. El formato de las paletas es de 16 bits y para trabajar con la screenlib deberias usar modos TLUT_RGB5A3, TLUT_RGB5A1, TLUT_SRGB5A1 #define TLUT_LITTLE_ENDIAN 128 // cambia el orden de los bytes del color (util para importaciones) #define TLUT_RGB5A3 2 // para RGB5A3 (si el bit Alpha de mas peso, es 1, el color es RGB5A1, en otro caso RGB4A3), Azul menor peso #define TLUT_RGB5A1 2 // pseudo RGB5A1 (es RGB5A3, pero asumiendo que si Alpha==0,color==0) ,Azul menor peso #define TLUT_RGB565 3 // este no lo deberias usar, al carecer de Alpha #define TLUT_SRGB565 4 // este tampoco lo deberias usar #define TLUT_SRGB5A1 5 // para RGB5A1 con R y B intercambiado, Rojo menor peso (al estilo de SpriteGen)

La memoria de la paleta debe estar alineada a 32 bytes y ser de tipo u16 (unsigned short). Si quieres asignar un color de paleta manualmente, partiendo desde un color RGBA8 (con Rojo como menor peso) puedes usar Color5551(x) para obtener un color en formato TLUT_SRGB5A1 Ejemplo escribi:Color5551(0xffff0000); //-> convierte color Solido y Azul intenso al formato SRGB5A1

La funcion para crear la paleta, es: Cdigo: Seleccionar todo


void CreatePalette(GXTlutObj *palette, int format, int pal_index, u16 *mem, int entries);

Donde: palette -> puntero a objeto de paleta format -> formato de color (TLUT_RGB5A3, TLUT_RGB5A1, TLUT_SRGB5A1) pal_index -> indice de paleta a usar dentro de las GX (0 a 15). Tambien fija una variable para relacionar paleta y textura mem -> memoria conteniendo las entradas de paleta (alineada a 32, recuerda). Recuerda que los datos originales sern modificados... entries -> numero de entradas de la paleta (hasta 256, aunque fsicamente, hay paletas que soportan 4096 colores)

La funcin SetPalette() SetPalette(int pal_index); se usa para fijar una variable interna con el ndice de paleta (de 0 a 15) a utilizar cuando creemos una textura que haga uso de paletas. Lo normal es que primero creemos paleta, lo cual nos ajusta esa variable de forma automtica y luego procediramos a crear nuestras texturas, pero que sucede si quiero hacer una serie de cambios en la textura en cada frame? Pues que nos tocar decirle de alguna forma que paleta debe usar al crearse la textura y para eso tenemos sta funcin. Creando Texturas Para eso tenemos sta funcin: Cdigo: Seleccionar todo
void CreateTexture(GXTexObj *texture, int format, void *mem, int width, int height, int repeat);

texture -> puntero objeto de textura format -> formato de color: TILE_CI4, TILE_CI8, TILE_RGB5A1, TILE_RGB5A3, TILE_SRGB5A1, TILE_RGBA8, TILE_SRGBA8 mem -> memoria que contiene los datos de textura y donde se formarn los tiles (es decir, que se modifican los datos). Alineada a 32 bytes!!! width -> ancho de la textura height -> alto de la textura repeat -> flag que ajusta si la textura se repite en forma de mosaico si se exceden las coordenadas de textura (por defecto, usa 0) Descripcin del formato de color: #define TILE_LITTLE_ENDIAN 128 // cambia el orden de los bytes de color (util para las importaciones) #define TILE_CI4 0 // para CI4 (color indirecto de 4 bits, o sea, con paleta) #define TILE_CI8 1 // for CI8 (color indirecto de 8 bits, o sea, con paleta) #define TILE_RGB5A3 2 // para RGB5A3 (si el bit Alpha de mas peso, es 1, el color es RGB5A1, en otro caso RGB4A3), Azul menor peso #define TILE_RGB5A1 2 // pseudo RGB5A1 (es RGB5A3, pero asumiendo que si Alpha==0,color==0) ,Azul menor peso #define TILE_RGB565 3 // no deberias usarlo #define TILE_SRGB565 4 // no deberias usarlo #define TILE_SRGB5A1 5 // para RGB5A1 con R y B intercambiados. Rojo menor peso #define TILE_RGBA8 6 // Para RGBA8. Azul menor peso #define TILE_SRGBA8 7 // Para RGBA8 con R y B intercambiados. Rojo menor peso

(Usuario avanzado) Por defecto las texturas usan filtrado bilineal. Es posible cambiar a GX_NEAR (para que se vean cuadraditos si ampliamos la textura) si utilizamos antes de crear la textura la siguiente funcin: UseTexLOD(GX_NEAR , GX_NEAR); Y para restablecer: UseTexLOD(GX_LINEAR , GX_LINEAR); Puesto que es un uso relativamente raro, no he querido aadir un nuevo parmetro a la funcin de crear textura que en el fondo, va a ser un lastre (fin usuario avanzado) Seleccionando textura Para seleccionar la textura en las funciones de dibujo, se utiliza sta funcin: Cdigo: Seleccionar todo
SetTexture(GXTexObj *texture);

Como se puede apreciar, se le pasa un puntero al objeto de textura. Si se le pasa NULL (Valor 0), esas funciones no usarn textura y simplemente, aplicarn el color que le pasemos. Existen una serie de texturas predefinidas para poder usarlas como patrones de relleno: #define STRIPED_PATTERN &tex_pattern[0] #define CROSS_PATTERN &tex_pattern[1] #define SQUARE_PATTERN &tex_pattern[2] #define MOSAIC_PATTERN &tex_pattern[3] #define NULL_PATTERN NULL

As con: SetTexture(MOSAIC_PATTERN); Se utilizara ese patrn de relleno con las funciones de dibujo. ********************************************************************** *****************************

La screenlib - Funciones de Dibujo


La screenlib se dise principalmente, como un conjunto de herramientas que permitiesen disear mens de una forma mas o menos sencilla, por lo que no es extrao que muchas de sus funciones se limiten a dibujar distintos tipos de cajas o los bordes de

dichas cajas, con el fin de embellecerlas y por otro lado, marcar la diferencia entre una caja seleccionada y otra que no lo es. Dentro de la librera, no hay ninguna regla que impida hacer un uso 3D o 2D diferente y no hace falta trastear mucho en los fuentes de la librera para ver de que forma utiliza las GX y de que forma pueden convivir. Desde un punto de vista 2D (o mas bien pseudo 2D) se ha tratado de facilitar ciertos usos especiales, pero adems estas funciones, suponen un claro ejemplo de como se utilizan las cosas desde las GX y os da pistas sobre que es lo que deberais alterar si por ejemplo, queremos una proyeccin que supere el rango de 0 a 999 en Z o usar grficos en perspectiva y meternos de lleno en las 3D.

Las funciones para dibujar cajas rellenas (con un color o con una textura), son estas: DrawFillBox(int x,int y, int width, int height, int layer, u32 color); // dibuja una caja cuadrada rellena DrawRoundFillBox(int x,int y, int width, int height, int layer, u32 color); // dibuja una caja con las esquinas redondeadas, rellena DrawRoundFillBox2(int x,int y, int width, int height, int layer, u32 color); // dibuja una caja con los lados izq-der redondeados, rellena

x, y -> posicin de la caja en pxeles width, height -> dimensiones de la caja en pxeles layer -> capa de 0 a 999, siendo 999 la capa mas alejada y 0 la mas cercana (equivale a -Z) color -> Color en formato RGBA8 (8 bits por componente y Rojo byte de menor peso) Las funciones para dibujar el borde de las cajas (con un color o con una textura), son estas: DrawBox(int x,int y, int width, int height, int layer, int thickness, u32 color); // dibuja el borde de una caja cuadrada DrawRoundBox(int x,int y, int width, int height, int layer, int thickness, u32 color); // dibuja el borde de una caja con esquinas redondeadas DrawRoundBox2(int x,int y, int width, int height, int layer, int thickness, u32 color); // dibuja el borde de una caja con los lados izq-der redondeados

x, y -> posicin de la caja en pxeles width, height -> dimensiones de la caja en pxeles

layer -> capa de 0 a 999, siendo 999 la capa mas alejada y 0 la mas cercana (equivale a -Z) thickness -> grosor de la linea de 1 a ... yo que se! lo que admita de listo eh? Que esta funcin no es a prueba de bombas. . Pero no te pases

color -> Color en formato RGBA8 (8 bits por componente y Rojo byte de menor peso) Como se puede apreciar, son funciones muy sencillitas Dibujando superficies curvas El soporte de la librera se limita a dibujar Elipses y grficos tipo tarta (DrawSlice) . Espero que nadie me reproche que no haya funciones para dibujar crculos y circunferencias porque sea tan ignorante que no sepa que con la funcin Elipse se pueden obtener este tipo de figuras . Y es ms, teniendo en cuenta que las proporciones de la pantalla pueden hacer que un "pxel" en el Viewport no sea completamente cuadrado, resulta conveniente hacer un ajuste de los radios para que nuestros crculos o circunferencias, no se vean alargadas o achatadas, segn el caso As pues para dibujar elipses tenis: DrawFillEllipse(int x,int y, int rx, int ry, int layer, u32 color); // dibuja una elipse con su superficie rellena DrawEllipse(int x,int y, int rx, int ry, int layer, int thickness, u32 color); // dibuja el borde de la elipse

x, y -> posicin en pxeles rx, ry -> radio en pxeles para las dimensiones X e Y layer -> capa de 0 a 999, siendo 999 la capa mas alejada y 0 la mas cercana (equivale a -Z) thickness -> grosor de la linea de 1 a ... yo que se! lo que admita de listo eh? Que esta funcin no es a prueba de bombas. . Pero no te pases

color -> Color en formato RGBA8 (8 bits por componente y Rojo byte de menor peso) Y para dibujar arcos de una elipse (grficos tipo tarta o el uso que le querais dar. El objeto se dibujara entre el arco que formen degrees_start y degrees_end): DrawFillSlice(int x,int y, int rx, int ry, int layer, int degrees_start, int degrees_end, u32 color); // dibuja el arco de una elipse con su superficie rellena DrawSlice(int x,int y, int rx, int ry, int layer, int thickness, int degrees_start, int degrees_end, u32 color); // dibuja el borde del arco de la elipse

x, y -> posicin en pxeles rx, ry -> radio en pxeles para las dimensiones X e Y layer -> capa de 0 a 999, siendo 999 la capa mas alejada y 0 la mas cercana (equivale a -Z) thickness -> grosor de la linea de 1 a ... yo que se! lo que admita de listo eh? Que esta funcin no es a prueba de bombas. degrees_start -> Angulo en grados (de 0 a 360) de inicio degrees_end -> Angulo en grados (de 0 a 360) final. color -> Color en formato RGBA8 (8 bits por componente y Rojo byte de menor peso) La funcin de linea La funcin DrawLine() se incluy pensando ms en ciertos usos especiales que pensando en ella como la funcin que "siempre deberias utilizar". La funcin est preparada para dibujar lineas de mas de un pxel de grosor y en realidad utiliza dos tringulos de forma interna. Pero tiene un pequeo defectillo: para dibujar lineas horizontales o verticales, tiene apoyo para poder ligar con otras lineas y formar cajas, pero si esa lnea es mas gruesa de un pxel y digamos que forma "rampas", la unin no ser buena. Notese tambin que le influye si tenemos una textura o no en activo (con SetTexture()) Si quieres dibujar lneas u otros polgonos de forma rpida, deberas utilizar GX_Begin, lo cual describo luego, mas tarde DrawLine(int x,int y, int x2, int y2, int layer, int thickness, u32 color); // dibuja lineas con o sin textura y de diferentes grosores . Pero no te pases

x, y -> posicin de inicio en pxeles x2, y2 -> posicin final en pxeles layer -> capa de 0 a 999, siendo 999 la capa mas alejada y 0 la mas cercana (equivale a -Z) thickness -> grosor de la linea de 1 a ... yo que se! lo que admita de listo eh? Que esta funcin no es a prueba de bombas. . Pero no te pases

color -> Color en formato RGBA8 (8 bits por componente y Rojo byte de menor peso) Dibujando Superficies

Lo que hace de sta librera que resulte til para hacer juegos 2D de forma directa, es sin duda, la funcin DrawSurface(). Esta funcin permite dibujar un objeto de textura directamente, proporcionndole unas dimensiones que no tienen porque coincidir con las de la textura utilizada. Es decir: si la textura tiene un ancho de 32x32, no hay nada que impida dibujarla como superficie mayor o menor, lo cual lleva implcito un aumento o reduccin de tamao (de ah que nos resulte til utilizar GX_LINEAR en el LOD de la textura al crearla). As pues DrawSurface(), se puede utilizar tanto para dibujar una ventana en la cual utilizaremos una textura como superficie donde dibujar pxeles (en el ejemplo 1 os incluyo una muestra de como utilizo una textura para dibujar) o para dibujar sprites completamente acelerados por la consola. DrawSurface(GXTexObj *surface,int x,int y, int width, int height, int layer, u32 color); // dibuja un objeto de textura como superficie

surface -> Puntero a objeto de textura que describe la superficie (creada con CreateTexture()). NO usar NULL aqu x, y -> posicin de la superficie en pxeles width, height -> dimensiones visuales de la superficie en pxeles layer -> capa de 0 a 999, siendo 999 la capa mas alejada y 0 la mas cercana (equivale a -Z) color -> Color en formato RGBA8 (8 bits por componente y Rojo byte de menor peso) Conectando con las GX: GX_Begin() (usuario avanzado) Para dibujar polgonos, resulta mas adecuado usar las GX directamente, puesto que as ganamos en velocidad y realmente es muy sencillo con ayuda de unas pocas funciones de apoyo que he aadido. Un ejemplo de uso, lo podis ver en el ejemplo 2, donde utilizo GX_Begin para dibujar las lineas remarcadas que forman las "montaas" (aqu se hace un uso especial, partiendo de una coordenada de origen y usando esto para dibujar 4 lineas en una configuracin 2x2 para el remarcado y asi asegurar un grosor de 2 pxeles en la linea) . Lo primero que tenemos que hacer, es preparar lo necesario para dibujar polgonos con o sin texturas: ConfigureForColor();

Esta es la funcin necesaria para ajustar el uso de vrtices para admitir coordenadas de posicin y color ConfigureForTexture(int scale);

Esta es la funcin necesaria para ajustar el uso de vrtices con coordenadas de posicin, color y coordenadas de textura scale Este parmetro se utiliza para fijar el factor de escala para la textura, debido a que las coordenadas las especificamos como enteros de 16 bits. Para entender esto, tienes que saber que para el GX, los limites de una textura se especifican como nmeros flotantes siendo 0.0f uno de los extremos y 1.0f el otro, aunque tambin es posible utilizar magnitudes negativas (en caso de crear textura con el parmetro repeat=1). Asi pues, ste factor de scale es un desplazamiento cuyo valor real es 1<<scale para representar 1.0f. Es decir, que si fijamos scale=10, como 1<<10==1024, un valor de 0 representara a 0.0f mientras que 1024 representara a 1.0f Pongamos un ejemplo: imagnate que creamos una textura con unas dimensiones de 64x32 texels (el texel es como el pxel, pero referido a textura). A ojos del GX, las dimensiones irian desde 0.0f a 1.0f, de forma independiente a ese tamao Pues bien, si especificamos un factor de scale= 10, eso significa que si damos una coordenada de textura tx=1024( en el ancho), tx sera equivalente a 1.0f pero a efectos prcticos de textura sealara a 64, teniendo en cuenta el ancho de la textura en texels. Sin embargo si especificamos ty=1024 (el alto), a efectos prcticos sealara a 32 que es el alto de la textura y no a 64. Puede parecer lioso pero la idea es que las coordenadas de textura no dependan exactamente del tamao de la textura, aunque obviamente, hay una relacin directa. Vosotros lo que tenis que ver es que 1<<scale representa al lmite vertical y horizontal de la textura (y el otro es 0, obviamente) y no caer en el error de pensar que como el ancho de la textura es 64 y el alto 32, hay que pasarle esos valores numricos o 1.0f y 0.5f, porque la escala hace referencia al valor de 1.0f, ya que independientemente del tamao real de la textura, 1.0f es el limite derecho, pero tambin el inferior. Asi pues, si la textura es un rectngulo, las coordenadas quedaran as, para un factor escala de 10: izq-arr = (0.0f,0.0f)= (0,0) der-arr=(1.0f,0.0f)=(1024,0) izq-abj =(0,1.0f)=(0,1024) der-abj=(1.0f,1.0f)=(1024,1024) Exactamente igual que si fuera una textura cuadrada Puesto que 1.0f en realidad, rebasa el lmite de la textura, lo mejor es quedarse un poco corto, de forma que sera mejor usar 1023 para ese borde. Espero que con este ejemplo, hayis captado mejor lo de las coordenadas de textura y sus escalas (se puede especificar floats como coordenada de textura directamente, pero por cuestiones de velocidad y espacio, es mejor utilizar enteros de 16 bits y mas para un uso 2D)

GX_Begin() GX_Begin es la funcion que nos permite dibujar polgonos desde las GX. Para utilizarla desde screenlib debeis hacerlo as: Cdigo: Seleccionar todo
GX_Begin(primitive, GX_VTXFMT0, vertices);

primitive Puede ser GX_POINTS, GX_LINES, GX_LINESTRIP, GX_TRIANGLES, GX_TRIANGLESTRIP, GX_TRIANGLEFAN, GX_QUADS. Si quieres saber ms, en mi documentacin sobre las GX, se describe este tema, pero te hago un resumen aqui: - GX_POINTS : Dibuja puntos, necesita un vrtice por cada punto a dibujar - GX_LINES : Dibuja lineas, necesita dos vrtices por cada linea a dibujar - GX_LINESTRIP : Dibuja lineas, necesita dos vrtices para la primera linea, pero el resto de lineas utilizan el vrtice de la ltima linea dibujada, mas uno nuevo. Es decir, que para dibujar dos lineas se necesitaran 3 vrtices y las dos lineas compartiran uno de ellos - GX_TRIANGLES: Dibuja tringulos, necesita tres vrtices por tringulo. - GX_TRIANGLESTRIP: Dibuja tringulos enlazados de forma que el primero requiere tres vrtices, pero los siguientes comparten dos vrtices con el ltimo tringulo dibujado mas uno nuevo. Resulta adecuado para crear objetos en forma de tiras o anillos (objetos torno, por ejemplo).Por ejemplo, dos trianglestrips requieren 4 vrtices, mientras que tres trianglestrips requieren 5 vertices - GX_TRIANGLEFAN: Dibuja tringulos utilizando un vrtice como referencia central (el primer vrtice especificado), el ultimo del anterior tringulo mas uno nuevo. Obviamente, el primer tringulo precisar de trs vertices. Por ejemplo, dos trianglefan requieren 4 vertices, mientras que tres trianglefan requieren 5 vertices. Resulta adecuado para hacer graficos de tarta, cerrar figuras, para modelar una sombrilla... - GX_QUADS: Los quads son figuras que requieren 4 vrtices. Los vrtices requiere usar un orden horario o antihorario (es decir izq-arr, der-arr, der-abj, izq-abj por ejemplo) y se debe evitar vrtices cruzados o que puedan generar dos planos. Son tiles para dibujar cuadrados/rectangulos y desde un punto de vista 3D, paredes, techos, suelos... GX_VTXFMT0 Esto especifica que vamos a usar el formato de vrtices 0, que es el que programamos con ConfigureForColor() y ConfigureForTexture() vertices Numero de vrtices a usar, hasta un lmite de 65535 (quiz el 0 represente 65536, pero vamos, que puedes usar un buen puado de una sola llamada). Y por supuesto, siempre que no superes el tamao del FIFO. Recuerda que debes darle ls vertices exactos (o se producir un cuelgue de la muerte)

Pasando vrtices a GX_Begin() En screen.h tienes dos funciones definidas para pasar los parmetros necesarios a los vrtices AddColorVertex(s16 x, s16 y, s16 z, unsigned color); // para aadir vrtices con posicin y color AddTextureVertex(s16 x, s16 y, s16 z, unsigned color, s16 tx, s16 ty); // para aadir vrtices con posicin, color y coordenada de textura.

Procura no equivocarte con la funcin utilizada si no quieres que se produzca un cuelgue de la muerte Para acabar, utiliza GX_End(); que se utiliza de forma decorativa Ejemplos de uso de GX_Begin() Cdigo: Seleccionar todo
ConfigureForColor(); // usa color GX_Begin(GX_LINE, GX_VTXFMT0, 2); // dibuja una linea (por tanto pasamos 2 vertices) AddColorVertex(0,0,0,0xffffffff); // primer vertice AddColorVertex(SCR_WIDTH, SCR_HEIGHT,0,0xffffffff); //segundo vertice GX_End();

Cdigo: Seleccionar todo


SetTexture(&texture); // fija una textura ConfigureForTexture(10); // usa textura y fija escala a 10 ( 1<<10== 1024) (notese que usar siempre la textura especificada por SetTexture()) GX_Begin(GX_QUADS, GX_VTXFMT0, 4); // dibuja un QUAD (por tanto pasamos 4 vertices) AddTextureVertex(0,0,0,0xffffffff, 0, 0); // primer vertice AddTextureVertex(SCR_WIDTH, 0,0,0xffffffff, 1023, 0); // segundo vertice AddTextureVertex(SCR_WIDTH, SCR_HEIGHT,0,0xffffffff, 1023,1023); // tercer vertice AddTextureVertex(0, SCR_HEIGHT,0,0xffffffff,0, 1023); // cuarto vertice GX_End();

El ultimo recurso: Restaurar el entorno 2D Es posible que necesitis saltar a usar 3D o incluso que hagis un uso mas avanzado de las 2D, cambiando la matriz mundial, etc. En ese caso, tendris que restaurar el entorno para volver a trabajar con screenlib y para eso, se ha aadido la funcion: Recover2DContext();

Esta funcin recupera en lo posible el contexto 2D de screenlib, pero si tu haces usos avanzados (como cambiar la matriz de transformacin de texturas y cosas asi), es posible que eso lo tengas que restablecer a mano. Obviamente, ser tu responsabilidad cambiar a los distintos contextos y esta funcin no puede saber todo lo que has cambiado y restaurarlo por ti. En todo caso, dispones del cdigo fuente de la librera y algunas cosas se han organizado de forma que te sea sencillo ver que es lo que precisa screenlib para trabajar. Y esto es todo lo referente a mi librera grafica ********************************************************************** *****************************

La ASNDLIB (Introduccin)
La ASNDLIB es la sucesora de una librera anterior, llamada SNDLIB que fue una de mis primeras aportaciones al homebrew de Wii. Se diferencian en que la primera utiliza el DSP, lo cual permite acelerar las voces, mientras que la segunda usa el procesador de la Wii para calcular las distintas voces y adems soporta unas voces especiales pensadas para generar las voces de instrumentos, con efectos como ataque, sostenimiento y cada. El DSP es un procesador relativamente poco potente para la tarea encomendada: mezclar 16 voces en estreo y con un rango de frecuencias que va desde un 1Hz a 144000Hz es algo que requiere bastantes multiplicaciones y operaciones para adaptar y mezclar las voces: no es tarea fcil para un procesador de 16 bits y que est pensado para trabajar de otra manera y ni os cuento los obstculos que me encontr para conseguir su "colaboracin" (baste decir que he sido el primero en darle utilidad, basndome en una informacin con 3 aos de antigedad y resolviendo varias piezas del puzzle hasta hace bien poco desconocidas). El caso es que por razones de capacidad de proceso, era imposible mantener 16 voces y aadir la posibilidad de que el DSP trabajara con los efectos de ataque, sostenimiento y cada: se podran aadir desde fuera y probablemente, utilizando pocos recursos y algunos trucos, pero como aparte de m, creo que nadie las ha usado (y yo solo para mostrar ejemplos), pues decid suprimirlas. Por cuestiones de proceso, no es conveniente usar muchas voces por encima de 48000Hz. La Wii trabaja a 48000Hz a 16bits por sample y en estreo y la ASNDLIB permite reproducir voces Mono o Stereo, con samples de 8 o 16 Bits (con signo) y en el rango mencionado de 1 Hz (MIN_PITCH) a 144000Hz (MAX_PITCH) lo cual significa que la librera es capaz de adaptar la frecuencia de las voces para que acaben sonando a la frecuencia especificada en las voces en esos 48000Hz. Sin embargo, es obvio que usar voces por encima de esa frecuencia, implica una prdida de calidad de sonido y adems, multiplicar la carga de trabajo al DSP. Cuanto menor sea la frecuencia a la que se reproduce una voz, mas desahogado ir el DSP, aunque es cierto que el cdigo se ha diseado para que el DSP cumpla bien con su trabajo incluso soportando una carga de trabajo enorme.

El DSP se coordina con la interrupcin de audio que es llamada cada 21,333 ms aproximadamente, una vez que se interrumpe la DMA de Audio y entonces se inicia un proceso de llamadas en cascada a la DMA del DSP de hasta 17 pasos, para procesar las voces. Por ltimo me gustara sealar en ste apartado, que las voces de sonido a usar, deben estar alineadas a 32 bytes y usar preferentemente un padding de 32 bytes. De nuevo estamos en el mismo caso que con las texturas.

La ASNDLIB - funciones
Como en muchas otras libreras, lo primero que hay que hacer es iniciarla: Iniciando ASND_Init();

Al iniciar la ASNDLIB, las voces estn pausadas mediante una pausa general: Pausa general ASND_Pause(s32 paused);

paused Pon 1 para pausar y 0 para continuar. As pues, antes de trabajar con voces, necesitars hacer: Cdigo: Seleccionar todo
ASND_Init(); ASND_Pause(0);

Ten en cuenta que en el momento de quitar la pausa y aunque no se oiga sonido, la librera estar trabajando y el contador interno general de samples, estar contando. Para finalizar la librera, podis usar: Finalizando ASND_End();

Si os habeis fijado en mis ejemplos, veris que sta funcin es llamada en fun_exit() que es fijada con atexit() para que al salir del programa, salir de algunas libreras. Esta practica es conveniente debido a que pueden haber algunas DMAs en marcha y as evitamos un posible cuelgue. Bien, una vez tenemos la librera iniciada y funcionando (quitando la pausa ), ahora podemos decidir que voces usar desde la 0 a la 15 (ya he comentado que dispones de

16). La voz 0, se suele reservar para usar con reproductores de msica y yo as lo tengo adaptado en mis programas. Es por eso que si usamos la funcin: Buscando voces libres s32 ASND_GetFirstUnusedVoice(); // devuelve la primera voz libre empezando por la voz 1

La ultima voz que nos devolver como libre es la 0. Esta funcin se puede utilizar para conocer la primera voz sin uso, pero no hay ninguna regla escrita que diga que no se pueden interrumpir voces en ejecucin en cualquier momento. Para ejecutar una voz se utiliza estas funciones: Reproduciendo una voz s32 ASND_SetVoice(s32 voice, s32 format, s32 pitch,s32 delay, void *snd, s32 size_snd, s32 volume_l, s32 volume_r, ASNDVoiceCallback callback); s32 ASND_SetInfiniteVoice(s32 voice, s32 format, s32 pitch,s32 delay, void *snd, s32 size_snd, s32 volume_l, s32 volume_r);

La primera es el uso normal, mientras que la segunda se usa para ejecutar una voz de forma continua e ininterrumpida y que queda por tanto, sometida al control del usuario (cambio de pitch, volumen o momento de pararla). Las funciones devuelven SND_OK o SND_INVALID en caso de error. Parmetros: voice: Una desde 0 a 15 (MAX_SND_VOICES-1) format: Formato PCM desde VOICE_MONO_8BIT a VOICE_STEREO_16BIT (ver asndlib.h) pitch: frecuencia del pitch (en Hz) delay: Tiempo de retardo en milisegundos (ms). Tiempo de espera antes de que se inicie la voz snd: buffer con los samples a reproducir (alineado y con padding a 32 bytes) size_snd: tamao del buffer en bytes volume_l: volumen del canal izquierdo en el rango de 0 a 255 volume_r: volumen del canal derecho en el rango de 0 a 255

callback: Puede ser NULL, lo que indicara que la voz se usa nicamente para reproducir los samples especificados y despus quedar libre o la direccin de una funcin callback para trabajar a doble buffer, que ser invocada desde la interrupcin (cuidado con el gasto de CPU aqu) cada vez que se detecte que al menos uno de los buffers de voz queda libre y as asignar un nuevo buffer con ayuda de la funcin ASND_AddVoice(). En el modo doble buffer (usando callback) la voz no se libera por si misma y devuelve SND_WAITING en caso de quedarse sin samples para seguir reproduciendo. Cmo se puede observar, el modo con callback resulta mas adecuado para reproductores de audio, mientras que sin callback es mas adecuado para efectos de sonido sueltos. La funcin callback de ASND_SetVoice La estructura es la siguiente:

void my_callback(s32 voice) { }

Donde voice recibe el numero de voz por si sta callback es compartida con otras voces. Repito de nuevo: en el callback ests en tiempo de interrupcin y cuanto menos tiempo gastes aqu, mejor: si necesitas hacer algo pesado, despierta un hilo desde aqu, pero no sobrecargues sta funcin porque comprometers otras cosas. La funcin ASND_AddVoice() s32 ASND_AddVoice(s32 voice, void *snd, s32 size_snd);

Las funciones devuelven SND_OK, SND_BUSY si no es posible aadir la voz o SND_INVALID en caso de error. Parmetros: voice: Una desde 0 a 15 (MAX_SND_VOICES-1) snd: buffer con los samples a aadir (alineado y con padding a 32 bytes) size_snd: tamao del buffer en bytes Es decir: se puede observar que la funcin solo devolver SND_OK en caso de tratarse de una voz correcta y encontrar uno de los bufferes libres para ser asignado. En ese caso, la voz ser aadida y nosotros podremos comparar y proceder a intercambiar buffers en caso de usar la voz en un reproductor Lgica adicional para sistema de doble buffer

En un sistema de doble buffer lo usual es que un buffer se est reproduciendo, mientras otro se est rellenado descomprimiendo formato Ogg, por ejemplo. Sin embargo la librera ASNDLIB tiene un funcionamiento asncrono y necesita de mecanismos de proteccin para evitar por ejemplo, que tengamos dos buffers aadidos en la cola de reproduccin (uno en ejecucin y otro en espera) y que machaquemos esos datos al descomprimir nuevos samples y volcarlos al buffer de ejecucin. As pues, se necesita algo que le diga al lector-descompresor de samples que se est quieto-parado esperando que el buffer actual de lectura quede libre al cesar la reproduccin. Para ello contamos con dos funciones que tienen resultado similar: s32 ASND_TestPointer(s32 voice, void *pointer);

En esta funcin, debemos pasarle el inicio del buffer y ese puntero ser comprobado y en caso de coincidir con el buffer en reproduccin o el buffer en espera, se nos devolver el valor 1, lo que indicara que deberamos esperar antes de "rellenar" el buffer con nuevos datos y 0 en caso de no estar en uso ese puntero. Por tanto antes de refrescar los datos del buffer, deberamos comprobar que ASND_TestPointer(voice, puntero)==0 y proceder a refrescar si se cumple s32 ASND_TestVoiceBufferReady(s32 voice);

Esta funcin devuelve 1 si uno de los dos buffers est libre. Puesto que nosotros controlamos la secuencia de intercambio con ASND_AddVoice(), es obvio que si por ejemplo, acabamos de aadir un nuevo buffer y nos queda uno libre, es que podemos leer/descomprimir sin problemas nuevos datos. As pues, deberamos comprobar que ASND_TestVoiceBufferReady(voice)==1 antes de refrescar los datos del buffer Funciones de tiempo global Existe un timer de sonido global asignado a todas las voces y cuya utilidad puede ser coordinar el manejo de las voces en el tiempo. Para leerlo/controlarlo, dispones de las siguientes funciones: u32 ASND_GetTime(); // devuelve el tiempo transcurrido en milisegundos (ms) u32 ASND_GetSampleCounter(); // devuelve el numero de samples reproducidos hasta el momento en relacin a 48000Hz (48000 samples = 1 segundo)

u32 ASND_GetSamplesPerTick(); // devuelve el numero de samples reproducidos a 48000Hz por cada interrupcin

void ASND_SetTime(u32 time); // fija el tiempo del contador global por el especificado en milisegundos (ms)

Funciones de estado y tiempo de las voces Para conocer el estado de una voz, se utiliza sta funcin: s32 ASND_StatusVoice(s32 voice);

Que nos devuelve: -SND_INVALID -> voz no valida (fuera del rango 0 a 15) -SND_UNUSED -> voz sin uso -SND_WORKING -> voz en uso y reproduciendo -SND_WAITING -> voz en uso y esperando nuevos samples (doble buffer) Las voces poseen una medida de tiempo particular que es til para determinados usos: u32 ASND_GetTimerVoice(s32 voice); // devuelve el tiempo transcurrido en milisegundos, sin tener en cuenta el tiempo de delay especificado en la voz u32 ASND_GetTickCounterVoice(s32 voice); // devuelve el numero de samples reproducidos a 48000Hz (es independiente de la frecuencia de la voz)

En este caso, no se puede fijar el tiempo de inicio de la voz, ya que de ello se encarga ASND_SetVoice() ********************************************************************** *****************************

La ASNDLIB - Funciones de control


Control global Como funciones de control global cuentas con: void ASND_Pause(s32 paused); // pausa general (1=pausa, 0= continua) s32 ASND_Is_Paused(); // retorna uno si la est activa la pausa general o 0 en otro caso

void ASND_SetCallback(void (*callback)()); // aade una callback de propsito general que ser llamada cada 21,333 ms (p.e: void my_globalcallback() {})

Control de Voces s32 ASND_StopVoice(s32 voice); // para la voz (si est en ejecucin, obviamente) s32 ASND_PauseVoice(s32 voice, s32 pause); // si pause=1, pausa la voz, si pause=0 continua. Est pausa se anula con ASND_SetVoice() tambin

s32 ASND_StatusVoice(s32 voice);

Que nos devuelve: -SND_INVALID -> voz no valida (fuera del rango 0 a 15) -SND_UNUSED -> voz sin uso -SND_WORKING -> voz en uso y reproduciendo -SND_WAITING -> voz en uso y esperando nuevos samples (doble buffer) s32 ASND_TestPointer(s32 voice, void *pointer);

En esta funcin, debemos pasarle el inicio del buffer y ese puntero ser comprobado y en caso de coincidir con el buffer en reproduccin o el buffer en espera, se nos devolver el valor 1, lo que indicara que deberamos esperar antes de "rellenar" el buffer con nuevos datos y 0 en caso de no estar en uso ese puntero. Por tanto antes de refrescar los datos del buffer, deberamos comprobar que ASND_TestPointer(voice, puntero)==0 y proceder a refrescar si se cumple s32 ASND_TestVoiceBufferReady(s32 voice);

Esta funcin devuelve 1 si uno de los dos buffers est libre. Puesto que nosotros controlamos la secuencia de intercambio con ASND_AddVoice(), es obvio que si por ejemplo, acabamos de aadir un nuevo buffer y nos queda uno libre, es que podemos leer/descomprimir sin problemas nuevos datos. As pues, deberamos comprobar que ASND_TestVoiceBufferReady(voice)==1 antes de refrescar los datos del buffer NOTA: Si, ya se que en algunas funciones he repetido la explicacin, pero se trata de facilitaros la bsqueda de las cosas, no de ahorrar espacio Control dinmico de Voces

Para voces de tipo infinito o de doble buffer, puede ser til el uso de estas funciones: s32 ASND_ChangePitchVoice(s32 voice, s32 pitch); // cambio dinmico de la frecuencia de reproduccin (pitch) (rango de 1Hz a 144Khz) s32 ASND_ChangeVolumeVoice(s32 voice, s32 volume_l, s32 volume_r); // cambio dinmico del volumen (recuerda que el rango es de 0 a 255)

A golpe de nota Hay una funcin que nos permite utilizar voces para hacerlas sonar como si fueran notas de un instrumento, proporcionando la frecuencia a partir de los datos de nota y frecuencia base del sample: int ANote2Freq(int note, int freq_base,int note_base);

-note: Nota codificada a reproducir. Por ejemplo: NOTE( NOTE_C,4) para nota C y octava 4 -freq_base: Frecuencia base de los samples. Por ejemplo 8000Hz -note_base: Nota codificada de los samples. Por ejemplo: NOTE( NOTE_LA,3) para nota LA y octava 3 (LA 3) retorna: frecuencia (en Hz) Para entender esto, en asndlib.h tenis un definido NOTE( note, octave) que compone la nota codificada a usar en esta funcin, asi como la enumeracin de las distintas notas (NOTE_DO o NOTE_C). La frecuencia base es la frecuencia a la que capturas un sonido y la nota base, es la nota que capturaste. Por ejemplo, imagnate que capturas la nota Do3 de un rgano a 11025 Hz... pues obviamente, para que ASNDLIB pueda simular un Do4, tiene que saber que relacin hay entre la frecuencia base y las distintas notas Rendimiento del DSP Se dispone de una curiosa funcin para medir el uso del DSP: u32 ASND_GetDSP_PercentUse();

Devuelve el tanto por ciento usado en un determinado instante, por si queris saber el tiempo que gasta el DSP en procesar las voces (si supera el 100%, es que al DSP le falta tiempo para procesar las voces que le estis mandando). Digamos que es una funcin de debug

Y colorn colorado, la documentacin sobre la ASNDLIB se ha acabado: podis consultar asndlib.h para mas detalles ********************************************************************** *****************************

Lectura de Mandos: Wiimote


La librera del Wiimote libwiiuse.a (-lwiiuse) contiene las funciones necesarias para leer el Wiimote y sus expansiones, tales como el Nunchuk, Mando Clasico o la Guitarra de GHIII Como podeis observar en mis ejemplos, necesita incluir: #include <wiiuse/wpad.h> que es donde se definen las funciones necesarias, aunque en wiiuse.h podeis observar tambien, que se definen ciertas estructuras necesarias. Iniciar el Wiimote WPAD_Init();

es todo lo necesario para iniciar la librera, pero tambin necesitars configurar el modo o cosas como el tiempo que los mandos aguardarn antes de apagarse por falta de uso: void WPAD_SetIdleTimeout(u32 seconds); // tiempo en segundos que aguardar el mando antes de apagarse por falta de uso (p. e: 5*60 para 5 minutos)

El modo de operacin lo podis ajustar con: s32 WPAD_SetDataFormat(s32 chan, s32 fmt); // ajusta el modo de operacin

-chan: Un nmero de 0 a 3 para cada uno de los wiimotes o mejor WPAD_CHAN_ALL para ajustar el modo de todos los mandos -fmt: WPAD_FMT_BTNS (por defecto) para leer solo los botones y palancas, WPAD_FMT_BTNS_ACC para tener en cuenta adems los acelermetros y WPAD_FMT_BTNS_ACC_IR para utilizar los acelermetros y la cmara infrarroja. Como es evidente, el gasto de bateras depender de la activacin de estos modos, as que es conveniente activar slo lo necesario. s32 WPAD_SetVRes(s32 chan,u32 xres,u32 yres); // ajusta la resolucin de video para cada canal, cuando se usa la cmara IR

-chan: Un nmero de 0 a 3 para cada uno de los wiimotes o mejor

WPAD_CHAN_ALL para ajustar el modo de todos los mandos -xres: Resolucin horizontal, por ejemplo SCR_WIDTH si usas screenlib -yres: Resolucin vertical, por ejemplo SCR_HEIGHT si usas screenlib Obviamente, sta funcion solo es necesaria si activas el modo WPAD_FMT_BTNS_ACC_IR Leyendo los wiimotes s32 WPAD_ScanPads();

Esta funcin se encarga de leer todos los mandos mediante el mtodo polling y debe ser llamada en cada frame. Devuelve el nmero de PAD en activo (salvo que se produzca un error, por lo que no es del todo fiable) Leyendo los datos de un wiimote en particular s32 WPAD_Probe(s32 chan,u32 *type);

Esta es la funcin clave que deberas llamar para conocer si un mando est conectado o no y que tipo de datos devuelve: retorno <0 si se produce un error, otro caso, mando conectado y datos servidos -chan: Un nmero de 0 a 3 para cada uno de los wiimotes -type: Puntero a una variable u32 donde podremos leer el tipo de expansin: WPAD_EXP_NONE, si no hay ninguna, WPAD_EXP_NUNCHUK, si el Nunchuk est conectado, WPAD_EXP_CLASSIC y WPAD_EXP_GUITARHERO3 creo que no merecen ms explicacin Bien, una vez que verificamos que WPAD_Probe() devuelve un valor >=0 y que conocemos el tipo, podemos pasar a leer todos los datos: u32 WPAD_ButtonsHeld(int chan);

Devuelve todos los botones pulsados tantos del Wiimote, como de las expansiones. En wpad.h puedes encontrar las definiciones que son del tipo WPAD_BUTTON_X para los botones del wiimote, WPAD_NUNCHUK_BUTTON_X para los del Nunchuk y WPAD_CLASSIC_BUTTON_X o WPAD_GUITAR_HERO_3_BUTTON_X para el resto de las expansiones. Hacer and (& ) de esta forma: u32 buttons=WPAD_ButtonsHeld(0); if(buttons & WPAD_BUTTON_A) { /* A pulsado */}

WPADData *WPAD_Data(int chan);

Esta funcin nos devuelve un puntero a la estructura WPADData interna (mira en wpad.h). De esta forma, se pueden acceder a todos los datos procedentes de las expansiones, sticks analgicos o la cmara IR. Existen tambin funciones separadas para leer cada elemento: void WPAD_IR(int chan, struct ir_t *ir); void WPAD_Orientation(int chan, struct orient_t *orient); void WPAD_GForce(int chan, struct gforce_t *gforce); void WPAD_Accel(int chan, struct vec3w_t *accel); void WPAD_Expansion(int chan, struct expansion_t *exp);

Las diferentes estructuras podis verlas definidas en wiiuse.h Por ejemplo con: Cdigo: Seleccionar todo
WPADData * wmote_datas; wmote_datas=WPAD_Data(0);

Leeramos los datos del Wiimote 0 (el del led 1 encendido) y si tuviramos el Nunchuk conectado, podramos leer los datos del stick con: wmote_datas->exp.nunchuk.js.pos.x; // datos izquierda-derecha del stick analogico del nunchuk devuelta como u8 (128== centro aprox) wmote_datas->exp.nunchuk.js.pos.y; // datos abajo-arriba del stick analogico del nunchuk devuelta como u8 (128== centro aprox) wmote_datas->exp.nunchuk.js.center.x; // datos correspondientes a la posicin central del stick wmote_datas->exp.nunchuk.js.center.y; (hay mas datos como min y max, pero eso lo miras tu )

O por ejemplo, en caso de activar la cmara IR y ajustar la resolucin con WPAD_SetVRes() podras leer la posicin desde aqu wmote_datas->ir.x // posicin X de pantalla devuelta como float wmote_datas->ir.y // posicin Y de pantalla devuelta como float wmote_datas->valid // devuelve 1 si los datos de x e y son vlidos o 0 si no lo son (apuntando fuera de la barra sensora) Y de esta forma usar el wiimote como un puntero.

Os recomiendo hacer vuestras propias pruebas y buscar en wpad.h y sobre todo, en wiiuse.h la definicin de las diferentes estructuras y visualizar los datos en pantalla con s_printf(), para comprender mejor que es lo que devuelven (se siente: mi funcin es ensearte a encontrar el camino, pero eres tu quien debe aprender lo dems ) Eso si: parece ser que hay algunos fallos en relacin al nunchuk y por ejemplo, el uso de los acelermetros solo funciona en raw (por eso en mi ejemplo 4 uso el Nunchuk desde exp.nunchuk.accel.x y no de forma similar a como uso el Wiimote). El Rumble El Rumble se puede activar o desactivar con: s32 WPAD_Rumble(s32 chan, int status);

Simplemente haciendo status=1, pondremos el motor en marcha y lo pararemos con status=0. Si queremos simular distintas vibraciones, nos tocar ir activando y desactivando el rumble con una determinada cadencia. Apagando mandos de forma manual De eso se ocupa la funcin: Cdigo: Seleccionar todo
s32 WPAD_Disconnect(s32 chan);

Saliendo de la librera Usa WPAD_Shutdown(); antes de salir del programa y seremos buenos amigos Algo ms? Porque yo creo que ya est todo lo importante :evil:. Y si algo no lo sabes, pues es tan fcil como probarlo o preguntar en otra parte ********************************************************************** *****************************

Lectura de Mandos: Gamecube Pad


Las funciones se definen en ogc/pad.h y todo se asimila de forma interna a libogc y no necesitas incluir librera aparte Iniciando la librera del Gamecube PAD PAD_Init();

Leyendo los PADs de Gamecube u32 PAD_ScanPads();

Con sta funcin se leen todos los PADs conectados y devuelve una mscara que indica que mandos estn conectados o no. En la versin original de pad.c, hay un fallo (que no se si se produce solo en Wii o no) que hace que si conectas en el puerto 2, sin haber un mando en el puerto 1, a veces falle la conexin con ese mando 2 (me d cuenta desarrollando el emulador Wiiengine), por lo que vosotros disponis de una versin modificada que evita este fallo. Recordad la lectura usa mtodo polling y que por tanto, tendras que llamar sta funcin en cada frame, antes de leer los datos del pad Ejemplo de uso: Cdigo: Seleccionar todo
u32 npads; .... npads=PAD_ScanPads(); if(npads if(npads if(npads if(npads & & & & 1) 2) 4) 8) {} {} {} {} // // // // mando mando mando mando 1 2 3 4 conectado conectado conectado conectado

Leyendo los datos de un PAD de Gamecube en particular Una vez que conocemos los mandos que tenemos conectados, podemos usar estas funciones (mirar pad.h) u16 PAD_ButtonsHeld(int pad);

Devuelve todos los botones pulsados. El rango va desde PAD_BUTTON_LEFT a PAD_BUTTON_START, como podeis observar en pad.h s8 PAD_StickX(int pad); s8 PAD_StickY(int pad);

Para conocer los valores del stick (observar que utiliza un rango de -128 a 127, siendo 0 la posicin central) s8 PAD_SubStickX(int pad); s8 PAD_SubStickY(int pad);

Para conocer los valores del substick o como lo llamo yo, el pezn amarillo que utiliza un rango de -128 a 127, siendo 0 la posicin central) u8 PAD_TriggerL(int pad); u8 PAD_TriggerR(int pad);

(observar

Para conocer los valores analgicos de los gatillos, si se estn pulsando. Dan un rango de 0 a 255 Recordad que los valores analgicos son de referencia y que en el caso de los sticks, siempre es conveniente usar una zona muerta (un rango de valores que se ignora, por ejemplo, entre -32 y +32, para evitar que la posicin centrada del stick sea un cachondeo y haya cierta deriva).

Y con esto ya sabis todo lo imprescindible para usar ste tipo de pads ********************************************************************** *****************************

Punto de inflexin
Bien, hasta ahora, tenis acceso al vdeo, al audio y a los distintos mandos. Tambin te he proporcionado las libreras compiladas, herramientas para crear sprites e integrar las cosas en el propio programa, de forma que creo que he cumplido mi promesa de acercaros la programacin de Wii a los que aoris los viejos ordenadores como el Spectrum y su facilidad para realizar juegos. En el ejemplo 3 podis observar la integracin con los sprites, incluso podis ver como leer el tiempo en milisegundos por mediacin get_ms_clock() y as poder regular la velocidad de actualizacin de vuestros sprites de forma independiente al refresco de pantalla (cuidando que ese tiempo sea siempre superior o igual 20ms por frame, claro). En mis ejemplos os muestro como utilizar el Modplayer e incluso en el ejemplo 3, os muestro como controlar "manualmente" el modplayer para que cuando llegue al final del MOD, cambiarlo por otro. Con todo lo que os he explicado aqu y con lo que veis en los ejemplos, tenis todo lo necesario para crear juegos y aprender a programarlos. Alguno pensar: "Eh! No nos has contado nada sobre como acceder a la SD, ni al DVD ni a los dispositivos USB. Tampoco me has hablado de programacin multihilo y cosas asi". De algunas de esas cosas me ocupar a continuacin, pero realmente lo necesitas? Es decir, plantate si de veras, crees que necesitas todo eso para realizar un juego de plataformas, un juego de marcianos y cosas as. Evidentemente, si lo que quieres es realizar un juego donde la programacin pasa a un segundo plano y los grficos ocuparn una burrada, pues es evidente que necesitars acceder a dispositivos para cargar tus grficos, pero es que una de dos:

- O tu no necesitas ste cursillo y por tanto, ya sabes muchas de estas cosas o lo tienes muy fcil de averiguar por tu cuenta. Este curso es principalmente, para principiantes - O tu tienes muchos pjaros en la cabeza, pues todava no has empezado a gatear y ya quieres correr Lo que quiero que te des cuenta es que con la informacin que tienes hasta ahora, los ejemplos y esas herramientas de las que dispones, YA puedes incluso echar a correr y que si ste cursillo se parara aqu, con todo lo que tienes, sera suficiente para hacer buenos trabajos. Pero no se para aqu, aunque tampoco esperes que yo te cuente para que sirven todas las funciones, ni que te explique cosas que yo no he utilizado nunca, ni me interesan

También podría gustarte