Para comenzar:
- Nivel de dificultad: fácil.
- Lenguaje: ANSI C/C++.
- Herramientas: SaturnOrbit, 3Deditor
Copiamos el ejemplo S_2_2 de la carpeta C:SaturnOrbitSGL_302jSAMPLE en la raiz del disco C: ó donde quieran.
Cambiamos las rutas necesarias para que compile, si no sabes como hacerlo aca podes ver el como.
Lo que tenemos que saber, es que existen dos formas de tomar datos en Sega Saturn, ó lo hacemos levantando un binario desde el directorio del CD ó
lo incluimos dentro del main.c . Nosotros vamos a ver la segunda opción.
En Sega Saturn, las texturas y los sprites son considerados la misma cosa.
Vamos a prepartar los datos necesarios.
Utilizando la herramienta 3Deditor dentro del SegaSaturnSDK (C:SaturnOrbitTOOLSSSSDK) .
La abrimos, vamos a View y tildamos texture list.
Antes debemos saber que todas las imagenes deben ser multipo de 8, tanto en x como en y. En mi caso son todas de 48x48.
Normalmente los estandares para estas consolas son de 16x16,32x32 ó 64x64.
El color usado como transparencia, es el 0x0000 osea el negro. Por lo tanto todo sprite que tenga este color lo debes cambiar por 0x8000 así de esta forma se verá.
Como mis sprites tiene este color los voy a reemplazar uno por uno por 0x8000, usen algun editor de texto, sino va a largar todo por la ventana .
Luego para obtener las tranparencias, yo utilize el Magenta (es horrible) que es el color 0xfc1f a este lo cambio por el 0x0000 (que es el que Saturn no pinta).
Vamos a File, Import y seleccionamos las imagenes que queremos.
Guardamos el archivo con formato .TXR; en mi caso SonicX.TXR
Un Archito TXR tiene las siguientes estructuras:
- Uint16 texture_SonicX000[] : Son los datos de los colores, pixel por pixel en 15bits.
- TEXTURE tex_SonicX[] : Es la tabla con las sprites/texturas medidas en bytes.
- PICTURE pic_SonicX[]: Es la tabla sprites/texturas determinando el color de cada uno de los sprites. Podemos utilizar distintos tipos de colores por cada uno.
- SPR_ATTR attr_SonicX[] : Aca van los atriburos de los sprites, esta tabla la tenemos que escribir a mano, todos los sprites son llamados desde esta tabla, ademas podemos especificar si tiene flip, grourad y paleteado.
En mi caso tengo dos tablas, una con los Datos originales, y otra con los invertidos (con flip horizontal)
Les dejo las talbas que deben pegar donde quiera, yo las pegue junto al SonicX.TXR
Uint16 texture_SonicX000[] = {}
........
Uint16 texture_SonicX010[] = {}
TEXTURE tex_SonicX[] = {}
TEXTBL( 48, 48, CGADDRESS+0),
TEXTBL( 48, 48, CGADDRESS+4608),
..............
TEXTBL( 48, 48, CGADDRESS+46080),
};
PICTURE pic_SonicX[] = {
PICTBL( 0, COL_32K, texture_SonicX000),
...........
PICTBL( 10, COL_32K, texture_SonicX010),
};
SPR_ATTR attr_SonicX[] = {
SPR_ATTRIBUTE(0,No_Palet,No_Gouraud,CL32KRGB|SPenb|ECdis,sprNoflip),
............
SPR_ATTRIBUTE(10,No_Palet,No_Gouraud,CL32KRGB|SPenb|ECdis,sprNoflip),
};
SPR_ATTR attr_SonicX_flip[] = {
SPR_ATTRIBUTE(0,No_Palet,No_Gouraud,CL32KRGB|SPenb|ECdis,sprHflip),
.............
SPR_ATTRIBUTE(10,No_Palet,No_Gouraud,CL32KRGB|SPenb|ECdis,sprHflip),
};
........
Uint16 texture_SonicX010[] = {}
TEXTURE tex_SonicX[] = {}
TEXTBL( 48, 48, CGADDRESS+0),
TEXTBL( 48, 48, CGADDRESS+4608),
..............
TEXTBL( 48, 48, CGADDRESS+46080),
};
PICTURE pic_SonicX[] = {
PICTBL( 0, COL_32K, texture_SonicX000),
...........
PICTBL( 10, COL_32K, texture_SonicX010),
};
SPR_ATTR attr_SonicX[] = {
SPR_ATTRIBUTE(0,No_Palet,No_Gouraud,CL32KRGB|SPenb|ECdis,sprNoflip),
............
SPR_ATTRIBUTE(10,No_Palet,No_Gouraud,CL32KRGB|SPenb|ECdis,sprNoflip),
};
SPR_ATTR attr_SonicX_flip[] = {
SPR_ATTRIBUTE(0,No_Palet,No_Gouraud,CL32KRGB|SPenb|ECdis,sprHflip),
.............
SPR_ATTRIBUTE(10,No_Palet,No_Gouraud,CL32KRGB|SPenb|ECdis,sprHflip),
};
Una vez que tenmos todo esto listo, vamos a comenzar con el codigo
primero debemos inlcuir en nuestro main.c el archivo SonicX.TXR
#include "SonicX.txr"
Ahora necesitamos inicializar la lista con los sprites/texturas.
slInitSystem(TV_320x240, tex_SonicX , 1);
Luego necesitamos copiar los sprites/texturas a la memoria del VDP1.
Con la siguiente función (la extraje de un ejemplo) copiamos los sprites a la memoria, asi que debemos incluirla en el main.c .
static void set_sprite(PICTURE *pcptr, Uint32 NbPicture, TEXTURE *texptr)
{
TEXTURE *txptr;
for(; NbPicture-- > 0; pcptr++){
txptr = texptr + pcptr->texno;
slDMACopy((void *)pcptr->pcsrc,
(void *)(SpriteVRAM + ((txptr->CGadr) << 3)),
(Uint32)((txptr->Hsize * txptr->Vsize * 4) >> (pcptr->cmode)));
}
}
Para llamar a esta funcion le pasamos los parametros correspondientes
set_sprite(pic_SonicX, 11, tex_SonicX);
El segundo parametro indica, la cantidad de sprites/texturas que tenemos, si te equivocas, el sistema va a leer una posición erronea y se va a corromper algun sprite (lo vas a ver feo).
Ahora vamos a mostrar dentro del bucle while los sprites. Los sprites se deben mostrar siempre y debe existir la función slSynch() para sincronizar con el sistema.
Según los manuales hay varias funciones para mostrar sprite, vamos a ver la más simple.
slDispSprite(FIXED pos, SPR_ATTR *atr, ANGLE Zrot);
El primer parametro es un vector, de cuatro componentes que indican, X,Y,Z y S que es la escala.
El segundo es la tabla que tuvimos que escribir a mano, le tenemos que pasar la variable más el número de sprite que queremos mostrar.
El Tercero, es el angulo de rotación en la que se va a mostrar.
Por ejemplo para ver el escudo re River Plate :
El Sprite de River es el numero 11, pero como comienza de 0, entonces es el 10.
FIXED pos_river[XYZS] = {toFIXED(60.0), toFIXED(60.0), toFIXED(169), toFIXED(1.0)};
ANGLE ang_river = DEGtoANG(0.0);
slDispSprite(pos_river, attr_SonicX + 10, ang_river);
Ahora ponemos una variable para que paresca un sprite y no una imagen estática.
Uint32 sprite_walk_ID=0;
Uint32 sprite_running_ID=6;
Estas son para Sonic caminando y corriendo respectivamente. En cada ciclo debemos incrementar el número, pero también controlar que no se pase, sino va a mostrar otros sprites y no queremos eso .
Entonces cada vez que se pase, lo volvemos al inicio:
if(sprite_walk_ID>=5)
{ sprite_walk_ID=0;
}
if(sprite_running_ID>=9)
{ sprite_running_ID=6;
}
Con Sonic caminando muestro los sprites usando la primera trabla que creé.
slDispSprite(pos1, attr_SonicX + sprite_walk_ID, ang1);
Con Sonic corriendo muestro los sprites usando la segunda trabla (la invertida).
slDispSprite(pos2, attr_SonicX_flip + sprite_running_ID, ang1);
Todo esto me sirve para ahorrar memoria y no tener que cargar los sprites invertidos.
Les paso el código del main.c
#include "sgl.h"
#include "SonicX.txr"
static void set_sprite(PICTURE *pcptr, Uint32 NbPicture, TEXTURE *texptr)
{
TEXTURE *txptr;
for(; NbPicture-- > 0; pcptr++){
txptr = texptr + pcptr->texno;
slDMACopy((void *)pcptr->pcsrc,
(void *)(SpriteVRAM + ((txptr->CGadr) << 3)),
(Uint32)((txptr->Hsize * txptr->Vsize * 4) >> (pcptr->cmode)));
}
}
void ss_main(void)
{
static ANGLE ang[XYZ];
static FIXED pos[XYZ];
//Inicializamos el sistema con los sprites----------------------------
slInitSystem(TV_320x240, tex_SonicX , 1);
//Pintamos el fondo de color azul, asi comprobamos que el negro se ve.
slBack1ColSet((void *)BACK_CRAM, CD_Blue);
//Cargamos al VDP1 los sprites----------------------------------------
set_sprite(pic_SonicX, 11, tex_SonicX);
slPrint("VDP1 - Sprite Manipulation", slLocate(9,2));
slPrint("www.segasaturno.com", slLocate(9,3));
slPrint("Jano Dev Team - FacundoARG",slLocate(12,28));
FIXED pos1[XYZS] = {toFIXED(-60.0), toFIXED(0.0), toFIXED(169), toFIXED(1.0)};
ANGLE ang1 = DEGtoANG(0.0);
FIXED pos2[XYZS] = {toFIXED(60.0), toFIXED(0.0), toFIXED(169), toFIXED(1.0)};
FIXED pos_river[XYZS] = {toFIXED(0.0), toFIXED(0.0), toFIXED(169), toFIXED(1.0)};
ANGLE ang_river = DEGtoANG(0.0);
//Para usar con el escudo de River Plate
pos[X] = toFIXED( 0.0);
pos[Y] = toFIXED( 0.0);
Uint32 sprite_walk_ID=0;
Uint32 sprite_running_ID=6;
while(-1){
if((Per_Connect1) == 0)
{slPrint("No conectaste el PAD",slLocate(2,20));}
//Pintamos los sprites.----------------------------------------
slDispSprite(pos1, attr_SonicX + sprite_walk_ID, ang1);
slDispSprite(pos2, attr_SonicX_flip + sprite_running_ID, ang1);
slDispSprite(pos_river, attr_SonicX + 10, ang_river);
//Incrementamos la posicion en la tabla-------------------------
sprite_walk_ID++;
sprite_running_ID++;
if(sprite_walk_ID>=5)
{ sprite_walk_ID=0;}
if(sprite_running_ID>=9)
{ sprite_running_ID=6;}
Uint8 index=0;
//Controlando KEY-PAD------------------------------
if( (Smpc_Peripheral[index].data & PER_DGT_KU) == 0)
{ pos[Y] = pos[Y] - toFIXED( 5.0);
pos_river[Y] = pos[Y];
}
if( (Smpc_Peripheral[index].data & PER_DGT_KR) == 0)
{ pos[X] = pos[X] + toFIXED( 5.0);
pos_river[X] = pos[X];
}
if( (Smpc_Peripheral[index].data & PER_DGT_KD) == 0)
{ pos[Y] = pos[Y] + toFIXED( 5.0);
pos_river[Y] = pos[Y];
}
if( (Smpc_Peripheral[index].data & PER_DGT_KL) == 0)
{ pos[X] = pos[X] - toFIXED( 5.0);
pos_river[X] = pos[X]; }
if( (Smpc_Peripheral[index].data & PER_DGT_TL) == 0)
{ang_river = ang_river - DEGtoANG(5.0);}
if( (Smpc_Peripheral[index].data & PER_DGT_TR) == 0)
{ang_river = ang_river + DEGtoANG(5.0);}
slSynch();
}
}
#include "SonicX.txr"
static void set_sprite(PICTURE *pcptr, Uint32 NbPicture, TEXTURE *texptr)
{
TEXTURE *txptr;
for(; NbPicture-- > 0; pcptr++){
txptr = texptr + pcptr->texno;
slDMACopy((void *)pcptr->pcsrc,
(void *)(SpriteVRAM + ((txptr->CGadr) << 3)),
(Uint32)((txptr->Hsize * txptr->Vsize * 4) >> (pcptr->cmode)));
}
}
void ss_main(void)
{
static ANGLE ang[XYZ];
static FIXED pos[XYZ];
//Inicializamos el sistema con los sprites----------------------------
slInitSystem(TV_320x240, tex_SonicX , 1);
//Pintamos el fondo de color azul, asi comprobamos que el negro se ve.
slBack1ColSet((void *)BACK_CRAM, CD_Blue);
//Cargamos al VDP1 los sprites----------------------------------------
set_sprite(pic_SonicX, 11, tex_SonicX);
slPrint("VDP1 - Sprite Manipulation", slLocate(9,2));
slPrint("www.segasaturno.com", slLocate(9,3));
slPrint("Jano Dev Team - FacundoARG",slLocate(12,28));
FIXED pos1[XYZS] = {toFIXED(-60.0), toFIXED(0.0), toFIXED(169), toFIXED(1.0)};
ANGLE ang1 = DEGtoANG(0.0);
FIXED pos2[XYZS] = {toFIXED(60.0), toFIXED(0.0), toFIXED(169), toFIXED(1.0)};
FIXED pos_river[XYZS] = {toFIXED(0.0), toFIXED(0.0), toFIXED(169), toFIXED(1.0)};
ANGLE ang_river = DEGtoANG(0.0);
//Para usar con el escudo de River Plate
pos[X] = toFIXED( 0.0);
pos[Y] = toFIXED( 0.0);
Uint32 sprite_walk_ID=0;
Uint32 sprite_running_ID=6;
while(-1){
if((Per_Connect1) == 0)
{slPrint("No conectaste el PAD",slLocate(2,20));}
//Pintamos los sprites.----------------------------------------
slDispSprite(pos1, attr_SonicX + sprite_walk_ID, ang1);
slDispSprite(pos2, attr_SonicX_flip + sprite_running_ID, ang1);
slDispSprite(pos_river, attr_SonicX + 10, ang_river);
//Incrementamos la posicion en la tabla-------------------------
sprite_walk_ID++;
sprite_running_ID++;
if(sprite_walk_ID>=5)
{ sprite_walk_ID=0;}
if(sprite_running_ID>=9)
{ sprite_running_ID=6;}
Uint8 index=0;
//Controlando KEY-PAD------------------------------
if( (Smpc_Peripheral[index].data & PER_DGT_KU) == 0)
{ pos[Y] = pos[Y] - toFIXED( 5.0);
pos_river[Y] = pos[Y];
}
if( (Smpc_Peripheral[index].data & PER_DGT_KR) == 0)
{ pos[X] = pos[X] + toFIXED( 5.0);
pos_river[X] = pos[X];
}
if( (Smpc_Peripheral[index].data & PER_DGT_KD) == 0)
{ pos[Y] = pos[Y] + toFIXED( 5.0);
pos_river[Y] = pos[Y];
}
if( (Smpc_Peripheral[index].data & PER_DGT_KL) == 0)
{ pos[X] = pos[X] - toFIXED( 5.0);
pos_river[X] = pos[X]; }
if( (Smpc_Peripheral[index].data & PER_DGT_TL) == 0)
{ang_river = ang_river - DEGtoANG(5.0);}
if( (Smpc_Peripheral[index].data & PER_DGT_TR) == 0)
{ang_river = ang_river + DEGtoANG(5.0);}
slSynch();
}
}