/*
	Sample code to draw a set of cubes of various colors for demonstrating
	the use of color in OpenGL

	(c) 2000, Steve Cunningham
*/
#include "glut.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#define NUMSTEPS 20
#define SIZE 20.0

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 display(void);
void cube(float r, float g, float b);
void ribboncube(void);
void reshape(int,int);
void keyboard(unsigned char,int,int);

void myinit(void)
{
	glClearColor( 0.0, 0.0, 0.0, 0.0 );
	glEnable(GL_DEPTH_TEST); // allow z-buffer display
}

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

	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
	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();
		ribboncube();
	glPopMatrix();
	glutSwapBuffers();
	}

void reshape(int w,int h)
{
	glViewport(0,0,(GLsizei)w,(GLsizei)h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0,1.0,1.0,30.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	/*           eye point        center of view      up   */
	gluLookAt( 0.0,  0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}
/*
	Basic function to draw a unit cube centered at the origin with
	faces perpendicular to the standard x, y, and z axes, in a color
	specified through the parameters to the function.  This cube is
	extremely simple but is enough to allow us to build examples on
	it.
*/
void cube(float r, float g, float b)
{
//	define point and color data types
	color cubecolor;
	point3 vertices[8]={{-1.0, -1.0, -1.0},
				  {-1.0, -1.0,  1.0},
				  {-1.0,  1.0, -1.0},
				  {-1.0,  1.0,  1.0},
				  { 1.0, -1.0, -1.0},
				  { 1.0, -1.0,  1.0},
				  { 1.0,  1.0, -1.0},
				  { 1.0,  1.0,  1.0} };

	cubecolor[0] = r; cubecolor[1] = g; cubecolor[2] = b;
	cubecolor[3] = 1.0;
	glColor4fv(cubecolor);
	glBegin(GL_QUADS);
		glVertex3fv(vertices[1]);   // first quad: positive Z face
		glVertex3fv(vertices[5]);
		glVertex3fv(vertices[7]);
		glVertex3fv(vertices[3]);
		glVertex3fv(vertices[7]);   // second quad: positive Y face
		glVertex3fv(vertices[6]);
		glVertex3fv(vertices[2]);
		glVertex3fv(vertices[3]);
		glVertex3fv(vertices[2]);   // third quad: negative Z face
		glVertex3fv(vertices[6]);
		glVertex3fv(vertices[4]);
		glVertex3fv(vertices[0]);
		glVertex3fv(vertices[5]);  // fourth quad: positive X face
		glVertex3fv(vertices[4]);
		glVertex3fv(vertices[6]);
		glVertex3fv(vertices[7]);
		glVertex3fv(vertices[4]);  // fifth quad: negative Y face
		glVertex3fv(vertices[5]);
		glVertex3fv(vertices[1]);
		glVertex3fv(vertices[0]);
		glVertex3fv(vertices[0]);  // sixth quad: negative X face
		glVertex3fv(vertices[1]);
		glVertex3fv(vertices[3]);
		glVertex3fv(vertices[2]);
	glEnd();
}

/*
	Basic function to draw a unit cube centered at the origin with
	faces perpendicular to the standard x, y, and z axes, with the
	edges of the cube shown by displaying a number of individual cubes,
	each with a color appropriate to its location.  Uses the usual
	cube(r,g,b) function to display the individual cubes.
*/
void ribboncube(void)
{

	int i, j, k;	// indices for the nested for loops that will
				// display all the little cubes

	float scale = 1.0/(float)NUMSTEPS;
	for (i=0; i<=NUMSTEPS; i++) {	// red parts
		glPushMatrix();
		glScalef(scale,scale,scale);
		glTranslatef(-SIZE+(float)i*2.0*scale*SIZE,SIZE,SIZE);
		cube((float)i/(float)NUMSTEPS,1.0,1.0);
		glPopMatrix();

		glPushMatrix();
		glScalef(scale,scale,scale);
		glTranslatef(-SIZE+(float)i*2.0*scale*SIZE,-SIZE,SIZE);
		cube((float)i/(float)NUMSTEPS,0.0,1.0);
		glPopMatrix();

		glPushMatrix();
		glScalef(scale,scale,scale);
		glTranslatef(-SIZE+(float)i*2.0*scale*SIZE,SIZE,-SIZE);
		cube((float)i/(float)NUMSTEPS,1.0,0.0);
		glPopMatrix();

		glPushMatrix();
		glScalef(scale,scale,scale);
		glTranslatef(-SIZE+(float)i*2.0*scale*SIZE,-SIZE,-SIZE);
		cube((float)i/(float)NUMSTEPS,0.0,0.0);
		glPopMatrix();
	}
	
	for (j=0; j<=NUMSTEPS; j++) {	// green parts
		glPushMatrix();
		glScalef(scale,scale,scale);
		glTranslatef(SIZE,-SIZE+(float)j*2.0*scale*SIZE,SIZE);
		cube(1.0,(float)j/(float)NUMSTEPS,1.0);
		glPopMatrix();

		glPushMatrix();
		glScalef(scale,scale,scale);
		glTranslatef(-SIZE,-SIZE+(float)j*2.0*scale*SIZE,SIZE);
		cube(0.0,(float)j/(float)NUMSTEPS,1.0);
		glPopMatrix();

		glPushMatrix();
		glScalef(scale,scale,scale);
		glTranslatef(SIZE,-SIZE+(float)j*2.0*scale*SIZE,-SIZE);
		cube(1.0,(float)j/(float)NUMSTEPS,0.0);
		glPopMatrix();

		glPushMatrix();
		glScalef(scale,scale,scale);
		glTranslatef(-SIZE,-SIZE+(float)j*2.0*scale*SIZE,-SIZE);
		cube(0.0,(float)j/(float)NUMSTEPS,0.0);
		glPopMatrix();
	}
			
	for (k=0; k<=NUMSTEPS; k++) {	// blue parts
		glPushMatrix();
		glScalef(scale,scale,scale);
		glTranslatef(SIZE,SIZE,-SIZE+(float)k*2.0*scale*SIZE);
		cube(1.0,1.0,(float)k/(float)NUMSTEPS);
		glPopMatrix();

		glPushMatrix();
		glScalef(scale,scale,scale);
		glTranslatef(-SIZE,SIZE,-SIZE+(float)k*2.0*scale*SIZE);
		cube(0.0,1.0,(float)k/(float)NUMSTEPS);
		glPopMatrix();

		glPushMatrix();
		glScalef(scale,scale,scale);
		glTranslatef(SIZE,-SIZE,-SIZE+(float)k*2.0*scale*SIZE);
		cube(1.0,0.0,(float)k/(float)NUMSTEPS);
		glPopMatrix();

		glPushMatrix();
		glScalef(scale,scale,scale);
		glTranslatef(-SIZE,-SIZE,-SIZE+(float)k*2.0*scale*SIZE);
		cube(0.0,0.0,(float)k/(float)NUMSTEPS);
		glPopMatrix();
	}
 }

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("color cube");
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard); // enable keyboard callback

	myinit();
	glutMainLoop();
}
