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 > 3D on both screens

#105133 - Dan Forever - Fri Oct 06, 2006 9:50 am

Does anyone have some source code I could look at or a link to a tutorial explaining how do 3D on both screens of the DS? I know it can be done but my searches aren't having any luck :(

#105168 - ecurtz - Fri Oct 06, 2006 3:17 pm

http://forum.gbadev.org/viewtopic.php?t=7958

Second demo in that post.

#105952 - FireSlash - Fri Oct 13, 2006 3:56 pm

I did it in LumiDS.

The code uses glOrtho, and it's pretty hacky, but you should be able to get the idea.

http://www.fireslash.net/~fireslash/files/lumids_0.2b_src.zip

Important bits with my crap trimmed out:
Code:
// ------ LumiDS --------
// Written by Christopher 'FireSlash' Kadar
// <GPL goes here>

//Needed for the second screen
#define   REG_CAPTURE      (*(vu32*)0x04000064)

// General includes
#include <nds.h>

// Needed for dual screen 3D
void SetRegCapture(bool enable, uint8 srcBlend, uint8 destBlend, uint8 bank, uint8 offset, uint8 size, uint8 source, uint8 srcOffset);

bool busy;

float   xrot;
float   yrot;
float   zrot;

//a global copy of sprite attribute memory
SpriteEntry sprites[128];

//turn off all the sprites
void InitSprites()
{
   for(int i = 0; i < 128; i++)
   {
      sprites[i].attribute[0] = ATTR0_DISABLED;
      sprites[i].attribute[1] = 0;
      sprites[i].attribute[2] = 0;
      sprites[i].attribute[3] = 0;
    }
}
//copy our sprite to object attribute memory
void UpdateOAM()
{
   dmaCopy(sprites, OAM_SUB, 128 * sizeof(SpriteEntry));
}


int main()
{   
   touchPosition touch;
   // Turn on everything
   powerON(POWER_ALL);
   FAT_InitFiles(); // Get our GBAMP/M3/Whatever all warmed up ^^
   
   // Setup the Main screen for 3D
   videoSetMode(MODE_0_3D);
   //vramSetBankA(VRAM_A_TEXTURE);                       

   videoSetModeSub(MODE_5_2D | DISPLAY_BG2_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_2D_BMP_256);
   SUB_BG2_CR = BG_BMP16_256x256;
   SUB_BG2_XDX = 256;
   SUB_BG2_XDY = 0;
   SUB_BG2_YDX = 0;
   SUB_BG2_YDY = 256;
   SUB_BG2_CY = 0;
   SUB_BG2_CX = 0;
      
   vramSetMainBanks(VRAM_A_TEXTURE, VRAM_B_TEXTURE, VRAM_C_SUB_BG, VRAM_D_SUB_SPRITE);
   
   
   memset(commandControl, 0, sizeof(CommandControl));

   int y = 0;
   int x = 0;
   for (y = 0; y < SCREEN_HEIGHT; y++)
   for (x = 0; x < SCREEN_WIDTH; x++)
   {
      BG_GFX_SUB[y * SCREEN_WIDTH + x]  = RGB15(31, x % 32, y % 32) | BIT(15);
   }

   // IRQ basic setup
   irqInit();
   irqSet(IRQ_VBLANK, 0);
    //irqEnable(IRQ_VBLANK);

   glViewPort(0,0,255,191);
   
   // Specify the Clear Color and Depth
   glClearColor(0,0,0);
   glClearDepth(0x7FFF);
   InitSprites();
   
      int i = 0;
   for (y = 0; y < 3; y++)
   for (x = 0; x < 4; x++)
   {
      sprites[i].attribute[0] = ATTR0_BMP | ATTR0_SQUARE | (64 * y);
      sprites[i].attribute[1] = ATTR1_SIZE_64 | (64 * x);
      sprites[i].attribute[2] = ATTR2_ALPHA(1) | (8 * 32 * y) | (8 * x);
      i++;
   }
   // framecount won't really work here... needs to be replaced
   // with a more useful variable for deltas. try IPC->heartbeat.
   uint32 frameCount = 0;
   
   while (1)
   {
 
      glReset();

      glOrtho(0,1,0,1,0,12);

      glColor3f(1,1,1);
      
      glLight(0, RGB15(31,31,31) , 0,              floattov10(-1.0),       0);
      glLight(1, RGB15(31,31,31) , 0,              0,   floattov10(-1.0));
      glLight(2, RGB15(31,31,31) , 0,              0,   floattov10(1.0));

      glPushMatrix();
      
      glMatrixMode(GL_TEXTURE);
      glIdentity();
      
      glMatrixMode(GL_MODELVIEW);

      //need to set up some material properties since DS does not have them set by default
      glMaterialf(GL_AMBIENT, RGB15(16,16,16));
      glMaterialf(GL_DIFFUSE, RGB15(16,16,16));
      glMaterialf(GL_SPECULAR, BIT(15) | RGB15(8,8,8));
      glMaterialf(GL_EMISSION, RGB15(16,16,16));

      //ds uses a table for shinyness..this generates a half-ass one
      glMaterialShinyness();
      glEnable(GL_BLEND);
      glEnable(GL_ALPHA_TEST);
      
      glMatrixMode(GL_MODELVIEW);
      
      //Push our original Matrix onto the stack (save state)
      glPushMatrix();   


      // This is where the magic happens

      if (frameCount & 0x1)
      {
         vramSetBankC(VRAM_C_SUB_BG);
         vramSetBankD(VRAM_D_LCD);
         SetRegCapture(true, 0, 15, 3, 0, 3, 0, 0);
         scene->draw_bottom(frameCount);
      } else {
         vramSetBankC(VRAM_C_LCD);
         vramSetBankD(VRAM_D_SUB_SPRITE);
         SetRegCapture(true, 0, 15, 2, 0, 3, 0, 0);
         scene->draw_top(frameCount);
      }
      // Pop our Matrix from the stack (restore state)
      glPopMatrix(1);
      frameCount++;
      // flush to screen   
      glFlush();
      lcdSwap();
      UpdateOAM();
      //swiWaitForVBlank();
   }
   return 0;
}


// Capture code from ecurtz from gbadev.org forums
// Sets the DS to render straight to a VRAM bank instead of to the screen's bank.
void SetRegCapture(bool enable, uint8 srcBlend, uint8 destBlend, uint8 bank, uint8 offset, uint8 size, uint8 source, uint8 srcOffset)
{
   uint32 value = 0;
   if (enable)
      value |= 1 << 31; // 31 is enable
   value |= 3 << 29; // 29-30 seems to have something to do with the blending
   value |= (srcOffset & 0x3) << 26; // capture source offset is 26-27
   value |= (source & 0x3) << 24; // capture source is 24-25
   value |= (size & 0x3) << 20; // capture data write size is 20-21
   value |= (offset & 0x3) << 18; // write offset is 18-19
   value |= (bank & 0x3) << 16; // vram bank select is 16-17
   value |= (srcBlend & 0xF) << 8; // graphics blend evb is 8..12
   value |= (destBlend & 0xF) << 0; // ram blend EVA is bits 0..4
   
   REG_CAPTURE = value;
}


I hope this helps.
_________________
FireSlash.net

#111770 - djmcbell - Sat Dec 09, 2006 11:50 pm

Sorry, but I'd like to bump this topic rather than make a new topic of my own.

I'm also looking at 3D on both screens. Basically, the top screen will be one angle of a scene, and the bottom screen will be another angle of the same scene. I've got the top screen set up, but am having a lot of trouble setting up the bottom one.

The demos listed above have helped me somewhat - from what I understand I should try capturing the information from the top screen, use an lcdSwap (or indeed and lcdMainOnTop/lcdMainOnBottom) and then paint the captured elements to the other screen.

I'm using the basic stuff from devKitPro's 3D textured cube (with a bit of nehe thrown in). I've also tried the demo from the above link but that doesn't seem to work properly (I get nothing on the top screen and a flashing checkerboard pattern on the bottom).

Any suggestions? And again, I curse the Wizard of Oz for my feeble brain.

#111803 - Rockviech - Sun Dec 10, 2006 10:14 am

well you could try, swap screen (now you have top screen )-> render to bottom(top) screen, swap again (bottom selected) -> render to bottom (bottom) screen

#111806 - djmcbell - Sun Dec 10, 2006 12:08 pm

Sounds good Rockviech - I've tried the lcdSwap, lcdMainOnBottom and lcdMainOnTop. Each basically swaps both screens over.

Would there be a way just to draw the top screen only as normal, then swap and draw the bottom screen only? This sounds probably the easiest way to go.

#111807 - Sausage Boy - Sun Dec 10, 2006 12:51 pm

If you want flickering, yes. The way most people do it involves capturing the display, switching screens and displaying the captured frame on one screen, and repeating.
_________________
"no offense, but this is the gayest game ever"

#111818 - Payk - Sun Dec 10, 2006 5:32 pm

Well 3d on both screens is evil.
You will 256KB VRAM less, and half framerate. I saw 2 offical games doing that (labyrinth and cars), and both gave me a headache...

#111831 - djmcbell - Sun Dec 10, 2006 8:24 pm

Right, so it looks like I'm going to have to capture. Have to experiment with that code some more.

By the way, the game is a puzzle game. Top screen is a view you can move, just to rotate around the board, and the bottom is a static view that never moves. I know it can be done in 2D but I just want to cheat, save resources and make things look more consistent.

#111840 - tepples - Sun Dec 10, 2006 9:52 pm

djmcbell wrote:
By the way, the game is a puzzle game. Top screen is a view you can move, just to rotate around the board, and the bottom is a static view that never moves.

Is this a clone of Block Out (Genesis) or 3D Tetris (Virtual Boy) or Geom Cube (PS1) by any chance?

Quote:
I know it can be done in 2D but I just want to cheat, save resources and make things look more consistent.

It'll probably be better to do the static view in 2D. Remember that sprite scaling is still in full effect on the DS's 2D GPU.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#111842 - djmcbell - Sun Dec 10, 2006 9:59 pm

Sorry tepples, not allowed to say what it is. Needless to say, I'm just trying to get a foot in the door of the games industry, and have produced a GBA version of this (the company I'm producing it for is just waiting on the owners of the puzzle in question to give us the go-ahead). However I have been asked to see how it'd look on the DS, and decided I'd try my hand at 3D at the same time.

2D on the bottom screen I can do now quite easily. However, because the bottom screen is essentially the top screen from overhead (the top screen is just something to show off with really, and will contain 3D objects on the puzzle board itself) I was just mainly wanting to see how it'd look.

EDIT - code below -

Code:
void initVideo3DAll()
{
   //set mode 0, enable BG0 and set it to 3D
   videoSetMode(MODE_0_3D);
   //this should work the same as the normal gl call
   glViewPort(0,0,255,191);
   glClearColor(0,0,0);
   glClearDepth(0x7FFF);
   //set sub-screen
   videoSetModeSub(MODE_5_2D | DISPLAY_BG2_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_2D_BMP_256);
   SUB_BG2_CR = BG_BMP16_256x256;
   SUB_BG2_XDX = 256;
   SUB_BG2_XDY = 0;
   SUB_BG2_YDX = 0;
   SUB_BG2_YDY = 256;
   SUB_BG2_CY = 0;
   SUB_BG2_CX = 0;
    //set out banks   
   vramSetMainBanks(VRAM_A_TEXTURE, VRAM_B_TEXTURE, VRAM_C_SUB_BG, VRAM_D_SUB_SPRITE);
}

int main()
{
   //turn on to all cores
   powerON(POWER_ALL);
   //touch stuff
   int min_x  = 4096 , min_y  = 4096, max_x  = 0, max_y   = 0;
   int min_px = 4096 , min_py = 4096, max_px = 0 , max_py = 0;
   touchPosition touch;
   //set up our video banks
   initVideo3DAll();
   //set up IRQs
   irqInit();
   irqSet(IRQ_VBLANK,0);
   //set up rotates
   float rotateX = 45;
   float rotateY = 225;
   //load our texture from pcx file
   int textureID[10];   //ten textures, hopefully
   sImage pcx;
   loadPCX((u8*)board1_pcx, &pcx);
   image8to16(&pcx);
   glGenTextures(1, &textureID[0]);
   glBindTexture(0, textureID[0]);
   glTexImage2D(0, 0, GL_RGB, TEXTURE_SIZE_128, TEXTURE_SIZE_128, 0, TEXGEN_TEXCOORD, pcx.data8);
   imageDestroy(&pcx);

   loadPCX((u8*)board2_pcx, &pcx);
   image8to16(&pcx);
   glGenTextures(1, &textureID[1]);
   glBindTexture(0, textureID[1]);
   glTexImage2D(0, 0, GL_RGB, TEXTURE_SIZE_8, TEXTURE_SIZE_8, 0, TEXGEN_TEXCOORD, pcx.data8);
   imageDestroy(&pcx);
   //main stuff
   uint32 frameCount=0;
   while(1)
   {
      touch=touchReadXY();
      //binding our touch coordinates into the screen
      //iprintf("\x1b[12;12H(%d,%d)      ",touch.px,touch.py); - prints coords to the screen
      if ( touch.x > max_x)      max_x = touch.x;
      if ( touch.y > max_y)      max_y = touch.y;
      if ( touch.px > max_px)   max_px = touch.px;
      if ( touch.py > max_py)   max_py = touch.py;
      if ( touch.x < min_x)      min_x = touch.x;
      if ( touch.y < min_y)      min_y = touch.y;
      if ( touch.px < min_px)   min_px = touch.px;
      if ( touch.py < min_py)   min_py = touch.py;
      //if(touch.px>0){barx=touch.px-16;}

   //3D stuff
      glReset();
      glOrtho(0,1,0,1,0,12);

      //any floating point gl call is being converted to fixed prior to being implemented
      gluPerspective(35, 256.0 / 192.0, 0.1, 40);
      gluLookAt(   0.0, 0.0, 1.0,      //camera possition
               0.0, 0.0, 0.0,      //look at
               0.0, 1.0, 0.0);      //up
      glLight(0, RGB15(31,31,31),0,floattov10(-1.0),0);
      glLight(1, RGB15(31,0,31),0,floattov10(1) - 1,0);
      glLight(2, RGB15(0,31,0),floattov10(-1.0),0,0);
      glLight(3, RGB15(0,0,31),floattov10(1.0) - 1,0,0);
      glPushMatrix();
      //move it away from the camera
      glTranslate3f32(0, 0, floattof32(-1));
      glRotateX(rotateX);
      glRotateY(rotateY);
      glMatrixMode(GL_TEXTURE);
      glIdentity();
      glMatrixMode(GL_MODELVIEW);
      glMaterialf(GL_AMBIENT, RGB15(8,8,8));
      glMaterialf(GL_DIFFUSE, RGB15(16,16,16));
      glMaterialf(GL_SPECULAR, BIT(15) | RGB15(8,8,8));
      glMaterialf(GL_EMISSION, RGB15(5,5,5));
      //ds uses a table for shinyness..this generates a half-ass one
      glMaterialShinyness();
      //not a real gl function and will likely change
      glPolyFmt(POLY_ALPHA(31) | POLY_CULL_BACK | POLY_FORMAT_LIGHT0 | POLY_FORMAT_LIGHT1 |
                                       POLY_FORMAT_LIGHT2 | POLY_FORMAT_LIGHT3 ) ;
      scanKeys();      
      u16 keys = keysHeld();
      if((keys & KEY_UP)) rotateX += 3;
      if((keys & KEY_DOWN)) rotateX -= 3;
      if((keys & KEY_LEFT)) rotateY += 3;
      if((keys & KEY_RIGHT)) rotateY -= 3;
      if(rotateX<0)rotateX=0;
      if(rotateX>90)rotateX=90;

      glPushMatrix();
      
      // This is where the magic happens
      if (frameCount & 0x1)
      {
         vramSetBankC(VRAM_C_SUB_BG);
         vramSetBankD(VRAM_D_LCD);
         SetRegCapture(true, 0, 15, 3, 0, 3, 0, 0);
         //draw the board base
         glBindTexture(0, textureID[0]);   //change texture
         glBegin(GL_QUAD);
         for(int i = 0; i < 6; i++)
            drawBoardBase(i);
         glEnd();
         //draw the board pieces
         glBindTexture(0, textureID[1]);   //change texture
         glBegin(GL_QUAD);
         for(int i = 0; i < 6; i++)
            drawBoardPieces(i);
         glEnd();
      }
      else
      {
         vramSetBankC(VRAM_C_LCD);
         vramSetBankD(VRAM_D_SUB_SPRITE);
         SetRegCapture(true, 0, 15, 2, 0, 3, 0, 0);
         //draw the board base
         glBindTexture(0, textureID[0]);   //change texture
         glBegin(GL_QUAD);
         for(int i = 0; i < 6; i++)
            drawBoardBase(i);
         glEnd();
         //draw the board pieces
         glBindTexture(0, textureID[1]);   //change texture
         glBegin(GL_QUAD);
         for(int i = 0; i < 6; i++)
            drawBoardPieces(i);
         glEnd();
      }

      glPopMatrix(1);
      frameCount++;
      glFlush();
      lcdSwap();
   //end 3D stuff

      //update screens
//      swiWaitForVBlank();
   }
   //loop back in main proc
   return 0;
}

#112065 - djmcbell - Tue Dec 12, 2006 10:31 pm

Okay, officially stuck.

I take it my code is capturing the screen, but I don't know how to display what it's got in the REG_CAPTURE (basically, as in the code above).

I've got my two scenes, however it's flickering due to the lcdSwap (if I take out the lcdSwap it flickers on the top display between the scenes, as expected).

Any takers? Help would be greatly appreciated - it's basically the last thing I need nailed down before I start getting the game up and running.

EDIT - and yes, the SetRegCapture routine is actually there - it's just not in the portion of the code I've posted.

#136224 - a128 - Mon Jul 30, 2007 8:38 am

Dan Forever wrote:
Does anyone have some source code I could look at or a link to a tutorial explaining how do 3D on both screens of the DS? I know it can be done but my searches aren't having any luck :(


Just a bugfixed version of the twopass demo from Eli
http://forum.gbadev.org/viewtopic.php?t=7958&start=15&postdays=0&postorder=asc&highlight=