// Shape.cpp: implementation of the Shape class.
//
//////////////////////////////////////////////////////////////////////

#include "Shape.h"

float sqr(float a){ return a*a;}
#define shf(a) (((a)>=(0))?(1):(-1))

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

Shape::Shape(){
	mtrl.textureType = 0;
	mtrl.numParams = 0;
}

// Shape methods
void Shape::setMaterial(Material& mt)
	{mtrl.set(mt);}
void Shape :: tellMaterialsGL()
{
	float amb[4],diff[4],spec[4], emiss[4];
	float zero[] = {0,0,0,1};
	mtrl.ambient.build4tuple(amb); // fill the array
	mtrl.diffuse.build4tuple(diff);
	mtrl.specular.build4tuple(spec);
	mtrl.emissive.build4tuple(emiss);
	glMaterialfv(GL_FRONT/*_AND_BACK*/,GL_AMBIENT,amb);
	glMaterialfv(GL_FRONT/*_AND_BACK*/,GL_DIFFUSE,diff);
			glMaterialfv(GL_FRONT/*_AND_BACK*/,GL_SPECULAR,spec);
			glMaterialfv(GL_FRONT/*_AND_BACK*/,GL_EMISSION,emiss);
			glMaterialf(GL_FRONT/*_AND_BACK*/,GL_SHININESS,mtrl.specularExponent);
}
/*************Extents **************/
bool BoxExtent::hit(Ray& genRay){
	/* initial assumptions */
	int num = 0;
	float tHit;
	float numer, denom;
	/* +/- infinity */
	float tIn = -100000;
	float tOut = 100000;

	/* */
	for (int i = 0; i < 6; i++){
		switch(i){
			case 0:	// top
				numer = fabs(maxY) - genRay.start.y;
				denom = genRay.dir.y;
				break;
			case 1:	// bottom
				numer = fabs(minY) + genRay.start.y;
				denom = genRay.dir.y * -1;
				break;
			case 2:	// right
				numer = fabs(maxX) - genRay.start.x;
				denom = genRay.dir.x;
				break;
			case 3:	// left
				numer = fabs(minX) + genRay.start.x;
				denom = genRay.dir.x * -1;
				break;
			case 4:	// front
				numer = fabs(maxZ) - genRay.start.z;
				denom = genRay.dir.z;
				break;
			case 5:	// back
				numer = fabs(minZ) + genRay.start.z;
				denom = genRay.dir.z * -1;
				break;
		}
		if (fabs(denom) < 0.00001){ // ray is parallel
			if (numer < 0) {
				return false;
			}
		}
		else{
			tHit = numer / denom;
			if (denom > 0){ //exiting
				if (tHit < tOut){ // a new earlier exit
					tOut = tHit;
				}
			}
			else{ // denom negative
				if (tHit > tIn) { // a new later entrance
					tIn = tHit;
				}
			}
		}

		if (tIn >= tOut ){
			return false;	// it's a miss, so exit
		}
	} // end for loop

	if (tIn > 0.000001){ // first hit in front of eye
		num++;
	}

	if (tOut > 0.00001){
		num++;
	}

	return (num > 0);

}
void BoxExtent::print(){
	printf("left : %f\n",minX);
	printf("right: %f\n",maxX);
	printf("bottom: %f\n",minY);
	printf("top: %f\n",maxY);
	printf("front: %f\n",maxZ);
	printf("back: %f\n\n",minZ);

}


/************ Sphere ***************/
void Sphere::drawOpenGL()
{
	tellMaterialsGL();
	glPushMatrix();
		glMultMatrixf(transf.m);
		glutSolidSphere(1.0,20,20);
	glPopMatrix();
}

bool Sphere::hit(Ray& r, Intersection& inter)
{
	Ray genRay; // generic ray
	//use shape's distortion and r to transform genRay into shape space
	genRay.xfrmRay(invTransf.m, r);
	genRay.dir.normalize();
	double B, C; // don't need A because genRay is normalized.
//	A = genRay.dir.dot(genRay.dir);
	B = genRay.start.dot(genRay.dir);
	C = genRay.start.dot(genRay.start) - 1.0;
	double discrim = B*B - C;
	if(discrim < 0.0) // ray misses shape
		return false;
	int num = 0;
	double discrimRoot = sqrt(discrim);
	double t1 = (-B - discrimRoot);
	if (t1 > 0.000001){ // hit in front of eye?
		inter.hit[0].hitTime = t1;
		inter.hit[0].hitObject = this;
		inter.hit[0].isEntering = true;
		inter.hit[0].surface = 0;
		Point3 Origin(0,0,0);
		Point3 HN(genRay.rayPos(t1)); // hit normal
		Point3 HP(r.rayPos(t1)); // hit point
		inter.hit[0].hitPoint.set(HP);
		inter.hit[0].hitNormal.set(HN);
		num = 1;
	}
	double t2 = (-B + discrimRoot); // later hit
	if (t2 > 0.000001){
		inter.hit[num].hitTime = t2;
		inter.hit[num].hitObject = this;
		inter.hit[num].isEntering = false;
		inter.hit[num].surface = 0;
		Point3 HN(genRay.rayPos(t2)); // hit normal
		Point3 HP(r.rayPos(t2)); // hit point
		inter.hit[num].hitPoint.set(HP);
		inter.hit[num].hitNormal.set(HN);
		num++;  // have another hit
	}
	inter.numHits = num;
	return (num > 0);
}

/************ Plane ***************/
void Plane::drawOpenGL()
{
	tellMaterialsGL();
	glPushMatrix();
		glMultMatrixf(transf.m);
		glEnable(GL_NORMALIZE);
		glBegin(GL_QUADS);
			glNormal3f(0,0,1.0);
			glVertex3f(-1.0,-1.0,0);
			glVertex3f( 1.0,-1.0,0);
			glVertex3f( 1.0, 1.0,0);
			glVertex3f(-1.0, 1.0,0);
		glEnd();
	glPopMatrix();
}

/********************
*	Hit for Plane	*
* a unit plane is a *
* 1x1x0 square that *
* lies along the z  *
* axis				*
********************/

bool Plane::hit(Ray& r, Intersection& inter){
	/* initial assumptions */

	inter.numHits = 0;

	/* make generic ray and transform it */
	Ray genRay;
	genRay.xfrmRay(invTransf.m, r);

	/* denominator for ... */
	double denom = genRay.dir.z;

	/* return false if ray parallel to unit plane */
	if(fabs(denom) < 0.0001)
		return false;

	/* hit time... */
	double time = -genRay.start.z / denom;

	/* return false if object behind the eye */
	if (time <= 0.0)
		return false;

	/* x and y of hit point */
	double hx = genRay.start.x + genRay.dir.x * time;
	double hy = genRay.start.y + genRay.dir.y * time;
	double hz;

	/* Is the ray outside of the unit plane */
	if ((hx > 1.0) || (hx < -1.0)) return false;
	if ((hy > 1.0) || (hy < -1.0)) return false;

	/* The ray must have hit the plane, so
	   we set the needed information about
	   the intersection.*/
	inter.numHits = 1;
	inter.hit[0].hitObject = this;
	inter.hit[0].hitTime = time;
	inter.hit[0].isEntering = true;
	inter.hit[0].surface = 0;
	hx = r.start.x + r.dir.x * time;
	hy = r.start.y + r.dir.y * time;
	hz = r.start.z + r.dir.z * time;

	inter.hit[0].hitPoint.set(hx,hy,hz);
	inter.hit[0].hitNormal.set(0,0,1);

	return true;
}

/************ TaperedCylinder ***************/
void TaperedCylinder::drawOpenGL()
{
	tellMaterialsGL();
	glPushMatrix();
		glMultMatrixf(transf.m);
		GLUquadricObj * qobj = gluNewQuadric();
		gluCylinder(qobj,1.0,smallRadius,1.0, 30,3);
		gluDisk(qobj, 0.0, 1.0, 10,4); //base at z = 0
		glTranslatef(0,0,1.0);
		gluDisk(qobj, 0.0, smallRadius, 30,2); //cap at z = 1
	glPopMatrix();

}

/*****************************
*	Hit for Tapered	Cylinder *
* a unit Tapered Cylinder is *
* directed along the Z-axis, *
* has a radius 1 base which  *
* is centered at the origin  *
* and has smallRadius cap at *
* the other end				 *
*****************************/

bool TaperedCylinder::hit(Ray& r, Intersection& inter){
	/* initial assumptions */
	int num = 0;

	/* make generic ray and transform it */
	Ray genRay;
	genRay.xfrmRay(invTransf.m, r);

	/* */
	double A, B, C, discrim, discRoot, t1, t2, tb, tc;

	/* why -1? It works if we don't use it. */
	double sm = smallRadius;// - 1;


	/* What is f? */
	double fDir = sm * genRay.dir.z;
	double fStart = sm * genRay.start.z + 1;

	/* we use a modified version of the
	   quadratic equation to determine
	   if the ray hits the sphere */
	/* A is ... */
	/* B is ... */
	/* C is ... */
	Point3 s = genRay.start;
	Vector3 c = genRay.dir;
	double d = (sm - 1)*c.z;
	double f = 1+(sm-1)*s.z;
	A = c.x*c.x + c.y*c.y - d*d;
	B = s.x*c.x + s.y*c.y - f*d;
	C = s.x*s.x + s.y*s.y - f*f;

	discrim = B*B - A*C;

	/* test if hit is with the cap and/or base */
	if (discrim > 0.0){
		discRoot = (double)sqrt(discrim);
		t1 = (-B - discRoot)/A;

		/* z hitPoint */
		float zHit = genRay.start.z + genRay.dir.z * t1;

		/* z hitPoint is in front of eye
		   and is at the cap or base */
		if ((t1 > 0.00001) && (zHit <= 1.0) && (zHit >= 0.0)){
			inter.hit[num].hitTime = t1;
			inter.hit[num++].surface = 0;
		}

		/* */
		t2 = (-B + discRoot)/A;
		zHit = genRay.start.z + genRay.dir.z * t2;
		if((t2 > 0.00001) && (zHit <= 1.0) && (zHit >= 0.0)){
			inter.hit[num].hitTime = t2;
			inter.hit[num++].surface = 0;
		}
	}

	/* test base at z=0 */
	tb = -genRay.start.z/genRay.dir.z; // hit time at z=0 plane
	/* hit in front of eye and within disc of base */
	if((tb > 0.0001) && (sqr(genRay.start.x + genRay.dir.x*tb) +
						 sqr(genRay.start.y + genRay.dir.y*tb) < 1.0)){

		inter.hit[num].hitTime = tb;
		inter.hit[num++].surface = 1; // for the base
	}

	// test the cap at z=1
	tc = (1 - genRay.start.z)/genRay.dir.z; // hit time at z=1 plane
	/* within disc of base */
	if((tc > 0.00001) && (sqr(genRay.start.x + genRay.dir.x*tc) +
						  sqr(genRay.start.y + genRay.dir.y*tc) < sqr(smallRadius))){
		inter.hit[num].hitTime = tc;
		inter.hit[num++].surface = 2; // for the cap
	}

	if(num == 0)
		return false; // either missed or is behind the eye


	/*Done detecting hits and misses,
	  now we have to analyze the hits that we got*/


	inter.numHits = num;

	if(num == 1){
		inter.hit[0].isEntering = false;
		inter.hit[0].hitObject = this;
	}
	else // two hits and first is entering
	{
		if(inter.hit[0].hitTime > inter.hit[1].hitTime){
			double tempt = inter.hit[0].hitTime;
			inter.hit[0].hitTime = inter.hit[1].hitTime;
			inter.hit[1].hitTime = tempt;

			int temps = inter.hit[0].surface;
			inter.hit[0].surface = inter.hit[1].surface;
			inter.hit[1].surface = temps;
		}
		inter.hit[0].isEntering = true;
		inter.hit[1].isEntering = false;
		inter.hit[0].hitObject = inter.hit[1].hitObject = this;
	}

	/* now set the hit point and normal for the hit(s) */
	for (int i=0; i<num; i++){
		Point3 HN(genRay.rayPos(inter.hit[i].hitTime));
		Point3 HP(r.rayPos(inter.hit[i].hitTime));
		inter.hit[i].hitPoint.set(HP);
		int surf = inter.hit[i].surface;
		if(surf == 0){ // cylinder
			// I don't think this is physically correct
			sm -= 1;
			inter.hit[i].hitNormal.set(HN.x, HN.y, -sm*(1+sm*HN.z));
		}else if(surf == 1){ // base
			inter.hit[i].hitNormal.set(0,0,-1);
		}else{ // cap
			inter.hit[i].hitNormal.set(0,0,1);
		}
	}
	return true;
}

/************ Cube ***************/
void Cube::drawOpenGL()
{
	tellMaterialsGL();
	glPushMatrix();
		glMultMatrixf(transf.m);
		glutSolidCube(2.0);
	glPopMatrix();
}

Vector3 Cube::cubeNormal(int surf){
	int m = surf/2; // along y, x, or z axis?
	int n = (surf%2)? -1:1; // is it along positive or negative axis?

	Vector3 normal;
	if (m == 0){ // top or bottom
		normal.set(0,n,0);
	}else{
		if (m == 1){ // left or right
			normal.set(n,0,0);
		}else{ // front or back
			normal.set(0,0,n);
		}
	}
	return normal;
}

bool Cube::hit(Ray& r, Intersection& inter)
{
	/* initial assumptions */
	int num = 0;
	double tHit;
	double numer, denom;
	/* +/- infinity */
	double tIn = -100000;
	double tOut = 100000;

	int inSurf, outSurf;

	/* make generic ray and transform it */
	Ray genRay;
	genRay.xfrmRay(invTransf.m, r);

	/* */
	for (int i = 0; i < 6; i++){
		switch(i){
			case 0:	// top
				numer = 1.0 - genRay.start.y;
				denom = genRay.dir.y;
				break;
			case 1:	// bottom
				numer = 1.0 + genRay.start.y;
				denom = genRay.dir.y * -1;
				break;
			case 2:	// right
				numer = 1.0 - genRay.start.x;
				denom = genRay.dir.x;
				break;
			case 3:	// left
				numer = 1.0 + genRay.start.x;
				denom = genRay.dir.x * -1;
				break;
			case 4:	// front
				numer = 1.0 - genRay.start.z;
				denom = genRay.dir.z;
				break;
			case 5:	// back
				numer = 1.0 + genRay.start.z;
				denom = genRay.dir.z * -1;
				break;
		}
		if (fabs(denom) < 0.00001){ // ray is parallel
			if (numer < 0) {
				return false;
			}
		}
		else{
			tHit = numer / denom;
			if (denom > 0){ //exiting
				if (tHit < tOut){ // a new earlier exit
					tOut = tHit;
					outSurf = i;
				}
			}
			else{ // denom negative
				if (tHit > tIn) { // a new later entrance
					tIn = tHit;
					inSurf = i;
				}
			}
		}

		if (tIn >= tOut ){
			return false;	// it's a miss, so exit
		}
	} // end for loop

	if (tIn > 0.000001){ // first hit in front of eye
		inter.hit[0].hitTime = tIn;
		inter.hit[0].surface = inSurf;
		inter.hit[0].isEntering = true;
		inter.hit[0].hitObject = this;
		inter.hit[0].hitPoint.set(r.rayPos(tIn));
		inter.hit[0].hitNormal.set(cubeNormal(inSurf));
		num++;
	}

	if (tOut > 0.00001){
		inter.hit[num].hitTime = tOut;
		inter.hit[num].surface = outSurf;
		inter.hit[num].isEntering = false;
		inter.hit[num].hitObject = this;
		inter.hit[num].hitPoint.set(r.rayPos(tOut));
		inter.hit[num].hitNormal.set(cubeNormal(outSurf));
		num++;
	}

	inter.numHits = num; // number of hits;

	return (num > 0);
}

/************ Mesh ***************/
void Mesh::drawOpenGL()
{
	tellMaterialsGL();
	glPushMatrix();
	glMultMatrixf(transf.m);
	if(doEdges2)
		drawEdges();
	else
		drawMesh();

	glPopMatrix();
}

/************ Triangle Hit ***********/
bool Mesh::triangleHit(Ray& r, Intersection& inter, Point3 v0, Point3 v1, Point3 v2){
	Vector3 e1,e2,p,q,s;
	float det, denom, alpha, beta, time;

	e1 = v1-v0;
	e2 = v2-v0;
	p = r.dir.cross(e2);
	det = e1.dot(p);

	if (fabs(det) < 0.00001){
		return false;
	}

	denom = 1/det; // one divide instead of two
	s = r.start - v0;
	alpha = s.dot(p)*denom;

	if ((alpha < 0.0) || (alpha > 1.0)){
		return false;
	}

	q = s.cross(e1);
	beta = r.dir.dot(q)*denom;

	if ((beta < 0.0) || (alpha + beta > 1.0)){
		return false;
	}

	time = e2.dot(q)*denom;
	if(time > 0.0)
    {
        //cout << " hit time is " << stime << endl;
        inter.hit[1].hitTime = time;
        return true;
    }
	return false;
}

bool Mesh::hit(Ray &r, Intersection &inter)
{
    Ray genray;
    genray.xfrmRay(invTransf.m, r);

	if (Bextent.hit(genray) == false){
		return false;
	}

	Point3 v0, v1, v2, hitPoint;
    Vector3 normal;
    float minTime = 1000000;

    for(int f = 0; f < numFaces; f++)
    {
		v0.set(pt[face[f].vert[0].vertIndex]); // set point to face vertex 0

		// else surface facing eye, so check all triangles on face
		// use point (vert[0]) as a vert for all triangles
        for(int i = 0; i < face[f].nVerts-2; i++)
        {

			v1 = pt[face[f].vert[i+1].vertIndex];
			v2 = pt[face[f].vert[i+2].vertIndex];

			if (triangleHit(genray, inter, v0, v1, v2)){
				if (inter.hit[1].hitTime < minTime){
					normal.set(norm[face[f].vert[0].normIndex]);
					float angle = genray.dir.dot(normal);
					minTime = inter.hit[1].hitTime;
					inter.hit[0].hitTime = minTime;
					inter.hit[0].surface = f;
					inter.hit[0].isEntering = (angle < 1.0);
					inter.hit[0].hitObject = this;
					hitPoint.set(r.rayPos(minTime));
					inter.hit[0].hitPoint.set(hitPoint);
					inter.hit[0].hitNormal.set(normal);
					inter.numHits = 1;
				}
			}
		}
	}

	return (inter.numHits == 1);
}

Mesh :: Mesh(){
			numVerts = numFaces = numNorms = 0;
			pt = NULL; norm  =  NULL; face = NULL;
			lastVertUsed = lastNormUsed = lastFaceUsed = -1;
}
void Mesh::freeMesh()
{ // free up memory used by this mesh.
			delete [] pt; // release whole vertex list
			delete [] norm;
			for(int f = 0; f < numFaces; f++)
				delete[] face[f].vert; // delete the vert[] array of this face
			delete [] face;
}
int Mesh :: isEmpty()
{
	return (numVerts == 0) || (numFaces == 0) || (numNorms == 0);
}
void Mesh::makeEmpty()
{
	numVerts = numFaces = numNorms = 0;
}

Mesh :: Mesh(string fname){ // read this file to build mesh
			numVerts = numFaces = numNorms = 0;
			pt = NULL; norm  =  NULL; face = NULL;
			lastVertUsed = lastNormUsed = lastFaceUsed = -1;
			readMesh(fname);
}
Vector3 Mesh :: newell4(int indx[])
{ /* return the normalized normal to face with vertices
  pt[indx[0]],...,pt[indx[3]]. i.e. indx[] contains the four indices
	into the vertex list to be used in the Newell calculation */
			Vector3 m;
			for(int i = 0; i < 4 ; i++)
			{
				int next = (i== 3) ? 0 : i + 1; // which index is next?
				int f = indx[i], n = indx[next]; // names for the indices in the pair
				m.x += (pt[f].y - pt[n].y) * (pt[f].z + pt[n].z);
				m.y += (pt[f].z - pt[n].z) * (pt[f].x + pt[n].x);
				m.z += (pt[f].x - pt[n].x) * (pt[f].y + pt[n].y);
			}
			m.normalize();
			return m;
}
//<<<<<<<<<<<<<<<<<<<<<<<<<<<< readMesh >>>>>>>>>>>>>>>>>>>>>>>>
void Mesh::readMesh(string fname)
{
	fstream inStream;
	inStream.open(fname.c_str(), ios ::in); //open needs a c-like string
	if(inStream.fail() || inStream.eof())
	{
		cout << "can't open file or eof: " << fname << endl;
		makeEmpty();
		return;
	}
	inStream >> numVerts >> numNorms >> numFaces;
	// make arrays for vertices, normals, and faces
	pt = new Point3[numVerts];        assert(pt != NULL);
	norm = new Vector3[numNorms];     assert(norm != NULL);
	face = new Face[numFaces];        assert(face != NULL);
	float minX,minY,minZ,maxX,maxY,maxZ;
	minX = minY = minZ = 1000000;
	maxX = maxY = maxZ = -1000000;
	for(int i = 0; i < numVerts; i++){ 	// read in the vertices
		inStream >> pt[i].x >> pt[i].y >> pt[i].z;
		if (pt[i].x < minX) minX = pt[i].x;
		if (pt[i].x > maxX) maxX = pt[i].x;
		if (pt[i].y < minY) minY = pt[i].y;
		if (pt[i].y > maxY) maxY = pt[i].y;
		if (pt[i].z < minZ) minZ = pt[i].z;
		if (pt[i].z > maxZ) maxZ = pt[i].z;
	}
	Bextent = BoxExtent(minX,minY,minZ,maxX,maxY,maxZ);
	for(int ii = 0; ii < numNorms; ii++) 	// read in the normals
		inStream >> norm[ii].x >> norm[ii].y >> norm[ii].z;
	for(int f = 0; f < numFaces; f++)   // read in face data
	{
		inStream >> face[f].nVerts;
		int n = face[f].nVerts;
		face[f].vert = new VertexID[n]; assert(face[f].vert != NULL);
		for(int k = 0; k < n; k++) 		// read vertex indices for this face
			inStream >> face[f].vert[k].vertIndex;
		for(int kk = 0; kk < n; kk++) 		// read normal indices for this face
			inStream >> face[f].vert[kk].normIndex;
	}
	inStream.close();
} // end of readMesh
//<<<<<<<<<<<<<<<<<<<<<< drawMesh >>>>>>>>>>>>>>>>>>>>
void Mesh :: drawMesh()
{ // draw each face of this mesh using OpenGL: draw each polygon.
	if(isEmpty()) return; // mesh is empty
	for(int f = 0; f < numFaces; f++)
	{
		glBegin(GL_POLYGON);
		int n = face[f].nVerts;
//		int fn = face[f].vert[0].normIndex;
//		int fv = face[f].vert[0].vertIndex;
		for(int v = 0; v < n; v++)
//		for(int v = 1; v < n-1; v++)
		{
//			glBegin(GL_POLYGON);
//			int in = face[f].vert[v].normIndex;
//			assert(in >= 0 && in < numNorms);
//			glNormal3f(norm[in].x, norm[in].y, norm[in].z);
//			int iv = face[f].vert[v].vertIndex;
//			assert(iv >= 0 && iv < numVerts-2);
//			glVertex3f(pt[iv].x, pt[iv].y, pt[iv].z);
//			glVertex3f(pt[iv+1].x, pt[iv+1].y, pt[iv+1].z);
//			glVertex3f(pt[iv+2].x, pt[iv+2].y, pt[iv+2].z);
//			glEnd();

			int in = face[f].vert[v].normIndex;
			assert(in >= 0 && in < numNorms);
			glNormal3f(norm[in].x, norm[in].y, norm[in].z);
			int iv = face[f].vert[v].vertIndex; assert(iv >= 0 && iv < numVerts);
			glVertex3f(pt[iv].x, pt[iv].y, pt[iv].z);

		}
		glEnd();
	}
	glFlush();
}
//<<<<<<<<<<<<<<<<<<<<<< drawEdges >>>>>>>>>>>>>>>>>
void Mesh::drawEdges()
{ // draw each Edge of this mesh using OpenGL: draw each polygon.
	if(isEmpty())
		return; // mesh is empty
	for(int f = 0; f < numFaces; f++)
	{
		int n = face[f].nVerts;
		glBegin(GL_LINES);
		for(int v = 0; v < n; v++)
		{
			int in = face[f].vert[v].normIndex;
			assert(in >= 0 && in < numNorms);
			glNormal3f(norm[in].x, norm[in].y, norm[in].z);
			int iv = face[f].vert[v].vertIndex; assert(iv >= 0 && iv < numVerts);
			glVertex3f(pt[iv].x, pt[iv].y, pt[iv].z);
		}
		glEnd();
	}
	glFlush();
	return;
}
//<<<<<<<<<<<<<<<<<<<<<<<<<<<< write mesh>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
void Mesh::writeMesh(char * fname)
{ // write this mesh object into a new Chapter 6 format file.
	if(numVerts == 0 || numNorms   == 0 || numFaces == 0)
		return; //empty
	fstream outStream(fname, ios ::out); // open the output stream
	if(outStream.fail()) {
		cout << "can't make new file: " << fname << endl;
		return;
	}
	outStream << numVerts << " " << numNorms << " " << numFaces << "\n";
	// write the vertex and vertex normal list
	for(int i = 0; i < numVerts; i++)
		outStream << pt[i].x   << " " << pt[i].y   << " " << pt[i].z << "\n";
	for(int ii = 0; ii < numNorms; ii++)
		outStream  << norm[ii].x << " " << norm[ii].y << " " << norm[ii].z << "\n";
	// write the face data
	for(int f = 0; f < numFaces; f++)
	{
		int n = face[f].nVerts;
		outStream << n << "\n";
		for(int v = 0; v < n; v++)// write vertex indices for this face
			outStream << face[f].vert[v].vertIndex << " ";	outStream << "\n";
		for(int k = 0; k < n; k++)	// write normal indices for this face
			outStream << face[f].vert[k].normIndex << " "; outStream << "\n";
	}
	outStream.close();
	return;
}
