/**********************************************************************
* EINFUEHRUNG IN DIE COMPUTERGRAFIK                      BEISPIEL 8_1 *
*                                                                     *
* aus dem Jahr 1999                                                   *
*                                                                     *
* Zahnraeder                                                          *
**********************************************************************/

#include <GL/glos.h>
#include <GL/glut.h>

#include <stdio.h>
#include <math.h>

static GLfloat spin1 = 0.0;
static GLfloat spin2 = 15.0;
static float M_PI = 3.14159265;

float col1[] = {0.0, 0.8, 0.2, 1.0};
float col2[] = {0.8, 0.0, 0.2, 1.0};

float Azimut = 46, Elevation = 157;

void displayHandler(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glPushMatrix();

	glRotatef(Elevation, 1.0, 0.0, 0.0);
	glRotatef(Azimut, 0.0, 1.0, 0.0);

	glTranslatef(2.0, 0.0, 0.0);
	glPushMatrix();
	glRotatef(spin1, 0.0, 0.0, 1.0);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, col1);
	glCallList(1);
	glPopMatrix();

	glTranslatef(-5.1, 0.0, 0.0);
	glPushMatrix();
	glMaterialfv(GL_FRONT, GL_DIFFUSE, col2);
	glRotatef(spin2, 0.0, 0.0, 1.0);
	glCallList(5);
	glPopMatrix();

	glPopMatrix();
	glutSwapBuffers();
}

void reshapeHandler(int w, int h)
{
	int x;

	w < h ? (x = w) : (x = h);
	glViewport(0, 0, x, x);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();	
	
	gluPerspective(90.0, 1.0, 1.0, 30.0);
	gluLookAt(0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void idleHandler(void)
{
	spin1 -= 0.25 * (360 / 30); if (spin1 < 0) spin1 += 360;
	spin2 += 0.25 * (360 / 12); if (spin2 > 360) spin2 -= 360;
	glutPostRedisplay();
}

void gear(int ListNr, float r1, float r2, int zaehne)
{
	int i;
	float deg, rad, wheelR, holeV, holeH, scale, transX, transY;
	
	float front[6][3] = {{0.0, 0.0, 0.0}, {0.2, 0.25, 0.0}, {0.6, 0.35, 0.0}, 
		{0.6, 0.65, 0.0}, {0.2, 0.75, 0.0},	{0.0, 1.0, 0.0}};					

	float back[6][3] = {{0.0, 0.0 , -1.0}, {0.2, 0.25, -1.0}, {0.6, 0.35, -1.0},                      
		{0.6, 0.65, -1.0}, {0.2, 0.75, -1.0}, {0.0, 1.0 , -1.0}};	

	float normal [8][3] = {{0.0, 0.0, 1.0}, {0.0, 0.0, -1.0}, {0.25, -0.2, 0.0}, 
		{0.1, -4.0, 0.0}, {1.0, 0.0, 0.0}, {-0.1, -0.4, 0.0}, {-0.25, -0.2, 0.0}, 
		{-1.0, 0.0, 0.0}};				/* haendisch berechnet */
	
	deg = 360.0 / zaehne; 
	rad = (M_PI * 2) / zaehne; 
	wheelR = 0.5 / tan(rad / 2);
	holeV = r2 / r1 * wheelR; 
	holeH = tan(rad / 2) * holeV;
	
	/* Tooth */

	glNewList(ListNr + 3, GL_COMPILE);
		
		/* vorne */

		glBegin(GL_POLYGON);			/* Basis der Zahnaussenseite */
			glNormal3fv(normal[0]);
			glVertex3fv(front[0]);
			glVertex3fv(front[1]);
			glVertex3fv(front[4]);
			glVertex3fv(front[5]);
		glEnd();

		glBegin(GL_POLYGON);			/* Spitze der Zahnaussenseite */
			glNormal3fv(normal[0]);
			for(i = 1; i < 5; i++)
				glVertex3fv(front[i]);
		glEnd();
		
		glBegin(GL_POLYGON);			/* Zacken zur Mitte */
			glNormal3fv(normal[0]);
			glVertex3fv(front[0]);
			glVertex3fv(front[5]);
			glVertex2f(-(wheelR - holeV), (0.5 + holeH));
			glVertex2f(-(wheelR - holeV), (0.5 - holeH));
		glEnd(); 

		/* hinten */

		glBegin(GL_POLYGON);			/* Basis der Zahnaussenseite */
			glNormal3fv(normal[1]);
			glVertex3fv(back[0]);
			glVertex3fv(back[1]);
			glVertex3fv(back[4]);
			glVertex3fv(back[5]);
		glEnd();

		glBegin(GL_POLYGON);			/* Spitze der Zahnaussenseite */
			glNormal3fv(normal[1]);
			for(i = 1; i < 5; i++)
				glVertex3fv(back[i]);
		glEnd();
	
		glBegin(GL_POLYGON);			/* Zacken zur Mitte */
			glNormal3fv(normal[1]);
			glVertex3fv(back[0]);
			glVertex3fv(back[5]);
			glVertex3f(-(wheelR-holeV), (0.5 + holeH), -1.0);
			glVertex3f(-(wheelR-holeV), (0.5 - holeH), -1.0);
		glEnd();

		/* Verbindung zwischen Vorder- und Rückseite */

		glBegin(GL_QUAD_STRIP);			/* Rand aussen (einfacher mit Quadstrip) */
			glNormal3fv(normal[2]);			
			glVertex3fv(front[0]);
			glVertex3fv(back[0]);
			glVertex3fv(front[1]);
			glVertex3fv(back[1]);
			for (i = 2; i < 6; i++)
			{
				glNormal3fv(normal[i+1]);	
				glVertex3fv(front[i]);
				glVertex3fv(back[i]);
			}
		glEnd();

		glBegin(GL_POLYGON);			/* Rand innen */
			glNormal3fv(normal[7]);
			glVertex2f(-(wheelR-holeV), (0.5 + holeH));
			glVertex2f(-(wheelR-holeV), (0.5 - holeH));
			glVertex3f(-(wheelR-holeV), (0.5 - holeH), -1.0);
			glVertex3f(-(wheelR-holeV), (0.5 + holeH), -1.0);
		glEnd();


	glEndList();

	/* ScaledTooth - skalierter Zahn */
	
	scale = 2 * r1 * sin(rad / 2);
	glNewList(ListNr + 2, GL_COMPILE);
		glScalef(scale, scale, scale);
		glCallList(ListNr + 3);
	glEndList();

	/* CorrectTooth - skalierter und verschobener Zahn */
	
	transX = r1 * cos(rad / 2); 
	transY = -r1 * sin(rad / 2);
	glNewList(ListNr + 1, GL_COMPILE);
		glTranslatef(transX, transY, 0);
		glCallList(ListNr + 2);
	glEndList();

	/* Gear - zeichnet komplettes Zahnrad */
	glNewList(ListNr, GL_COMPILE);
		for (i = 1; i <= zaehne; i++)
		{
			glPushMatrix();
			glCallList(ListNr + 1);
			glPopMatrix();
			glRotatef(deg, 0.0, 0.0, 1.0);
		}
	glEndList();
}

/* aus LitCube */

void motionHandler(int mx, int my) 
{
	Azimut = mx; 
	Elevation = my;
	glutPostRedisplay();
}

void main(int argc, char **argv)
{
	glutInit(&argc,argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);	

	glutCreateWindow("Zahnraeder");

	gear(1, 3.0, 0.3, 30);
	gear(5, 1.5, 0.6, 12);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glLoadIdentity();

	/* Tiefenpuffer und Beleuchtung einschalten */

	glEnable(GL_DEPTH_TEST); 
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_NORMALIZE);
	glShadeModel(GL_FLAT);

	glutReshapeFunc(reshapeHandler);
	glutDisplayFunc(displayHandler);
	glutIdleFunc(idleHandler);
	glutMotionFunc(motionHandler);
	glutMainLoop();

}
