/*
Ben Eadington
CS 3600

  This program draws a pool using Bézier surfaces.  The pool's surface is
  defined by an array of control points.  The array in this program is defined
  to be 16 x 16 in size.  This 16 x 16 array is subdivided into 4 x 4 arrays 
  using midpoints between points to assure smoothness.  The space bar toggles 
  the display of the control points on and off (a control point is represented 
  by a small cube.)  When the control points are being displayed they can be 
  selected using the mouse.  All of the points are selectable and the selected 
  point is indicated by being red rather than green.  The selected point can be 
  moved, thus altering the shape of the pool with the following keys:
  n and m  -  positive and negative movement parallel to the x axis
  j and k  -  positive and negative movement parallel to the y axis
  i and o  -  positive and negative movement parallel to the z axis

  The entire display can be rotated about the world coordinates with the
  following keys:
  z and x  -  positive and negative rotation about x axis
  a and s  -  positive and negative rotation about y axis
  q and w  -  positive and negative rotation about z axis
*/
#define OSX

#ifdef MAC
#include "glut.h"
#endif
#ifdef OSX
#include <GLUT/glut.h>
#endif
#ifdef UNIX
#include <GL/glut.h>
#endif
#ifdef PC
#include <GL/glut.h>
#endif

#include <stdlib.h>
#include <stdio.h>

#define ROT_ANG 5    // angle of rotation for each keystroke
#define GRIDSIZE 16  // control point array is of size GRIDSIZE x GRIDSIZE
#define MAXHITS 200  // size of array to hold hit records for selection

typedef GLfloat point3[3];
typedef GLfloat point4[4];

static GLfloat Angle = 0.0;  // current angle of rotation
static int RotAxis = -1;  // axis model is to be rotated about

// initial size of window and initialize that no item is selected
GLint windW = 500, windH = 500, hit=-1;
// data structures for selection process
GLuint selectBuf[MAXHITS];
GLint vp[4];

static int points=0; // 1 if control points are to be displayed, 0 if not

// variables to hold light properties color values
point4 white={1.0, 1.0, 1.0, 1.0};
point4 gray={0.2, 0.2, 0.2, 1.0};
point4 red={1.0, 0.0, 0.0, 1.0};
point4 green={0.0, 1.0, 0.0, 1.0};
point4 wat_col={0.0, 0.0, 0.5, 0.25};
point4 light_specular={0.5, 0.5, 0.5, 1.0};
point4 light_pos={30.0, 30.0, 30.0, 1.0};

// array to hold rotation matrix, initialized to identity
static GLfloat RotMatrix[16] =
	{1.0, 0.0, 0.0, 0.0,
	 0.0, 1.0, 0.0, 0.0,
	 0.0, 0.0, 1.0, 0.0,
	 0.0, 0.0, 0.0, 1.0};

// control point arrays for water and pool surface
point3 water[GRIDSIZE][GRIDSIZE];
point3 ctrlpts[GRIDSIZE][GRIDSIZE] ={
 {{-8.0,0.0,8.0}, {-7.0,0.0,10.0}, {-6.0,0.0,11.0}, {-5.0,0.0,11.0},
 {-4.0,0.0,12.0}, {-3.0,0.0,12.0}, {-2.0,0.0,10.0}, {-1.0,0.0,8.0},
 {1.0,0.0,8.0}, {2.0,0.0,9.0}, {3.0,0.0,10.0}, {4.0,0.0,12.0},
 {5.0,0.0,12.0}, {6.0,0.0,10.0}, {7.0,0.0,9.0}, {8.0,0.0,8.0}},

 {{-9.0,0.0,7.0}, {-6.0,-5.0,9.0}, {-6.0,-5.0,10.0}, {-5.0,-5.0,10.0},
 {-4.0,-5.0,11.0}, {-3.0,-6.5,11.0}, {-2.0,-7.0,9.0}, {-1.0,-7.5,7.0},
 {1.0,-8.0,7.0}, {2.0,-8.5,8.0}, {3.0,-9.0,8.0}, {4.0,-9.5,11.0},
 {5.0,-9.5,11.0}, {6.0,-9.5,9.0}, {8.0,-9.5,8.0}, {9.0,0.0,7.0}},

 {{-10.0,0.0,6.0}, {-9.0,-5.0,6.0}, {-6.0,-5.0,6.0}, {-5.0,-5.0,6.0},
 {-4.0,-5.0,6.0}, {-3.0,-6.5,6.0}, {-2.0,-7.0,6.0}, {-1.0,-7.5,6.0},
 {1.0,-8.0,6.0}, {2.0,-8.5,6.0}, {3.0,-9.0,6.0}, {4.0,-9.5,6.0},
 {5.0,-9.5,6.0}, {6.0,-9.5,6.0}, {10.0,-9.5,6.0}, {11.0,0.0,6.0}},

 {{-11.0,0.0,5.0}, {-10.0,-5.0,5.0}, {-6.0,-5.0,5.0}, {-5.0,-5.0,5.0},
 {-4.0,-5.0,5.0}, {-3.0,-6.5,5.0}, {-2.0,-7.0,5.0}, {-1.0,-7.5,5.0},
 {1.0,-8.0,5.0}, {2.0,-8.5,5.0}, {3.0,-9.0,5.0}, {4.0,-9.5,5.0},
 {5.0,-9.5,5.0}, {6.0,-9.5,5.0}, {10.0,-9.5,5.0}, {11.0,0.0,5.0}},

 {{-13.0,0.0,4.0}, {-12.0,-5.0,4.0}, {-6.0,-5.0,4.0}, {-5.0,-5.0,4.0},
 {-4.0,-5.0,4.0}, {-3.0,-6.5,4.0}, {-2.0,-7.0,4.0}, {-1.0,-7.5,4.0},
 {1.0,-8.0,4.0}, {2.0,-8.5,4.0}, {3.0,-9.0,4.0}, {4.0,-9.5,4.0},
 {5.0,-9.5,4.0}, {6.0,-9.5,4.0}, {12.0,-9.5,4.0}, {12.0,0.0,4.0}},

 {{-14.0,0.0,3.0}, {-13.0,-5.0,3.0}, {-6.0,-5.0,3.0}, {-5.0,-5.0,3.0},
 {-4.0,-5.0,3.0}, {-3.0,-6.5,3.0}, {-2.0,-7.0,3.0}, {-1.0,-7.5,3.0},
 {1.0,-8.0,3.0}, {2.0,-8.5,3.0}, {3.0,-9.0,3.0}, {4.0,-9.5,3.0},
 {5.0,-9.5,3.0}, {6.0,-9.5,3.0}, {11.0,-9.5,3.0}, {12.0,0.0,3.0}},

 {{-14.0,0.0,2.0}, {-13.0,-5.0,2.0}, {-6.0,-5.0,2.0}, {-5.0,-5.0,2.0},
 {-4.0,-5.0,2.0}, {-3.0,-6.5,2.0}, {-2.0,-7.0,2.0}, {-1.0,-7.5,2.0},
 {1.0,-8.0,2.0}, {2.0,-8.5,2.0}, {3.0,-9.0,2.0}, {4.0,-9.5,2.0},
 {5.0,-9.5,2.0}, {6.0,-9.5,2.0}, {11.0,-9.5,2.0}, {12.0,0.0,2.0}},

 {{-14.0,0.0,1.0}, {-13.0,-5.0,1.0}, {-6.0,-5.0,1.0}, {-5.0,-5.0,1.0},
 {-4.0,-5.0,1.0}, {-3.0,-6.5,1.0}, {-2.0,-7.0,1.0}, {-1.0,-7.5,1.0},
 {1.0,-8.0,1.0}, {2.0,-8.5,1.0}, {3.0,-9.0,1.0}, {4.0,-9.5,1.0},
 {5.0,-9.5,1.0}, {6.0,-9.5,1.0}, {12.0,-9.5,1.0}, {13.0,0.0,1.0}},

 {{-13.0,0.0,-1.0}, {-12.0,-5.0,-1.0}, {-6.0,-5.0,-1.0}, {-5.0,-5.0,-1.0},
 {-4.0,-5.0,-1.0}, {-3.0,-6.5,-1.0}, {-2.0,-7.0,-1.0}, {-1.0,-7.5,-1.0},
 {1.0,-8.0,-1.0}, {2.0,-8.5,-1.0}, {3.0,-9.0,-1.0}, {4.0,-9.5,-1.0},
 {5.0,-9.5,-1.0}, {6.0,-9.5,-1.0}, {12.0,-9.5,-1.0}, {13.0,0.0,-1.0}},

 {{-12.0,0.0,-2.0}, {-11.0,-5.0,-2.0}, {-6.0,-5.0,-2.0}, {-5.0,-5.0,-2.0},
 {-4.0,-5.0,-2.0}, {-3.0,-6.5,-2.0}, {-2.0,-7.0,-2.0}, {-1.0,-7.5,-2.0},
 {1.0,-8.0,-2.0}, {2.0,-8.5,-2.0}, {3.0,-9.0,-2.0}, {4.0,-9.5,-2.0},
 {5.0,-9.5,-2.0}, {6.0,-9.5,-2.0}, {11.0,-9.5,-2.0}, {12.0,0.0,-2.0}},

 {{-12.0,0.0,-3.0}, {-11.0,-5.0,-3.0}, {-6.0,-5.0,-3.0}, {-5.0,-5.0,-3.0},
 {-4.0,-5.0,-3.0}, {-3.0,-6.5,-3.0}, {-2.0,-7.0,-3.0}, {-1.0,-7.5,-3.0},
 {1.0,-8.0,-3.0}, {2.0,-8.5,-3.0}, {3.0,-9.0,-3.0}, {4.0,-9.5,-3.0},
 {5.0,-9.5,-3.0}, {6.0,-9.5,-3.0}, {11.0,-9.5,-3.0}, {12.0,0.0,-3.0}},

 {{-11.0,0.0,-4.0}, {-10.0,-5.0,-4.0}, {-6.0,-5.0,-4.0}, {-5.0,-5.0,-4.0},
 {-4.0,-5.0,-4.0}, {-3.0,-6.5,-4.0}, {-2.0,-7.0,-4.0}, {-1.0,-7.5,-4.0},
 {1.0,-8.0,-4.0}, {2.0,-8.5,-4.0}, {3.0,-9.0,-4.0}, {4.0,-9.5,-4.0},
 {5.0,-9.5,-4.0}, {6.0,-9.5,-4.0}, {11.0,-9.5,-4.0}, {12.0,0.0,-4.0}},

 {{-9.0,0.0,-5.0}, {-8.0,-5.0,-5.0}, {-6.0,-5.0,-5.0}, {-5.0,-5.0,-5.0},
 {-4.0,-5.0,-5.0}, {-3.0,-6.5,-5.0}, {-2.0,-7.0,-5.0}, {-1.0,-7.5,-5.0},
 {1.0,-8.0,-5.0}, {2.0,-8.5,-5.0}, {3.0,-9.0,-5.0}, {4.0,-9.5,-5.0},
 {5.0,-9.5,-5.0}, {6.0,-9.5,-5.0}, {10.0,-9.5,-5.0}, {11.0,0.0,-5.0}},

 {{-8.0,0.0,-6.0}, {-7.0,-5.0,-6.0}, {-6.0,-5.0,-6.0}, {-5.0,-5.0,-6.0},
 {-4.0,-5.0,-6.0}, {-3.0,-6.5,-6.0}, {-2.0,-7.0,-6.0}, {-1.0,-7.5,-6.0},
 {1.0,-8.0,-6.0}, {2.0,-8.5,-6.0}, {3.0,-9.0,-6.0}, {4.0,-9.5,-6.0},
 {5.0,-9.5,-6.0}, {6.0,-9.5,-6.0}, {8.0,-9.5,-6.0}, {9.0,0.0,-6.0}},

 {{-9.0,0.0,-7.0}, {-6.0,-5.0,-9.0}, {-6.0,-5.0,-10.0}, {-5.0,-5.0,-11.0},
 {-4.0,-5.0,-11.0}, {-3.0,-6.5,-12.0}, {-2.0,-7.0,-12.0}, {-1.0,-7.5,-13.0},
 {1.0,-8.0,-13.0}, {2.0,-8.5,-12.0}, {3.0,-9.0,-12.0}, {4.0,-9.5,-11.0},
 {5.0,-9.5,-10.0}, {6.0,-9.5,-10.0}, {8.0,-9.5,-8.0}, {9.0,0.0,-7.0}},

 {{-8.0,0.0,-8.0}, {-7.0,0.0,-10.0}, {-6.0,0.0,-11.0}, {-5.0,0.0,-12.0},
 {-4.0,0.0,-12.0}, {-3.0,0.0,-13.0}, {-2.0,0.0,-13.0}, {-1.0,0.0,-14.0},
 {1.0,0.0,-14.0}, {2.0,0.0,-13.0}, {3.0,0.0,-13.0}, {4.0,0.0,-12.0},
 {5.0,0.0,-11.0}, {6.0,0.0,-11.0}, {7.0,0.0,-9.0}, {8.0,0.0,-8.0}},
};

// prototypes for functions
void set_water(void);
GLint DoSelect(GLint x, GLint y);

void myinit(void)
{
	glClearColor(0.0, 0.0, 0.0, 1.0);     // black background
	// set light properties
	glLightfv(GL_LIGHT0, GL_AMBIENT, white);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, white);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	glLightfv(GL_LIGHT0, GL_POSITION, light_pos);

	glShadeModel(GL_SMOOTH);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_AUTO_NORMAL);
	glEnable(GL_MAP2_VERTEX_3);

	set_water(); /* initialize water surface control points */
}

void Mouse(int button, int state, int mouseX, int mouseY)
{	// mouse callback
	if (state == GLUT_DOWN) { // find which object if any was selected
		hit = DoSelect((GLint) mouseX, (GLint) mouseY);
	}
	glutPostRedisplay(); /* redraw display */
}

void kybd(unsigned char key, int x, int y)
{
	switch(key) {
		case 'z': {
			Angle = ROT_ANG; // set angle for rotation in display
			RotAxis=0; break; // set rotation axis
		}
		case 'x': {
			Angle = -ROT_ANG; RotAxis=0; break;
		}
		case 'a': {
			Angle = ROT_ANG; RotAxis=1; break;
		}
		case 's': {
			Angle = -ROT_ANG; RotAxis=1; break;
		}
		case 'q': {
			Angle = ROT_ANG; RotAxis=2; break; }
		case 'w': {
			Angle = -ROT_ANG; RotAxis=2; break;
		}
		case 'n': {
		/* if points are displayed and a point has been selected, move
		   the corresponding control point and recalculate water points */
			if(points && hit>-1) ctrlpts[hit/16][hit%16][0]+=0.5;
			set_water(); break;
		}
		case 'm': {
			if(points && hit>-1) ctrlpts[hit/16][hit%16][0]-=0.5;
			set_water(); break;
		}
		case 'j': {
			if(points && hit>-1) ctrlpts[hit/16][hit%16][1]+=0.5;
			set_water(); break;
		}
		case 'k': {
			if(points && hit>-1) ctrlpts[hit/16][hit%16][1]-=0.5;
			set_water(); break;
		}
		case 'i': {
			if(points && hit>-1) ctrlpts[hit/16][hit%16][2]+=0.5;
			set_water(); break;
		}
		case 'o': {
			if(points && hit>-1) ctrlpts[hit/16][hit%16][2]-=0.5;
			set_water(); break;
		}
		case ' ': {
			points=(points+1)%2; break; // toggle control points
		}
		default: {return;}
	}
	glutPostRedisplay();
}

void drawpoints(GLenum mode)
{
	int i, j;
	int name=0;
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, green);
	// iterate through control point array
	for(i=0; i<GRIDSIZE; i++)
		for(j=0; j<GRIDSIZE; j++) {
			if (mode == GL_SELECT) {
				glLoadName(name); /* assign a name to each point */
				name++;    /* increment name number */
			}
			glPushMatrix();
			// move point to appropriate location
			glTranslatef(ctrlpts[i][j][0],ctrlpts[i][j][1],ctrlpts[i][j][2]);
			// scale it to match scale of pool (so it is still a cube)
			glScalef(1.0, 1.5, 1.5);
			if(hit==i*16+j%16) { // selected point, draw it red
				glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,red);
				glutSolidCube(0.25);
				glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,green);
			}
			else glutSolidCube(0.25);
			glPopMatrix();
		}
}

void set_water(void)
{
	int i, j;
	for(i=0; i<GRIDSIZE; i++)
		for(j=0; j<GRIDSIZE; j++) {
		// bring x and z coordinates inward slightly to prevent water from
		// being displayed outside pool surface and set a constant y value
			if(j==0 || j==1) water[i][j][0]=ctrlpts[i][j][0]+0.5;
			else if(j==GRIDSIZE-1 || j==GRIDSIZE-2)
				water[i][j][0]=ctrlpts[i][j][0]-0.5;
			else water[i][j][0]=ctrlpts[i][j][0];
			water[i][j][1]=-1.0;
			if(i==0 || i==1) water[i][j][2]=ctrlpts[i][j][2]-0.5;
			else if(i==GRIDSIZE-1 || i==GRIDSIZE-2)
				water[i][j][2]=ctrlpts[i][j][2]+0.5;
			else water[i][j][2]=ctrlpts[i][j][2];
		}
}

void drawpatch(point3 patch[4][4])
{
// this function draws a patch defined by a 4 x 4 array
#define NUM 10

	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 surface(point3 ctrlpts[GRIDSIZE][GRIDSIZE])
{
/* This function draws the entire surface defined by the control point array.
   special cases must be defined for each corner and each edge, for a total of
   9 cases in all.  In each case the appropriate midpoints are calculated, and
   the points are copied to a 4 x 4 array to be drawn.  */
#define LAST_STEP (GRIDSIZE/2)-1

	point3 patch[4][4];
	int xstep, ystep, i, j, k;

	for(xstep=0; xstep<LAST_STEP; xstep++)
		for(ystep=0; ystep<LAST_STEP; ystep++) {
			if(xstep==0 && ystep==0) { // front left corner of grid
				for(i=0; i<3; i++)
					for(j=0; j<3; j++)
						for(k=0; k<3; k++) patch[i][j][k] = ctrlpts[i][j][k];
				for(i=0; i<3; i++)
					for(k=0; k<3; k++)  {
						patch[i][3][k]=(ctrlpts[i][2][k]+ctrlpts[i][3][k])/2.0;
						patch[3][i][k]=(ctrlpts[2][i][k]+ctrlpts[3][i][k])/2.0;
					}
				for(k=0; k<3; k++)
					patch[3][3][k]=(ctrlpts[2][2][k]+ctrlpts[2][3][k]
										+ctrlpts[3][2][k]+ctrlpts[3][3][k])/4.0;
			}
			else if(xstep==0 && ystep==LAST_STEP-1) { // back left corner of grid
				for(i=0; i<3; i++)
					for(j=1; j<4; j++)
						for(k=0; k<3; k++) patch[i][j][k]=ctrlpts[i][GRIDSIZE-4+j][k];
				for(i=0; i<3; i++)
					for(k=0; k<3; k++)
						patch[i][0][k]=(ctrlpts[i][GRIDSIZE-4][k]
											+ctrlpts[i][GRIDSIZE-3][k])/2.0;
				for(i=1; i<4; i++)
					for(k=0; k<3; k++)
						patch[3][i][k]=(ctrlpts[2][GRIDSIZE-4+i][k]
											+ctrlpts[3][GRIDSIZE-4+i][k])/2.0;
				for(k=0; k<3; k++)
					patch[3][0][k]=(ctrlpts[2][GRIDSIZE-4][k]
										+ctrlpts[2][GRIDSIZE-3][k]
										+ctrlpts[3][GRIDSIZE-4][k]
										+ctrlpts[3][GRIDSIZE-3][k])/4.0;
			}
			else if(xstep==0) { /* patch is along left edge of grid */
				for(i=0; i<3; i++)
					for(j=1; j<3; j++)
						for(k=0; k<3; k++) patch[i][j][k]=ctrlpts[i][2*ystep+j][k];
				for(i=0; i<3; i++)
					for(k=0; k<3; k++) {
						patch[i][0][k]=(ctrlpts[i][2*ystep][k]
											+ctrlpts[i][2*ystep+1][k])/2.0;
						patch[i][3][k]=(ctrlpts[i][2*ystep+2][k]
											+ctrlpts[i][2*ystep+3][k])/2.0;
					}
				for(k=0; k<3; k++) {
					patch[3][0][k]=(ctrlpts[2][2*ystep][k]+ctrlpts[2][2*ystep+1][k]
										+ctrlpts[3][2*ystep][k]
										+ctrlpts[3][2*ystep+1][k])/4.0;
					patch[3][1][k]=(ctrlpts[2][2*ystep+1][k]
										+ctrlpts[3][2*ystep+1][k])/2.0;
					patch[3][2][k]=(ctrlpts[2][2*ystep+2][k]
										+ctrlpts[3][2*ystep+2][k])/2.0;
					patch[3][3][k]=(ctrlpts[2][2*ystep+2][k]+ctrlpts[2][2*ystep+3][k]
										+ctrlpts[3][2*ystep+2][k]
										+ctrlpts[3][2*ystep+3][k])/4.0;
				}
			}
			else if(xstep==LAST_STEP-1 && ystep==0) { // front right corner
				for(i=1; i<4; i++)
					for(j=0; j<3; j++)
						for(k=0; k<3; k++) patch[i][j][k]=ctrlpts[GRIDSIZE-4+i][j][k];
				for(j=0; j<3; j++)
					for(k=0; k<3; k++)
						patch[0][j][k]=(ctrlpts[GRIDSIZE-4][j][k]
											+ctrlpts[GRIDSIZE-3][j][k])/2.0;
				for(i=1; i<4; i++)
					for(k=0; k<3; k++)
						patch[i][3][k]=(ctrlpts[GRIDSIZE-4+i][3][k]
											+ctrlpts[GRIDSIZE-4+i][4][k])/2.0;
				for(k=0; k<3; k++)
					patch[0][3][k]=(ctrlpts[GRIDSIZE-4][2][k]
										+ctrlpts[GRIDSIZE-3][2][k]
										+ctrlpts[GRIDSIZE-4][3][k]
										+ctrlpts[GRIDSIZE-3][3][k])/4.0;
			}
			else if(ystep==0) { // patch on front edge of grid
				for(i=1; i<3; i++)
					for(j=0; j<3; j++)
						for(k=0; k<3; k++) patch[i][j][k]=ctrlpts[2*xstep+i][j][k];
				for(j=0; j<3; j++)
					for(k=0; k<3; k++) {
						patch[0][j][k]=(ctrlpts[2*xstep][j][k]
											+ctrlpts[2*xstep+1][j][k])/2.0;
						patch[3][j][k]=(ctrlpts[2*xstep+2][j][k]
											+ctrlpts[2*xstep+3][j][k])/2.0;
					}
				for(k=0; k<3; k++) {
					patch[0][3][k]=(ctrlpts[2*xstep][2][k]+ctrlpts[2*xstep+1][2][k]
										+ctrlpts[2*xstep][3][k]
										+ctrlpts[2*xstep+1][3][k])/4.0;
					patch[1][3][k]=(ctrlpts[2*xstep+1][2][k]
										+ctrlpts[2*xstep+1][3][k])/2.0;
					patch[2][3][k]=(ctrlpts[2*xstep+2][2][k]
										+ctrlpts[2*xstep+2][3][k])/2.0;
					patch[3][3][k]=(ctrlpts[2*xstep+2][2][k]+ctrlpts[2*xstep+3][2][k]
										+ctrlpts[2*xstep+2][3][k]
										+ctrlpts[2*xstep+3][3][k])/4.0;
				}
			}
			else if(xstep==LAST_STEP-1&&ystep==LAST_STEP-1) { //back right corner
				for(i=1; i<4; i++)
					for(j=1; j<4; j++)
						for(k=0; k<3; k++)
							patch[i][j][k]=ctrlpts[GRIDSIZE-4+i][GRIDSIZE-4+j][k];
				for(i=1; i<4; i++)
					for(k=0; k<3; k++) {
						patch[i][0][k]=(ctrlpts[GRIDSIZE-4+i][GRIDSIZE-4][k]
											+ctrlpts[GRIDSIZE-4+i][GRIDSIZE-3][k])/2.0;
						patch[0][i][k]=(ctrlpts[GRIDSIZE-4][GRIDSIZE-4+i][k]
											+ctrlpts[GRIDSIZE-3][GRIDSIZE-4+i][k])/2.0;
					}
				for(k=0; k<3; k++)
					patch[0][0][k]=(ctrlpts[GRIDSIZE-4][GRIDSIZE-4][k]
										+ctrlpts[GRIDSIZE-3][GRIDSIZE-4][k]
										+ctrlpts[GRIDSIZE-4][GRIDSIZE-3][k]
										+ctrlpts[GRIDSIZE-3][GRIDSIZE-3][k])/4.0;
			}
			else if(xstep==LAST_STEP-1) { // patch on right edge of grid
				for(i=1; i<4; i++)
					for(j=1; j<3; j++)
						for(k=0; k<3; k++)
							patch[i][j][k]=ctrlpts[GRIDSIZE-4+i][2*ystep+j][k];
				for(i=1; i<4; i++)
					for(k=0; k<3; k++) {
						patch[i][0][k]=(ctrlpts[GRIDSIZE-4+i][2*ystep][k]
											+ctrlpts[GRIDSIZE-4+i][2*ystep+1][k])/2.0;
						patch[i][3][k]=(ctrlpts[GRIDSIZE-4+i][2*ystep+2][k]
											+ctrlpts[GRIDSIZE-4+i][2*ystep+3][k])/2.0;
					}
				for(k=0; k<3; k++) {
					patch[0][0][k]=(ctrlpts[GRIDSIZE-4][2*ystep][k]
										+ctrlpts[GRIDSIZE-4][2*ystep+1][k]
										+ctrlpts[GRIDSIZE-3][2*ystep][k]
										+ctrlpts[GRIDSIZE-3][2*ystep+1][k])/4.0;
					patch[0][1][k]=(ctrlpts[GRIDSIZE-4][2*ystep+1][k]
										+ctrlpts[GRIDSIZE-3][2*ystep+1][k])/2.0;
					patch[0][2][k]=(ctrlpts[GRIDSIZE-4][2*ystep+2][k]
										+ctrlpts[GRIDSIZE-3][2*ystep+2][k])/2.0;
					patch[0][3][k]=(ctrlpts[GRIDSIZE-4][2*ystep+2][k]
										+ctrlpts[GRIDSIZE-4][2*ystep+3][k]
										+ctrlpts[GRIDSIZE-3][2*ystep+2][k]
										+ctrlpts[GRIDSIZE-3][2*ystep+3][k])/4.0;
				}
			}
			else if(ystep==LAST_STEP-1) { // patch on back edge of grid
				for(i=1; i<3; i++)
					for(j=1; j<4; j++)
						for(k=0; k<3; k++)
							patch[i][j][k]=ctrlpts[2*xstep+i][GRIDSIZE-4+j][k];
					for(j=1; j<4; j++)
						for(k=0; k<3; k++) {
							patch[0][j][k]=(ctrlpts[2*xstep][GRIDSIZE-4+j][k]
												+ctrlpts[2*xstep+1][GRIDSIZE-4+j][k])/2.0;
							patch[3][j][k]=(ctrlpts[2*xstep+2][GRIDSIZE-4+j][k]
												+ctrlpts[2*xstep+3][GRIDSIZE-4+j][k])/2.0;
					}
				for(k=0; k<3; k++) {
					patch[0][0][k]=(ctrlpts[2*xstep][GRIDSIZE-4][k]
										+ctrlpts[2*xstep+1][GRIDSIZE-4][k]
										+ctrlpts[2*xstep][GRIDSIZE-3][k]
										+ctrlpts[2*xstep+1][GRIDSIZE-3][k])/4.0;
					patch[1][0][k]=(ctrlpts[2*xstep+1][GRIDSIZE-4][k]
										+ctrlpts[2*xstep+1][GRIDSIZE-3][k])/2.0;
					patch[2][0][k]=(ctrlpts[2*xstep+2][GRIDSIZE-4][k]
										+ctrlpts[2*xstep+2][GRIDSIZE-3][k])/2.0;
					patch[3][0][k]=(ctrlpts[2*xstep+2][GRIDSIZE-4][k]
										+ctrlpts[2*xstep+3][GRIDSIZE-4][k]
										+ctrlpts[2*xstep+2][GRIDSIZE-3][k]
										+ctrlpts[2*xstep+3][GRIDSIZE-3][k])/4.0;
				}
			}
			else { // general case (internal patch)
				for(i=1; i<3; i++)
					for(j=1; j<3; j++)
						for(k=0; k<3; k++)
							patch[i][j][k]=ctrlpts[2*xstep+i][2*ystep+j][k];
				for(i=1; i<3; i++)
					for(k=0; k<3; k++) {
						patch[i][0][k]=(ctrlpts[2*xstep+i][2*ystep][k]
											+ctrlpts[2*xstep+i][2*ystep+1][k])/2.0;
						patch[i][3][k]=(ctrlpts[2*xstep+i][2*ystep+2][k]
											+ctrlpts[2*xstep+i][2*ystep+3][k])/2.0;
						patch[0][i][k]=(ctrlpts[2*xstep][2*ystep+i][k]
											+ctrlpts[2*xstep+1][2*ystep+i][k])/2.0;
						patch[3][i][k]=(ctrlpts[2*xstep+2][2*ystep+i][k]
											+ctrlpts[2*xstep+3][2*ystep+i][k])/2.0;
					}
				for(k=0; k<3; k++) {
					patch[0][0][k]=(ctrlpts[2*xstep][2*ystep][k]
										+ctrlpts[2*xstep+1][2*ystep][k]
										+ctrlpts[2*xstep][2*ystep+1][k]
										+ctrlpts[2*xstep+1][2*ystep+1][k])/4.0;
					patch[3][0][k]=(ctrlpts[2*xstep+2][2*ystep][k]
										+ctrlpts[2*xstep+3][2*ystep][k]
										+ctrlpts[2*xstep+2][2*ystep+1][k]
										+ctrlpts[2*xstep+3][2*ystep+1][k])/4.0;
					patch[0][3][k]=(ctrlpts[2*xstep][2*ystep+2][k]
										+ctrlpts[2*xstep+1][2*ystep+2][k]
										+ctrlpts[2*xstep][2*ystep+3][k]
										+ctrlpts[2*xstep+1][2*ystep+3][k])/4.0;
					patch[3][3][k]=(ctrlpts[2*xstep+2][2*ystep+2][k]
										+ctrlpts[2*xstep+3][2*ystep+2][k]
										+ctrlpts[2*xstep+2][2*ystep+3][k]
										+ctrlpts[2*xstep+3][2*ystep+3][k])/4.0;
				}
			}
		drawpatch(patch);  // draw the currently calculated patch
		}
}

void render(GLenum mode)
{
	// perform appropriate rotations
	if (RotAxis==0) { // rotation about x-axis
		glPushMatrix();
		//	get the matrix for a rotation of Angle angles by performing the
		//	rotation on the identity matrix
		glLoadIdentity();
		glRotatef(Angle, 1.0, 0.0, 0.0);
		// multiply by the previous rotation matrix
		glMultMatrixf(RotMatrix);
		// save the new rotation matrix
		glGetFloatv(GL_MODELVIEW_MATRIX, RotMatrix);
		glPopMatrix();
	}
	else if (RotAxis==1) { // rotation about y-axis
		glPushMatrix();
		glLoadIdentity();
		glRotatef(Angle, 0.0, 1.0, 0.0);
		glMultMatrixf(RotMatrix);
		glGetFloatv(GL_MODELVIEW_MATRIX, RotMatrix);
		glPopMatrix();
	}
	else if (RotAxis==2) { // rotation about z-axis
		glPushMatrix();
		glLoadIdentity();
		glRotatef(Angle, 0.0, 0.0, 1.0);
		glMultMatrixf(RotMatrix);
		glGetFloatv(GL_MODELVIEW_MATRIX, RotMatrix);
		glPopMatrix();
	}
	glPushMatrix();
	// multiply modelview matrix (of the world view) by the rotation matrix
	glMultMatrixf(RotMatrix);
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, gray);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, light_specular);
	glPushMatrix();
	glScalef(1.5,1.0,1.0);
	if (mode == GL_RENDER) { // don't render surface if mode is SELECT
		surface(ctrlpts);
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, wat_col);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, white);
		surface(water);
	}
	if(points) drawpoints(mode);
	glPopMatrix();
	glPopMatrix();
	RotAxis=-1;  // don't rotate on next display
}

GLint DoSelect(GLint x, GLint y)
{
	int i;
	GLint hits, temphit;
	GLuint zval;

	glSelectBuffer(MAXHITS, selectBuf);
	glRenderMode(GL_SELECT);
	glInitNames();
	glPushName(0);

	// set up the viewing model
	glPushMatrix();
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPickMatrix(x, windH - y, 4, 4, vp);
	gluPerspective(100.0,1.0,1.0,300);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0.0, 10.0, 25.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

	render(GL_SELECT);  /* draw the scene for selection */

	glPopMatrix();
	// find the number of hits recorded and reset mode of render
	hits = glRenderMode(GL_RENDER);
	// reset viewing model
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(100.0,1.0,1.0,300);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0.0, 10.0, 25.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
	// return the label of the object selected, if any
	if (hits <= 0) {
		return -1;
	}
	else {
		zval = selectBuf[1];
		temphit = selectBuf[3];
		for (i = 1; i < hits; i++) { // for each hit
			if (selectBuf[4*i+1] < zval) {
				zval = selectBuf[4*i+1];
				temphit = selectBuf[4*i+3];
			}
		}
	}
	return temphit;
}

void display( void )
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	render(GL_RENDER);
	glutSwapBuffers();
}

void reshape(int w,int h)
{
	windW = w;
	windH = h;

	glViewport(0,0,(GLsizei)w,(GLsizei)h);
	glGetIntegerv(GL_VIEWPORT, vp);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(100.0,1.0,1.0,300);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0.0, 10.0, 25.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}

int main(int argc, char** argv)
{
	glutInit(&argc,argv);
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(500,500);
	glutInitWindowPosition(50, 50);
	glutCreateWindow("Project - Pool");
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(kybd);
	glutMouseFunc(Mouse);

	myinit();
	glutMainLoop();
}
