
/* Copyright (c) Mark J. Kilgard, 1994, 1997.  */

/* This program is freely distributable without licensing fees 
and is provided without guarantee or warrantee expressed or 
implied. This program is -not- in the public domain. */

#include <stdlib.h>
#include <math.h>       /* for sqrt() */
#include <glload/gl_all.h>
#include <GL/freeglut.h>

#include "dino.h"

typedef enum
{
	RESERVED, BODY_SIDE, BODY_EDGE, BODY_WHOLE, ARM_SIDE, ARM_EDGE, ARM_WHOLE,
	LEG_SIDE, LEG_EDGE, LEG_WHOLE, EYE_SIDE, EYE_EDGE, EYE_WHOLE, DINOSAUR
} displayLists;

/* *INDENT-OFF* */
static GLfloat body[][2] = { {0, 3}, {1, 1}, {5, 1}, {8, 4}, {10, 4}, {11, 5},
{11, 11.5}, {13, 12}, {13, 13}, {10, 13.5}, {13, 14}, {13, 15}, {11, 16},
{8, 16}, {7, 15}, {7, 13}, {8, 12}, {7, 11}, {6, 6}, {4, 3}, {3, 2},
{1, 2} };
static GLfloat arm[][2] = { {8, 10}, {9, 9}, {10, 9}, {13, 8}, {14, 9}, {16, 9},
{15, 9.5}, {16, 10}, {15, 10}, {15.5, 11}, {14.5, 10}, {14, 11}, {14, 10},
{13, 9}, {11, 11}, {9, 11} };
static GLfloat leg[][2] = { {8, 6}, {8, 4}, {9, 3}, {9, 2}, {8, 1}, {8, 0.5}, {9, 0},
{12, 0}, {10, 1}, {10, 2}, {12, 4}, {11, 6}, {10, 7}, {9, 7} };
static GLfloat eye[][2] = { {8.75f, 15}, {9, 14.7f}, {9.6f, 14.7f}, {10.1f, 15},
{9.6f, 15.25f}, {9, 15.25f} };
static GLfloat skinColor[] = {0.1f, 1.0f, 0.1f, 1.0f}, eyeColor[] = {1.0f, 0.2f, 0.2f, 1.0f};
/* *INDENT-ON* */

static void extrudeSolidFromPolygon(GLfloat data[][2], unsigned int dataSize,
									GLdouble thickness, GLuint side, GLuint edge, GLuint whole)
{
	static GLUtriangulatorObj *tobj = NULL;
	GLdouble vertex[3], dx, dy, len;
	int i;
	int count = dataSize / (int) (2 * sizeof(GLfloat));

	if (tobj == NULL)
	{
		tobj = gluNewTess();  /* create and initialize a GLU
							  polygon * * tesselation object */
		gluTessCallback(tobj, GLU_BEGIN, glBegin);
		gluTessCallback(tobj, GLU_VERTEX, glVertex2fv);  /* semi-tricky */
		gluTessCallback(tobj, GLU_END, glEnd);
	}
	glNewList(side, GL_COMPILE);
	glShadeModel(GL_SMOOTH);  /* smooth minimizes seeing
							  tessellation */
	gluBeginPolygon(tobj);
	for (i = 0; i < count; i++)
	{
		vertex[0] = data[i][0];
		vertex[1] = data[i][1];
		vertex[2] = 0;
		gluTessVertex(tobj, vertex, data[i]);
	}
	gluEndPolygon(tobj);
	glEndList();
	glNewList(edge, GL_COMPILE);
	glShadeModel(GL_FLAT);  /* flat shade keeps angular hands
							from being "smoothed" */
	glBegin(GL_QUAD_STRIP);
	for (i = 0; i <= count; i++)
	{
		/* mod function handles closing the edge */
		glVertex3f(data[i % count][0], data[i % count][1], 0.0f);
		glVertex3f(data[i % count][0], data[i % count][1], (float)thickness);
		/* Calculate a unit normal by dividing by Euclidean
		distance. We * could be lazy and use
		glEnable(GL_NORMALIZE) so we could pass in * arbitrary
		normals for a very slight performance hit. */
		dx = data[(i + 1) % count][1] - data[i % count][1];
		dy = data[i % count][0] - data[(i + 1) % count][0];
		len = sqrt(dx * dx + dy * dy);
		glNormal3f((float)(dx / len), (float)(dy / len), 0.0f);
	}
	glEnd();
	glEndList();
	glNewList(whole, GL_COMPILE);
	glFrontFace(GL_CW);
	glCallList(edge);
	glNormal3f(0.0, 0.0, -1.0);  /* constant normal for side */
	glCallList(side);
	glPushMatrix();
	glTranslatef(0.0f, 0.0f, (float)thickness);
	glFrontFace(GL_CCW);
	glNormal3f(0.0f, 0.0f, 1.0f);  /* opposite normal for other side */
	glCallList(side);
	glPopMatrix();
	glEndList();
}

int makeDinosaur(void)
{
	GLfloat bodyWidth = 3.0;
	GLuint dinoList = 0;

	extrudeSolidFromPolygon(body, sizeof(body), bodyWidth,
		BODY_SIDE, BODY_EDGE, BODY_WHOLE);
	extrudeSolidFromPolygon(arm, sizeof(arm), bodyWidth / 4,
		ARM_SIDE, ARM_EDGE, ARM_WHOLE);
	extrudeSolidFromPolygon(leg, sizeof(leg), bodyWidth / 2,
		LEG_SIDE, LEG_EDGE, LEG_WHOLE);
	extrudeSolidFromPolygon(eye, sizeof(eye), bodyWidth + 0.2,
		EYE_SIDE, EYE_EDGE, EYE_WHOLE);

	dinoList = glGenLists(1);
	glNewList(dinoList, GL_COMPILE);
	glPushMatrix();
	glTranslatef(-8, -8, -bodyWidth / 2);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, skinColor);
	glCallList(BODY_WHOLE);
	glPushMatrix();
	glTranslatef(0.0, 0.0, bodyWidth);
	glCallList(ARM_WHOLE);
	glCallList(LEG_WHOLE);
	glTranslatef(0.0, 0.0, -bodyWidth - bodyWidth / 4);
	glCallList(ARM_WHOLE);
	glTranslatef(0.0, 0.0, -bodyWidth / 4);
	glCallList(LEG_WHOLE);
	glTranslatef(0.0, 0.0, bodyWidth / 2 - 0.1f);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, eyeColor);
	glCallList(EYE_WHOLE);
	glPopMatrix();
	glPopMatrix();
	glEndList();

	return dinoList;
}

