/*
   Simple example of a single patch of a spline surface, with control points shown
   
   Example for introductory graphics course -- copyright (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];

void myinit(void);
void display(void);
void doPoints(void);
void doPatch(void);
void reshape(int,int);
void keyboard(unsigned char,int,int);

point3 patch[4][4] = { { { -2., -2.,  0.}, { -2., -1.,  1.}, { -2.,  1.,  1.}, { -2.,  2.,  0.} },
					   { { -1., -2.,  1.}, { -1., -1.,  2.}, { -1.,  1.,  2.}, { -1.,  2.,  1.} },
					   { {  1., -2.,  1.}, {  1., -1.,  2.}, {  1.,  1.,  2.}, {  1.,  2.,  1.} },
					   { {  2., -2.,  0.}, {  2., -1.,  1.}, {  2.,  1.,  1.}, {  2.,  2.,  0.} } };
GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
GLfloat gray[4] = {0.2, 0.2, 0.2, 1.0};
GLfloat red[4] = {1.0, 0.0, 0.0, 0.7};	// note alpha value so grid points can be seen behind surface
GLfloat green[4] = {0.0, 1.0, 0.0, 1.0};
GLfloat light_pos[4] = {30.0, 30.0, 30.0, 1.0};
GLfloat mat_shininess[] = { 50. };

void myinit(void)
{
	glClearColor( 0.0, 0.0, 0.0, 0.0 );
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//	light properties
	glLightfv(GL_LIGHT0, GL_AMBIENT, white);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, white);
	glLightfv(GL_LIGHT0, GL_SPECULAR, white);
	glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_DEPTH_TEST);
	glEnable(GL_AUTO_NORMAL);
	glEnable(GL_MAP2_VERTEX_3);
}

void display( void )
{
	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;
	}    
	glPushMatrix();
	doPoints();
	doPatch();
	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( 10.0,  0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
}

void doPoints(void)
{
	int i, j;
	
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, green);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, white);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);

	glPointSize(5.0);
	glBegin(GL_POINTS);
		for (i=0; i<4; i++)
			for (j=0; j<4; j++)
				glVertex3fv(patch[i][j]);
	glEnd();
}

void doPatch(void)
{
//	draws a patch defined by a 4 x 4 array of points
#define NUM 20	// 

	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, red);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, white);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);

	glMap2f(GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 4, 0.0, 1.0, 12, 4, &patch[0][0][0]);
	glMapGrid2f(NUM, 0.0, 1.0, NUM, 0.0, 1.0);
	glEvalMesh2(GL_FILL, 0, NUM, 0, NUM);
}

void keyboard(unsigned char key, int x, int y)
{
	switch (key) {
		case 'w' :
		case 'q' :
		case 's' :
		case 'a' :
		case 'x' :
		case 'z' :
			ch = key; break;
	}
	glutPostRedisplay();
}

void main(int argc, char** argv)
{
	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);

	myinit();
	glutMainLoop();
}