/*
   Example that compares perspective and orthographic projections of an object -- in
   this case, a simple house.  Change the #define and #undef for PERSP and ORTHO and
   recompile to compare the two views.
   
   Sample for the introductory computer graphics course -- Dr. Cunningham
*/
#include "glut.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

//	set up so you can switch between perspective and orthographic projections easily
#define PERSP
#undef  ORTHO

static char ch; // the character from the keyboard that controls the rotation
typedef GLfloat point3[3];
typedef GLfloat color [4];

// function prototypes
void myinit(void);
void drawAxis(void);
void drawAxes(void);
void drawHouse(void);
void display(void);
void reshape(int,int);
void keyboard(unsigned char,int,int);

void myinit(void)
{
//      set up overall light data
        GLfloat light_position[]={ 10.0, 10.0, 10.0, 1.0 };
        GLfloat light_color[]   ={ 1.0, 1.0, 1.0, 1.0 };
        GLfloat ambient_color[] ={ 1.0, 1.0, 1.0, 1.0 };

        glClearColor( 0.0, 0.0, 0.0, 0.0 );
        glShadeModel(GL_SMOOTH);
        glLightfv(GL_LIGHT0, GL_POSITION, light_position );
        glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color );
        glLightfv(GL_LIGHT0, GL_SPECULAR, light_color );
        glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color );

/* attributes */

        glEnable(GL_LIGHTING);   // so lighting models are used
        glEnable(GL_LIGHT0);     // we'll use LIGHT0
        glEnable(GL_DEPTH_TEST); // allow z-buffer display
}

void drawAxis(void)
{
	#define RAD 0.03
	#define LEN 5.0
// draw one axis, the Z-axis, as the template for all axes
//  Axes are to be colored white
    GLfloat white[]={1.0, 1.0, 1.0, 1.0};

    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, white );
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, white );

//  Draw the standard axis in Z-orientation
    glBegin(GL_QUAD_STRIP);
      glVertex3f( RAD, RAD,  LEN );
      glVertex3f( RAD, RAD, -LEN );
      glVertex3f(-RAD, RAD,  LEN );
      glVertex3f(-RAD, RAD, -LEN );
      glVertex3f(-RAD,-RAD,  LEN );
      glVertex3f(-RAD,-RAD, -LEN );
      glVertex3f( RAD,-RAD,  LEN );
      glVertex3f( RAD,-RAD, -LEN );
      glVertex3f( RAD, RAD,  LEN );
      glVertex3f( RAD, RAD, -LEN );
    glEnd();
    glPushMatrix();
    glTranslatef( 0.0, 0.0, LEN );
    glutSolidCone( 0.1, 0.2, 20, 20 );
    glPopMatrix();
}

void drawAxes(void)
{
//  Draw the Z-axis
    drawAxis();
//  Draw the standard axis in X-orientation
    glPushMatrix();
    glRotatef( 90.0, 0.0, 1.0, 0.0 );
    drawAxis();
    glPopMatrix();
//  Draw the standard axis in Y-orientation
    glPushMatrix();
    glRotatef( -90.0, 1.0, 0.0, 0.0 );
    drawAxis();
    glPopMatrix();
}

void drawHouse( void )
{
    point3 myHouse[10] = {	{ -1.0, -1.0,  2.0 },
    						{ -1.0,  1.0,  2.0 },
    						{  0.0,  2.0,  2.0 },
    						{  1.0,  1.0,  2.0 },
    						{  1.0, -1.0,  2.0 },
    						{ -1.0, -1.0, -2.0 },
    						{ -1.0,  1.0, -2.0 },
    						{  0.0,  2.0, -2.0 },
    						{  1.0,  1.0, -2.0 },
    						{  1.0, -1.0, -2.0 }
    					  };
    int i;

    glBegin(GL_LINE_STRIP);
      for ( i=0; i<5; i++)
        glVertex3fv(myHouse[i]);
      glVertex3fv(myHouse[0]);
    glEnd();
    glBegin(GL_LINE_STRIP);
      for ( i=0; i<5; i++)
        glVertex3fv(myHouse[i+5]);
      glVertex3fv(myHouse[5]);
    glEnd();
    for ( i=0; i<5; i++) {
        glBegin(GL_LINE_STRIP);
        	glVertex3fv(myHouse[i]);
        	glVertex3fv(myHouse[i+5]);
    	glEnd();
    }
}

void display( void )
{
/* define a point data type */
    typedef GLfloat point3[3];

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //  NOTE that we do not take ourselves back to the original raw-world state.
    //  We let the modelview matrix hold our global view of the model, and
    //  we modify it by including the new rotation each time a key is pressed
    
#define Angle 2.0

    switch(ch)
    {
        case 'w':
            glRotatef( Angle, 1.0, 0.0, 0.0); break;
        case 'q':
            glRotatef(-Angle, 1.0, 0.0, 0.0); break;
        case 's':
            glRotatef( Angle, 0.0, 1.0, 0.0); break;
        case 'a':
            glRotatef(-Angle, 0.0, 1.0, 0.0); break;
        case 'x':
            glRotatef( Angle, 0.0, 0.0, 1.0); break;
        case 'z':
            glRotatef(-Angle, 0.0, 0.0, 1.0); break;
    }
    
//  NOW we save the state of the modelview transformation so we can restore
//  it after the axes and cube have been drawn.
    glPushMatrix();
    drawAxes();    
	drawHouse();
    glPopMatrix();
    glutSwapBuffers();
 }

void reshape(int w,int h)
{
        glViewport(0,0,(GLsizei)w,(GLsizei)h);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        #ifdef PERSP
        gluPerspective(45.0,1.0,1.0,30.0);
        #endif
        #ifdef ORTHO
        glOrtho(-10.0, 10.0,-10.0, 10.0, 1.0, 30.0);
        #endif
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        /*           eye point        center of view      up   */
        gluLookAt( 10.0,  10.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}

void keyboard(unsigned char key, int x, int y)
{
        switch (key)
        {
        case 'w' :     // rotate around X; i = positive, j = negative
           ch = key; break;
        case 'q' :
           ch = key; break;
        case 's' :    // rotate around Y; k = positive, m = negative
           ch = key; break;
        case 'a' :
           ch = key; break;
        case 'x' :    // rotate around Z; a = positive, s = negative
           ch = key; break;
        case 'z' :
           ch = key; break;
        }
        glutPostRedisplay(); /* perform display again */
}

void main(int argc, char** argv)
{
/* Standard GLUT initialization */
        glutInit(&argc,argv);
        glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
        glutInitWindowSize(500,500);
        glutInitWindowPosition(70,70);
        glutCreateWindow("Projections");
        glutDisplayFunc(display);
        glutReshapeFunc(reshape);
        glutKeyboardFunc(keyboard); // enable keyboard callback

        myinit();
        glutMainLoop();
}