#!/usr/bin/perl -w

use OpenGL ":old",":glutfunctions",":gluconstants",":functions";
use Time::HiRes qw (gettimeofday tv_interval usleep);
use strict;
    # because of the OpenGL header functs
no strict "subs";
our (@tri3d,@options,$z,$t,$time,$fps,$zsize,$xmin,$xmax,$ymin,$ymax,$zmin,$zmax,$tmin,$tmax,$xres,$yres,$zres,$tres,$width,$height);



$xmin = -10;
$xmax = 10;
$ymin = -100;
$ymax = 100;
$zmin = -100;
$zmax = 100;
$tmin = -100;
$tmax = 100;

#Anzahl Punkte
$xres = 20;
$zres = 40;
$tres = 50;

$z = $zmin;
$t = $tmin;

$time = [gettimeofday];
$fps = 20;
$zsize = 0;

# 0(0): 2d
# 0(1): 3d wire
# 0(2): 3d solid
# 1(0): stable
# 1(1): time-function
# 2(0): orthographic viewing
# 2(1): perspective viewing
# 2(2): stereo viewing
# 2(3): 2nd way of stereo viewing

@options = (2,0,0);

init();
    # main loop
glutMainLoop;


#- eval error
#- perldoc -f eval

sub init{
    glutInit(@ARGV);
    glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(600, 600);
    glutInitWindowPosition(50, 50);
    glutCreateWindow("graph window");

    # black
    glClearColor(0,0,0,0);
    # reset
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    # set the window properties
    glOrtho($xmin,$xmax,$ymin,$ymax,$zmin,$zmax);
    idle();
    glRotatef(-30, 5, -0.5, 0.0);
    glFlush();

    glutKeyboardFunc(\&keyboard);
    glutDisplayFunc(\&display);
    glutReshapeFunc(\&resize);
    glutIdleFunc(\&idle);
}



sub idle {
    my (@tripar,$x,$y,$j);

    if (($options[1] == 0) and ($zsize<$zres)) {

    $x=$xmin;
    for ($j=0; $j<=(3*$xres-3); $j=$j+3) {

        $y=$x*sin($z+$x*sin($z));
    
        $tripar[$j]=$x;
        $tripar[$j+1]=$y;
        $tripar[$j+2]=$z;
        $x += ($xmax-$xmin)/($xres-1);
    }
    $j = 0;
    $z += ($zmax-$zmin)/($zres-1);
    $zsize++;

    if (tv_interval($time)<(1/$fps)) {
	usleep (1/120);
	return;
    }
    $time = [gettimeofday];

    unshift(@tri3d,@tripar);
    splice(@tri3d,(9*$xres*$zres));
 
    if ($zsize>1) {display();}
    }

}

sub display {
    glColor3f(1,1,1);
    glClear(GL_COLOR_BUFFER_BIT);
    my ($h,$i);

    # display2d()
    if ($options[0] == 0) {
    }
    
    # display3d_wire()
    elsif ($options[0] == 1) {
	for $h (0..$zsize) {
	    glBegin(GL_LINE_STRIP); {
	    	# jeweils um einen Punkt springen
	    	for ($i=0; $i<=(3*$xres-4); $i=$i+3) {
		    glVertex3f($tri3d[$i+3*$h*$xres],$tri3d[$i+1+3*$h*$xres],$tri3d[$i+2+3*$h*$xres]);
		    glVertex3f($tri3d[$i+3*$xres+3*$h*$xres],$tri3d[$i+3*$xres+1+3*$h*$xres],$tri3d[$i+3*$xres+2+3*$h*$xres]);
		    glVertex3f($tri3d[$i+3*$xres+3+3*$h*$xres],$tri3d[$i+3*$xres+4+3*$h*$xres],$tri3d[$i+3*$xres+5+3*$h*$xres]);
		    glVertex3f($tri3d[$i+3*$h*$xres],$tri3d[$i+1+3*$h*$xres],$tri3d[$i+2+3*$h*$xres]);
		}
	    } glEnd();
	}
    }
    
    # display3d_solid()
    # FALSCH
    elsif ($options[0] == 2) {
	for $h (0..$zsize) {
	    glBegin(GL_TRIANGLE_STRIP);
	    {
		glVertex3f($tri3d[3*$xres+3*$h*$xres],$tri3d[3*$xres+1+3*$h*$xres],$tri3d[3*$xres+2+3*$h*$xres]);
	    	for ($i=0; $i<=(3*$xres-4); $i=$i+3) {
		    glVertex3f($tri3d[$i+3*$h*$xres],$tri3d[$i+1+3*$h*$xres],$tri3d[$i+2+3*$h*$xres]);
		    glVertex3f($tri3d[$i+3*$xres+3*$h*$xres],$tri3d[$i+3*$xres+1+3*$h*$xres],$tri3d[$i+3*$xres+2+3*$h*$xres]);
		}
	    } glEnd();
	}
    }
    
    glFlush();
    glutSwapBuffers();    
}


sub keyboard {
    my ($key) = @_;
    $key = chr($key);
    if($key=~/w/i){
    # rotate up
	glRotatef(3.0, 0.5, 0, 0.0);
    }elsif($key=~/s/i){
    # rotate down
        glRotatef(-3.0, 0.5, 0, 0.0);
    }elsif($key=~/a/i){
    # rotate left
        glRotatef(3.0, 0.0, 0.5, 0);
    }elsif($key=~/d/i){
    # rotate right
        glRotatef(-3.0, 0.0, 0.5, 0);
    }elsif($key=~/q/i){
        exit();
    }elsif($key=~/m/i){
        screenshot();
    }elsif($key=~/r/i){
        glScalef(1.1,1.1,1.1);
    }elsif($key=~/f/i){
        glScalef(0.9,0.9,0.9);
    }
    
    # repaint the window
    display(); 
}


sub resize{
    ($width,$height)=@_;
}


sub getheader{
    my ($width,$height) = @_;
    my ($header);

    $header =  pack("s",hex("4d42"));	        #signature
    $header .= pack("i",$width*$height*3+hex(36));#size (inc header)
    $header .= pack("s",0);			#reserved
    $header .= pack("s",0);			#reserved
    $header .= pack("i",hex(36));		#offset
    $header .= pack("i",40);		        #size of BITMAPINFOHEADER structure, must be 40
    $header .= pack("i",$width);		#width
    $header .= pack("i",$height);		#hight
    $header .= pack("s",1);			#number of planes in the image, must be 1
    $header .= pack("s",24);	                #bites per pixel
    $header .= pack("i",0);			#compression type (0=none, 1=RLE-8, 2=RLE-4)
    $header .= pack("i",$width*$height*3);	#size of image data
    $header .= pack("i",0);			#horizontal resolution in pixels per meter (unreliable)
    $header .= pack("i",0);			#vertical resolution in pixels per meter (unreliable)
    $header .= pack("i",0);			#number of colors in image, or zero
    $header .= pack("i",0);			#number of important colors, or zero

    return $header;
}


sub screenshot {
    my $num = 1;
    my $i;
    $num++ while(-f "image$num.bmp");

    if(open(IMAGE,">image$num.bmp")){
        print IMAGE getheader($width,$height);
        my @pixels=glReadPixels_p(0, 0,$width,$height, GL_RGB,GL_UNSIGNED_INT);
        #{print (IMAGE pack("B*", $i )); }for $i (@pixels);
        for $i (0..(($#pixels-1)/3)){
            my ($red, $green, $blue) = ($pixels[$i*3],$pixels[$i*3+1],$pixels[$i*3+2]);
            print IMAGE pack("B B B",$blue,$green,$red);
        }
        #for $pixel (@pixels){ print IMAGE pack ("B*",$pixel);}
        close(IMAGE);
    }
}


sub resetall {
    $z = $zmin;
    $t = $tmin;
    glClearColor(0,0,0,0);
    glLoadIdentity();
    display();
}


