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 > NitroEngine: 2D Draw GetPixel

#177363 - hacker013 - Fri May 11, 2012 8:54 pm

heyhey ^^,

I wanted to use difference in pixel color for collision, because my game only use some basic colors. So I googled how to get pixel color in opengl, there is the function available called glReadPixel, but unfortunaly it wasn't supported by libnds. So I got an idea, I new NitroEngine supported screenshots. So I found the code which NitroEngine used to retreive the colors from vram. I created the following code from that code:

Code:
u16 drawGetColor(int x, int y)
{
   u16 color = 0;
         
   if(NE_Dual)
   {
      if(y>191)
      {
         color=VRAM_C[256*192-(y-192+1)*256+x];
      } else {
         color=VRAM_D[256*192-(y+1)*256+x];
      }
   } else {
      color=VRAM_D[256*192-(y+1)*256+x];
   }
         
   return color;
}


But this function always returns always 0, I use the basic dual screen setup from NitroEngine. Could somebody point me out what I'm doing wrong?

~Hacker013
_________________
Website / Blog

Let the nds be with you.

#177366 - AntonioND - Sat May 12, 2012 9:08 pm

Do you switch the VRAM banks to LCD mode before reading from them?

#177370 - sverx - Mon May 14, 2012 10:00 am

when a VRAM bank is 'allocated' to some mode (bg memory, for instance) it's no longer accessible thru its VRAM_x location...
_________________
libXM7|NDS programming tutorial (Italiano)|Waimanu DS / GBA|A DS Homebrewer's Diary

#177373 - wintermute - Tue May 15, 2012 4:58 pm

hacker013 wrote:
heyhey ^^,

Could somebody point me out what I'm doing wrong?

~Hacker013


Using pixel colors for collision ...

For most game uses a simple bounding box is more than sufficient, I've *never* seen a case where pixel checking does anything more than make code slow for no good reason.
_________________
devkitPro - professional toolchains at amateur prices
devkitPro IRC support
Personal Blog

#177374 - Dwedit - Tue May 15, 2012 6:27 pm

Worms and other landscape destruction games use pixel collision for their game logic.
_________________
"We are merely sprites that dance at the beck and call of our button pressing overlord."

#177375 - sverx - Wed May 16, 2012 1:16 pm

I bet Lemmings uses that too...
_________________
libXM7|NDS programming tutorial (Italiano)|Waimanu DS / GBA|A DS Homebrewer's Diary

#177384 - SchmendrickSchmuck - Tue May 22, 2012 3:03 pm

DSLiero uses pixel collision as well; It's kind of necessary when having a randomly destructible terrain. Though admittedly there aren't many games that have this.

@hacker013: As mentioned before, in order to get pixel data from textures or other VRAM sources, you need to set the VRAM bank to LCD mode.
Perhaps you could give a more concrete example of what it is you're trying to do? There may be other solutions to your problem.

Either way, if you really want to get a pixel value from a texture:
Code:

u16* data = (u16*)glGetTexturePointer(texId);
int vramTemp = vramSetPrimaryBanks(VRAM_A_LCD, VRAM_B_LCD, VRAM_C_LCD, VRAM_D_LCD);
int vramSubTemp = vramSetBanks_EFG(VRAM_E_LCD, VRAM_F_LCD, VRAM_G_LCD);
int index = posX + posY * texWidth;
int color = data[index];
vramRestorePrimaryBanks(vramTemp);
vramRestoreBanks_EFG(vramSubTemp);
return color;

Note that when using 8bit textures you'll need to work some magic:
Code:

u16 colors = data[index >> 1];
int color = 0;
if(index & 1)
    color = (colors & 0xFF00) >> 8;
else
    color = colors & 0x00FF;

EDIT: posted code that sets color >.<
_________________
http://DSLiero.DennisvanZwieten.com - Liero for NDS!

#177385 - Miked0801 - Tue May 22, 2012 10:38 pm

I'm of the opinion the pixel collision is also not the way to go. It's slow and only works in 2D space.

#177401 - hacker013 - Wed May 30, 2012 4:49 pm

Bounding box are only squares but my game isn't rectangular formed. But I already changed the code to:
Code:

32 drawGetColor(int x, int y)
{
   u32 color = 0;
   
   NE_SpecialEffectPause(true);
   
   if(!NE_Dual)
   {
      vramSetBankD(VRAM_D_LCD);
      
      REG_DISPCAPCNT=DCAP_BANK(3)|DCAP_ENABLE|DCAP_SIZE(3);
      while(REG_DISPCAPCNT & DCAP_ENABLE);
   }
   
   uint32 vramTemp = 0;
   if(NE_Dual) { vramTemp = vramSetMainBanks(VRAM_A_LCD, VRAM_B_LCD, VRAM_C_LCD, VRAM_D_LCD); }
   
   if(NE_Dual)
   {
      if(y>191)
      {
         color=VRAM_C[256*192-(y-192+1)*256+x];
      } else {
         color=VRAM_D[256*192-(y+1)*256+x];
      }
   } else {
      color=VRAM_D[256*192-(y+1)*256+x];
   }
   
   if(NE_Dual) vramRestoreMainBanks(vramTemp);
   
   NE_SpecialEffectPause(false);
   
   //u8 b=(color&31)<<3;
   //u8 g=((color>>5)&31)<<3;
   //u8 r=((color>>10)&31)<<3;
   
   //color=RGB15(r, g, b);
   
   //printf("White: %d, pixel: %d\n", NE_White, color);
         
   return color;
}


I think the pixels are stored as 8bit rgb but I'm not sure.. The code still doesn't work :(
_________________
Website / Blog

Let the nds be with you.

#177403 - SchmendrickSchmuck - Wed May 30, 2012 5:06 pm

I think there's a few issues with this:
1)
Code:
256*192-(y-192+1)*256+x

Why are there two *256 in there? Seems to be pretty easy to go out of bounds with an array index like this

2) What are you trying to do? Get a pixel from the screen, or a pixel from a texture? The two would need entirely different approaches. Your first question suggested the first (for which you can pretty much copy/paste my previous post to work), but if you want a pixel from the screen you'll have to find something else (similar though)

3) Which coordinates are you passing to drawGetColor? They seem to need to be screen coordinates.

4) This approach seems unnecessarily slow since it takes a screenshot for each color call

Either way, since this seems to use screen data, the colors are likely stored in 16bit format
_________________
http://DSLiero.DennisvanZwieten.com - Liero for NDS!

#177404 - hacker013 - Wed May 30, 2012 8:31 pm

I based my code on this code (working)

Code:


//----------------------------------------------------------------
//                      Screenshots
//----------------------------------------------------------------

inline void NE_write16(u16* address, u16 value) {

   u8* first=(u8*)address;
   u8* second=first+1;

   *first=value&0xff;
   *second=value>>8;
}

inline void NE_write32(u32* address, u32 value) {

   u8* first=(u8*)address;
   u8* second=first+1;
   u8* third=first+2;
   u8* fourth=first+3;

   *first=value&0xff;
   *second=(value>>8)&0xff;
   *third=(value>>16)&0xff;
   *fourth=(value>>24)&0xff;
}

extern bool NE_Dual;

int NE_ScreenshotBMP(char * filename)
{   
   FILE* file=fopen(filename, "wb");

   if(file == NULL)
      {
      NE_DebugPrint("NE_ScreenshotBMP: %s could't be opened!", filename);
      return 0;
      }
   
   NE_SpecialEffectPause(true);
   
   if(!NE_Dual)
      {
      vramSetBankD(VRAM_D_LCD);
      
      REG_DISPCAPCNT=DCAP_BANK(3)|DCAP_ENABLE|DCAP_SIZE(3);
      while(REG_DISPCAPCNT & DCAP_ENABLE);
      }

   int ysize = 0;
   
   if(NE_Dual) ysize = 384;
   else ysize = 192;   
   
   u8* temp=(u8*)malloc(256*ysize*3+sizeof(NE_INFO_BMP_HEADER)+sizeof(NE_BMP_HEADER));

   NE_BMP_HEADER* header=(NE_BMP_HEADER*)temp;
   NE_INFO_BMP_HEADER* infoheader=(NE_INFO_BMP_HEADER*)(temp+sizeof(NE_BMP_HEADER));

   NE_write16(&header->type, 0x4D42);
   NE_write32(&header->size, 256*ysize*3+sizeof(NE_INFO_BMP_HEADER)+sizeof(NE_BMP_HEADER));
   NE_write32(&header->offset, sizeof(NE_INFO_BMP_HEADER)+sizeof(NE_BMP_HEADER));
   NE_write16(&header->reserved1, 0);
   NE_write16(&header->reserved2, 0);

   NE_write16(&infoheader->bits, 24);
   NE_write32(&infoheader->size, sizeof(NE_INFO_BMP_HEADER));
   NE_write32(&infoheader->compression, 0);
   NE_write32(&infoheader->width, 256);
   NE_write32(&infoheader->height, ysize);
   NE_write16(&infoheader->planes, 1);
   NE_write32(&infoheader->imagesize, 256*ysize*3);
   NE_write32(&infoheader->xresolution, 0);
   NE_write32(&infoheader->yresolution, 0);
   NE_write32(&infoheader->importantcolors, 0);
   NE_write32(&infoheader->ncolors, 0);

   uint32 vramTemp = 0;
   if(NE_Dual) { vramTemp = vramSetMainBanks(VRAM_A_LCD, VRAM_B_LCD, VRAM_C_LCD, VRAM_D_LCD); }

   int y, x;
   for(y=0;y<ysize;y++)
      {
      for(x=0;x<256;x++)
         {
         u16 color = 0;
         
         if(NE_Dual)
            {
            if(y>191) color=VRAM_C[256*192-(y-192+1)*256+x];
            else color=VRAM_D[256*192-(y+1)*256+x];
            }
         else color=VRAM_D[256*192-(y+1)*256+x];

         u8 b=(color&31)<<3;
         u8 g=((color>>5)&31)<<3;
         u8 r=((color>>10)&31)<<3;

         temp[((y*256)+x)*3+sizeof(NE_INFO_BMP_HEADER)+sizeof(NE_BMP_HEADER)]=r;
         temp[((y*256)+x)*3+1+sizeof(NE_INFO_BMP_HEADER)+sizeof(NE_BMP_HEADER)]=g;
         temp[((y*256)+x)*3+2+sizeof(NE_INFO_BMP_HEADER)+sizeof(NE_BMP_HEADER)]=b;
         }
      }
   
   if(NE_Dual) vramRestoreMainBanks(vramTemp);
   
   DC_FlushAll();
   fwrite(temp, 1, 256*ysize*3+sizeof(NE_INFO_BMP_HEADER)+sizeof(NE_BMP_HEADER), file);
   fclose(file);
   free(temp);
   
   NE_SpecialEffectPause(false);
   
   return 1;
}


1. Because if I'm correct it loops back from the end of the buffer to the beginning... (The code above those the same) :$

2. I'm trying to get a pixel from the screen.

3. The coordinates on the screen.

4. I don't understand the NDS hardware very well so I used the above code as base.

I code I based my work on decodes it as 8Bit if I'm correct.

If you guys know a faster way please tell me. I'm using it in combination with 3D Nitro Engine.
_________________
Website / Blog

Let the nds be with you.

#177407 - PypeBros - Thu May 31, 2012 8:17 am

NDS VRAM is slower to access than main memory, and it doesn't get caching, afaik. If your game logic *requires* pixel-precise collisions, you're much better to have a buffer separated from your VRAM where you keep a mirror of the scene for collision checking.

Oh, and btw, have you thought about the fact that in many cases, a height map (one/few value(s) per column) is as good as a plain screen mirror ?
_________________
SEDS: Sprite Edition on DS :: modplayer

#177409 - hacker013 - Thu May 31, 2012 12:07 pm

But the screen is fully dynamic so then I would have to copy every frame, the whole buffer to the ram, and then search for the pixel. Wouldn't it be faster then to read directly from VRAM ?
_________________
Website / Blog

Let the nds be with you.

#177411 - SchmendrickSchmuck - Thu May 31, 2012 1:29 pm

I think what he means is use a buffer to put to the screen rather than the other way around, or keep a buffer that undergoes the same changes as the screen.

Perhaps you could post a screenshot of your current game to give us a better idea of what you want?
_________________
http://DSLiero.DennisvanZwieten.com - Liero for NDS!

#177415 - hacker013 - Thu May 31, 2012 5:19 pm

The problem was I have everywhere moving objects with different colors. So I thought it would be handy to check if I hit some color with my object to detect the collision.

Sorry, I don't have screenshot.
_________________
Website / Blog

Let the nds be with you.

#177430 - AntonioND - Fri Jun 01, 2012 10:55 pm

What are you trying to do? Are you drawing a 3D scene, capturing it and then reading it from VRAM?

That's a horrible idea, seriously. It's really slow, you'd better use another method.

Screen capturing is only useful if you want 3D on both screens. Other effects are slow and shouldn't be used outside demos or things like that. Doing it for geting data for in game calculations... that's evil.

If you really want to do it, at least copy the buffer to RAM.

But you really should do this without screen capturing...

#177433 - hacker013 - Sun Jun 03, 2012 4:43 pm

How can I otherwise read from the buffer in a array like manner?

#177437 - AntonioND - Tue Jun 05, 2012 2:20 pm

hacker013 wrote:How can I otherwise read from the buffer in a array like manner?
Copy it to RAM. Or don't use that method.

Anyway, the code that has been put in this thread will try to capture the screen whenever you want to read a single pixel. The correct thing would be: capture screen once, copy buffer to ram once, read from it everytime you need it, repeat next frame.

#177461 - PypeBros - Mon Jun 11, 2012 4:26 pm

AntonioND wrote:What are you trying to do? Are you drawing a 3D scene, Doing it for geting data for in game calculations... that's evil.
I suppose you realise that if you just render explosion effects over your screen, or what other special effects, the colors you will read will become different from what the "solid world" should have had. I used that trick back in 1996, and I couldn't count the number of occasions where I ended up with some sprites occasionnally moving into some walls because some colours were temporarily replaced by a text box, etc.

If you want to be a minimum serious with your game development, I strongly suggest that you go for something like

Code: Select all

destroy&#40;screen, mask&#41;;
destroy&#40;world, mask&#41;;
where "world" is an additional, in-RAM array defining what is solid and what isn't. Some people even add supplementary bits, such as "is it breakable?" "is it slippery?", etc.

For object-to-object collisions, one typically maintains a separate list of hit-boxes. Trying to rely on the "screen content" (or even an updated 'world') really mess up things here: how do you decide whether to test against someone else before or after it moves ?

#177487 - relminator - Fri Jun 29, 2012 4:53 am

Why not try some sort of geometric collision like SAT(Separating Axis Theorem) for particles?

For BG to particle collision, you could approximate landscapes by lines and use DOT projection to collide and respond much like what you do in SAT (Just use MTV(Minimum Translation Vector) ) .