///
/// file   : cubeworld.tks
/// author : Bastian Spiegel <fli@tkscript.de>
/// descr. :
/// license: provided "AS IS". no copyright, no liability. use as you want.
/// created: 190902
/// updated: 06Feb2003 <fli> added render2texture (DrawRasters())
///
///

module Main;
use "tksdl";

#define HST_LEN 88
#define R2T_SU 256.0
#define R2T_SV 256.0
#define GRIDSX 6
#define GRIDSY 6
#define GRIDSZ 6

int hst_idx=0;
int rubber_mode=1;
int tex_mode=1;
int draw_quads=1;
Camera cam;
Texture tex_fb;
Texture tex_r2t[HST_LEN];
float oval=rnd(2PI);
float rfld=rnd(2.0);
float rquadx=rnd(2PI);
float rquady=rnd(2PI);
float rquadz=rnd(2PI);
float scrcnt=0.0;
DisplayList dl_cube;
int need_dl=1;
int waitvblank=0;
int enable_rotation=1;

function DrawRasters() compile {
    int y=0;
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_BLEND);
    zglColorARGB(#ffffffff);
    int sy=Viewport.height;
    float sx=Viewport.width;
    float ival=oval;
    float iadd=2PI/sy;
    int idx=(hst_idx-1);
    if(idx<0) idx+=HST_LEN;
    float v=1.0;
    float vstep=1.0/sy;
    float vn;
    loop(sy)
	{
	    vn=v-vstep;
	    float c=sin(ival)*0.5+0.5;
	    idx=-1-c*(HST_LEN-1);
	    idx=(hst_idx+idx);
	    if(idx<0) idx+=HST_LEN;
	    _=tex_r2t[idx].bind();
	    
	    glBegin(GL_QUADS);
	    glTexCoord2f(0.0, v);
	    glVertex2f(0, y);
	    glTexCoord2f(1.0, v);
	    glVertex2f(sx, y);
	    glTexCoord2f(1.0, vn);
	    glVertex2f(sx, y+1);
	    glTexCoord2f(0.0, vn);
	    glVertex2f(0, y+1);
	    glEnd();
	    ival+=iadd;
	    y++;
	    v=vn;
	}
}

function DrawCube(Pointer _ca) {
    IntArray ca<=_ca;                        // get color array
    compile {
	glBegin(GL_QUADS);                   // Draw A Quad
	zglColorARGB(ca[0]);
	glVertex3f(1.0, 1.0, -1.0);          // Top Right Of The Quad (Top)
	zglColorARGB(ca[1]);
	glVertex3f(-1.0, 1.0, -1.0);         // Top Left Of The Quad (Top)
	zglColorARGB(ca[2]);
	glVertex3f(-1.0, 1.0, 1.0);          // Bottom Left Of The Quad (Top)
	zglColorARGB(ca[3]);
	glVertex3f(1.0, 1.0, 1.0);           // Bottom Right Of The Quad (Top)

	zglColorARGB(ca[4]);
	glVertex3f(1.0, -1.0, 1.0);          // Top Right Of The Quad (Bottom)
	zglColorARGB(ca[5]);
	glVertex3f(-1.0, -1.0, 1.0);         // Top Left Of The Quad (Bottom)
	zglColorARGB(ca[6]);
	glVertex3f(-1.0, -1.0, -1.0);        // Bottom Left Of The Quad (Bottom)
	zglColorARGB(ca[7]);
	glVertex3f(1.0, -1.0, -1.0);         // Bottom Right Of The Quad (Bottom)

	zglColorARGB(ca[3]);
	glVertex3f(1.0, 1.0, 1.0);           // Top Right Of The Quad (Front)
	zglColorARGB(ca[2]);
	glVertex3f(-1.0, 1.0, 1.0);          // Top Left Of The Quad (Front)
	zglColorARGB(ca[5]);
	glVertex3f(-1.0, -1.0, 1.0);         // Bottom Left Of The Quad (Front)
	zglColorARGB(ca[4]);
	glVertex3f(1.0, -1.0, 1.0);          // Bottom Right Of The Quad (Front)

	zglColorARGB(ca[7]);
	glVertex3f(1.0, -1.0, -1.0);         // Top Right Of The Quad (Back)
	zglColorARGB(ca[6]);
	glVertex3f(-1.0,-1.0,-1.0);          // Top Left Of The Quad (Back)
	zglColorARGB(ca[1]);
	glVertex3f(-1.0, 1.0,-1.0);          // Bottom Left Of The Quad (Back)
	zglColorARGB(ca[0]);
	glVertex3f(1.0, 1.0, -1.0);          // Bottom Right Of The Quad (Back)

	zglColorARGB(ca[2]);
	glVertex3f(-1.0, 1.0, 1.0);          // Top Right Of The Quad (Left)
	zglColorARGB(ca[1]);
	glVertex3f(-1.0, 1.0, -1.0);         // Top Left Of The Quad (Left)
	zglColorARGB(ca[6]);
	glVertex3f(-1.0, -1.0, -1.0);        // Bottom Left Of The Quad (Left)
	zglColorARGB(ca[5]);
	glVertex3f(-1.0, -1.0, 1.0);         // Bottom Right Of The Quad (Left)

	zglColorARGB(ca[0]);
	glVertex3f(1.0, 1.0, -1.0);          // Top Right Of The Quad (Right)
	zglColorARGB(ca[3]);
	glVertex3f(1.0, 1.0, 1.0);           // Top Left Of The Quad (Right)
	zglColorARGB(ca[4]);
	glVertex3f(1.0, -1.0, 1.0);          // Bottom Left Of The Quad (Right)
	zglColorARGB(ca[7]);
	glVertex3f(1.0, -1.0, -1.0);         // Bottom Right Of The Quad (Right)
	glEnd();                             // Done Drawing The Quad
    }
}


float DrawTexturedCube_u;
float DrawTexturedCube_v;
float DrawTexturedCube_ustep;
float DrawTexturedCube_vstep;
function DrawTexturedCube(Pointer _ca) {
    IntArray ca<=_ca;                        // get color array
    compile {
  	glDisable(GL_TEXTURE_2D);
	glBegin(GL_QUADS);                   // Draw A Quad
	zglColorARGB(ca[0]);
	glTexCoord2f(DrawTexturedCube_u+DrawTexturedCube_ustep, DrawTexturedCube_v);
	glVertex3f(1.0, 1.0, -1.0);          // Top Right Of The Quad (Top)
	zglColorARGB(ca[1]);
	glTexCoord2f(DrawTexturedCube_u, DrawTexturedCube_v);
	glVertex3f(-1.0, 1.0, -1.0);         // Top Left Of The Quad (Top)
	zglColorARGB(ca[2]);
	glTexCoord2f(DrawTexturedCube_u, DrawTexturedCube_v+DrawTexturedCube_vstep);
	glVertex3f(-1.0, 1.0, 1.0);          // Bottom Left Of The Quad (Top)
	zglColorARGB(ca[3]);
	glTexCoord2f(DrawTexturedCube_u+DrawTexturedCube_ustep, DrawTexturedCube_v+DrawTexturedCube_vstep);
	glVertex3f(1.0, 1.0, 1.0);           // Bottom Right Of The Quad (Top)
	glEnd();                             // Done Drawing The Quad

  	glDisable(GL_TEXTURE_2D);
	glBegin(GL_QUADS);                   // Draw A Quad
	zglColorARGB(ca[4]);
	glTexCoord2f(DrawTexturedCube_u+DrawTexturedCube_ustep, DrawTexturedCube_v);
	glVertex3f(1.0, -1.0, 1.0);          // Top Right Of The Quad (Bottom)
	zglColorARGB(ca[5]);
	glTexCoord2f(DrawTexturedCube_u, DrawTexturedCube_v+DrawTexturedCube_vstep);
	glVertex3f(-1.0, -1.0, 1.0);         // Top Left Of The Quad (Bottom)
	zglColorARGB(ca[6]);
	glTexCoord2f(DrawTexturedCube_u, DrawTexturedCube_v);
	glVertex3f(-1.0, -1.0, -1.0);        // Bottom Left Of The Quad (Bottom)
	zglColorARGB(ca[7]);
	glTexCoord2f(DrawTexturedCube_u+DrawTexturedCube_ustep, DrawTexturedCube_v);
	glVertex3f(1.0, -1.0, -1.0);         // Bottom Right Of The Quad (Bottom)
	glEnd();                             // Done Drawing The Quad

    	glEnable(GL_TEXTURE_2D);
	glBegin(GL_QUADS);                   // Draw A Quad
	zglColorARGB(ca[3]);
	glTexCoord2f(DrawTexturedCube_u+DrawTexturedCube_ustep, DrawTexturedCube_v+DrawTexturedCube_vstep);
	glVertex3f(1.0, 1.0, 1.0);           // Top Right Of The Quad (Front)
	zglColorARGB(ca[2]);
	glTexCoord2f(DrawTexturedCube_u, DrawTexturedCube_v+DrawTexturedCube_vstep);
	glVertex3f(-1.0, 1.0, 1.0);          // Top Left Of The Quad (Front)
	zglColorARGB(ca[5]);
	glTexCoord2f(DrawTexturedCube_u, DrawTexturedCube_v);
	glVertex3f(-1.0, -1.0, 1.0);         // Bottom Left Of The Quad (Front)
	zglColorARGB(ca[4]);
	glTexCoord2f(DrawTexturedCube_u+DrawTexturedCube_ustep, DrawTexturedCube_v);
	glVertex3f(1.0, -1.0, 1.0);          // Bottom Right Of The Quad (Front)
	glEnd();                             // Done Drawing The Quad

    	glEnable(GL_TEXTURE_2D);
	glBegin(GL_QUADS);                   // Draw A Quad
	zglColorARGB(ca[7]);
	glTexCoord2f(DrawTexturedCube_u+DrawTexturedCube_ustep, DrawTexturedCube_v);
	glVertex3f(1.0, -1.0, -1.0);         // Top Right Of The Quad (Back)
	zglColorARGB(ca[6]);
	glTexCoord2f(DrawTexturedCube_u, DrawTexturedCube_v);
	glVertex3f(-1.0,-1.0,-1.0);          // Top Left Of The Quad (Back)
	zglColorARGB(ca[1]);
	glTexCoord2f(DrawTexturedCube_u, DrawTexturedCube_v+DrawTexturedCube_vstep);
	glVertex3f(-1.0, 1.0,-1.0);          // Bottom Left Of The Quad (Back)
	zglColorARGB(ca[0]);
	glTexCoord2f(DrawTexturedCube_u+DrawTexturedCube_ustep, DrawTexturedCube_v+DrawTexturedCube_vstep);
	glVertex3f(1.0, 1.0, -1.0);          // Bottom Right Of The Quad (Back)
	glEnd();                             // Done Drawing The Quad

  	glDisable(GL_TEXTURE_2D);
	glBegin(GL_QUADS);                   // Draw A Quad
	zglColorARGB(ca[2]);
	glTexCoord2f(DrawTexturedCube_u+DrawTexturedCube_ustep, DrawTexturedCube_v+DrawTexturedCube_vstep);
	glVertex3f(-1.0, 1.0, 1.0);          // Top Right Of The Quad (Left)
	zglColorARGB(ca[1]);
	glTexCoord2f(DrawTexturedCube_u+DrawTexturedCube_ustep, DrawTexturedCube_v);
	glVertex3f(-1.0, 1.0, -1.0);         // Top Left Of The Quad (Left)
	zglColorARGB(ca[6]);
	glTexCoord2f(DrawTexturedCube_u, DrawTexturedCube_v);
	glVertex3f(-1.0, -1.0, -1.0);        // Bottom Left Of The Quad (Left)
	zglColorARGB(ca[5]);
	glTexCoord2f(DrawTexturedCube_u, DrawTexturedCube_v+DrawTexturedCube_vstep);
	glVertex3f(-1.0, -1.0, 1.0);         // Bottom Right Of The Quad (Left)
	glEnd();                             // Done Drawing The Quad

  	glDisable(GL_TEXTURE_2D);
	glBegin(GL_QUADS);                   // Draw A Quad
	zglColorARGB(ca[0]);
	glTexCoord2f(DrawTexturedCube_u+DrawTexturedCube_ustep, DrawTexturedCube_v);
	glVertex3f(1.0, 1.0, -1.0);          // Top Right Of The Quad (Right)
	zglColorARGB(ca[3]);
	glTexCoord2f(DrawTexturedCube_u+DrawTexturedCube_ustep, DrawTexturedCube_v+DrawTexturedCube_vstep);
	glVertex3f(1.0, 1.0, 1.0);           // Top Left Of The Quad (Right)
	zglColorARGB(ca[4]);
	glTexCoord2f(DrawTexturedCube_u, DrawTexturedCube_v+DrawTexturedCube_vstep);
	glVertex3f(1.0, -1.0, 1.0);          // Bottom Left Of The Quad (Right)
	zglColorARGB(ca[7]);
	glTexCoord2f(DrawTexturedCube_u, DrawTexturedCube_v);
	glVertex3f(1.0, -1.0, -1.0);         // Bottom Right Of The Quad (Right)
	glEnd();                             // Done Drawing The Quad
    }
}

// ---- define cube colors ----
IntArray cube_colors<=[
#ffffffff, //  ++-0
#ff00ff55, //  -+-1
#ff000000, //  -++2
#ffffff00, //  +++3
#ffffffff, //  +-+4
#ffffffff, //  --+5
#ff770000, //  ---6
#ff00afaf  //  +--7
];

// ---- define modulation cube colors ----
IntArray cube_colors2<=[
#ffffffff, //  -+-1
#fff79ea8, //  ++-0
#ffffffff, //  +-+4
#ffff0000, //  +--7
#ffff0000, //  +++3
#ff770000, //  ---6
#ffffffff, //  --+5
#ffffff00  //  -++2
];

int c_cube_colors[8]; c_cube_colors.numElements=c_cube_colors.maxElements;

function onDraw() {
    zglInitPerspective(1.0, 70.0, 0.001, 10.0);
    compile {
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);
	glFrontFace(GL_CCW);
	glEnable(GL_DEPTH_TEST);
	glShadeModel(GL_FLAT);
	glDisable(GL_TEXTURE_2D);
	glDisable(GL_BLEND);
	glClearColor(1.0, 1.0, 1.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
	glLoadIdentity();                // Reset The Current Modelview Matrix
    }
    glScalef(R2T_SU/Viewport.width, R2T_SV/Viewport.height, 1.0);

    // ---- update palette ----
    int i=0;
    float bf=sin(oval*6)*0.35+0.35;
    //Integer io;
    loop(c_cube_colors.numElements)
	{
	    int c32a=cube_colors[i];
	    int c32b=cube_colors2[i];
	    //int c32=mathC32Blend(c32a, c32, bf);
	    int c32=mathC32Multiply(c32a, c32, bf);
            //io.value=c32; trace "c32a*c32b="+io.printf("#%08x");
	    c_cube_colors[i]=c32;
	    i++;
	}

    need_dl=1;
    if(need_dl)
	{
	    // ---- create cube display list ----
	    need_dl=0;
  	    dl_cube.free();
	    dl_cube.alloc();
	    dl_cube.begin();
	    if(tex_mode)
		{
		    DrawTexturedCube_u=0.0;
		    DrawTexturedCube_ustep=1.0/GRIDSX;
		    DrawTexturedCube_v=0.0;
		    DrawTexturedCube_vstep=1.0/GRIDSY;
		    DrawTexturedCube(c_cube_colors);
		}
	    else
		{
		    DrawCube(c_cube_colors);
		}
	    dl_cube.end();
	}
    int id=dl_cube.id;
    cam.loadTransform();

    if(tex_mode)
	tex_fb.bind();

    compile {
	glRotatef(rquadx,1.0,0.0,0.0);
	glRotatef(rquady,0.0,1.0,0.0);
	glRotatef(rquadz,0.0,0.0,1.0);
	float fld=(0.5+sin(rfld)*0.5);
	fld=2.0+1.5*fld;
	glScalef(1/(GRIDSX*2.0), 1/(GRIDSY*2.0), 1.0/(GRIDSZ*2.0));
	glTranslatef(-fld*(GRIDSX/2), -fld*(GRIDSY/2), -fld*(GRIDSZ/2));
	float tstep=1.0/GRIDSX;
	float sq3=1.0/sqrt(3.0);
	if(tex_mode)
	    {
		glMatrixMode(GL_TEXTURE);
		glLoadIdentity();
		glMatrixMode(GL_MODELVIEW);
		float tz=0.0;
		loop(GRIDSZ)
		    {   // ---- loopz ----
			float ty=0.0;
			loop(GRIDSY)
			    {   // ---- loopy ----
				float tx=0.0;
				loop(GRIDSX)
				    {   // ---- loopx ----
					glCallList(id);
					glTranslatef(fld, 0.0, 0.0);
  					glMatrixMode(GL_TEXTURE);
					glLoadIdentity();
  					glTranslatef(tx,ty,tz);
  					glMatrixMode(GL_MODELVIEW);
					tx+=tstep;
				    }
				glTranslatef(-fld*GRIDSX, fld, 0.0);
				ty+=tstep;
			    }
			glTranslatef(0.0, -fld*GRIDSY, fld);
			tz+=tstep;
		    }
		glMatrixMode(GL_TEXTURE);
		glLoadIdentity();
		glMatrixMode(GL_MODELVIEW);
	    }
	else
	    {
		loop(GRIDSZ)
		    {   // ---- loopz ----
			loop(GRIDSY)
			    {   // ---- loopy ----
				loop(GRIDSX)
				    {   // ---- loopx ----
					glCallList(id);
					glTranslatef(fld, 0.0, 0.0);
				    }
				glTranslatef(-fld*GRIDSX, fld, 0.0);
			    }
			glTranslatef(0.0, -fld*GRIDSY, fld);
		    }
	    }
    }

    glDisable(GL_CULL_FACE);

    Debug.Draw();

    if(rubber_mode)
	{
	    glEnable(GL_TEXTURE_2D);
	    _=tex_r2t[hst_idx].bind(); // a workaround to evaluate an expr as a statement..
	    // ---- grab texture ----
	    glLoadIdentity();
	    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (Viewport.width-R2T_SU)/2,(Viewport.height-R2T_SV)/2,R2T_SU,R2T_SV, 0);
	    hst_idx=(hst_idx+1);
	    if(hst_idx>=HST_LEN) hst_idx-=HST_LEN;
	    zglInit2D(Viewport.width, Viewport.height);
	    glTranslatef(-Viewport.width/4, -Viewport.height/4, 0);
	    glScalef(1.45, 1.45,1);
	    // ---- draw raster lines ----
	    DrawRasters();
	}

    if(tex_mode)
	{
	    glEnable(GL_TEXTURE_2D);
	    tex_fb.bind();
	    // ---- grab texture ----
	    glLoadIdentity();
	    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (Viewport.width-R2T_SU)/2,(Viewport.height-R2T_SV)/2,R2T_SU,R2T_SV, 0);
	}

    if(waitvblank)Viewport.waitVBlank();
    // ---- increase counters ----
    float dt=FPS.precision;
    if enable_rotation rquadx+=dt*1.7321894395*(sin(oval)*0.5+0.5);
    if enable_rotation rquady+=dt*5.92321894395*(sin(oval)*0.15+0.85);
    if enable_rotation rquadz+=dt*2.64321894395;//*(cos(oval)*0.15+0.85);
    if(rquadx>=360.0) rquadx-=360.0;
    if(rquady>=360.0) rquady-=360.0;
    if(rquadz>=360.0) rquadz-=360.0;

    if enable_rotation rfld+=dt*0.10658227115*(sin(oval*2)*0.45+0.45)+0.005123456;
    scrcnt+=dt*0.05;
    wrap scrcnt 0 3;
    if enable_rotation oval+=2.64321894395/384; //0.041494345*dt;
    if(oval>=2PI)oval-=2PI;
}

function onReopen() {
    need_dl=1;
    Debug.Init();

    if(1/*rubber_mode*/)
	{
	    // ---- prepare texture to render to ----
	    // ---- the texture data is only allocated for the upload() call which
	    // ---- allocates a new texture with the given size in gfx memory
	    // ---- the (system memory) texture data can safely be deleted after
	    // ---- texture nameid generation (upload()).
	    int i=0;
	    int flags=TEX_MINFILTERLINEAR|TEX_MAGFILTERLINEAR;//|TEX_MODULATE;

	    // ---- feedback texture ----
	    tex_fb.free();
	    tex_fb.setFlags(TEX_MINFILTERLINEAR|TEX_MAGFILTERLINEAR|TEX_MODULATE);
	    tex_fb.upload();

	    _=tex_r2t[0].free();
	    _=tex_r2t[0].setFlags(flags);
	    _=tex_r2t[0].upload();
	    loop(HST_LEN-1)
		{
		    i++;
		    _=tex_r2t[i].free();
		    _=tex_r2t[i].setFlags(flags);
		    _=tex_r2t[i].upload();
		}
	}
}

function onMouse(int _dx, int _dy, int _cbs, int _nbs) {
    //cam.onMouse(_dx*0.01, _dy*0.01, _cbs, _nbs);
}

function onKeyboard(Key _k) {
    if(_k.pressed) switch(_k.pressed) {
    case 'r': rubber_mode=1-rubber_mode; trace "rubber_mode set to "+rubber_mode; break;
    case 't': tex_mode=1-tex_mode; trace "tex_mode set to "+tex_mode; break;
    case ' ': draw_quads=1-draw_quads; Debug.Init(); break;
    case VKEY_LCTRL: enable_rotation=0; enable_rotation=1; break;
    case 'v': waitvblank=1-waitvblank; trace "vsync set to "+waitvblank; Debug.Init(); break;
    default: Debug.OnKeyboard(_k); break;
    }
}

function main() {
    Viewport.openWindow(512, 384);
    FPS.tickInterval=1000.0/20;
    FPS.tickBuffer=0;
    Viewport.setScreenResolution(800,600,32);

    cam.init();
    onReopen();

    cam.lookatz=-3.63;
    cam.xrotclamp=false;

    trace "-------- cubeworld --------";
    trace "     g : (un-grab) mouse";
    trace "     d : toggle debug overlay";
    trace "ctrl-d : toggle console debug output";
    trace "     f : toggle fullscreen mode";
    trace "     v : toggle vsync";
    trace " lctrl : stop rotation";
    trace "     r : toggle rubber_mode";
    trace "\n\n";

    use callbacks;
    SDL.eventLoop();
}