gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS development > Sprites on both screens

#170160 - headspin - Fri Sep 04, 2009 7:37 pm

I got sprites on both screens working in a game previously but this is my first time using the latest libnds so I am trying to use the helper functions as much as possible.

Anyway I can't seem to get sprites to display on both screens using libnds and the bank layout I'm currently using. I decided to open up the bitmap_sprites demo and add display to the secondary screen. This works fine.

Code:
#include <nds.h>
#include <stdio.h>

//a simple sprite structure
//it is generally preferred to separate your game object
//from OAM
typedef struct 
{
   u16* gfx;
   SpriteSize size;
   SpriteColorFormat format;
   int rotationIndex;
   int paletteAlpha;
   int x;
   int y;
} MySprite;

int main(int argc, char** argv) {

   //three sprites of differing color format
   MySprite spritesMain[] = {
      {0, SpriteSize_32x32, SpriteColorFormat_Bmp, 0, 15, 20, 15},
      {0, SpriteSize_32x32, SpriteColorFormat_256Color, -1, 0, 20, 80},
      {0, SpriteSize_32x32, SpriteColorFormat_16Color, -1, 1, 20, 136}
   };
   
   MySprite spritesSub[] = {
      {0, SpriteSize_32x32, SpriteColorFormat_Bmp, 0, 15, 20, 15},
      {0, SpriteSize_32x32, SpriteColorFormat_256Color, -1, 0, 20, 80},
      {0, SpriteSize_32x32, SpriteColorFormat_16Color, -1, 1, 20, 136}
   };

   videoSetMode(MODE_0_2D);
   videoSetModeSub(MODE_0_2D);

   consoleDemoInit();

   //initialize the sub sprite engine with 1D mapping 128 byte boundary
   //and no external palette support
   oamInit(&oamMain, SpriteMapping_Bmp_1D_128, false);
   oamInit(&oamSub, SpriteMapping_Bmp_1D_128, false);

   vramSetBankA(VRAM_A_MAIN_SPRITE);
   vramSetBankD(VRAM_D_SUB_SPRITE);

   //allocate some space for the sprite graphics
   for(int i = 0; i < 3; i++)
   {
      spritesMain[i].gfx = oamAllocateGfx(&oamMain, spritesMain[i].size, spritesMain[i].format);
      spritesSub[i].gfx = oamAllocateGfx(&oamSub, spritesSub[i].size, spritesSub[i].format);
   }

   //ugly positional printf
   iprintf("\x1b[1;1HDirect Bitmap:");
   iprintf("\x1b[9;1H256 color:");
   iprintf("\x1b[16;1H16 color:");

   //fill bmp sprite with the color red
   dmaFillHalfWords(ARGB16(1,31,0,0), spritesMain[0].gfx, 32*32*2);
   dmaFillHalfWords(ARGB16(1,31,0,0), spritesSub[0].gfx, 32*32*2);
   
   //fill the 256 color sprite with index 1 (2 pixels at a time)
   dmaFillHalfWords((1<<8)|1, spritesMain[1].gfx, 32*32);
   dmaFillHalfWords((1<<8)|1, spritesSub[1].gfx, 32*32);
   
   //fill the 16 color sprite with index 1 (4 pixels at a time)
   dmaFillHalfWords((1<<12)|(1<<8)|(1<<4)|1, spritesMain[2].gfx, 32*32 / 2);
   dmaFillHalfWords((1<<12)|(1<<8)|(1<<4)|1, spritesSub[2].gfx, 32*32 / 2);
   
   //set index 1 to blue...this will be the 256 color sprite
   SPRITE_PALETTE[1] = RGB15(0,31,0);
   //set index 17 to green...this will be the 16 color sprite
   SPRITE_PALETTE[16 + 1] = RGB15(0,0,31);

   //set index 1 to blue...this will be the 256 color sprite
   SPRITE_PALETTE_SUB[1] = RGB15(0,31,0);
   //set index 17 to green...this will be the 16 color sprite
   SPRITE_PALETTE_SUB[16 + 1] = RGB15(0,0,31);

   int angle = 0;

   while(1) {
      for(int i = 0; i < 3; i++) {
         oamSet(
         &oamMain, //sub display
         i,       //oam entry to set
         spritesMain[i].x, spritesMain[i].y, //position
         0, //priority
         spritesMain[i].paletteAlpha, //palette for 16 color sprite or alpha for bmp sprite
         spritesMain[i].size,
         spritesMain[i].format,
         spritesMain[i].gfx,
         spritesMain[i].rotationIndex,
         true, //double the size of rotated sprites
         false, //don't hide the sprite
         false, false, //vflip, hflip
         false //apply mosaic
         );
         
         oamSet(
         &oamSub, //sub display
         i,       //oam entry to set
         spritesSub[i].x, spritesSub[i].y, //position
         0, //priority
         spritesSub[i].paletteAlpha, //palette for 16 color sprite or alpha for bmp sprite
         spritesSub[i].size,
         spritesSub[i].format,
         spritesSub[i].gfx,
         spritesSub[i].rotationIndex,
         true, //double the size of rotated sprites
         false, //don't hide the sprite
         false, false, //vflip, hflip
         false //apply mosaic
         );
      }

      oamRotateScale(&oamMain, 0, angle, (1 << 8), (1<<8));
      oamRotateScale(&oamSub, 0, angle, (1 << 8), (1<<8));

      angle += 64;

      swiWaitForVBlank();

      //send the updates to the hardware
      oamUpdate(&oamMain);
      oamUpdate(&oamSub);
   }
   return 0;
}


The problem is when I change the graphics layout to add support for bg tiles the sprites don't appear on the main screen.

ie.
Code:
vramSetMainBanks(VRAM_A_MAIN_BG, VRAM_B_MAIN_SPRITE, VRAM_C_SUB_BG, VRAM_D_SUB_SPRITE);


When I change the bank layout to the following I get sprites displaying on both screens again but in my game my main screen disappears (I get the sprite on both screens but none of the backgrounds appear on main screen!).

Code:
vramSetMainBanks(VRAM_A_MAIN_SPRITE, VRAM_B_MAIN_BG, VRAM_C_SUB_BG, VRAM_D_SUB_SPRITE);


So my question is why can't I get sprites displaying on both screens using the following memory layout:

Code:
vramSetMainBanks(VRAM_A_MAIN_BG, VRAM_B_MAIN_SPRITE, VRAM_C_SUB_BG, VRAM_D_SUB_SPRITE);


I thought that perhaps it was an issue caused by using bitmap sprites but I changed them all to 256 color sprites and still the same issue.

Here is my video setup code:

Code:
#define BG0_MAP_BASE            26
#define BG0_MAP_BASE_SUB         26

#define BG1_MAP_BASE            27
#define BG1_MAP_BASE_SUB         27

#define BG2_MAP_BASE            28
#define BG2_MAP_BASE_SUB         28

#define BG3_MAP_BASE            30
#define BG3_MAP_BASE_SUB         30

#define BG0_TILE_BASE            7
#define BG0_TILE_BASE_SUB         7

#define BG1_TILE_BASE            5
#define BG1_TILE_BASE_SUB         5

#define BG2_TILE_BASE            6
#define BG2_TILE_BASE_SUB         6

#define BG3_TILE_BASE            0
#define BG3_TILE_BASE_SUB         0

videoSetMode(MODE_0_2D | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D_LAYOUT | DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE | DISPLAY_BG2_ACTIVE | DISPLAY_BG3_ACTIVE);
videoSetModeSub(MODE_0_2D | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D_LAYOUT | DISPLAY_BG1_ACTIVE | DISPLAY_BG2_ACTIVE | DISPLAY_BG3_ACTIVE);

vramSetMainBanks(VRAM_A_MAIN_BG, VRAM_B_MAIN_SPRITE, VRAM_C_SUB_BG, VRAM_D_SUB_SPRITE);

bgInit(0, BgType_Text4bpp, BgSize_T_256x256, BG0_MAP_BASE, BG0_TILE_BASE);
bgInit(1, BgType_Text8bpp, BgSize_T_256x256, BG1_MAP_BASE, BG1_TILE_BASE);
bgInit(2, BgType_Text8bpp, BgSize_T_256x256, BG2_MAP_BASE, BG2_TILE_BASE);
bgInit(3, BgType_Text8bpp, BgSize_T_256x256, BG3_MAP_BASE, BG3_TILE_BASE);

bgInitSub(1, BgType_Text8bpp, BgSize_T_256x256, BG1_MAP_BASE_SUB, BG1_TILE_BASE_SUB);
int bg2Sub = bgInitSub(2, BgType_Text8bpp, BgSize_T_512x256, BG2_MAP_BASE_SUB, BG2_TILE_BASE_SUB);
int bg3Sub = bgInitSub(3, BgType_Text8bpp, BgSize_T_512x256, BG3_MAP_BASE_SUB, BG3_TILE_BASE_SUB);

bgSetControlBits(bg2Sub, BG_PRIORITY_0);
bgSetControlBits(bg3Sub, BG_PRIORITY_2);

lcdMainOnBottom();

// Some dma'ing of tiles and stuff here

oamInit(&oamMain, SpriteMapping_Bmp_1D_128, false);
oamInit(&oamSub, SpriteMapping_Bmp_1D_128, false);

_________________
Warhawk DS | Manic Miner: The Lost Levels | The Detective Game

#170183 - headspin - Sun Sep 06, 2009 6:16 pm

I'm still having trouble with this. I thought I would attach the bitmap example that uses my memory layout and has been modified to show sprites on both screens. Can anyone explain why the sprites don't display on both screens?

bitmap_sprites.zip
_________________
Warhawk DS | Manic Miner: The Lost Levels | The Detective Game

#170185 - vuurrobin - Sun Sep 06, 2009 6:45 pm

it seems to be exactly like this problem:
http://forums.devkitpro.org/viewtopic.php?f=6&t=1432

unfortunately, I don't know how to fix this.

#170186 - DiscoStew - Sun Sep 06, 2009 7:55 pm

This is just a guess in the dark, but are the background/Sprite functions taking into consideration the OFFSET value when using the banks a certain way? What I mean is this...


Code:
Taken from video.h

(this is what the sprite demo uses)
VRAM_A_MAIN_SPRITE = 2
VRAM_D_SUB_SPRITE = 4

(this is what you are trying to use)
VRAM_A_MAIN_BG = 1
VRAM_B_MAIN_SPRITE = 2 | VRAM_OFFSET(1)
VRAM_C_SUB_BG = 4
VRAM_D_SUB_SPRITE = 4


Notice that with the set up you had, the only one with an offset value is for your sprites on your main screen, of which they aren't appearing?

Also...
Code:
VRAM_A_MAIN_SPRITE = 2
VRAM_B_MAIN_BG = 1 | VRAM_OFFSET(1),
VRAM_C_SUB_BG = 4
VRAM_D_SUB_SPRITE = 4


Now there is an offset for the backgrounds on the main screen instead of the sprite, and as you said, the backgrounds won't show up?

Again, this is purely speculation, as I have never touched these recently added functions (yet), but it seems to me that thre must be something involving them.
_________________
DS - It's all about DiscoStew

#170187 - headspin - Sun Sep 06, 2009 9:06 pm

Thanks for putting me on the right track DiscoStew! I noticed the following define's in video.h

Code:
VRAM_B_MAIN_SPRITE   = 2 | VRAM_OFFSET(1),
VRAM_B_MAIN_SPRITE_0x06400000   = 2,


So I tried changing to use the following banks

Code:
vramSetMainBanks(VRAM_A_MAIN_BG, VRAM_B_MAIN_SPRITE_0x06400000, VRAM_C_SUB_BG, VRAM_D_SUB_SPRITE);


Yay! Sprites working on both screens :)
_________________
Warhawk DS | Manic Miner: The Lost Levels | The Detective Game

#170188 - DiscoStew - Sun Sep 06, 2009 9:43 pm

I'm glad that your problem got fixed, but it also shows a limitation to the background/sprite functions in it's current form. I'll make a suggestion on the devkitPro forums about it.
_________________
DS - It's all about DiscoStew

#170192 - sverx - Mon Sep 07, 2009 10:25 am

I think the problem is about the definitions. I mean, if you define

VRAM_x_MAIN_SPRITE

you will be thinking that you're using that x bank 'left aligned' in the memory space (and if you don't need another bank you won't even worry about how to declare a 'second' bank...)

So actually I think there should be another set of defines, leaving those already defined for backward compatibilty. Something like

VRAM_x_MAIN_SPRITE_1ST_BANK
VRAM_x_MAIN_SPRITE_2ND_BANK

for instance (or something that sounds better but has the same meaning). The same with backgrounds and textures (if you check, there's the same problem...)

My 2 cents.

#170193 - vuurrobin - Mon Sep 07, 2009 1:39 pm

those defines are already there in the vram enum. from libnds's video.h:

Code:
/** \brief  Allowed VRAM bank A modes */
typedef enum {
   VRAM_A_LCD   =   0,
   VRAM_A_MAIN_BG  = 1,
   VRAM_A_MAIN_BG_0x06000000   = 1 | VRAM_OFFSET(0),
   VRAM_A_MAIN_BG_0x06020000   = 1 | VRAM_OFFSET(1),
   VRAM_A_MAIN_BG_0x06040000   = 1 | VRAM_OFFSET(2),
   VRAM_A_MAIN_BG_0x06060000   = 1 | VRAM_OFFSET(3),
   VRAM_A_MAIN_SPRITE = 2,
   VRAM_A_MAIN_SPRITE_0x06400000   = 2,
   VRAM_A_MAIN_SPRITE_0x06420000   = 2 | VRAM_OFFSET(1),
   VRAM_A_TEXTURE = 3,
   VRAM_A_TEXTURE_SLOT0   = 3 | VRAM_OFFSET(0),
   VRAM_A_TEXTURE_SLOT1   = 3 | VRAM_OFFSET(1),
   VRAM_A_TEXTURE_SLOT2   = 3 | VRAM_OFFSET(2),
   VRAM_A_TEXTURE_SLOT3   = 3 | VRAM_OFFSET(3)
} VRAM_A_TYPE;


see that VRAM_A_MAIN_SPRITE and VRAM_A_MAIN_SPRITE_0x06400000 have the same value, and that VRAM_A_MAIN_SPRITE_0x06420000 would represent VRAM_x_MAIN_SPRITE_2ND_BANK.

I think that VRAM_A_MAIN_SPRITE is only ment be used when you have 1 bank, and the other enum values when you have multiple banks.
_________________
my blog:
http://vuurrobin.100webcustomers.com/

#170194 - sverx - Mon Sep 07, 2009 1:55 pm

vuurrobin wrote:
see that VRAM_A_MAIN_SPRITE and VRAM_A_MAIN_SPRITE_0x06400000 have the same value, and that VRAM_A_MAIN_SPRITE_0x06420000 would represent VRAM_x_MAIN_SPRITE_2ND_BANK.

I think that VRAM_A_MAIN_SPRITE is only ment be used when you have 1 bank, and the other enum values when you have multiple banks.


Yes, it works with VRAM_A, it's different with VRAM_B, for instance...

Code:
typedef enum {
   VRAM_B_LCD = 0,
   VRAM_B_MAIN_BG   = 1 | VRAM_OFFSET(1),
   VRAM_B_MAIN_BG_0x06000000   = 1 | VRAM_OFFSET(0),
   VRAM_B_MAIN_BG_0x06020000   = 1 | VRAM_OFFSET(1),
   VRAM_B_MAIN_BG_0x06040000   = 1 | VRAM_OFFSET(2),
   VRAM_B_MAIN_BG_0x06060000   = 1 | VRAM_OFFSET(3),
   VRAM_B_MAIN_SPRITE   = 2 | VRAM_OFFSET(1),
   VRAM_B_MAIN_SPRITE_0x06400000   = 2,
   VRAM_B_MAIN_SPRITE_0x06420000   = 2 | VRAM_OFFSET(1),
   VRAM_B_TEXTURE   = 3 | VRAM_OFFSET(1),
   VRAM_B_TEXTURE_SLOT0   = 3 | VRAM_OFFSET(0),
   VRAM_B_TEXTURE_SLOT1   = 3 | VRAM_OFFSET(1),
   VRAM_B_TEXTURE_SLOT2   = 3 | VRAM_OFFSET(2),
   VRAM_B_TEXTURE_SLOT3   = 3 | VRAM_OFFSET(3)
} VRAM_B_TYPE;


in this case, if you use VRAM_B_MAIN_SPRITE and you're using only 1 bank, it's "wrong" (it's not allocated where GFX[] points...)

So my 2 cents were about defining also something like this, or similar... (ex: for bank B)

Code:
   VRAM_B_MAIN_BG_1ST_BANK   = 1 | VRAM_OFFSET(0),
   VRAM_B_MAIN_BG_2ND_BANK   = 1 | VRAM_OFFSET(1),
   VRAM_B_MAIN_BG_3RD_BANK   = 1 | VRAM_OFFSET(2),
   VRAM_B_MAIN_BG_4TH_BANK   = 1 | VRAM_OFFSET(3),
   VRAM_B_MAIN_SPRITE_1ST_BANK   = 2 | VRAM_OFFSET(0),
   VRAM_B_MAIN_SPRITE_1ND_BANK   = 2 | VRAM_OFFSET(1)


which would be easier. IMHO of course.

#170202 - Echo49 - Mon Sep 07, 2009 11:50 pm

I would prefer VRAM_B_MAIN_BG_BANK_n rather than mixing up ST, ND, RD and TH, which might be unintuitive to some people.

#170208 - sverx - Tue Sep 08, 2009 1:28 pm

Yep, that's true... maybe then from BANK0 to BANK3?

#170209 - Cearn - Tue Sep 08, 2009 3:31 pm

Or _SLOT0-3, then it's consistent with the texture constants as well.

I'm not sure that adding an additional set to the already long list of VRAM bank constants is a good idea though. It may simplify the definitions themselves, but because the addresses that some of the configurations attach to is sometimes non-intuitive (for example, sub-BG can only use bank I at 0600:8000), removing the addresses from the names hides which address ranges are accessible, which can be problematic.

Also, +1 to VRAM_B_MAIN_SPRITE's current definition being somewhat awkward.

#170210 - sverx - Tue Sep 08, 2009 3:50 pm

Cearn wrote:
Or _SLOT0-3, then it's consistent with the texture constants as well.


... right :)

Cearn wrote:
I'm not sure that adding an additional set to the already long list of VRAM bank constants is a good idea though. It may simplify the definitions themselves, but because the addresses that some of the configurations attach to is sometimes non-intuitive (for example, sub-BG can only use bank I at 0600:8000), removing the addresses from the names hides which address ranges are accessible, which can be problematic.


we could define these:

Code:
VRAM_C_SUB_BG_SLOT01 = VRAM_C_SUB_BG
VRAM_H_SUB_BG_SLOT0  = VRAM_H_SUB_BG
VRAM_I_SUB_BG_SLOT1  = VRAM_I_SUB_BG_0x06208000


so that it will be clear that you can use VRAM bank C o_OR_ VRAM bank (H and/or I)...

(well, it's not that this way it becomes really simple...)