use tksdl;
use tkopengl;

float frot=0;
int numframesrendered=0;

int tex_id;

float aspect = 1.0;

int TEX_W = 512;
int TEX_H = 256;
int TEX_D = 32;

int slice_offset = 0;

float anim_c = 0;
float anim_o = 0;
float anim_o2 = 0;
float anim_o3 = 0;

Texture tex_font;
Texture tex_pal;
Texture tex_circle;
Texture tex_fog;
int pal_off = 0;

int stars_num = 2048;
FloatArray stars;
stars.alloc(4 * 3 * stars_num); stars.useAll();
FloatArray stars_uv;
stars_uv.alloc(4 * 2 * stars_num); stars_uv.useAll();
FloatArray stars_spd;
stars_spd.alloc(stars_num); stars_spd.useAll();
float stars_sz = 0.023;
float stars_spread = 4.0;
function InitStarfield() {
   int i = 0;
   int k = 0;
   int l = 0;
   loop(stars_num)
   {
      float x = rnd(stars_spread) - stars_spread*0.5;
      float y = rnd(stars_spread) - stars_spread*0.5;
      float z = - rnd(5.0);

      stars_uv[l+0] = 0.0;
      stars_uv[l+1] = 0.0;
      l += 2;
      stars[k+0] = x - stars_sz;
      stars[k+1] = y - stars_sz;
      stars[k+2] = z;
      k += 3;

      stars_uv[l+0] = 1.0;
      stars_uv[l+1] = 0.0;
      l += 2;
      stars[k+0] = x + stars_sz;
      stars[k+1] = y - stars_sz;
      stars[k+2] = z;
      k += 3;

      stars_uv[l+0] = 1.0;
      stars_uv[l+1] = 1.0;
      l += 2;
      stars[k+0] = x + stars_sz;
      stars[k+1] = y + stars_sz;
      stars[k+2] = z;
      k += 3;

      stars_uv[l+0] = 0.0;
      stars_uv[l+1] = 1.0;
      l += 2;
      stars[k+0] = x - stars_sz;
      stars[k+1] = y + stars_sz;
      stars[k+2] = z;
      k += 3;

      stars_spd[i++] = 0.008 + rnd(0.02);
      
   }
}

function RenderStarfield(float dt) {
   // :P

   glDisable(GL_TEXTURE_3D);
   glActiveTexture(GL_TEXTURE0);
   glEnable(GL_TEXTURE_2D);

   tex_circle.bind();

   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   zglInitPerspective(float(TEX_W)/float(TEX_H), 40.0, 0.0001, 10.0);
   glLoadIdentity();

   zglVertexPointer(stars);
   glEnableClientState(GL_VERTEX_ARRAY);
   zglTexCoordPointer2f(stars_uv);
   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
   glColor4f(1,1,1,1.0);

   // Setup second texture unit (used to fade out stars depending on distance)
   // Note: Fog ignores alpha !
   glActiveTexture(GL_TEXTURE1);
   glClientActiveTexture(GL_TEXTURE1);
   glEnable(GL_TEXTURE_1D);
   tex_fog.bind();
   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
   // Sample RGB, multiply by previous texunit result
   glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
   glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
   glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
   glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
   glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
   // Sample ALPHA, multiply by previous texunit result
   glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
   glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
   glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
   glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
   glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
   // Enable texcoord generation
   glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
   zglTexGen4f(GL_S, GL_OBJECT_PLANE, 0, 0, -0.2, 0);
   ////glMultiTexCoord1f(GL_TEXTURE1, 0.38);
   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
   glEnable(GL_TEXTURE_GEN_S);

   glDrawArrays(GL_QUADS, 0, stars_num);

   glDisable(GL_TEXTURE_1D);
   glActiveTexture(GL_TEXTURE0);
   glClientActiveTexture(GL_TEXTURE0);

   glDisableClientState(GL_VERTEX_ARRAY);
   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
   glDisable(GL_TEXTURE_2D);
   glDisable(GL_BLEND);

   int i = 0;
   int k = 0;
   compile loop(stars_num)
   {
      float z = stars[k+2];
      float zspd = stars_spd[i++] ;
      z += zspd * dt;
      if(z > 0)
      {
         float x = rnd(stars_spread) - stars_spread*0.5;
         float y = rnd(stars_spread) - stars_spread*0.5;
         z = -5.0;
         stars[k+3*0+0] = x - stars_sz;
         stars[k+3*0+1] = y - stars_sz;

         stars[k+3*1+0] = x + stars_sz;
         stars[k+3*1+1] = y - stars_sz;

         stars[k+3*2+0] = x + stars_sz;
         stars[k+3*2+1] = y + stars_sz;

         stars[k+3*3+0] = x - stars_sz;
         stars[k+3*3+1] = y + stars_sz;
      }
      stars[k+3*0+2] = z;
      stars[k+3*1+2] = z;
      stars[k+3*2+2] = z;
      stars[k+3*3+2] = z;
      k += 3 * 4;
   }

}

int scroll_off = 0;
float scroll_csx = 64;
float scroll_csy = 64;
float scroll_csx_anim = 0;
float scroll_csy_anim = 0;
float scroll_csy_anim2 = 0;
float scroll_cnt = scroll_csx;
String scroll_text = "      to scroll or not to scroll.....   ahhh...to be young again...and also a ROB0T !11      scrolly wrapZzzz        ";
float scroll_oa = 0;
function RenderScrollText(float dt) {

   scroll_csx = 64;
   scroll_csy = 128 + sin(scroll_csy_anim)*64;
   scroll_csx_anim += 0.0323910234 * dt;
   wrap scroll_csx_anim 0 2PI;
   scroll_csy_anim += ( (0.6 + 0.4 * sin(scroll_csy_anim2))) *  0.1293131231249 * dt;
   wrap scroll_csy_anim2 0 2PI;
   scroll_csy_anim2 += 0.058239402341 * dt;
   wrap scroll_csy_anim2 0 2PI;

   float vw = TEX_W;
   float vh = TEX_H;

   zglInit2D(vw, vh);
   glLoadIdentity();

   tex_font.bind();
   glEnable(GL_TEXTURE_2D);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   int txti = scroll_off;
   float cx = -scroll_csx + scroll_cnt;
   float basecy = (vh-scroll_csy) * 0.5;
   float ustep = 8.0 / tex_font.sx;
   float u2step = (8.0 / scroll_csx) * (1.0 / tex_font.sx);
   float vstep = 12.0 / tex_font.sy;

   float f8 =  (0.6 + sin(anim_c)*0.4);
   int c32 = tex_pal[pal_off];
   int r = ((c32>>16)&255) * f8;
   int g = ((c32>> 8)&255) * f8;
   int b = ((c32    )&255) * f8;
   zglColorARGB(argb(255, r, g, b));
   pal_off = (pal_off + 1) % tex_pal.sx;

   float iaspd = 0.01310394134 * 1.3;
   float ia = scroll_oa + cx*iaspd;
   glBegin(GL_QUADS);
   compile loop( (vw/scroll_csx) +2)
   {
      char c = scroll_text.getc(txti);
      if(c > ' ')
      {
         c -= 33;
         float u = c * ustep;
         int k = 0;
         loop(int(scroll_csx))
         {
            float cy = basecy + sin(ia) * (vh-scroll_csy) * 0.5;
            glTexCoord2f(u, 0);
            glVertex2f(cx, cy);

            glTexCoord2f(u+u2step, 0);
            glVertex2f(cx+1, cy);
            
            glTexCoord2f(u+u2step, vstep);
            glVertex2f(cx+1, cy+scroll_csy);
            
            glTexCoord2f(u, vstep);
            glVertex2f(cx, cy+scroll_csy);

            cx += 1;
            u += u2step;
            ia += iaspd;
            k++;
         }
      }
      else
      {
         ia += scroll_csx * iaspd; 
         cx += scroll_csx;
      }

      txti++;
      if(txti >= (scroll_text.length-1))
      {
         txti = 0;
      }
   }
   glEnd();

   scroll_cnt -= 2* dt;
   if(scroll_cnt <= 0)
   {
      scroll_cnt += scroll_csx;
      scroll_off++;
      if(scroll_off >= (scroll_text.length-1))
      {
         scroll_off = 0;
      }
   }

   scroll_oa -= 0.032412891234 * dt;
   wrap scroll_oa 0 2PI;

   anim_c += 0.11076134 * dt;
   wrap anim_c 0 2PI;

}


int GRID_W = 64;
int GRID_H = 64;
FloatArray plasma_r; plasma_r.alloc((GRID_W + 1) * (GRID_H + 1)); plasma_r.useAll();
function RenderPlasmaGrid(float dt) {

   float iaspd = 0.0101020123;
   float oa = anim_o;
   float oa2 = anim_o2;
   float oa3 = anim_o3;
   float oaspd = 0.105103724239612389;
   float oaspd2 = 0.1312102342341039612389;
   float oaspd3 = 0.142312341351345134;

   float y = 0;
   float xstep = TEX_W / float(GRID_W);
   float ystep = TEX_H / float(GRID_H);
   float v = 1.0;
   float ustep = 1.0 / GRID_W;
   float vstep = -1.0 / GRID_H;
   float roff = (slice_offset/(float(TEX_D)));

   // Calc texture coords
   int k = 0;
   compile loop(GRID_H + 1)
   {
      float ia = (0.5 + 0.5 * sin(oa)) * 2PI;
      float ia2 = (0.5 + 0.5 * sin(oa3)) * 2PI;
      loop(GRID_W + 1)
      {
         float tz = roff - (float(TEX_D-2)/TEX_D) * (0.5 + 0.5 * sin(ia));
         plasma_r[k++] = tz;
         ia += iaspd * (0.7 + 0.3 * sin(ia2));
         ia2 += 2.93123491345013;
      }
      oa += oaspd * (0.8 + 0.2 * sin(oa2));
      oa2 += oaspd2;
      oa3 += oaspd3;
   }

   // Now render
   k = 0;
   glBegin(GL_QUADS);
   compile loop(GRID_H)
   {
      int ix = 0;
      float u = 0;
      float x = 0;
      loop(GRID_W)
      {
         float tzul = plasma_r[k];
         float tzur = plasma_r[k + 1];
         float tzdl = plasma_r[k + (GRID_W + 1)];
         float tzdr = plasma_r[k + 1 + (GRID_W + 1)];
         
         glTexCoord3f(u, v, tzul);
         glVertex3f(x, y, 0);

         glTexCoord3f(u+ustep, v, tzur);
         glVertex3f(x + xstep, y, 0);

         glTexCoord3f(u+ustep, v+vstep, tzdr);
         glVertex3f(x + xstep, y + ystep, 0);

         glTexCoord3f(u, v+vstep, tzdl);
         glVertex3f(x, y + ystep, 0);

         u += ustep;
         x += xstep;
         ix++;
         k++;
      }
      k++;
      v += vstep;
      y += ystep;
   }
   glEnd();

   anim_o += 0.0103423 * dt;
   wrap anim_o 0 2PI;

   anim_o2 += 0.021231359003423 * dt;
   wrap anim_o2 0 2PI;

   anim_o3 += 0.019230890130941321231359003423 * dt;
   wrap anim_o3 0 2PI;
  
}


function onDraw() {

   glDrawBuffer(GL_BACK);

    float dt = FPS.precision;
    dt = 1.0;

    glViewport(0, 0, TEX_W, TEX_H);

    // Render new texture slice
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_TEXTURE_3D);
    glClearColor(0,0,0.0,1);
    glClear(GL_COLOR_BUFFER_BIT);
    if( !(++numframesrendered&127) )
      trace "FPS.real="+FPS.real;

    RenderStarfield(dt);

    // Render texture slice
    RenderScrollText(dt);

    glViewport(0, 0, Viewport.width, Viewport.height);

    // Copy texture slice
    glBindTexture(GL_TEXTURE_3D, tex_id);
    glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 
                        0, 0, slice_offset,
                        0, 0,
                        TEX_W, TEX_H
                        );

    // Pass2: Render 3D texture
    glClearColor(1,0,0,1);
    glClear(GL_COLOR_BUFFER_BIT);
    zglInit2D(TEX_W / aspect, TEX_H);
    glLoadIdentity();
    if(aspect != 1.0f)
    {
       glTranslatef( -(640-512)/2, 0, 0);
    }
    glEnable(GL_TEXTURE_3D);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glDisable(GL_BLEND);
    glColor3f(0.1, 0.9, 0.1);
    glDisable(GL_CULL_FACE);

    RenderPlasmaGrid(dt);

    glDisable(GL_TEXTURE_3D);

      
    
    // Next slice
    slice_offset = (slice_offset + 1) & (TEX_D-1);
    
}

function onKeyboard(Key _k) {
   switch(_k.pressed)
   {
      case VKEY_ESCAPE:
         SDL.exitEventLoop();
         break;

      case VKEY_RETURN:
         Viewport.toggleFullScreen();
         if(Viewport.width == 640)
         {
            aspect = 1.3333;
         }
         else
         {
            aspect = 1.0;
         }
         break;
   }
}

function onReopen() {
   tex_id = zglGenTexture();
   glBindTexture(GL_TEXTURE_3D, tex_id);

   // Note: omitting the first glTexParameteri call will cause GL to simply skip rendering textured triangles. WTF.

 	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT);

   zglTexImage3D(GL_TEXTURE_3D,
                 0,
                 GL_RGB8,
                 TEX_W, TEX_H, TEX_D,
                 0,
                 GL_RGB,
                 GL_UNSIGNED_BYTE,
                 null
                 );
   tex_font.loadImage("font.png", 0,0,0);
   tex_font.expand2n();
   tex_font.flags = TEX_MODULATE | TEX_ALPHA;
   tex_font.unload();

   tex_pal.loadImage("pal.png", 0,0,0);
   tex_pal.unload();

   tex_circle.loadImage("circle.png", 0,0,0);
   tex_circle.flags = TEX_MODULATE | TEX_ALPHA | TEX_AUTOMIPMAP | TEX_MINFILTERTRILINEAR | TEX_MAGFILTERLINEAR;
   tex_circle.unload();

   // Create 1D fog/alpha fade texture
   tex_fog.alloc(256, 1, 4);
   tex_fog.flags = TEX_1D | TEX_ALPHA;
   int i = 0;
   float a = 0;
   loop(64)
   {
      tex_fog[i++] = argb(a,255,255,255);
      a += 4;
   }
   a = 255;
   loop(192)
   {
      tex_fog[i++] = argb(a,255,255,255);
      a-= 255 / 192.0;
   }
   tex_fog.unload();

   InitStarfield();
}

function main() {
   Viewport.doubleBuffer = true;
   Viewport.openWindow(512, 256);
   Viewport.setScreenResolution(640, 480, 32);
   zglLoadExtensions();
   use callbacks;
   onReopen();
   FPS.tickInterval=1000.0/60;
   FPS.limit=60;
   Viewport.caption="to scroll or not to scroll..";
   Viewport.swapInterval(1);
   SDL.eventLoop();
}