(*
  Easy GL2D
  Relminator 2011 
  Richard Eric M. Lope BSN RN
  Http://Rel.Phatcode.Net
  A very small, simple, yet very fast DS 2D rendering lib using the DS' 3D core.
  --
  Translated in Object Pascal by Francesco Lombardi - 2012
  http://itaprogaming.free.fr
*)
program TwoDplusThreeD;

{$mode objfpc}
{$H+}

{$L build/flyer.o}
{$L build/shuttle.o}
{$L build/enemies.o}
{$L build/organ16.o}

uses
  ctypes, nds9, gl2d, cearn_atan, vbuffer, uvcoord_enemies;
 
const
  flyerBitmapLen = 2048;
  flyerPalLen = 512;
  shuttleBitmapLen = 2048;
  shuttlePalLen = 32;
  enemiesBitmapLen = 65536;
  enemiesPalLen = 512;
  organ16BitmapLen = 32768;

var
  flyerBitmap: array [0..0] of cuint; cvar; external;
  flyerPal: array [0..0] of cushort; cvar; external;
  shuttleBitmap: array [0..0] of cuint; cvar; external;
  shuttlePal: array [0..0] of cushort; cvar; external;
  enemiesBitmap: array [0..0] of cuint; cvar; external;
  enemiesPal: array [0..0] of cushort; cvar; external;
  organ16Bitmap: array [0..0] of cuint; cvar; external;

const
  // Mode7-like 3D effect parameters
  GRID_RINGS: cint = 2;
  GRID_BANDS: cint = 2;
  GRID_WIDTH: cint = 15;
  GRID_HEIGHT: cint = 12;

  // PI for our binary radian measure
  BRAD_PI = 1 shl 14;

var
// Our sprites
  Enemies: array [0..ENEMIES_NUM_IMAGES-1] of glImage;  // spriteset
  Shuttle: array [0..0] of glImage;         // single image 
  Flyer: array [0..0] of glImage;           // single image

  vb: PVertexBuffer;

  EnemiesTextureID: cint; 
  ShuttleTextureID: cint; 
  FlyerTextureID: cint;

  TextureSize: cint;
  ox, oy: cint32;
  Frame, PhoenixFrame, BeeFrame, Rotation: cint;      // rotation value of the rotating sprites
  x, y: cint;
  sx, sy, angle: cint32;


// function to draw our mode7 fx
// draws 2 planes
// for a fake mode7 effect
procedure draw_grid();
const
  text_off_u: cint32 = 0;
  text_off_v: cint32 = 0;
  frame: cint = 0;
begin
  inc(frame);
  
  glPushMatrix();
    
    glLoadIdentity();
    
    glTranslate3f32( 0, 0, floattof32(3.0) );
    glScalef32(floattof32(1),floattof32(1.5),floattof32(1.5));
  
    // floor
    glPushMatrix();

      glTranslate3f32( 0, -1 shl 12, -2 shl 12 );   
      glRotateXi( inttof32(244) );
      
      vb^.render( text_off_u, text_off_v, true );
      
    glPopMatrix( 1 );
    
    // ceiling
    glPushMatrix();

      glTranslate3f32( 0, 1 shl 12, -2 shl 12 );
      glRotateXi( inttof32(180) );
      
      vb^.render( text_off_u ,text_off_v, true );
    
    glPopMatrix( 1 );
  
  glPopMatrix( 1 );
    

  // "move" the floor and ceiling
  text_off_u := (sinLerp(frame*30)*2) and 4095;
  text_off_v := (sinLerp(-frame*50)*3) and 4095;

end;



begin
  
  // Initialize fake mode7 planes
  vb := init_grid (GRID_RINGS, GRID_BANDS, GRID_WIDTH, GRID_HEIGHT, 8, 8);
  
  //set mode 5, enable BG0 and set it to 3D
  videoSetMode(MODE_5_3D);
  
  consoleDemoInit();

  // initialize gl2d
  glScreen2D();
  
  //set up enough texture memory for our textures
  vramSetBankA( VRAM_A_TEXTURE );
  
  // Very Important!!! Or you get black sprites
  vramSetBankE(VRAM_E_TEX_PALETTE);  // Allocate VRAM bank for all the palettes
  
  // Load our texture for our mode7 effect
  vb^.load_texture( @organ16Bitmap );
  
  // Load our Enemies texture
  // We used glLoadSpriteSet since the texture was made
  // with my texture packer.
  EnemiesTextureID := glLoadSpriteSet( 
             Enemies,       // pointer to glImage array
             ENEMIES_NUM_IMAGES,  // Texture packer auto-generated #define
             @enemies_texcoords,    // Texture packer auto-generated array
             GL_RGB256,       // texture type for glTexImage2D() in videoGL.h 
             TEXTURE_SIZE_256,    // sizeX for glTexImage2D() in videoGL.h
             TEXTURE_SIZE_256,    // sizeY for glTexImage2D() in videoGL.h
             GL_TEXTURE_WRAP_S or GL_TEXTURE_WRAP_T or TEXGEN_OFF or GL_TEXTURE_COLOR0_TRANSPARENT, // param for glTexImage2D() in videoGL.h
             256,         // Length of the palette to use (256 colors)
             @enemiesPal,   // Load our 256 color enemies palette
             @enemiesBitmap   // image data generated by GRIT
             );
  
  
  
  // Shuttle
  // Since the shuttle is just a single 64x64 image,
  // We use glLoadTileSet() giving the right dimensions.
  ShuttleTextureID := glLoadTileSet(
            Shuttle,      // pointer to glImage array
             64,        // sprite width
             64,        // sprite height
             64,        // bitmap image width
             64,        // bitmap image height
             GL_RGB16,    // texture type for glTexImage2D() in videoGL.h
             TEXTURE_SIZE_64, // sizeX for glTexImage2D() in videoGL.h
             TEXTURE_SIZE_64, // sizeY for glTexImage2D() in videoGL.h
             GL_TEXTURE_WRAP_S or GL_TEXTURE_WRAP_T or TEXGEN_OFF or GL_TEXTURE_COLOR0_TRANSPARENT,
             16,          // Length of the palette to use (16 colors)
             @shuttlePal, // Load our 16 color Shuttle palette
             @shuttleBitmap  // image data generated by GRIT
           );

  
  // Flyer
  FlyerTextureID := glLoadTileSet(
            Flyer,
             64,
             64,
             64,
             64,
             GL_RGB16,
             TEXTURE_SIZE_64,
             TEXTURE_SIZE_64,
             GL_TEXTURE_WRAP_S or GL_TEXTURE_WRAP_T or TEXGEN_OFF or GL_TEXTURE_COLOR0_TRANSPARENT,
             16,          // Length of the palette to use (16 colors)
             @flyerPal,     // Load our 16 color Flyer palette
             @flyerBitmap
           );

  
  

  // Print some console stuff
  
  iprintf(#$1b'[1;1HEasy GL2D + 3D');
  iprintf(#$1b'[2;1HRelminator');
  iprintf(#$1b'[4;1HHttp://Rel.Phatcode.Net');
  
  iprintf(#$1b'[6;1HA demo showing a very easy');
  iprintf(#$1b'[7;1Hway to combine 3D and 2D');
  
  iprintf(#$1b'[ 9;1HSprites by:');
  iprintf(#$1b'[10;1H  Adigun A. Polack and Patater');
  iprintf(#$1b'[11;1HFixed Point atan2Lerp By Cearn');
  
  iprintf(#$1b'[13;1HEnemiesTextureID = %i', EnemiesTextureID);
  iprintf(#$1b'[14;1HFlyerTextureID = %i', FlyerTextureID);
  iprintf(#$1b'[15;1HShuttleTextureID = %i', ShuttleTextureID);
  
  
  iprintf(#$1b'[17;1HEnemies use a 256 color pal');
  iprintf(#$1b'[18;1HShuttle uses a 16 color pal');
  iprintf(#$1b'[19;1HFlyer uses a 16 color pal');
  iprintf(#$1b'[20;1H3D plane is a 16 bit image');
  
  TextureSize := enemiesBitmapLen + flyerBitmapLen + organ16BitmapLen + shuttleBitmapLen;
            
  iprintf(#$1b'[22;1HTotal Texture size= %i kb', TextureSize div 1024);
  
  // some variables for our demo
  ox := 0; oy := 0;     // needed for shuttle angle calculation
  Frame := 0;       // just the standard frame counter
  PhoenixFrame := 0;    // animation frame for our firebird
  BeeFrame := 0;      // animation frame for the bee
  Rotation := 0;      // rotation value of the rotating sprites
  
  while true do 
  begin
    inc(frame);
    
    rotation := frame * 240;    // speed up our rotation
    
    // animate some of our animated sprites
    // every 8th frame
    if ( (Frame and 7) = 0 ) then
    begin
      BeeFrame := (BeeFrame + 1) and 1;
      inc(PhoenixFrame);
      if (PhoenixFrame > 2) then 
        PhoenixFrame := 0;
    end;
    
    
    // calculate positions for our rotating sprites
    x := 128 + SarLongint((cosLerp(Frame) + sinLerp(BRAD_PI + Rotation) * 70), 12);
    y := 96 + SarLongint((cosLerp(Frame) + cosLerp(-Rotation) * 50), 12);
      
    
    // Calculate moving shuttle position
    // Get new ship position in f32 format for accuracy since
    // shifting here by >> 12 looses accuracy
    sx := (cosLerp(Frame * 150) + sinLerp(Frame * 70)) * (SCREEN_WIDTH div 4) ;
    sy := (sinLerp(-(Frame * 80)) + sinLerp(Frame * 190)) * (SCREEN_HEIGHT div 4) ;   
    
    // get angle(binary radian) to draw the rotated shuttle
    angle := atan2Lerp(ox - sx, oy - sy);    
    
    // save positions for use on the nexgt frame to get the new angle
    ox := sx;
    oy := sy;
    
     
    // Get new shuttle position
    // Shifting by >> 12 so that we can screen coordinates to draw
    sx := SCREEN_WIDTH div 2 + SarLongint(sx, 12);
    sy := SCREEN_HEIGHT div 2 + SarLongint(sy, 12);   
    
    
    
    
    // Drawing the fake mode7 FX in 3D mode
    
    draw_grid();  
      
    // End drawing in 3D mode
    
    // Start 2D mode
    glBegin2D();
      // Draw our enemies
      // draw some rotated and/or animated sprites
      glSpriteRotate(    x,     y,      Rotation,          GL_FLIP_NONE, @Enemies[30+BeeFrame]);
      glSpriteRotate(255-x, 191-y,  Rotation * 4,             GL_FLIP_H, @Enemies[84]);
      glSpriteRotate(255-x,     y,     -Rotation,             GL_FLIP_V, @Enemies[32]);
      glSpriteRotate(    x, 191-y, -Rotation * 3, GL_FLIP_H  or  GL_FLIP_V, @Enemies[81]);
      
      // Some phoenix enemies on the right
      // Note the flipmodes 
      // Also shows how we can draw in "color mode" and shadow mode
      glSprite(200, 30,  GL_FLIP_NONE,           @Enemies[87 + PhoenixFrame]);
      glColor( RGB15(31,0,0) );
      glSprite(200, 60,  GL_FLIP_H,              @Enemies[87 + PhoenixFrame]);
      
      // Make the last two sprites translucent
      glPolyFmt(POLY_ALPHA(20)  or  POLY_CULL_NONE  or  POLY_ID(1));
      glColor( RGB15(0,31,20) );
      glSprite(200, 90,  GL_FLIP_V,              @Enemies[87 + PhoenixFrame]);
      glColor( RGB15(0,0,0) );
      glSprite(200, 130, GL_FLIP_V  or  GL_FLIP_H , @Enemies[87 + PhoenixFrame]);
      
      //Restore color and translucency to normal 
      glColor( RGB15(31,31,31) );
      glPolyFmt(POLY_ALPHA(31)  or  POLY_CULL_NONE );
  
  
     
      // Offset the angle by -PI/2 since Patater's
      // Original sprite faces upwards
      glSpriteRotate( sx, sy, angle - (BRAD_PI div 2), GL_FLIP_NONE, @Shuttle);
      
      // Make the flyer translucent just for kicks
      glPolyFmt(POLY_ALPHA(20)  or  POLY_CULL_NONE  or  POLY_ID(2));
      glSpriteRotateScaleXY(SCREEN_WIDTH div 2, SCREEN_HEIGHT div 2, Frame * 140, sinLerp(Frame * 120) * 3, sinLerp(Frame * 210) * 2,  GL_FLIP_NONE, @Flyer);
      
      // Restore to normal rendering just to be safe
      glPolyFmt(POLY_ALPHA(31)  or  POLY_CULL_NONE );
      
    glEnd2D();
    glFlush( 0 );

    swiWaitForVBlank();
  
    
  end;

end.

