/*
	Simple example of a single patch of a spline surface, with a texture map and with control points shown
   
	Source file to be used with
	Cunningham, Computer Graphics: Programming in OpenGL for Visual Communication, Prentice-Hall, 2007

	Intended for class use only
*/
#include <GLUT/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];

#define TEX_WIDTH 512
#define TEX_HEIGHT 512
static  GLubyte texImage[TEX_WIDTH][TEX_HEIGHT][3];
static  GLuint texName; // parameter is the number of textures in program

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.2f, 0.2f, 0.2f, 1.0};
GLfloat red[4] = {1.0, 0.0, 0.0, 0.7f};	// 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 doPoints(void);
void doPatch(void);
void setTexture(void);

void myinit(void)
{
	glClearColor( 0.3f, 0.3f, 0.3f, 1.0 );
	glEnable(GL_DEPTH_TEST);
	// glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1.0);
	
//	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);
    setTexture();
    glEnable(GL_TEXTURE_2D); // allow 2D texture maps
	glEnable(GL_AUTO_NORMAL);
	glEnable(GL_MAP2_VERTEX_3);
}

void setTexture(void)
{
	FILE * fd;
	GLubyte ch;
	int i,j,k;

	fd = fopen("penguin.512.512.rgb", "rb");
	for (i=0; i<TEX_WIDTH; i++)     // for each row
	{
		for (j=0; j<TEX_HEIGHT; j++) // for each column
		{
			for (k=0; k<3; k++)       // read RGB components of the pixel
			{
				fread(&ch, 1, 1, fd);
				texImage[i][j][k] = (GLubyte) ch;
			}
		}
	}
	fclose(fd);

	glGenTextures(1, &texName);
    glBindTexture(GL_TEXTURE_2D,texName);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,TEX_WIDTH,TEX_HEIGHT,
                     0,GL_RGB,GL_UNSIGNED_BYTE,texImage);
}

void display( void )
{
	typedef GLfloat point3[3];
	#define Angle 2.0

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	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( 0.0,  0.0, 7.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0);
}

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

	glColor3f( 0., 1., 0. );
	glDisable( GL_TEXTURE_2D );

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

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, white);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, white);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);

// now enable textures for the last face
	glEnable(GL_MAP2_TEXTURE_COORD_2);
	glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texName);

	glMap2f(GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 4, 0.0, 1.0, 12, 4, &patch[0][0][0]);
	glMap2f(GL_MAP2_TEXTURE_COORD_2, 0.0, 4.0, 3, 4, 0.0, 4.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)
{
	ch = ' ';
	switch (key) {
		case 'w' :
		case 'q' :
		case 's' :
		case 'a' :
		case 'x' :
		case 'z' :
			ch = key; break;
	}
	glutPostRedisplay();
}

int main(int argc, char** argv)
{
	glutInit(&argc,argv);
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(800,800);
	glutInitWindowPosition(0,0);
	glutCreateWindow("Spline patch with texture");
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);

	myinit();
	glutMainLoop();
}