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 > Mixing 3D, backgrounds and keyboard

#178041 - Rajveer - Thu Aug 15, 2013 8:18 pm

I'm currently using MODE_5 for both my main and sub screens. I'm rendering 3D on my main screen with a background overlaying it, and drawing a background on the sub screen too. This works fine, however now I'm trying to add a keyboard to the sub screen, which I'm having trouble with. Here is my setup code:

Code:
//Main Screen as OpenGL with background overlayed, subscreen as a background with keyboard overlayed
   videoSetMode(MODE_5_3D | DISPLAY_BG3_ACTIVE);
   videoSetModeSub(MODE_5_2D | DISPLAY_BG3_ACTIVE | DISPLAY_BG2_ACTIVE);

   //Set VRAM Banks (can be moved to specific menu/game functions if required
   vramSetBankA(VRAM_A_TEXTURE); // 128 KB
   vramSetBankB(VRAM_B_TEXTURE); // 128 KB
   vramSetBankC(VRAM_C_SUB_BG); // 128 KB
   vramSetBankD(VRAM_D_MAIN_BG_0x06000000); // 128 KB
   
   vramSetBankE(VRAM_E_TEX_PALETTE); // 64 KB
   vramSetBankF(VRAM_F_TEX_PALETTE); // 16 KB

   bgInit(3, BgType_Bmp16, BgSize_B16_256x256, 0, 0);
   bgInitSub(3, BgType_Bmp16, BgSize_B16_256x256, 0, 0);
   keyboardInit(NULL, 2, BgType_Text4bpp, BgSize_T_256x512, 48, 6, false, true);

   bgSetPriority(0, 2); //3D layer 0 priority 2
   bgSetPriority(3, 1); //Bitmap layer 3 priority 1
   bgSetPriority(2, 3); //Keyboard layer 2 priority 0


however I can't get the keyboard to show. I'm sure I'm using the wrong base for map and/or tile data in keyboardInit. I'm trying to use the memory for sub background past 256*192 pixels for the keyboard so for the sub screen, a 16bpp background with 256*192 pixels takes up 98304 bytes, so thats 6 for the tile map (as a base is 16KB) and 48 for the map data (as a base is 2KB), but I guess I offset the map data by how much the tile data is using too? The source for the default keyboard says map data is at 20, so that would be 68 for me, which is out of the bank...so not possible?


Last edited by Rajveer on Sun Sep 15, 2013 7:29 pm; edited 1 time in total

#178042 - elhobbs - Fri Aug 16, 2013 2:57 pm

it is possible to push the BG down and have the keyboard first. this should work here

Code:

keyboardInit(NULL, 2, BgType_Text4bpp, BgSize_T_256x512, 2, 0, false, true);
bgInitSub(3, BgType_Bmp16, BgSize_B16_256x192, 1, 0);


also I think you have VRAM_E and VRAM_F mapped to the same place. you could also throw G in there as well as you cannot use it for much else in this config here
Code:

vramSetBankE(VRAM_E_TEX_PALETTE);
vramSetBankF(VRAM_F_TEX_PALETTE_SLOT4);
vramSetBankG(VRAM_G_TEX_PALETTE_SLOT5);


you would have a lot more options if you were using 8bit backgrounds instead of 16bit.

#178043 - Cydrak - Sat Aug 17, 2013 7:06 pm

Rajveer wrote:
The source for the default keyboard says map data is at 20, so that would be 68 for me, which is out of the bank...so not possible?

You've got it right. Here's the problem:
keyboardGfx.png

That's 40K of tiles plus another 4K for the map, way too much. Since the subscreen is so cramped for memory, you don't have a lot of options here. Dropping to 8bpp will work, if you're okay with sharing palettes.

Probably the best thing you can do is redraw the keyboard. If you look again at keyboardGfx.png, you may notice the bottom half looks like it should be a palette swap... except, for some reason, it isn't. Since most will put the keyboard on subscreen, it's pretty wasteful. Especially since the top half reduces to 14K of unique tiles.

What you want is to copy the top over the bottom half, but add 16 to all the colors, so they use another subpalette. This is unfortunately difficult without a good pixel editor, but here's a quick edit to show what I mean. (There may be a better way to handle this; I haven't used grit too much.)

You can then reduce the PNG to a 4bpp tilemap with a 32-color palette:
Code:
grit myKeyboard.png -gB4 -mR4 -pe32

and fit the entire BG in 14K + 4K, since the only differences in the bottom half are map data. To use this, you need to modify the default keyboard struct before calling keyboardInit:
Code:
#include "myKeyboard.h"
...
  Keyboard *keyboard = keyboardGetDefault();
  KeyMap   *lower    = keyboard->mappings[0];
  KeyMap   *upper    = keyboard->mappings[1];

  keyboard->tiles        = (u16*)myKeyboardTiles;
  keyboard->tileLen      = myKeyboardTilesLen;
  keyboard->palette      = (u16*)myKeyboardPal;
  keyboard->paletteLen   = myKeyboardPalLen;
 
  upper->mapDataReleased = myKeyboardMap +  0*32;
  lower->mapDataReleased = myKeyboardMap + 10*32;
  upper->mapDataPressed  = myKeyboardMap + 20*32;
  lower->mapDataPressed  = myKeyboardMap + 30*32;

By the way, a couple other minor things. BG2 is an extended type in mode 5. Since extended tilemaps are always 8bpp, that won't work with the keyboard, but you can use BG1 or change to mode 3.

Also it isn't normally possible to put a map above 64K, or tiles above 256K. The offset values are limited to 31 and 15, respectively, so as already mentioned, you should move the bitmap higher. (It's actually possible to do it your way, but the solution is a little more involved.)

#178044 - elhobbs - Sun Aug 18, 2013 9:37 am

The default keyboard graphics are already converted to 4bit.

#178045 - Cydrak - Sun Aug 18, 2013 6:03 pm

elhobbs wrote:
The default keyboard graphics are already converted to 4bit.

That's exactly the problem, they are 4bpp already, and the "pressed" keys have unique graphics, despite not being noticeably different.

It seems I overlooked grit's defaults, which do at least tile-reduce the original graphics to around 28K + 4K (although this does contradict the libnds source as well as comments in <nds/arm9/keyboard.h>...). However, they'll still be around half the size when subpalettes are used correctly. In some cases, this is the difference between being stuck with the keyboard and actually being able to draw something else on the subscreen.

#178046 - elhobbs - Mon Aug 19, 2013 5:12 am

Cydrak - I am not sure how you are coming up with 40k or even 28k for the keyboard. at most the 4bit keyboard will use about 8k tiles (128*64) + 2.5k map. also the default configuration (as this example is using) causes only half of the tiles to be loaded to vram - just the top part of the keyboard image is loaded into vram. Am I missing something?

#178048 - Rajveer - Mon Aug 19, 2013 3:43 pm

Thanks for the feedback guys. I tried your suggestion elhobbs but that didn't work out as expected (my background was pushed down and the keyboard was still not showing). I'm using 16bit backgrounds as I'm drawing my GUI with WoopsiGfx which uses this format. In the end I whipped up my own temporary keyboard, not ideal but it works for now.

Later I'll probably switch over to using the full Woopsi GUI library instead of using my own, that comes with what looks to be a nice keyboard too.

#178049 - elhobbs - Mon Aug 19, 2013 6:35 pm

ok, I was thinking console sizes - so my offsets were off. I actually tested it this time. I used the drunken coders image from the 16bit display example. this does not leave room for a text console on the sub screen, but it does show the keyboard and a 16bit background. I commented the code showing what I changed. the display priority code for the sub screen is still not being done properly, but it works as is. the code is currently only setting priorities for the main display.

edit - this may still not work with whoopsi if it does not correctly detect (or allow to be told etc) that the background does not start at the beging of sub display vram.
edit2 - fixed keyboard map offset to 14 to fixed partial overlap as cydrak pointed out
Code:
/*---------------------------------------------------------------------------------

   Basic template code for starting a DS app

---------------------------------------------------------------------------------*/
#include <nds.h>
#include <stdio.h>
#include "drunkenlogo.h"

//---------------------------------------------------------------------------------
int main(void) {
   int bg_sub_id;
   //Main Screen as OpenGL with background overlayed, subscreen as a background with keyboard overlayed
   videoSetMode(MODE_5_3D | DISPLAY_BG3_ACTIVE);
   
//changed display mode
   //videoSetModeSub(MODE_5_2D | DISPLAY_BG3_ACTIVE | DISPLAY_BG2_ACTIVE);
   videoSetModeSub(MODE_3_2D | DISPLAY_BG3_ACTIVE | DISPLAY_BG2_ACTIVE);

   //Set VRAM Banks (can be moved to specific menu/game functions if required
   vramSetBankA(VRAM_A_TEXTURE); // 128 KB
   vramSetBankB(VRAM_B_TEXTURE); // 128 KB
   vramSetBankC(VRAM_C_SUB_BG); // 128 KB
   vramSetBankD(VRAM_D_MAIN_BG_0x06000000); // 128 KB

//changed mapping for tex palette
   //vramSetBankE(VRAM_E_TEX_PALETTE); // 64 KB
   //vramSetBankF(VRAM_F_TEX_PALETTE); // 16 KB
   vramSetBankE(VRAM_E_TEX_PALETTE);
   vramSetBankF(VRAM_F_TEX_PALETTE_SLOT4);
   vramSetBankG(VRAM_G_TEX_PALETTE_SLOT5);

   bgInit(3, BgType_Bmp16, BgSize_B16_256x256, 0, 0);
   
//change layout for sub screen vram
   //bgInitSub(3, BgType_Bmp16, BgSize_B16_256x256, 0, 0);
   //keyboardInit(NULL, 2, BgType_Text4bpp, BgSize_T_256x512, 48, 6, false, true);
   keyboardInit(NULL, 2, BgType_Text4bpp, BgSize_T_256x512, 14, 0, false, true);
   bg_sub_id = bgInitSub(3, BgType_Bmp16, BgSize_B16_256x256, 2, 0);

   bgSetPriority(0, 2); //3D layer 0 priority 2
   bgSetPriority(3, 1); //Bitmap layer 3 priority 1
   bgSetPriority(2, 3); //Keyboard layer 2 priority 0
   
   decompress(drunkenlogoBitmap, bgGetGfxPtr(bg_sub_id),  LZ77Vram);
   keyboardShow();

   while(1) {
      int key = keyboardUpdate();
      swiWaitForVBlank();
   }

}


Last edited by elhobbs on Tue Aug 20, 2013 12:28 am; edited 2 times in total

#178050 - Cydrak - Mon Aug 19, 2013 7:00 pm

The 40k is from the comment at the top of <nds/arm9/keyboard.h>, which turned out to be incorrect. That is what you'd get if you didn't de-duplicate. 32 * 40 tiles * 32 bytes/tile.

Running grit on the default graphics gives ~28k of (unpacked) tile data. In libnds, this is decompressed and loaded all at once in keyboardInit(). It's true you can only see about 9k of tiles onscreen, but the rest are still loaded: it's the map and not the tiles which are being redrawn at runtime.

Due to the way the map has been scrolled, it would be awkward to reuse the offscreen space, and so that effectively needs all 4k as well.

If you check the VRAM map in desmume you can clearly see well over 8k of tiles. (The keyboard's map is visible down at 40k, with the console and font another 4k past that.)

ED: Yes, you should be able to squeeze in a 16bpp bitmap with the keyboard, but absolutely nothing else. I would use a map offset of 14 otherwise you'll probably corrupt the tail end of the highlighted lowercase graphics.

#178051 - elhobbs - Tue Aug 20, 2013 12:29 am

cydrak thanks for the detailed explanation - I was think of the space for a console rather than the keyboard.

#178053 - Rajveer - Tue Aug 20, 2013 2:31 pm

I tried your example elhobbs and it works perfectly, thanks guys for working it out! WoopsiGfx accepts a pointer to the memory where your background memory begins, so I'm using BG_BMP_RAM_SUB(2). Now to rework my code to use the default keyboard :)