/* (C) 2005 Sean Brennan.  All rights reserved */
#include <stdio.h>
#include <math.h>
#include <GL/glut.h>

#include "ply2tri.h"


#define ZOOMFACTOR 0.100


GLint bunlist;

int button[3];
int statexy[2];
GLfloat oldxy = 0.0 , oldyz = 0.0;

GLfloat lightposdef[] = {1.0, 1.0, 1.0, 0.0};
GLfloat lightposcam[] = {0.0, 0.0, 1.0, 0.0};
GLfloat lightpos[] = {1.0, 1.0, 1.0, 0.0};

GLfloat lookmatrix[16];
GLfloat orbx = 0.0, orby = 0.0, orbz = 1.0;
float xzangle = 90.0, azimuth = 90.0, height=0.0, xoff=0.0, zoff=0.0 , zoom=1.0, gaspect;

/* rotate model about y axis */
float incr=0.1;
float incer = 0.0;

GLboolean lighttoggle = GL_FALSE, flattoggle = GL_FALSE, linetoggle = GL_FALSE, axistoggle = GL_FALSE;

double deg2rad(double deg) {
	double rad;

	rad = (deg / 360.0 * M_PI * 2.0);
	return rad;
}

void neworb() {
	GLfloat r;


	/* xz can wrap, azimuth cannot, unless up vec changes */
	
	if (xzangle > 360.0) xzangle -= 360.0;	
	if (xzangle < 0.0) xzangle += 360.0;	
	if (azimuth > 180.0) azimuth = 180.0;	
	if (azimuth < 0.1) azimuth = 0.1;	
	/* r = zoom * sin(deg2rad(azimuth)); */
	r = sin(deg2rad(azimuth));
	if ( r < 0.0 ) r = -r;

	orby = zoom * cos(deg2rad(azimuth));
	orbx = zoom * r * cos(deg2rad(xzangle));
	orbz = zoom * r * sin(deg2rad(xzangle));
}
	
void init(char *filename) {

	GLfloat whitemat[] = {1., 1., 1., 1.0};
	GLfloat aquamat[]  = {0.0, 1., 1., 1.0};
	GLfloat shininess[]  = {50.0};

	/* glClearColor(0.1, 0.1, 0.1, 0.0); */
	glClearColor(0.0, 0.0, 0.0, 0.0);

	button[0] = button[1] = button[2] = GLUT_UP;

	glShadeModel(GL_SMOOTH);

	bunlist = glGenLists(1);
	glNewList(bunlist, GL_COMPILE);
	genPlyTris(filename);
	glEndList();

	glLightfv(GL_LIGHT0, GL_SPECULAR, whitemat);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, whitemat);

	glMaterialfv(GL_FRONT, GL_SPECULAR, whitemat);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, aquamat);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);

	glEnable(GL_DEPTH_TEST);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	neworb();
}
/* draw axies of x, y and z */
void drawAxis() {
	glDisable(GL_LIGHTING);
		glPushMatrix();
		/* X */
		glColor3ub(0xff, 0x00, 0x00);
		glScalef(20.0, 0.3, 0.3);
		glutSolidCube(0.01);
		glPopMatrix();
		glPushMatrix();
		glTranslatef(0.1, 0.0, 0.0);
		glutSolidCube(0.015);
		glPopMatrix();
		/* Y */
		glColor3ub(0x00, 0xff, 0x00);
		glPushMatrix();
		glScalef(0.3, 20.0, 0.3);
		glutSolidCube(0.01);
		glPopMatrix();
		glPushMatrix();
		glTranslatef(0.0, 0.1, 0.0);
		glutSolidCube(0.015);
		glPopMatrix();
		/* Z */
		glColor3ub(0x00, 0x00, 0xff);
		glPushMatrix();
		glScalef(0.3, 0.3, 20.0);
		glutSolidCube(0.01);
		glPopMatrix();
		glPushMatrix();
		glTranslatef(0.0, 0.0, 0.1);
		glutSolidCube(0.015);
		glPopMatrix();
	glEnable(GL_LIGHTING);
}

void display() {


	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glLoadIdentity();
	if (lighttoggle) {
		/* cam light */
		glLightfv(GL_LIGHT0, GL_POSITION, lightposcam);
	}
	gluLookAt(orbx, orby, orbz, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
	if (axistoggle) 	
		drawAxis();
	
	glPushMatrix();
	glTranslatef(xoff, -height, zoff);
	/* glRotatef(xzangle, 0.0, 1.0, 0.0); */
	/* glTranslatef(0.0, height, 0.0); */
	if ( ! lighttoggle)
		glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
	if (linetoggle)
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	else
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	glCallList(bunlist);
	glPopMatrix();
	glFlush();
	glutSwapBuffers();
}

void idle() {
	xzangle += incer;
	glutPostRedisplay();
}

void mouse(int but, int state, int x, int y) {
	switch (but) {
		case GLUT_LEFT_BUTTON:
			if (state == GLUT_DOWN) {
				button[0] = GLUT_DOWN;
			} else {
				button[0] = GLUT_UP;
			} 	break;
		case GLUT_MIDDLE_BUTTON:
			if (state == GLUT_DOWN) {
				button[1] = GLUT_DOWN;
			} else {
				button[1] = GLUT_UP;
			} 	break;
		case GLUT_RIGHT_BUTTON:
			if (state == GLUT_DOWN) {
				button[2] = GLUT_DOWN;
			} else {
				button[2] = GLUT_UP;
			} 	break;
	}
	statexy[0] = x;
	statexy[1] = y;
}

void motion(int x, int y) {
	if (button[0] == GLUT_DOWN) {
	/* orbit left/right and up/down */
		azimuth -= y - statexy[1];
		xzangle += x - statexy[0];
		neworb();
		
	}
	if (button[1] == GLUT_DOWN) {
		zoom *= ( 1.0 +  (GLfloat) ( y - statexy[1]) * ZOOMFACTOR);
		if (zoom <= 0.0) zoom = 0.1;
        		glMatrixMode(GL_PROJECTION);
        		glLoadIdentity();
			gluPerspective(40.0, gaspect, 
				zoom / 10.0, zoom * 100.0);
			glMatrixMode(GL_MODELVIEW);
		neworb();
	}
	if (button[2] == GLUT_DOWN) {
		height += 0.05 * (GLfloat) ( y - statexy[1]) * ZOOMFACTOR * zoom;
		xoff += ZOOMFACTOR * zoom *  sin(deg2rad(xzangle)) * 0.05 * (GLfloat) (x - statexy[0]);
		zoff -= ZOOMFACTOR * zoom *  cos(deg2rad(xzangle)) * 0.05  *(GLfloat) (x - statexy[0]);
		neworb();
	}
	statexy[0] = x; statexy[1] = y;
}

void reshape( int w, int h)
{
        GLfloat xaspect, yaspect;
 
        yaspect = 1.0;
        xaspect = 1.0;
 
        if ( w < h)
                xaspect = (GLfloat) h / (GLfloat) w;
        else
                yaspect = (GLfloat) w / (GLfloat) h;
 
        glViewport(0, 0, w, h);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
 
	gaspect =  (GLfloat) w / (GLfloat) h;
	gluPerspective(40.0, gaspect, 
		zoom / 10.0, zoom * 100.0);
        glMatrixMode(GL_MODELVIEW);
}


void keyboard(unsigned char c, int x, int y) {
	switch (c) {
		case 27 : case 'q': case 'Q': exit(0);
			break;
		case ' ': if ( incer == 0.0 )
				incer = incr;
			  else
				incer = 0.0;
			  break;
		case 'l': case 'L': lighttoggle = !lighttoggle;
			break;
		case 'f': case 'F': flattoggle = !flattoggle;
			if (flattoggle)
				glShadeModel(GL_FLAT);
			else
				glShadeModel(GL_SMOOTH);
			break;
		case 'W': case 'w': linetoggle = !linetoggle;
			break;
		case '-': incr -= 0.1;
			  incer = incr;
			break;
		case '+': incr += 0.1;
			  incer = incr;
			break;
		case 'x': case 'X': axistoggle = !axistoggle;
			break;
		case 'z': zoom *= 1.2;
        		glMatrixMode(GL_PROJECTION);
        		glLoadIdentity();
			gluPerspective(40.0, gaspect, 
				zoom / 10.0, zoom * 100.0);
			glMatrixMode(GL_MODELVIEW);

			break;
		case 'Z': zoom /= 1.2;
        		glMatrixMode(GL_PROJECTION);
        		glLoadIdentity();
			gluPerspective(40.0, gaspect, 
				zoom / 10.0, zoom * 100.0);
			glMatrixMode(GL_MODELVIEW);
			break;
	}
}

int main(int argc, char **argv) {
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB);
	glutInitWindowSize(400, 400);
	glutInitWindowPosition(100, 100);
	glutCreateWindow(argv[0]);
	init(argv[1]);
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutIdleFunc(idle);
	glutMouseFunc(mouse);
	glutMotionFunc(motion);
	glutMainLoop();
	return(0);
}

