//
//  OBJModel.m
//  Lesson37_OSXCocoa
//
//  Created by Will Thimbleby on Mon Sep 01 2003.
//  Copyright (c) 2003 __MyCompanyName__. All rights reserved.
//

#import "OBJModel.h"
#import "algebra3.h"

/*
# some text					Line is a comment until the end of the line

v float float float			A single vertex's geometric position in space. The first vertex listed in the file has index 1, and subsequent vertices are numbered sequentially.

vn float float float		A normal. The first normal in the file is index 1, and subsequent normals are numbered sequentially.

vt float float				A texture coordinate. The first texture coordinate in the file is index 1, and subsequent textures are numbered sequentially.

f int int int ...
f int/int int/int int/int         . . .
f int/int/int int/int/int         int/int/int ...

A polygonal face. The numbers are indexes into the arrays of vertex positions, texture coordinates, and normals respectively. A number may be omitted if, for example, texture coordinates are not being defined in the model.
There is no maximum number of vertices that a single polygon may contain. The .obj file specification says that each face must be flat and convex. In JavaView polygonal face may be triangulated.
*/

GLuint shaderTexture = 0;          // Storage For One Texture

inline void getNormal(Vector *a, Vector *b, Vector *c, Vector *result)
{
	*result = (*a-*c)*(*b-*c);
}

inline void MatrixPreMutiply(Matrix *m, Vector *v, Vector *result)
{
	float		w;
	
	result->x = ( m->data[ 0 ] * v->x ) + ( m->data[ 4 ] * v->y ) + ( m->data[ 8  ] * v->z ) + ( m->data[ 12 ] );
	result->y = ( m->data[ 1 ] * v->x ) + ( m->data[ 5 ] * v->y ) + ( m->data[ 9  ] * v->z ) + ( m->data[ 13 ] );
	result->z = ( m->data[ 2 ] * v->x ) + ( m->data[ 6 ] * v->y ) + ( m->data[ 10 ] * v->z ) + ( m->data[ 14 ] );
	
	w		  = ( m->data[ 3 ] * v->x ) + ( m->data[ 7 ] * v->y ) + ( m->data[ 11 ] * v->z ) + ( m->data[ 15 ] );
	
	//*result *= 1.0/w;
}

inline void MatrixPostMutiply(Matrix *m, Vector *v, Vector *result)
{
	float		w;
	
	result->x = ( m->data[ 0 ] * v->x ) + ( m->data[ 1 ] * v->y ) + ( m->data[ 2  ] * v->z ) + ( m->data[ 3 ] );
	result->y = ( m->data[ 4 ] * v->x ) + ( m->data[ 5 ] * v->y ) + ( m->data[ 6  ] * v->z ) + ( m->data[ 7 ] );
	result->z = ( m->data[ 8 ] * v->x ) + ( m->data[ 9 ] * v->y ) + ( m->data[ 10 ] * v->z ) + ( m->data[ 11 ] );
	
	w		  = ( m->data[ 3 ] * v->x ) + ( m->data[ 7 ] * v->y ) + ( m->data[ 11 ] * v->z ) + ( m->data[ 15 ] );
	
	//*result *= 1.0/w;
}

@implementation OBJModel

-(id) initWithFile:(NSString*)path
{
	NSString		*objString, *line;
	NSScanner		*scan;
	int				location = 0;
	NSRange			lineRange;
	
	float			x, y, z;
	int				i;
	int				vertex, texture, normal;
	MTLMaterial		*material;

	[super init];
	
	collision = NULL;
	vertexCount = 0;
	normalCount = 0;
	textureCount = 0;
	faceCount = 0;
	triangleCount = 0;
	
	objString = [NSString stringWithContentsOfFile:path];
	
	while(location < [objString length])
	{
		lineRange = [objString lineRangeForRange:NSMakeRange(location, 0)];
		line = [objString substringWithRange:lineRange];
		
		location += lineRange.length;
		
		
		if([line hasPrefix:@"#"])
			continue;
		else if([line hasPrefix:@"mtllib"])
		{	
			NSString		*materialFile;
			
			scan = [NSScanner scannerWithString:line];
			[scan scanString:@"mtllib" intoString:nil];
			[scan scanUpToString:@"\r" intoString:&materialFile];
			
			materialFile = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:materialFile];

			materialLoader = [[MTLLoader alloc] initWithFile:materialFile];
		}
		else if([line hasPrefix:@"usemtl"])
		{	
			NSString		*materialName;
			
			scan = [NSScanner scannerWithString:line];
			[scan scanString:@"usemtl" intoString:nil];
			[scan scanUpToString:@"\r" intoString:&materialName];
			
			material = [materialLoader getMaterial:materialName];
		}
		else if([line hasPrefix:@"vn"])
		{	
			scan = [NSScanner scannerWithString:line];
			[scan scanString:@"vn" intoString:nil];
			
			[scan scanFloat:&x];
			[scan scanFloat:&y];
			[scan scanFloat:&z];
			normals[normalCount].x = x;
			normals[normalCount].y = y;
			normals[normalCount].z = z;
			normalCount++;
		}
		else if([line hasPrefix:@"vt"])
		{	
			scan = [NSScanner scannerWithString:line];
			[scan scanString:@"vt" intoString:nil];
			
			[scan scanFloat:&x];
			[scan scanFloat:&y];
			textures[textureCount].u = x;
			textures[textureCount].v = -y;
			textureCount++;
		}
		else if([line hasPrefix:@"v"])
		{	
			scan = [NSScanner scannerWithString:line];
			[scan scanString:@"v" intoString:nil];
			
			[scan scanFloat:&x];
			[scan scanFloat:&y];
			[scan scanFloat:&z];
			vertices[vertexCount].x = x;
			vertices[vertexCount].y = y;
			vertices[vertexCount].z = z;
			vertexCount++;
		}
		else if([line hasPrefix:@"f"])
		{
			scan = [NSScanner scannerWithString:line];
			[scan scanString:@"f" intoString:nil];
			
			for(i=0;i<3;i++)
			{
				[scan scanInt:&vertex];
				if([scan scanString:@"/" intoString:nil])
				{
					[scan scanInt:&texture];
					
					if([scan scanString:@"/" intoString:nil])
						[scan scanInt:&normal];
				}
				
				//if(vertex != normal)
				//	NSLog(@"Loader Error: assumes each vertex has the same normal.");
				
				faces[faceCount].vertex[i] = vertex-1;
				faces[faceCount].normals[i] = normal-1;
				faces[faceCount].texture[i] = texture-1;
				faces[faceCount].neighbours[i] = -1;
				
				triangles[triangleCount].vertex[i] = vertex-1;
			}
			faces[faceCount].material = material;
			faceCount++;
			triangleCount++;
			
			if([scan scanInt:nil])
				NSLog(@"Loader Error: assumes model is fully triangulated.");
		}
	}
	NSLog(@"%d,%d,%d", vertexCount, normalCount, faceCount);
	
	[self calculateNeigbours];
	[self calculateNormals];
	
		
	return self;
}

- (void) calculateNormals
{
	int		A;

	for(A=0;A<faceCount;A++)
	{
		getNormal(&vertices[faces[A].vertex[0]], &vertices[faces[A].vertex[1]], &vertices[faces[A].vertex[2]], &faces[A].normal);
	}
}

- (void) calculateNeigbours
{
	int		A, B, edgeA, edgeB;

	for(A=0;A<faceCount;A++)						// for each face (A)
	{
		for(B=0;B<faceCount;B++)					// for every other face (B)
		{
			for(edgeA=0;edgeA<3;edgeA++)			// for each edge in A
			{
				if(faces[A].neighbours[edgeA] < 0)	// if we don't have a neighbour yet
				{
					for(edgeB=0;edgeB<3;edgeB++)	// for each edge in B
					{
						int vertA1 = faces[A].vertex[edgeA];
						int vertA2 = faces[A].vertex[( edgeA+1 )%3];
				
						int vertB1 = faces[B].vertex[edgeB];
						int vertB2 = faces[B].vertex[( edgeB+1 )%3];
						
						//neighbours
						if (( vertA1 == vertB1 && vertA2 == vertB2 ) || ( vertA1 == vertB2 && vertA2 == vertB1 ))
						{
							faces[A].neighbours[edgeA] = B;	  
							faces[B].neighbours[edgeB] = A;	  
						}
					}
				}
			}
		}
	}
}

- (void) dealloc
{
   [materialLoader release];
   [super dealloc];
}

- (MTLLoader*)materialLoader
{
	return materialLoader;
}

- (NewtonCollision*)newtonCollisionInWorld:(const NewtonWorld*)nWorld withCallback:(NewtonTreeCollisionCallback)levelCallback
{
	if(!collision)
	{
		// create the collsion tree geometry
		collision = NewtonCreateTreeCollision(nWorld, levelCallback);

		// prepare to create collsion geometry
		NewtonTreeCollisionBeginBuild(collision);
		
		int defaultID = NewtonMaterialGetDefaultGroupID (nWorld);
		
		int f;
		for(f=0;f<faceCount;f++)
		{
			float face[3*3];
			face[0+0] = vertices[faces[f].vertex[0]].x;
			face[0+1] = vertices[faces[f].vertex[0]].y;
			face[0+2] = vertices[faces[f].vertex[0]].z;
			face[3+0] = vertices[faces[f].vertex[1]].x;
			face[3+1] = vertices[faces[f].vertex[1]].y;
			face[3+2] = vertices[faces[f].vertex[1]].z;
			face[6+0] = vertices[faces[f].vertex[2]].x;
			face[6+1] = vertices[faces[f].vertex[2]].y;
			face[6+2] = vertices[faces[f].vertex[2]].z;
			// add this face to the collsion tree
			NewtonTreeCollisionAddFace(collision, 3, face, 3*sizeof (float), defaultID);
		}
		
		// finalize the collition tree build
		NewtonTreeCollisionEndBuild(collision, 0);
	}
	return collision;
}

#pragma mark -

- (void)createShaderTexture
{
   int i;                         // Looping Variable
   char Line[ 255 ];              // Storage For 255 Characters
   float shaderData[ 32 ][ 3 ];   // Storate For The 96 Shader Values
   FILE *In;                      // File Pointer
   
	// Open The Shader File
   In = fopen( [ [ [ NSBundle mainBundle ] pathForResource:@"Shader"
                                           ofType:@"txt" ] UTF8String ], "r" );
   if( In )                                 // Check To See If The File Opened
   {
      for( i = 0; i < 32; i++ )             // Loop Though The 32 Greyscale Values
      {
         if( feof( In ) )                   // Check For The End Of The File
            break;
         fgets( Line, 255, In );            // Get The Current Line

         shaderData[ i ][ 0 ] = shaderData[ i ][ 1 ] = shaderData[ i ][ 2 ] =
            atof( Line );                   // Copy Over The Value
      }

      fclose( In );                         // Close The File
   }
   else
      return FALSE;                         // It Went Horribly Horribly Wrong

   glGenTextures( 1, &shaderTexture );           // Get A Free Texture ID

   // Bind This Texture. From Now On It Will Be 1D
   glBindTexture( GL_TEXTURE_1D, shaderTexture );

   // For Crying Out Loud Don't Let OpenGL Use Bi/Trilinear Filtering!
   glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
   glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );

   // Upload
   glTexImage1D( GL_TEXTURE_1D, 0, GL_RGB, 32, 0, GL_RGB , GL_FLOAT, shaderData );
}

-(void) render
{
	int			i;
	MTLMaterial	*material = NULL;
	
	//model = glGenLists(1);
	//glNewList(model, GL_COMPILE);
	
	//glEnableClientState (GL_COLOR_ARRAY);
	//glEnableClientState (GL_VERTEX_ARRAY);
	//glEnableClientState (GL_TEXTURE_COORD_ARRAY);
	//glEnableClientState (GL_NORMAL_ARRAY);
	//glColorPointer (4, GL_FLOAT, 0, glcolors);
	//glVertexPointer (3, GL_FLOAT, 0, vertices);
	//glTexCoordPointer (3, GL_FLOAT, 0, textures);
	//glNormalPointer (GL_FLOAT, 0, normals);
	//glEnable(GL_LIGHTING);
	i = 0;
	
	while(i<faceCount)
	{
		if(!material || material->textureMap == (GLuint)-1)
		{
			// non-textured
			glColor3fv((const GLfloat*)&(faces[ i ].material->diffuseColor));
			glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, (const GLfloat*)&(faces[ i ].material->diffuseColor));
			glBegin(GL_TRIANGLES);
			for(;i<faceCount;i++)
			{
				if(faces[ i ].material != material )
				{
					material = faces[ i ].material;
					break;
				}
				
				//glArrayElement(faces[i].vertex[0]);
				//glArrayElement(faces[i].vertex[1]);
				//glArrayElement(faces[i].vertex[2]);
			glNormal3fv((const GLfloat*) &normals[ faces[ i ].normals[ 0 ] ] );
			glVertex3fv((const GLfloat*) &vertices[ faces[ i ].vertex[ 0 ] ] );
			glNormal3fv((const GLfloat*) &normals[ faces[ i ].normals[ 1 ] ] );
			glVertex3fv((const GLfloat*) &vertices[ faces[ i ].vertex[ 1 ] ] );
			glNormal3fv((const GLfloat*) &normals[ faces[ i ].normals[ 2 ] ] );
			glVertex3fv((const GLfloat*) &vertices[ faces[ i ].vertex[ 2 ] ] );
			}
			glEnd();
		}
		else
		{
			//textured
			glEnable( GL_TEXTURE_2D );
			glBindTexture(GL_TEXTURE_2D, material->textureMap);
			
			
			//glDisable(GL_LIGHTING);
		
			glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, (const GLfloat*)&(faces[ i ].material->diffuseColor));
			glBegin(GL_TRIANGLES);
			for(;i<faceCount;i++)
			{
				Vector		n;
			
				if(faces[ i ].material != material)
				{
					material = faces[ i ].material;
					break;
				}
				
				//glNormal3fv((const GLfloat*)&faces[i].normal);
				
				glNormal3fv((const GLfloat*) &normals[ faces[ i ].normals[ 0 ] ] );
				glTexCoord2fv((const GLfloat*)&(textures[faces[i].texture[0]]));
				glVertex3fv((const GLfloat*) &vertices[ faces[ i ].vertex[ 0 ] ] );
				
				glNormal3fv((const GLfloat*) &normals[ faces[ i ].normals[ 1 ] ] );
				glTexCoord2fv((const GLfloat*)&(textures[faces[i].texture[1]]));
				glVertex3fv((const GLfloat*) &vertices[ faces[ i ].vertex[ 1 ] ] );
				
				glNormal3fv((const GLfloat*) &normals[ faces[ i ].normals[ 2 ] ] );
				glTexCoord2fv((const GLfloat*)&(textures[faces[i].texture[2]]));
				glVertex3fv((const GLfloat*) &vertices[ faces[ i ].vertex[ 2 ] ] );
				
			}
			glEnd();
			glDisable( GL_TEXTURE_2D );
		}
	}
	//glEndList();
}

-(void) renderCel
{
	int		i, j;
	float	tmpShade;
	Matrix	tmpMatrix;
	Vector	tmpVector, tmpNormal;
	Vector	lightAngle(-0.0, 1.0, 2.0);
	static float outlineColor[ 3 ] = { 0.0f, 0.0f, 0.0f };   // Color Of The Lines
	
	lightAngle = lightAngle.Unit();
	
	if(shaderTexture == 0)
		[self createShaderTexture];
	
	// Cel-Shading Code //
	glEnable( GL_TEXTURE_1D );                   // Enable 1D Texturing
	glBindTexture( GL_TEXTURE_1D, shaderTexture );   // Bind Our Texture
	
	glGetFloatv( GL_MODELVIEW_MATRIX, tmpMatrix.data );

	glBegin( GL_TRIANGLES );
	for( i = 0; i < faceCount; i++ )            // Loop Through Each Polygon
	{
		glColor3fv((const GLfloat*)&(faces[ i ].material->diffuseColor));
		
		//tmpNormal = [self getNormal:vertices[faces[i].vertex[0]] :vertices[faces[i].vertex[1]] :vertices[faces[i].vertex[2]]];
		
		for( j = 0; j < 3; j++ )               // Loop Through Each Vertex
		{
			/*
				* Fill Up The TmpNormal Structure With The Current Vertices' Normal
				* Values
				*/
			tmpNormal = normals[ faces[ i ].normals[ j ] ];
	
			// Rotate This By The Matrix
			MatrixPreMutiply(&tmpMatrix, &tmpNormal, &tmpVector);
	
			tmpVector = tmpVector.Unit();            // Normalize The New Normal
	
			// Calculate The Shade Value
			tmpShade = tmpVector^lightAngle;
	
			if( tmpShade < 0.0f )
				tmpShade = 0.0f;               // Clamp The Value to 0 If Negative
	
			// Set The Texture Co-ordinate As The Shade Value
			glTexCoord1f( tmpShade );
			// Send The Vertex Position
			glVertex3fv((const GLfloat*) &vertices[ faces[ i ].vertex[ j ] ] );
		}
	}
   glEnd();
   
   glDisable( GL_TEXTURE_1D );               // Disable 1D Textures

   // Outline Code //
   {
      glEnable( GL_BLEND );                  // Enable Blending
      glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );  // Set The Blend Mode

      // Draw Backfacing Polygons As Wireframes
      glPolygonMode( GL_BACK, GL_LINE );
      glLineWidth( 2.0f );                         // Set The Line Width

      glCullFace( GL_FRONT );             // Don't Draw Any Front-Facing Polygons
      glDepthFunc( GL_LEQUAL );           // Change The Depth Mode

      glColor3fv( &outlineColor[ 0 ] );   // Set The Outline Color

      glBegin( GL_TRIANGLES );            // Tell OpenGL What We Want To Draw
         for( i = 0; i < faceCount; i++ )   // Loop Through Each Polygon
            for( j = 0; j < 3; j++ )      // Loop Through Each Vertex
            {
               // Send The Vertex Position
               glVertex3fv((const GLfloat*) &vertices[ faces[ i ].vertex[ j ] ] );
            }
      glEnd();                            // Tell OpenGL We've Finished

      glDepthFunc( GL_LESS );             // Reset The Depth-Testing Mode
      glCullFace( GL_BACK );              // Reset The Face To Be Culled
      // Reset Back-Facing Polygon Drawing Mode
      glPolygonMode( GL_BACK, GL_FILL );
      glDisable( GL_BLEND );              // Disable Blending
   }
}

-(void) renderCache
{
	glCallList(model);
}

-(void) renderCombo
{
	int			i, j;
	GLuint		tMap = -1;
	float	tmpShade;
	Matrix	tmpMatrix;
	Vector	tmpVector, tmpNormal;
	Vector	lightAngle(0.0, 1.0, 0.0);
	static float outlineColor[ 3 ] = { 0.0f, 0.0f, 0.0f };   // Color Of The Lines
	
	glEnableClientState (GL_VERTEX_ARRAY);
	glEnableClientState (GL_NORMAL_ARRAY);
	glVertexPointer (3, GL_FLOAT, 0, vertices);
	glNormalPointer (GL_FLOAT, 0, normals);
	
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
	
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
	
	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
	
	glActiveTextureARB(GL_TEXTURE1_ARB);
	glEnable(GL_TEXTURE_1D);
	glBindTexture(GL_TEXTURE_1D, shaderTexture);
	
	glActiveTextureARB(GL_TEXTURE0_ARB);
	glEnable(GL_TEXTURE_2D);
	
	
	glGetFloatv( GL_MODELVIEW_MATRIX, tmpMatrix.data );
	
	i = 0;
	
	while(i<faceCount)
	{
		if(tMap < 0)
		{
			// non-textured
			glBegin(GL_TRIANGLES);
			for(;i<faceCount;i++)
			{
				if(faces[ i ].material->textureMap != tMap)
				{
					tMap = faces[ i ].material->textureMap;
					break;
				}
				
				glArrayElement(faces[i].vertex[0]);
				glArrayElement(faces[i].vertex[1]);
				glArrayElement(faces[i].vertex[2]);
			}
			glEnd();
		}
		else
		{
			//textured
			glEnable( GL_TEXTURE_2D );
			glBindTexture(GL_TEXTURE_2D, tMap);
		
			glBegin(GL_TRIANGLES);
			for(;i<faceCount;i++)
			{
				if(faces[ i ].material->textureMap != tMap)
				{
					tMap = faces[ i ].material->textureMap;
					break;
				}
				
				glColor3fv((const GLfloat*)&(faces[ i ].material->diffuseColor));
				
				for( j = 0; j < 3; j++ )               // Loop Through Each Vertex
				{
					tmpNormal = normals[ faces[ i ].vertex[ j ] ];
			
					// Rotate This By The Matrix
					MatrixPreMutiply(&tmpMatrix, &tmpNormal, &tmpVector);
			
					tmpVector = tmpVector.Unit();            // Normalize The New Normal
			
					// Calculate The Shade Value
					tmpShade = tmpVector^lightAngle;
			
					if( tmpShade < 0.0f )
						tmpShade = 0.0f;               // Clamp The Value to 0 If Negative
					
					glMultiTexCoord2fvARB(GL_TEXTURE0_ARB, (const GLfloat*)&(textures[faces[i].texture[j]]));
					glMultiTexCoord1fARB(GL_TEXTURE1_ARB, tmpShade);
					glArrayElement(faces[i].vertex[j]);
				}
			}
			glEnd();
			glDisable( GL_TEXTURE_2D );
		}
	}
	
	glActiveTextureARB(GL_TEXTURE1_ARB);
	glDisable(GL_TEXTURE_1D);
	glActiveTextureARB(GL_TEXTURE0_ARB);
	
	// Outline Code //
   {
      glEnable( GL_BLEND );                  // Enable Blending
      glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );  // Set The Blend Mode

      // Draw Backfacing Polygons As Wireframes
      glPolygonMode( GL_BACK, GL_LINE );
      glLineWidth( 3.0f );                         // Set The Line Width

      glCullFace( GL_FRONT );             // Don't Draw Any Front-Facing Polygons
      glDepthFunc( GL_LEQUAL );           // Change The Depth Mode

      glColor3fv( &outlineColor[ 0 ] );   // Set The Outline Color

      glBegin( GL_TRIANGLES );            // Tell OpenGL What We Want To Draw
         for( i = 0; i < faceCount; i++ )   // Loop Through Each Polygon
            for( j = 0; j < 3; j++ )      // Loop Through Each Vertex
            {
               // Send The Vertex Position
               glVertex3fv((const GLfloat*) &vertices[ faces[ i ].vertex[ j ] ] );
            }
      glEnd();                            // Tell OpenGL We've Finished

      glDepthFunc( GL_LESS );             // Reset The Depth-Testing Mode
      glCullFace( GL_BACK );              // Reset The Face To Be Culled
      // Reset Back-Facing Polygon Drawing Mode
      glPolygonMode( GL_BACK, GL_FILL );
      glDisable( GL_BLEND );              // Disable Blending
   }

}

- (void) doShadowPass:(Vector*)lightDirection
{
	int		i, j;
	int count=0;
	
	for (i=0; i<faceCount; i++)
	{
		if (faces[i].visible)						// for every visible face
		{
			for (j=0; j<3; j++)						// for each edge
			{
				int			neighbour;
								
				neighbour = faces[i].neighbours[j];	// get the neighbour
				 
				if (neighbour < 0  || (!faces[neighbour].visible))	// neighbour doesn't exist/hidden
				{
					Vector	vectorA, vectorB;
					Vector	vectorAA, vectorBB;
					
					// here we have an edge, we must draw a polygon
					vectorAA = vectorA = vertices[faces[i].vertex[j]];
					vectorBB = vectorB = vertices[faces[i].vertex[(j+1)%3]];

					//calculate the length of the Vector
					//subtract(&verticies[pointA], &lightPosition, &VectorA);
					vectorAA = *lightDirection;
					vectorAA *= 100;
					vectorAA += vectorA;
					//subtract(&verticies[pointB], &lightPosition, &VectorB);
					vectorBB = *lightDirection;
					vectorBB *= 100;
					vectorBB += vectorB;
					
					//draw the polygon
					glBegin(GL_TRIANGLE_STRIP);
						glVertex3fv((float*)&vectorA);
						glVertex3fv((float*)&vectorAA);
						glVertex3fv((float*)&vectorB);
						glVertex3fv((float*)&vectorBB);
					glEnd();
					count++;
				}
			}
		}
	}
	NSLog(@"shadow map has %d/%d faces",count,faceCount);
}

- (void) castShadow
{
	static		float r=0;
	int			i;
	
	if(!shadowList)
	{
		//vec4		*lightVec, tvec();
		mat4		matrix;
		Vector		lightAngle(1, -1,0);
		Vector		tmpVector(1,-1, 0);
		
		vec4 lightVec(0.75, -1, -1.0, 0.0);
		
		r += 0.025;
		
		float mmatrix[16];
		
		glGetFloatv( GL_MODELVIEW_MATRIX, mmatrix );
		matrix[0][0] = mmatrix[0];
		matrix[0][1] = mmatrix[4];
		matrix[0][2] = mmatrix[8];
		matrix[0][3] = mmatrix[12];
		
		matrix[1][0] = mmatrix[1];
		matrix[1][1] = mmatrix[5];
		matrix[1][2] = mmatrix[9];
		matrix[1][3] = mmatrix[13];
		
		matrix[2][0] = mmatrix[2];
		matrix[2][1] = mmatrix[6];
		matrix[2][2] = mmatrix[10];
		matrix[2][3] = mmatrix[14];
		
		matrix[3][0] = mmatrix[3];
		matrix[3][1] = mmatrix[7];
		matrix[3][2] = mmatrix[11];
		matrix[3][3] = mmatrix[15];
		
		mat4 imatrix = matrix.inverse();
		
		vec4 v = imatrix*(lightVec);
		
		lightAngle.x = v[0];
		lightAngle.y = v[1];
		lightAngle.z = v[2];
		
		// Determine Which Faces Are Visible By The Light.
		for ( i=0; i<faceCount; i++ )
		{
			if ( (faces[i].normal^lightAngle) < 0 )
				faces[i].visible = YES;
			else
				faces[i].visible = NO;
		}

		//setup opengl
		//glClear(GL_STENCIL_BUFFER_BIT);
		//*/
		// First Pass. Increase Stencil Value In The Shadow
	
		shadowList = glGenLists(1);
		glNewList(shadowList, GL_COMPILE);
		[self doShadowPass:&lightAngle];
		glEndList();
	}
	
	glFrontFace( GL_CCW );
	glStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
	//[self doShadowPass:&lightAngle];
	glCallList(shadowList);
	// Second Pass. Decrease Stencil Value In The Shadow
	glFrontFace( GL_CW );
	glStencilOp( GL_KEEP, GL_KEEP, GL_DECR );
	//[self doShadowPass:&lightAngle];
	glCallList(shadowList);

/*	
	glMatrixMode (GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	glMatrixMode (GL_MODELVIEW);
	glFrontFace( GL_CCW );
	glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );      // Enable Rendering To Colour Buffer For All Components
	// Draw A Shadowing Rectangle Covering The Entire Screen
	glColor4f( 0.0f, 0.0f, 0.0f, 0.4f );
	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	glStencilFunc( GL_NOTEQUAL, 0, 0xFFFFFFFFL );
	glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
	glPushMatrix();
	glLoadIdentity();
	glBegin( GL_TRIANGLE_STRIP );
		glVertex3f(-10.0f, 10.0f,-0.20f);
		glVertex3f(-10.0f,-10.0f,-0.20f);
		glVertex3f( 10.0f, 10.0f,-0.20f);
		glVertex3f( 10.0f,-10.0f,-0.20f);
	glEnd();
	
	glMatrixMode (GL_PROJECTION);
	glPopMatrix();
	glMatrixMode (GL_MODELVIEW);*/
	//glPopMatrix();
	//glPopAttrib();

}

+ (void)startShadows
{
	glPushAttrib( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT | GL_POLYGON_BIT | GL_STENCIL_BUFFER_BIT );
	glDisable( GL_LIGHTING );                                       // Turn Off Lighting
	glDisable(GL_BLEND);
	glDepthMask( GL_FALSE );                                        // Turn Off Writing To The Depth-Buffer
	glDepthFunc( GL_LEQUAL );
	glEnable( GL_STENCIL_TEST );                                    // Turn On Stencil Buffer Testing
	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);			// Don't Draw Into The Colour Buffer
	glStencilFunc( GL_ALWAYS, 40, 0xFFFFFFFFL );
}

+ (void)finishShadows
{
	glMatrixMode (GL_PROJECTION);
	//glPushMatrix();
	glLoadIdentity();
	glMatrixMode (GL_MODELVIEW);
	glFrontFace( GL_CCW );
	glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );      // Enable Rendering To Colour Buffer For All Components
	// Draw A Shadowing Rectangle Covering The Entire Screen
	glColor4f( 0.0f, 0.0f, 0.0f, 0.3f );
	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	glStencilFunc( GL_NOTEQUAL, 0, 0xFFFFFFFFL );
	glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
	//glPushMatrix();
	glLoadIdentity();
	glBegin( GL_TRIANGLE_STRIP );
		glVertex3f(-10.0f, 10.0f,-0.20f);
		glVertex3f(-10.0f,-10.0f,-0.20f);
		glVertex3f( 10.0f, 10.0f,-0.20f);
		glVertex3f( 10.0f,-10.0f,-0.20f);
	glEnd();
	
	glPopAttrib();
}

@end
