Dark Bit Factory & Gravity
PROGRAMMING => Other languages => Console homebrew => Topic started by: relsoft on April 17, 2010
-
:updance:
Kind of liking this Homebrew DS coding. I love the framebuffer mode. It's like TPTC on the DS.
-
I'm not into DS coding, but it's something I'd like to learn about.
Where would be a good place to start?
(edit) Sorry, I've asked this question before, and got a good answer. I'll follow it up.
-
I just read some docs on the net and downloaded the DevkitPro package. The example files there are easy to follow.
I've uploaded some NDS files along with sources and screenshots....
BTW, to try this on Windows or DOS, you can DL the No$GBA emulator for free here, then load the NDS file.
http://nocash.emubase.de/gba.htm
/*
NDS Translucent Plasma
Relminator
http://rel.betterwebber.com
*/
#include <nds.h>
#include <stdio.h>
#include <math.h>
// some sine LUTs
u8 lsin1[2048];
u8 lsin2[2048];
u8 lsin3[2048];
// RGB palette
u16 pal[256];
int main(void)
{
// constant sine divisor
const u8 K1 = 32;
const u8 K2 = 12;
const u8 K3 = 28;
// maximum values our dynamic plasmas can move
u16 KSIN1 = u8(K1 *(360/57.3f));
u16 KSIN2 = u8(K2 *(360/57.3f));
u16 KSIN3 = u8(K3 *(360/57.3f));
// precalculate our luts
for (int i = 0; i< ((2048) - 1); i++)
{
lsin1[i] = sin(i/(float)K1) * 32+32;
lsin2[i] = sin(i/(float)K2) * 16+16;
lsin3[i] = sin(i/(float)K3) * 20+20;
}
// generate our 256 color palette
for (int i=0; i<256; i++)
{
u8 r = (u8)(abs(int(16 - 15 * sin(i * M_PI / 16.0f))));
u8 g = (u8)(abs(int(16 - 15 * sin(i * M_PI / 12.0f))));
u8 b = (u8)(abs(int(16 - 15 * sin(i * M_PI / 18.0f))));
pal[i] = RGB15(r,g,b);
}
irqInit();
irqEnable(IRQ_VBLANK);
//initialize the DS Dos-like functionality
consoleDemoInit();
iprintf("\x1b[1;1HTranslucent Plasma!");
iprintf("\x1b[2;1HFramebuffer madness!");
iprintf("\x1b[3;1HRelsoft");
iprintf("\x1b[4;1Hhttp://rel.betterwebber.com");
iprintf("\x1b[6;1HAnya Therese B. Lope");
//set frame buffer mode 0
videoSetMode(MODE_FB0);
//enable VRAM A for writting by the cpu and use
//as a framebuffer by video hardware
vramSetBankA(VRAM_A_LCD);
u8 rot; // translucent factor
u16 counter =0; // frame counter
u16 a = 0, b = 0, ct = 0; // movement deltas
u16 c; // palette color index
while(1)
{
// dynamically move our plasmas
a= (a + 1) % (KSIN1 + 1);
b = (b + 1) % (KSIN2 + 1);
ct = (ct + 1) % (KSIN3 + 1);
// offset 2nd plasma by a factor of 64 * 2 or (-64 to 64)
rot = 64 * (((counter & 1) == 1) | 1);
// inc frame
counter++;
//write to vram directly
short unsigned int *vram_offset = VRAM_A;
for (int ya = 0; ya<SCREEN_HEIGHT; ya++)
{
for (int xa = 0; xa <SCREEN_WIDTH-1; xa++)
{
rot = -rot; // draw plasmas every other pixel
// calculate colors
c = (lsin1[xa + a + rot] + lsin2[ya + b + rot] +lsin3[1024+xa + ya - ct]);
c = (lsin1[xa + a + rot+c] + lsin2[ya + b + rot+c] +lsin3[1024+xa + ya - ct+c]);
// write to vram
*vram_offset++ = pal[c];
}
vram_offset++; // magic
}
swiWaitForVBlank();
}
return 0;
}
-
I'll download the emu and give these a go!
Nice to see you coding btw dude :)
All the very best,
Clyde.
-
I'll download the emu and give these a go!
Nice to see you coding btw dude :)
All the very best,
Clyde.
Yeah, it's been a while since I last coded something worthwhile. My daughter was actually the one who motivated me to learn DS coding as she wanted me to make a game on the DS. Nut its more likely that I'll be making a demo first. :)
-
ahh k+ for bringing some memories back rel!
doesnt look like things have changed much from your code, as too when i was messing about when the ds first come out.
there is a couple of things that can really get the performance of the ds up especially when shifting data around from from SYS_MEM->SYS_MEM SYS_MEM->V_MEM.
and that is the dma blitter and maths co_processor (the latter i only skimmed on but looked like it might have been handy for some perspective correct span lines).
the former is a hardware accelerated memcpy function. the more data shifted in one go the more benifical you will find it. i remeber having some real trouble with a game 25fps then when i discoverd it i hit the vsync.
also the tile modes and oam memory are fun. can be a bit confussing at first but really rewarding when figuared out. i have a tilemap demo that i messed about with plus a few bits and peices that i will dig out.
and if there is any thing i can help with just give a shout.
keep looking threw the includes in the sdk and trying stuff out, you will have some great fun and it really wont be difficult for someone with your skills to crack.
-
Yeah, I've been playing with the oam. I'm also using DMA whenever I can. I don't think the DS has a math coprocessor.
http://rel.betterwebber.com/junk.php?id=100
/*
* SHMUP test
* relminator
* http://rel.betterwebber.com
*
*/
#include <nds.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#define MAX_SPRITES 128
#define MAX_VIPER_BULLETS 24
#define MAX_VIPER_SHOT_DELAY 6
#define VIPER_SHOT_SPEED 8
#define MAX_OPTIONS 2
#define MAX_OPTION_BULLETS 8
#define MAX_OPTION_SHOT_DELAY 10
#define FP_PI 16384
enum E_shot_ID
{
E_SHOT_NORMAL = 0,
E_SHOT_WAVE
};
class Csprite
{
public:
//static u8 current_oamID;
u8 oamID;
s16 x;
s16 y;
u8 width;
u8 height;
bool active;
u16 *gfx;
SpriteColorFormat format;
SpriteSize size;
};
class Cbullet
{
public:
s32 x;
s32 y;
s32 dx;
s32 dy;
s32 angle; // 0-32767
s32 radius; // FP 1.19.12
E_shot_ID shot_ID;
Csprite sprite;
};
class Coption
{
public:
s32 x;
s32 y;
s32 dx;
s32 dy;
s32 angle; //0-32767
s32 radius_x;
s32 radius_y;
Cbullet shots[MAX_OPTION_BULLETS];
Csprite sprite;
};
class Cviper
{
public:
s32 x;
s32 y;
s32 dx;
s32 dy;
u8 shot_level;
Csprite sprite;
};
void start_shots(Cbullet bullet[], s32 start_x, s32 start_y, u8 max);
void start_sine_shots(Cbullet bullet[], s32 start_x, s32 start_y, u8 max);
void do_shots(Cbullet bullet[], u8 max);
void do_options(Coption option[], s32 cx, s32 cy);
Cviper viper;
Cbullet bullets[MAX_VIPER_BULLETS];
Coption options[MAX_OPTIONS];
OamState *oam = &oamMain;
//a sprite constructor
void create_sprite (Csprite &spr,s32 width, s32 height, SpriteSize size, SpriteColorFormat format)
{
static u8 oamID = 0;
spr.oamID = oamID;
spr.active = true;
spr.width = width;
spr.height = height;
spr.size = size;
spr.format = format;
//api: allocate a chunk of sprite graphics memory
spr.gfx = oamAllocateGfx(oam, size, format);
oamID++;
}
//sprite deconstructor
void destroy_sprite(Csprite &spr)
{
spr.active = false;
//api: free the graphics
if(spr.gfx)
{
oamFreeGfx(oam, spr.gfx);
}
spr.gfx = 0;
}
//map our sprite to oam entries
void update_sprite(Csprite &s)
{
//oamSet (OamState *oam, int id, int x, int y, int priority,
// int palette_alpha, SpriteSize size, SpriteColorFormat format,
// const void *gfxOffset, int affineIndex, bool sizeDouble,
// bool hide, bool hflip, bool vflip, bool mosaic)
oamSet(oam,
s.oamID, // id
s.x, s.y, // x,y
0, // priority
0, // palette alpha
s.size, // size
s.format, // color format
s.gfx, // gfx offset
-1, // affine index
false, // double if rotating?
!s.active, // hidden if true
false, // hflip
false, // vflip
false); // apply mosaic?
}
int main(void)
{
videoSetMode(MODE_0_2D);
videoSetModeSub(MODE_0_2D);
vramSetBankA(VRAM_A_MAIN_SPRITE);
vramSetBankB(VRAM_B_MAIN_SPRITE);
vramSetBankD(VRAM_D_SUB_SPRITE);
consoleDemoInit();
iprintf("\x1b[1;1HSHMUP Test alpha");
iprintf("\x1b[2;1HRelminator");
iprintf("\x1b[3;1Hhttp://rel.betterwebber.com");
iprintf("\x1b[5;1HControls:");
iprintf("\x1b[6;1HArrows->move");
iprintf("\x1b[7;1HKey A->Fire weapons");
//api: initialize OAM to 1D mapping with XX byte offsets and no external palette
oamInit(oam, SpriteMapping_1D_128, false);
// let's make a 32x16 ship
int width = 32;
int height = 16;
SpriteSize size = SpriteSize_32x16;
create_sprite (viper.sprite,width, height, size, SpriteColorFormat_256Color);
// allocate gfx ok fill with garbage
if(viper.sprite.gfx)
{
//fill the 256 color sprite with index 1 (2 pixels at a time)
dmaFillHalfWords((1<<8)|1, viper.sprite.gfx, width*height);
}
// let's some a 16x8 bullets
width = 16;
height = 8;
size = SpriteSize_16x8;
for (int i=0; i<MAX_VIPER_BULLETS; i++)
{
create_sprite (bullets[i].sprite, width, height, size, SpriteColorFormat_256Color);
// allocate gfx ok fill with garbage
if(bullets[i].sprite.gfx)
{
//fill the 256 color sprite with random index (2 pixels at a time)
u8 c = rand() % 256;
dmaFillHalfWords((c<<8)|c, bullets[i].sprite.gfx, width*height);
}
bullets[i].sprite.active = false;
}
// let's some a 16x16 options
width = 16;
height = 16;
size = SpriteSize_16x16;
for (int i=0; i<MAX_OPTIONS; i++)
{
create_sprite (options[i].sprite, width, height, size, SpriteColorFormat_256Color);
// allocate gfx ok fill with garbage
if(options[i].sprite.gfx)
{
//fill the 256 color sprite with random index (2 pixels at a time)
u8 c = rand() % 256;
dmaFillHalfWords((c<<8)|c, options[i].sprite.gfx, width*height);
}
//set radius and angle
options[i].angle = i * FP_PI;
options[i].radius_x = 50;
options[i].radius_y = 40;
// setup option bullets
for (int j=0; j<MAX_OPTION_BULLETS; j++)
{
int s_width = 16;
int s_height = 8;
SpriteSize s_size = SpriteSize_16x8;
create_sprite (options[i].shots[j].sprite, s_width, s_height, s_size, SpriteColorFormat_256Color);
// allocate gfx ok fill with garbage
if(options[i].shots[j].sprite.gfx)
{
//fill the 256 color sprite with random index (2 pixels at a time)
u8 c = rand() % 256;
dmaFillHalfWords((c<<8)|c, options[i].shots[j].sprite.gfx, s_width*s_height);
}
options[i].shots[j].sprite.active = false;
}
}
//load a randomly colored palette
for(int i = 0; i < 256; i++)
{
SPRITE_PALETTE[i] = rand();
SPRITE_PALETTE_SUB[i] = rand();
}
u8 viper_shot_delay = MAX_VIPER_SHOT_DELAY;
u8 option_shot_delay = MAX_OPTION_SHOT_DELAY;
viper.x = 10;
viper.y = 96-16;
while(1)
{
int keys = 0;
scanKeys();
keys = keysHeld();
if (keys & KEY_LEFT)
if (viper.x > 0) viper.x -= 3;
if (keys & KEY_RIGHT)
if (viper.x < SCREEN_WIDTH-viper.sprite.width) viper.x += 3;
if (keys & KEY_UP)
if (viper.y > 0) viper.y -= 2;
if (keys & KEY_DOWN)
if (viper.y < SCREEN_HEIGHT-viper.sprite.height) viper.y += 2;
if (keys & KEY_A)
{
viper_shot_delay--;
if(viper_shot_delay == 0)
{
viper_shot_delay = MAX_VIPER_SHOT_DELAY;
start_shots(bullets,viper.x+32,viper.y+4,MAX_VIPER_BULLETS);
start_sine_shots(bullets,viper.x+32,viper.y+4,MAX_VIPER_BULLETS);
}
// options
option_shot_delay--;
if(option_shot_delay == 0)
{
option_shot_delay = MAX_OPTION_SHOT_DELAY;
for (int i=0; i<MAX_OPTIONS; i++)
start_shots(options[i].shots,options[i].x+16,options[i].y+4,MAX_OPTION_BULLETS);
}
}
// copy bullet pos to oam compatible sprite struct
viper.sprite.x = viper.x;
viper.sprite.y = viper.y;
do_shots(bullets,MAX_VIPER_BULLETS);
do_options(options,viper.x+8,viper.y);
// update to oam
// bullets
for (int i=0; i<MAX_VIPER_BULLETS; i++)
{
update_sprite(bullets[i].sprite);
}
// options and option shots
for (int i=0; i<MAX_OPTIONS; i++)
{
update_sprite(options[i].sprite);
for (int j=0; j<MAX_OPTION_BULLETS; j++)
update_sprite(options[i].shots[j].sprite);
}
// viper
update_sprite(viper.sprite);
// wait retrace
swiWaitForVBlank();
//api: updates real oam memory
oamUpdate(oam);
//consoleClear();
//printf("temp= %i \n", Gtemp);
}
return 0;
}
void start_shots(Cbullet bullet[], s32 start_x, s32 start_y,u8 max)
{
for(int i=0; i<max; i++)
{
if (!bullet[i].sprite.active)
{
bullet[i].sprite.active = true;
bullet[i].x = start_x;
bullet[i].y = start_y;
bullet[i].dx = VIPER_SHOT_SPEED;
bullet[i].dy = 0;
bullet[i].shot_ID = E_SHOT_NORMAL;
break;
}
}
}
void start_sine_shots(Cbullet bullet[], s32 start_x, s32 start_y, u8 max)
{
static s16 sine_off = 0;
s16 angle;
sine_off += 512;
for (int j=0; j<2; j++)
{
if (j==0)
angle = FP_PI+sine_off;
else
angle = sine_off;
for (int i=0; i<max; i++)
{
if (!bullet[i].sprite.active)
{
bullet[i].sprite.active = true;
bullet[i].x = start_x;
bullet[i].y = start_y;
bullet[i].dx = VIPER_SHOT_SPEED-2;
bullet[i].dy = start_y;
bullet[i].angle = angle;
bullet[i].radius = 0;
bullet[i].shot_ID = E_SHOT_WAVE;
break;
}
}
}
}
void do_shots(Cbullet bullet[], u8 max)
{
for(int i=0; i<max; i++)
{
if (bullet[i].shot_ID == E_SHOT_NORMAL)
{
bullet[i].x += bullet[i].dx;
bullet[i].y += bullet[i].dy;
}
else
{
bullet[i].angle += 800;
bullet[i].radius += 3000;
s32 dy = (sinLerp(bullet[i].angle)) * (bullet[i].radius);
bullet[i].x += bullet[i].dx;
bullet[i].y = bullet[i].dy + (dy >> 24);
}
if (bullet[i].x>SCREEN_WIDTH)
{
bullet[i].sprite.active = false;
}
// copy bullet pos to oam compatible sprite struct
bullet[i].sprite.x = bullet[i].x;
bullet[i].sprite.y = bullet[i].y;
}
}
void do_options(Coption option[], s32 cx, s32 cy)
{
for(int i=0; i<MAX_OPTIONS; i++)
{
option[i].angle += 256;
option[i].x = cx + ( (cosLerp(option[i].angle) * option[i].radius_x) >> 12);
option[i].y = cy + ( (sinLerp(option[i].angle) * option[i].radius_y) >> 12);
// copy bullet pos to oam compatible sprite struct
option[i].sprite.x = option[i].x;
option[i].sprite.y = option[i].y;
do_shots(option[i].shots, MAX_OPTION_BULLETS);
}
}
I've read the example files from the devkit package and I'm leaning on using the "man" animation scheme instead of the "woman" animation scheme.
Pros of the man animation scheme:
1. GFX are stored in main mem
2. Animations are easier to manage since the frames if a single sprite would share the same oamID.
3. The createsprite function would automatically set the oamID for each sprite (128 at most)
Con:
1. DMA every frame for every sprite that is active. (would that be a big overhead?) You would at most only use 128 DMAcopies.
Pros of the woman scheme:
1. all GFX are in Vram, so it should be faster
Con:
1. oamID would be really cumbersome to manage(I just tried it.)
ie. Allocate gfx for each sprite and some of them share the same oamID so you need to manually set oamIDs for each sprite.
So what do you think is the better approach?
Thanks in advance!
-
wow it looks like your really getting to the meat and bones of it quick.
i think its really a case of prefrence and for clarity and managment i would always go with the man scheme i dont think 128 dma copys are asking too much,when i was flat mapping tris i was doing about that for span lines no probs.
its probably when you get too the 000's mark that it starts becoming irrelevent as too using the cpu or dma.
i personally would only look at the woman approch if speed started becoming a problem.
i defo remember finding info about the maths co-processor burried in the sdk includes and jim telling me ways of possibly wringing a little bit of performance from a maths coprocessor. of course it is intirely possible i picked the include file up wrong i was very early on with my coding and just getting a feel for it.
ahh i found it.
http://yabasicprogramming.yuku.com/topic/1058/t/looks-like-the-ds-has-a-maths-CoProssesor.html
it obviously doesnt have any fpu or anything but it looks like it has some paralel sqrt and div capabilites.
-
I've decided to use the "man" scheme. I also made a fixed point catmull-rom spline routine.
http://rel.betterwebber.com/junk.php?id=101
BTW, it has animated images now.