// Sphere Example

#include <cmath>
#include <cstdio>
#include <cstdlib>

#if __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#include <Cg/cg.h>
#include <Cg/cgGL.h>

#ifndef M_PI
static const float M_PI = 3.14159265358979323846f;
#endif

static CGcontext cgContext;

static CGprofile cgFragmentProfile;
static CGprofile cgVertexProfile;

static CGprogram cgFragmentProgram;
static CGprogram cgVertexProgram;

static CGparameter cgVertexParamModelViewProjection;
static CGparameter cgVertexParamGlobalAmbient;
static CGparameter cgVertexParamLightColor;
static CGparameter cgVertexParamLightPosition;
static CGparameter cgVertexParamAmbientColor;
static CGparameter cgVertexParamDiffuseColor;

static float gLightPosition[] = { 100.0f, 100.0f, 400.0f, 1.0f };

static float gEyePosition[] = {  50.0f, 50.0f, 50.0f, 1.0f };
static float gDirection[] = { -0.6f, -0.2f, -0.7f, 1.0f };

static float gViewY[] = { 0.0f, 0.0f, 0.0f, 1.0f };

static float gProjectionMatrix[16];

static int gMouseX = 0;
static int gMouseY = 0;

static void MakePerspectiveMatrix(float fov, float aspect, float zNear,
								  float zFar, float perspective[16])
{
	float radians = fov * M_PI / 360.0f;
	
	float deltaZ = zFar - zNear;
	float cot = std::cos(radians) / std::sin(radians);
	
	perspective[0] = cot / aspect;
	perspective[1] = 0.0f;
	perspective[2] = 0.0f;
	perspective[3] = 0.0f;
	
	perspective[4] = 0.0f;
	perspective[5] = cot;
	perspective[6] = 0.0f;
	perspective[7] = 0.0f;
	
	perspective[8] = 0.0f;
	perspective[9] = 0.0f;
	perspective[10] = -(zFar + zNear) / deltaZ;
	perspective[11] = -2.0f * zNear * zFar / deltaZ;
	
	perspective[12] = 0.0f;
	perspective[13] = 0.0f;
	perspective[14] = -1.0f;
	perspective[15] = 0.0f;
}

static void MakeLookAtMatrix(float eyeX, float eyeY, float eyeZ, float centerX,
							 float centerY, float centerZ, float upX,
							 float upY, float upZ, float lookAt[16])
{
	float x[3];
	float y[3];	
	float z[3];
	
	z[0] = eyeX - centerX;
	z[1] = eyeY - centerY;
	z[2] = eyeZ - centerZ;
	
	float lengthZ = std::sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);
	
	if (lengthZ > 0.0f) {
		z[0] /= lengthZ;
		z[1] /= lengthZ;
		z[2] /= lengthZ;
	}
	
	y[0] = upX;
	y[1] = upY;
	y[2] = upZ;
	
	x[0] =  y[1] * z[2] - y[2] * z[1];
	x[1] = -y[0] * z[2] + y[2] * z[0];
	x[2] =  y[0] * z[1] - y[1] * z[0];
	
	y[0] =  z[1] * x[2] - z[2] * x[1];
	y[1] = -z[0] * x[2] + z[2] * x[0];
	y[2] =  z[0] * x[1] - z[1] * x[0];
	
	float lengthX = std::sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
	
	if (lengthX > 0.0f) {
		x[0] /= lengthX;
		x[1] /= lengthX;
		x[2] /= lengthX;
	}
	
	float lengthY = std::sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);
	
	if (lengthY > 0.0f) {
		y[0] /= lengthY;
		y[1] /= lengthY;
		y[2] /= lengthY;
	}
	
	lookAt[0] = x[0];
	lookAt[1] = x[1];
	lookAt[2] = x[2];
	
	lookAt[3] = -x[0] * eyeX - x[1] * eyeY - x[2] * eyeZ;
	
	lookAt[4] = y[0];
	lookAt[5] = y[1];
	lookAt[6] = y[2];
	
	lookAt[7] = -y[0] * eyeX - y[1] * eyeY - y[2] * eyeZ;
	
	lookAt[8] = z[0];
	lookAt[9] = z[1];
	lookAt[10] = z[2];
	
	lookAt[11] = -z[0] * eyeX - z[1] * eyeY - z[2] * eyeZ;
	
	lookAt[12] = 0.0f;
	lookAt[13] = 0.0f;
	lookAt[14] = 0.0f;
	lookAt[15] = 1.0f;
}

static void MakeTranslateMatrix(float x, float y, float z, float translate[16])
{
	translate[0]  = 1.0f;
	translate[1]  = 0.0f;
	translate[2]  = 0.0f;
	
	translate[3]  = x;
	
	translate[4]  = 0.0f;
	translate[5]  = 1.0f;
	translate[6]  = 0.0f;
	
	translate[7]  = y;
	
	translate[8]  = 0.0f;
	translate[9]  = 0.0f;
	translate[10] = 1.0f;
	
	translate[11] = z;
	
	translate[12] = 0.0f;
	translate[13] = 0.0f;
	translate[14] = 0.0f;
	translate[15] = 1.0f;
}

static void MakeRotateMatrix(float angle, float axisX, float axisY, float axisZ,
							 float rotate[16])
{
	float axis[3];
	
	axis[0] = axisX;
	axis[1] = axisY;
	axis[2] = axisZ;
	
	float lengthAxis = std::sqrt(axis[0] * axis[0] + axis[1] * axis[1] +
								 axis[2] * axis[2]);
	
	if (lengthAxis > 0.0f) {
		axis[0] /= lengthAxis;
		axis[1] /= lengthAxis;
		axis[2] /= lengthAxis;
	}
	
	float radians = angle * M_PI / 180.0f;
	float sine = sin(radians);
	float cosine = cos(radians);
	
	float ab = axis[0] * axis[1] * (1.0f - cosine);
	float bc = axis[1] * axis[2] * (1.0f - cosine);
	float ca = axis[2] * axis[0] * (1.0f - cosine);
	
	float tx = axis[0] * axis[0];
	float ty = axis[1] * axis[1];
	float tz = axis[2] * axis[2];
	
	rotate[0]  = tx + cosine * (1.0f - tx);
	rotate[1]  = ab + axis[2] * sine;
	rotate[2]  = ca - axis[1] * sine;
	
	rotate[3]  = 0.0f;
	
	rotate[4]  = ab - axis[2] * sine;
	rotate[5]  = ty + cosine * (1.0f - ty);
	rotate[6]  = bc + axis[0] * sine;
	
	rotate[7]  = 0.0f;
	
	rotate[8]  = ca + axis[1] * sine;
	rotate[9]  = bc - axis[0] * sine;
	rotate[10] = tz + cosine * (1.0f - tz);
	
	rotate[11] = 0.0f;
	
	rotate[12] = 0.0f;
	rotate[13] = 0.0f;
	rotate[14] = 0.0f;
	
	rotate[15] = 1.0f;
}

static void MultMatrix(const float matrix1[16], const float matrix2[16],
					   float result[16])
{
	float temp[16];
	
	for (int i = 0; i < 4; ++i) {
		for (int j = 0; j < 4; ++j) {
			temp[i*4+j] = matrix1[i*4] * matrix2[j] +
						  matrix1[i*4+1] * matrix2[4+j] +
						  matrix1[i*4+2] * matrix2[8+j] +
						  matrix1[i*4+3] * matrix2[12+j];
		}
	}

	for (int i = 0; i < 16; ++i) {
		result[i] = temp[i];
	}
}

static void Transform(const float matrix[16], const float vector[4],
					  float result[4])
{
	float temp[4];
	
	for (int i = 0; i < 4; ++i) {
		temp[i] = matrix[i*4] * vector[0] + matrix[i*4+1] * vector[1] +
				  matrix[i*4+2] * vector[2] + matrix[i*4+3] * vector[3];
	}
	
	for (int i = 0; i < 3; ++i) {
		result[i] = temp[i] * temp[3];
	}
	
	result[3] = 1;
}

static void InvertMatrix(const float* matrix, float* result)
{
#define SWAP_ROWS(a, b) { GLfloat *_temp = a; (a)=(b); (b)=_temp; }
#define MAT(m,r,c) (m)[(r)*4+(c)]
	
	float temp[4][8];
	
	float* r0;
	float* r1;
	float* r2;
	float* r3;
	
	r0 = temp[0], r1 = temp[1], r2 = temp[2], r3 = temp[3];
	
	r0[0] = MAT(matrix,0,0), r0[1] = MAT(matrix,0,1), r0[2] = MAT(matrix,0,2),
	r0[3] = MAT(matrix,0,3), r0[4] = 1.0f, r0[5] = r0[6] = r0[7] = 0.0f,
	
	r1[0] = MAT(matrix,1,0), r1[1] = MAT(matrix,1,1), r1[2] = MAT(matrix,1,2),
	r1[3] = MAT(matrix,1,3), r1[5] = 1.0f, r1[4] = r1[6] = r1[7] = 0.0f,
	
	r2[0] = MAT(matrix,2,0), r2[1] = MAT(matrix,2,1), r2[2] = MAT(matrix,2,2),
	r2[3] = MAT(matrix,2,3), r2[6] = 1.0f, r2[4] = r2[5] = r2[7] = 0.0f,
	
	r3[0] = MAT(matrix,3,0), r3[1] = MAT(matrix,3,1), r3[2] = MAT(matrix,3,2),
	r3[3] = MAT(matrix,3,3), r3[7] = 1.0f, r3[4] = r3[5] = r3[6] = 0.0f;
	
	if (std::fabs(r3[0]) > std::fabs(r2[0])) {
		SWAP_ROWS(r3, r2);
	}
	
	if (std::fabs(r2[0]) > std::fabs(r1[0])) {
		SWAP_ROWS(r2, r1);
	}
	
	if (std::fabs(r1[0]) > std::fabs(r0[0])) {
		SWAP_ROWS(r1, r0);
	}
	
	float m1 = r1[0] / r0[0];
	float m2 = r2[0] / r0[0];
	float m3 = r3[0] / r0[0];
	
	float s = r0[1];
	
	r1[1] -= m1 * s;
	r2[1] -= m2 * s;
	r3[1] -= m3 * s;
	
	s = r0[2];
	
	r1[2] -= m1 * s;
	r2[2] -= m2 * s;
	r3[2] -= m3 * s;
	
	s = r0[3];
	
	r1[3] -= m1 * s;
	r2[3] -= m2 * s;
	r3[3] -= m3 * s;
	
	s = r0[4];
	
	if (s != 0.0f) {
		r1[4] -= m1 * s;
		r2[4] -= m2 * s;
		r3[4] -= m3 * s;
	}
	
	s = r0[5];
	
	if (s != 0.0f) {
		r1[5] -= m1 * s;
		r2[5] -= m2 * s;
		r3[5] -= m3 * s;
	}
	
	s = r0[6];
	
	if (s != 0.0f) {
		r1[6] -= m1 * s;
		r2[6] -= m2 * s;
		r3[6] -= m3 * s;
	}
	
	s = r0[7];
	
	if (s != 0.0f) {
		r1[7] -= m1 * s;
		r2[7] -= m2 * s;
		r3[7] -= m3 * s;
	}
	
	if (std::fabs(r3[1]) > std::fabs(r2[1])) {
		SWAP_ROWS(r3, r2);
	}
	
	if (std::fabs(r2[1]) > std::fabs(r1[1])) {
		SWAP_ROWS(r2, r1);
	}
	
	m2 = r2[1] / r1[1];
	m3 = r3[1] / r1[1];
	
	r2[2] -= m2 * r1[2];
	r3[2] -= m3 * r1[2];
	r2[3] -= m2 * r1[3];
	r3[3] -= m3 * r1[3];
	
	s = r1[4];
	
	if (s != 0.0f) {
		r2[4] -= m2 * s;
		r3[4] -= m3 * s;
	}
	
	s = r1[5];
	
	if (s != 0.0f) {
		r2[5] -= m2 * s;
		r3[5] -= m3 * s;
	}
	
	s = r1[6];
	
	if (s != 0.0f) {
		r2[6] -= m2 * s;
		r3[6] -= m3 * s;
	}
	
	s = r1[7];
	
	if (s != 0.0f) {
		r2[7] -= m2 * s;
		r3[7] -= m3 * s;
	}
	
	if (std::fabs(r3[2]) > std::fabs(r2[2])) {
		SWAP_ROWS(r3, r2);
	}
	
	m3 = r3[2] / r2[2];
	
	r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4], r3[5] -= m3 * r2[5],
	r3[6] -= m3 * r2[6], r3[7] -= m3 * r2[7];
	
	s = 1.0f / r3[3];
	
	r3[4] *= s;
	r3[5] *= s;
	r3[6] *= s;
	r3[7] *= s;
	
	m2 = r2[3];
	
	s  = 1.0f / r2[2];
	
	r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2),
	r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2);
	
	m1 = r1[3];
	
	r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1,
	r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1;
	
	float m0 = r0[3];
	
	r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0,
	r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0;
	
	m1 = r1[2];
	
	s  = 1.0f / r1[1];
	
	r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1),
	r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1);
	
	m0 = r0[2];
	
	r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0,
	r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0;
	
	m0 = r0[1];
	
	s  = 1.0f / r0[0];
	
	r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0),
	r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0);
	
	MAT(result,0,0) = r0[4];
	MAT(result,0,1) = r0[5], MAT(result,0,2) = r0[6];
	MAT(result,0,3) = r0[7], MAT(result,1,0) = r1[4];
	MAT(result,1,1) = r1[5], MAT(result,1,2) = r1[6];
	MAT(result,1,3) = r1[7], MAT(result,2,0) = r2[4];
	MAT(result,2,1) = r2[5], MAT(result,2,2) = r2[6];
	MAT(result,2,3) = r2[7], MAT(result,3,0) = r3[4];
	MAT(result,3,1) = r3[5], MAT(result,3,2) = r3[6];
	MAT(result,3,3) = r3[7];
	
#undef MAT
#undef SWAP_ROWS
}

static void CheckForCgError(const char* situation)
{
	CGerror cgError;
	const char* errorString = cgGetLastErrorString(&cgError);
	
	if (cgError != CG_NO_ERROR) {
		printf("%s: %s\n", situation, errorString);
		
		if (cgError == CG_COMPILER_ERROR) {
			printf("%s\n", cgGetLastListing(cgContext));
		}
		
		exit(1);
	}
}

static void InitCg()
{
	cgContext = cgCreateContext();
	CheckForCgError("Creating context");
	
	cgGLSetDebugMode(CG_FALSE);
	cgSetParameterSettingMode(cgContext, CG_DEFERRED_PARAMETER_SETTING);
	
	cgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
	cgGLSetOptimalOptions(cgVertexProfile);
	CheckForCgError("Selecting vertex profile");
	
	cgVertexProgram = cgCreateProgramFromFile(cgContext, CG_SOURCE,
											  "SphereV.cg", cgVertexProfile,
											  "SphereV", NULL);
	CheckForCgError("Creating vertex program");
	
	cgGLLoadProgram(cgVertexProgram);
	CheckForCgError("Loading vertex program");
	
	cgVertexParamModelViewProjection = cgGetNamedParameter(cgVertexProgram, "modelViewProjection");
	CheckForCgError("Could not get modelViewProjection parameter");
	
	cgVertexParamGlobalAmbient = cgGetNamedParameter(cgVertexProgram, "globalAmbient");
	CheckForCgError("Could not get globalAmbient parameter");
	
	cgVertexParamLightColor = cgGetNamedParameter(cgVertexProgram, "lightColor");
	CheckForCgError("Could not get lightColor parameter");
	
	cgVertexParamLightPosition = cgGetNamedParameter(cgVertexProgram, "lightPosition");
	CheckForCgError("Could not get lightPosition parameter");
	
	cgVertexParamAmbientColor = cgGetNamedParameter(cgVertexProgram, "ambientColor");
	CheckForCgError("Could not get ambientColor parameter");
	
	cgVertexParamDiffuseColor = cgGetNamedParameter(cgVertexProgram, "diffuseColor");
	CheckForCgError("Could not get diffuseColor parameter");
	
	cgFragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
	cgGLSetOptimalOptions(cgFragmentProfile);
	CheckForCgError("Selecting fragment profile");
	
	cgFragmentProgram = cgCreateProgramFromFile(cgContext, CG_SOURCE,
												"SphereF.cg", cgFragmentProfile, 
												"SphereF", NULL);
	CheckForCgError("Creating fragment program");
	
	cgGLLoadProgram(cgFragmentProgram);
	CheckForCgError("Loading fragment program");
}

static void ExitCg()
{
	cgDestroyProgram(cgVertexProgram);
	cgDestroyProgram(cgFragmentProgram);
	cgDestroyContext(cgContext);
	
	exit(0);
}

static void KeyboardCallback(unsigned char key, int x, int y)
{
	switch (key)
	{
		case 27:
			ExitCg();
			break;
		case GLUT_KEY_UP:
		case '8':
			gEyePosition[0] += gDirection[0] * 2.0f;
			gEyePosition[1] += gDirection[1] * 2.0f;
			gEyePosition[2] += gDirection[2] * 2.0f;
			break;
		case GLUT_KEY_DOWN:
		case '2':
			gEyePosition[0] -= gDirection[0] * 2.0f;
			gEyePosition[1] -= gDirection[1] * 2.0f;
			gEyePosition[2] -= gDirection[2] * 2.0f;
			break;
		case GLUT_KEY_LEFT:
		case '4':
			gEyePosition[0] -= gViewY[0] * 2.0f;
			gEyePosition[1] -= gViewY[1] * 2.0f;
			gEyePosition[2] -= gViewY[2] * 2.0f;
			break;
		case GLUT_KEY_RIGHT:
		case '6':
			gEyePosition[0] += gViewY[0] * 2.0f;
			gEyePosition[1] += gViewY[1] * 2.0f;
			gEyePosition[2] += gViewY[2] * 2.0f;
			break;
	}
}

static void ArrowKeyCallback(int key, int x, int y)
{
	KeyboardCallback(key, x, y);
}

static void MouseCallback(int button, int state, int x, int y)
{
	gMouseX = x;
	gMouseY = y;
}

static void MotionCallback(int x, int y)
{
	int dx = gMouseX - x;
	int dy = gMouseY - y;
	
	float lengthDir = std::sqrt(gDirection[0] * gDirection[0] + gDirection[1] * gDirection[1] + 
								gDirection[2] * gDirection[2]);
	
	if (lengthDir > 0.0f) {
		gDirection[0] /= lengthDir;
		gDirection[1] /= lengthDir;
		gDirection[2] /= lengthDir;
	}
	
	gViewY[0] = -gDirection[2];
	gViewY[1] = 0.0f;
	gViewY[2] = gDirection[0];
	
	float rotateMatrix1[16];
	
	MakeRotateMatrix(M_PI * dx / 9.0f, 0.0f, 1.0f, 0.0f, rotateMatrix1);
	Transform(rotateMatrix1, gDirection, gDirection);
	
	
	float rotateMatrix2[16];
	
	MakeRotateMatrix(M_PI * dy / 9.0f, gViewY[0], gViewY[1], gViewY[2], rotateMatrix2);
	Transform(rotateMatrix2, gDirection, gDirection);
	
	gMouseX = x;
	gMouseY = y;
}

static void RenderCallback()
{
	float viewMatrix[16];
	
	MakeLookAtMatrix(gEyePosition[0], gEyePosition[1], gEyePosition[2],
					 gEyePosition[0] + gDirection[0],
					 gEyePosition[1] + gDirection[1],
					 gEyePosition[2] + gDirection[2], 0.0f, 1.0f, 0.0f,
					 viewMatrix);	
	
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	cgGLBindProgram(cgVertexProgram);
	CheckForCgError("Binding vertex program");
	
	cgGLEnableProfile(cgVertexProfile);
	CheckForCgError("Enabling vertex profile");
	
	cgGLBindProgram(cgFragmentProgram);
	CheckForCgError("Binding fragment program");
	
	cgGLEnableProfile(cgFragmentProfile);
	CheckForCgError("Enabling fragment profile");
	
	const float yellowAmbientColor[3]  = { 0.0f, 0.0f, 0.0f };
	const float yellowDiffuseColor[3]  = { 0.5f, 0.5f, 0.0f };
	
	cgSetParameter3fv(cgVertexParamAmbientColor, yellowAmbientColor);
	CheckForCgError("Setting ambient color parameter");
	
	cgSetParameter3fv(cgVertexParamDiffuseColor, yellowDiffuseColor);
	CheckForCgError("Setting diffuse color parameter");
	
	const float sphereRotationAngle = 0.0f;
	const float sphereRotationAxis[] = { 1.0f, 1.0f, 1.0f };
	
	float rotateMatrix[16];
	
	MakeRotateMatrix(sphereRotationAngle, sphereRotationAxis[0], 
					 sphereRotationAxis[1], sphereRotationAxis[2],
					 rotateMatrix);
	
	const float spherePosition[] = { 0.0f, 0.0f, 0.0f };
	
	float translateMatrix[16];
	
	MakeTranslateMatrix(spherePosition[0], spherePosition[1], spherePosition[2],
						translateMatrix);
	
	float modelMatrix[16];
	
	MultMatrix(translateMatrix, rotateMatrix, modelMatrix);
	
	float inverseModelMatrix[16];
	
	InvertMatrix(modelMatrix, inverseModelMatrix);
	
	float objectSpaceLightPosition[16];
	
	Transform(inverseModelMatrix, gLightPosition, objectSpaceLightPosition);
	cgSetParameter3fv(cgVertexParamLightPosition, objectSpaceLightPosition);
	CheckForCgError("Setting light position parameter");
	
	float modelViewMatrix[16];
	
	MultMatrix(viewMatrix, modelMatrix, modelViewMatrix);
	
	float modelViewProjectionMatrix[16];
	
	MultMatrix(gProjectionMatrix, modelViewMatrix, modelViewProjectionMatrix);
	cgSetMatrixParameterfr(cgVertexParamModelViewProjection,
						   modelViewProjectionMatrix);
	CheckForCgError("Setting model view projection parameter");
	
	cgUpdateProgramParameters(cgVertexProgram);
	
	glutSolidSphere(2.0f, 40, 40);
	
	cgGLDisableProfile(cgVertexProfile);
	CheckForCgError("Disabling vertex profile");
	
	cgGLDisableProfile(cgFragmentProfile);
	CheckForCgError("Disabling fragment profile");
	
	glutSwapBuffers();
}

static void ReshapeCallback(int width, int height)
{	
	MakePerspectiveMatrix(60.0f, width / height, 1.0f, 10000.0f,
						  gProjectionMatrix);
	
	glViewport(0, 0, width, height);
}

static void IdleCallback()
{
	glutPostRedisplay();
}

int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitWindowSize(512, 512);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
	
	int mainHandle = glutCreateWindow("Cg Sphere Example");
	
	glutSetWindow(mainHandle);
	
	glutDisplayFunc(RenderCallback);
	glutReshapeFunc(ReshapeCallback);
	glutIdleFunc(IdleCallback);
	glutKeyboardFunc(KeyboardCallback);
	glutSpecialFunc(ArrowKeyCallback);
	glutMouseFunc(MouseCallback);
	glutMotionFunc(MotionCallback);
	MotionCallback(0, 0);
	
	glClearColor(0.1f, 0.3f, 0.6f, 0.0f);
	
	InitCg();
	
	float globalAmbient[] = { 0.0f, 0.1f, 0.2f };
	float lightColor[] = { 1.0f, 1.0f, 1.0f };		
	
	cgSetParameter3fv(cgVertexParamGlobalAmbient, globalAmbient);
	cgSetParameter3fv(cgVertexParamLightColor, lightColor);
	
	atexit(ExitCg);
	
	glutMainLoop();
	
	return 0;
}
