import java.awt.*;
import java.util.*;
import java.lang.Math;

public class test extends MISApplet
{
	private final double focus = 400;
	private final double EPSILON = 1.0 / 40.0;
	private final double RADIUS = 25;
	private final int EPSILON_INV = 40;
	
	private final int X = 0;
	private final int Y = 1;
	private final int Z = 2;

	private final int R = 0;
	private final int G = 1;
	private final int B = 2;
	
	private final int TOP = 0;
	private final int MID = 1;
	private final int BOT = 2;	
	
	private final int PRE_DRAW = 1;
	private final int POST_DRAW = -1;
	
//	private final int RGB = 255;
	
	ParamSphere Head = new ParamSphere();
	ParamSphere Ears[] = new ParamSphere[2];
	ParamSphere Eyes[] = new ParamSphere[2];
	ParamSphere Shoulders[] = new ParamSphere[2];
	ParamSphere Elbows[] = new ParamSphere[2];
	ParamSphere Hands[] = new ParamSphere[2];
	ParamSphere Fin = new ParamSphere();
	ParamSphere Chest = new ParamSphere();
	ParamSphere Hip = new ParamSphere();
	
	ParamConeTapered Mouth = new ParamConeTapered();
	ParamConeTapered Neck = new ParamConeTapered();
	ParamConeTapered Abdomen = new ParamConeTapered();
	ParamConeTapered Waist = new ParamConeTapered();
	ParamConeTapered Humerus[] = new ParamConeTapered[2];
	ParamConeTapered Forearm[] = new ParamConeTapered[2];
	ParamConeTapered Legs[] = new ParamConeTapered[2];
	ParamConeTapered ClawLeft[] = new ParamConeTapered[4];
	ParamConeTapered ClawRight[] = new ParamConeTapered[4];
	
	Matrix3D Matrix3D = new Matrix3D();
	double mStack[][][] = new double[100][4][4];
	double MatrixTemp[][] = new double[4][4];
	int mTop = 0;

	double dSurfaceNormal[][][];
	double dVertexNormal[][][];
	double dNormalized[][][];
	
	double u[] = new double[3];
	double v[] = new double[3];
	double t, dStartTime;
		
	VertexNode Triangle[] = new VertexNode[3];
	VertexNode Vertices[] = new VertexNode[4];
	VertexNode Trap1[] = new VertexNode[4];
	VertexNode Trap2[] = new VertexNode[4];
	VertexNode NewNode = new VertexNode();
	VertexNode LeftNode = new VertexNode();
	VertexNode RightNode = new VertexNode();

	Button BtnAmbience;
	Button BtnShine;
	Slider SldR;
	Slider SldG;
	Slider SldB;
	
	int iTopMidBot[];
	
	private int iHeight, iWidth;
	private int iX, iY;
	private int iNumLights = 0;
	private double iShine = 64;
	
	private int FrameBuffer[][];
	private double ZBuffer[][];
	private double dLight[][] = new double[1000][6];
	
	private double dAmbience[] = {.1,.1,.1};
	private double dDiffuse[] = {.7,.7,.7};
	private double dSpecularClr[] = {.8,.8,.8};

	private double dEye[] = {0,0,-1};
	
	double CameraX, CameraY;
	double dTheta;
	boolean bIsInited = false;
	boolean bCameraView = false;

	public void render(Graphics g, double dCurrentTime)
	{
		if (bIsInited == false)
		{	
			dStartTime = dCurrentTime;			
			InitStuff();
			bIsInited = true;
		

			//clear the FrameBuffer and the ZBuffer
			for (int i=0; i<iHeight; i++)
				for (int j=0; j<iWidth; j++)
				{
					FrameBuffer[i][j] = pack(230,230,230);
					ZBuffer[i][j] = 2;
			}
		
			DrawShade(g, Fin);
			DrawShade(g, Ears[0]);
			DrawShade(g, Ears[1]);
			DrawShade(g, Eyes[0]);
			DrawShade(g, Eyes[1]);
			DrawShade(g, Head);
			DrawShade(g, Mouth);
			DrawShade(g, Neck);
			DrawShade(g, Chest);
			DrawShade(g, Shoulders[0]);
			DrawShade(g, Shoulders[1]);
			DrawShade(g, Humerus[0]);
			DrawShade(g, Humerus[1]);
			DrawShade(g, Elbows[0]);
			DrawShade(g, Elbows[1]);
			DrawShade(g, Forearm[0]);
			DrawShade(g, Forearm[1]);
			DrawShade(g, Hands[0]);
			DrawShade(g, Hands[1]);
			DrawShade(g, Abdomen);
			DrawShade(g, Waist);
			DrawShade(g, Hip);
			DrawShade(g, Legs[0]);
			DrawShade(g, Legs[1]);
			DrawShade(g, ClawLeft[0]);
			DrawShade(g, ClawLeft[1]);
			DrawShade(g, ClawLeft[2]);
			DrawShade(g, ClawLeft[3]);
			DrawShade(g, ClawRight[0]);
			DrawShade(g, ClawRight[1]);
			DrawShade(g, ClawRight[2]);
			DrawShade(g, ClawRight[3]);
				
		}
		computeImage(System.currentTimeMillis() - dStartTime, FrameBuffer);
	}
	
	public void InitStuff()
	{
		iHeight = bounds().height;
		iWidth = bounds().width;
		
		FrameBuffer = new int[iHeight+1][iWidth+1];
		ZBuffer = new double[iHeight+1][iWidth+1];
					
		CameraX = 0;
		CameraY = 0;
		
		Head = new ParamSphere(0,0,0,50,EPSILON,iHeight,iWidth);
		Ears[0] = new ParamSphere(0,0,0,23,EPSILON,iHeight,iWidth);
		Ears[1] = new ParamSphere(0,0,0,23,EPSILON,iHeight,iWidth);
		Eyes[0] = new ParamSphere(0,0,0,12,EPSILON,iHeight,iWidth);
		Eyes[1] = new ParamSphere(0,0,0,12,EPSILON,iHeight,iWidth);
		
		Humerus[0] = new ParamConeTapered(0,0,0,20,EPSILON,0.008,iHeight,iWidth);
		Humerus[1] = new ParamConeTapered(0,0,0,20,EPSILON,0.008,iHeight,iWidth);
		Forearm[0] = new ParamConeTapered(0,0,0,15,EPSILON,0.005,iHeight,iWidth);
		Forearm[1] = new ParamConeTapered(0,0,0,15,EPSILON,0.005,iHeight,iWidth);
		Legs[0] = new ParamConeTapered(0,0,0,30,EPSILON,0.005,iHeight,iWidth);
		Legs[1] = new ParamConeTapered(0,0,0,30,EPSILON,0.005,iHeight,iWidth);

		Fin = new ParamSphere(0,0,0,50,EPSILON,iHeight,iWidth);
		Chest = new ParamSphere(0,0,0,100,EPSILON,iHeight,iWidth);
		Mouth = new ParamConeTapered(0,0,0,54,EPSILON, 0.005, iHeight, iWidth);
		Neck = new ParamConeTapered(0,0,0,30,EPSILON, 0.005, iHeight, iWidth);
		Abdomen = new ParamConeTapered(0,0,0,75,EPSILON, 0.007, iHeight, iWidth);
		Waist = new ParamConeTapered(0,0,0,60,EPSILON, 0.0035, iHeight, iWidth);
		ClawLeft[0] = new ParamConeTapered(0,0,0,5,EPSILON, 0.0035, iHeight, iWidth);
		ClawLeft[1] = new ParamConeTapered(0,0,0,5,EPSILON, 0.0035, iHeight, iWidth);
		ClawLeft[2] = new ParamConeTapered(0,0,0,3,EPSILON, 0.0035, iHeight, iWidth);
		ClawLeft[3] = new ParamConeTapered(0,0,0,3,EPSILON, 0.0035, iHeight, iWidth);
		ClawRight[0] = new ParamConeTapered(0,0,0,5,EPSILON, 0.0035, iHeight, iWidth);
		ClawRight[1] = new ParamConeTapered(0,0,0,5,EPSILON, 0.0035, iHeight, iWidth);
		ClawRight[2] = new ParamConeTapered(0,0,0,3,EPSILON, 0.0035, iHeight, iWidth);
		ClawRight[3] = new ParamConeTapered(0,0,0,3,EPSILON, 0.0035, iHeight, iWidth);
		

		Hip = new ParamSphere(0,0,0,61,EPSILON,iHeight,iWidth);
		Shoulders[0] = new ParamSphere(0,0,0,30,EPSILON,iHeight,iWidth);
		Shoulders[1] = new ParamSphere(0,0,0,30,EPSILON,iHeight,iWidth);
		Elbows[0] = new ParamSphere(0,0,0,20,EPSILON,iHeight,iWidth);
		Elbows[1] = new ParamSphere(0,0,0,20,EPSILON,iHeight,iWidth);
		Hands[0] = new ParamSphere(0,0,0,15,EPSILON,iHeight,iWidth);
		Hands[1] = new ParamSphere(0,0,0,15,EPSILON,iHeight,iWidth);

		Matrix3D.IdentityMatrix(mStack[mTop]);
		Head.Translate(mStack[mTop], 0,130,-focus+100);
		

		push();
			Neck.Translate(mStack[mTop], 0, -80, 0);

			//chest
			push();
				Chest.Translate(mStack[mTop], 0, -40, 0);
				
				//shoulders
				push();
					Shoulders[0].Translate(mStack[mTop], 90, 0, 30);
					Shoulders[0].Rotate(mStack[mTop],-Math.PI/4,X);
					
					//left upper arm
					push();
						Humerus[0].Rotate(mStack[mTop], Math.PI/32, Z);
						
						//elbow
						push();
							Elbows[0].Translate(mStack[mTop], 0,-100,0);
							Elbows[0].Rotate(mStack[mTop], -Math.PI/2, Z);
							
							//forearm
							push();
								Forearm[0].Rotate(mStack[mTop], -2*Math.PI/32, Z);
								
								//hand
								push();
									Hands[0].Translate(mStack[mTop],0,-90,0);
									Hands[0].Rotate(mStack[mTop], Math.PI/8,Y);
									
									//claw 
									push();
										ClawLeft[0].Rotate(mStack[mTop], Math.PI/6, Z);
										ClawLeft[0].Translate(mStack[mTop], 0,-10,0);
										
										//finger
										push();
											ClawLeft[2].Translate(mStack[mTop], 0,-20,0);
											ClawLeft[2].Rotate(mStack[mTop],-Math.PI/4,Z);
											ClawLeft[2].Rotate(mStack[mTop],Math.PI/2,X);
											ClawLeft[2].Scale(mStack[mTop],1,.3,20);
											ClawLeft[2].Transform(mStack[mTop], ClawLeft[2].getShape());
										pop();
									
										ClawLeft[0].Rotate(mStack[mTop], Math.PI/2, X);
										ClawLeft[0].Scale(mStack[mTop],1,.3,20);
										ClawLeft[0].Transform(mStack[mTop], ClawLeft[0].getShape());
									pop();
									
									//claw
									push();
										ClawLeft[1].Rotate(mStack[mTop], -Math.PI/6, Z);
										ClawLeft[1].Translate(mStack[mTop], 0,-10,0);
										
										//finger
										push();
											ClawLeft[3].Translate(mStack[mTop], 0,-20,0);
											ClawLeft[3].Rotate(mStack[mTop],Math.PI/4,Z);
											ClawLeft[3].Rotate(mStack[mTop],Math.PI/2,X);
											ClawLeft[3].Scale(mStack[mTop],1,.3,20);									
											ClawLeft[3].Transform(mStack[mTop], ClawLeft[3].getShape());
										pop();
										
										ClawLeft[1].Rotate(mStack[mTop], Math.PI/2, X);
										ClawLeft[1].Scale(mStack[mTop],1,.3,20);
										ClawLeft[1].Transform(mStack[mTop], ClawLeft[1].getShape());
									pop();
																		
									Hands[0].Transform(mStack[mTop], Hands[0].getShape());
								pop();
								
					
								Forearm[0].Rotate(mStack[mTop], Math.PI/2, X);
								Forearm[0].Scale(mStack[mTop], 1,1,90);
								Forearm[0].Transform(mStack[mTop], Forearm[0].getShape());
							pop();
							
							Elbows[0].Transform(mStack[mTop], Elbows[0].getShape());
						pop();						
						
						Humerus[0].Rotate(mStack[mTop], Math.PI/2, X);
						Humerus[0].Scale(mStack[mTop],1,1,100);
						Humerus[0].Transform(mStack[mTop], Humerus[0].getShape());
					pop();
					
					Shoulders[0].Transform(mStack[mTop], Shoulders[0].getShape());			
				pop();
		
				push();
					Shoulders[1].Translate(mStack[mTop], -90, 0, 30);
					
					//right upper arm
					push();
						Humerus[1].Rotate(mStack[mTop], -Math.PI/32, Z);
						//elbow
						push();
							Elbows[1].Translate(mStack[mTop], 0,-100,0);
							
							//forearm
							push();
								Forearm[1].Rotate(mStack[mTop], 2*Math.PI/32, Z);

								//hand
								push();
									Hands[1].Translate(mStack[mTop],0,-90,0);
									
									//claw 
									push();
										ClawRight[0].Rotate(mStack[mTop], Math.PI/6, Z);
										ClawRight[0].Translate(mStack[mTop], 0,-10,0);
										
										//finger
										push();
											ClawRight[2].Translate(mStack[mTop], 0,-20,0);
											ClawRight[2].Rotate(mStack[mTop],-Math.PI/4,Z);
											ClawRight[2].Rotate(mStack[mTop],Math.PI/2,X);
											ClawRight[2].Scale(mStack[mTop],1,.3,20);
											ClawRight[2].Transform(mStack[mTop], ClawRight[2].getShape());
										pop();
									
										ClawRight[0].Rotate(mStack[mTop], Math.PI/2, X);
										ClawRight[0].Scale(mStack[mTop],1,.3,20);
										ClawRight[0].Transform(mStack[mTop], ClawRight[0].getShape());
									pop();
									
									//claw
									push();
										ClawRight[1].Rotate(mStack[mTop], -Math.PI/6, Z);
										ClawRight[1].Translate(mStack[mTop], 0,-10,0);
										
										//finger
										push();
											ClawRight[3].Translate(mStack[mTop], 0,-20,0);
											ClawRight[3].Rotate(mStack[mTop],Math.PI/4,Z);
											ClawRight[3].Rotate(mStack[mTop],Math.PI/2,X);
											ClawRight[3].Scale(mStack[mTop],1,.3,20);									
											ClawRight[3].Transform(mStack[mTop], ClawRight[3].getShape());
										pop();
										
										ClawRight[1].Rotate(mStack[mTop], Math.PI/2, X);
										ClawRight[1].Scale(mStack[mTop],1,.3,20);
										ClawRight[1].Transform(mStack[mTop], ClawRight[1].getShape());
									pop();
									
									Hands[1].Transform(mStack[mTop], Hands[1].getShape());
								pop();

								Forearm[1].Rotate(mStack[mTop], Math.PI/2, X);
								Forearm[1].Scale(mStack[mTop], 1,1,90);
								Forearm[1].Transform(mStack[mTop], Forearm[1].getShape());
							pop();
							
							Elbows[1].Transform(mStack[mTop], Elbows[1].getShape());
						pop();						

						Humerus[1].Rotate(mStack[mTop], Math.PI/2, X);
						Humerus[1].Scale(mStack[mTop],1,1,100);
						Humerus[1].Transform(mStack[mTop], Humerus[1].getShape());
					pop();

					Shoulders[1].Transform(mStack[mTop], Shoulders[1].getShape());			
				pop();
				
				//abdomen
				push();
					Abdomen.Translate(mStack[mTop], 0, -30, 0);
					
					//waist
					push();
						Waist.Translate(mStack[mTop], 0,-95,10);
						
						//hip
						push();
							Hip.Translate(mStack[mTop], 0,-5,0);
							
							//leg
							push();
								Legs[0].Translate(mStack[mTop], 30, 0,10);
								Legs[0].Rotate(mStack[mTop],Math.PI/2, X);
								Legs[0].Scale(mStack[mTop], 1,1,130);
								Legs[0].Transform(mStack[mTop], Legs[0].getShape());							
							pop();
							
							//leg
							push();
								Legs[1].Translate(mStack[mTop], -30, 0,10);
								Legs[1].Rotate(mStack[mTop],Math.PI/2, X);
								Legs[1].Scale(mStack[mTop], 1,1,130);
								Legs[1].Transform(mStack[mTop], Legs[1].getShape());							
							pop();
							
							Hip.Scale(mStack[mTop],1,.5,1);
							Hip.Transform(mStack[mTop], Hip.getShape());
						pop();
						
						Waist.Rotate(mStack[mTop], -Math.PI/2, X);
						Waist.Scale(mStack[mTop],1,1,30);
						Waist.Transform(mStack[mTop], Waist.getShape());
					pop();
					
					Abdomen.Rotate(mStack[mTop], 1*Math.PI/2, X);
					Abdomen.Scale(mStack[mTop], 1, .75, 75);
					Abdomen.Transform(mStack[mTop], Abdomen.getShape());
				pop();
	
				Chest.Scale(mStack[mTop], 1,.5,.8);
				Chest.Transform(mStack[mTop], Chest.getShape());
			pop();

			Neck.Rotate(mStack[mTop], -Math.PI/2, X);
			Neck.Scale(mStack[mTop], 1, 1, 50);
			Neck.Transform(mStack[mTop], Neck.getShape());
		pop();

		push();
			Mouth.Translate(mStack[mTop], 0, -10, 0);
			Mouth.Rotate(mStack[mTop], 9*Math.PI/16, X);
			Mouth.Scale(mStack[mTop], 1, 1, 40);
			Mouth.Transform(mStack[mTop], Mouth.getShape());
		pop();

		push();
			Fin.Translate(mStack[mTop], 1, 20, 0);
			Fin.Scale(mStack[mTop], .2, 1,1);
			Fin.Transform(mStack[mTop], Fin.getShape());
		pop();
		
		push();
			Head.Scale(mStack[mTop], 1,1.2,1);
			Head.Transform(mStack[mTop], Head.getShape());
		pop();

		push();
			Ears[0].Rotate(mStack[mTop], -Math.PI/16, Y);
			Ears[0].Translate(mStack[mTop], 50,0,0);
			Ears[0].Scale(mStack[mTop], .3,1,1);
			Ears[0].Transform(mStack[mTop], Ears[0].getShape());
		pop();

		push();
			Ears[1].Rotate(mStack[mTop], Math.PI/16, Y);
			Ears[1].Translate(mStack[mTop], -50,0,0);
			Ears[1].Scale(mStack[mTop], .3,1,1);
			Ears[1].Transform(mStack[mTop], Ears[1].getShape());
		pop();
		
		push();
			Eyes[0].Translate(mStack[mTop], -16, 5, 40);
			Eyes[0].Transform(mStack[mTop], Eyes[0].getShape());
		pop();
					
		push();
			Eyes[1].Translate(mStack[mTop], 16, 5, 40);
			Eyes[1].Transform(mStack[mTop], Eyes[1].getShape());
		pop();
					
		dSurfaceNormal = new double[iHeight][iWidth][3];
		dVertexNormal = new double[iHeight][iWidth][3];
		dNormalized = new double[iHeight][iWidth][3];

		BtnAmbience = new Button(5,iHeight+5,60, 20);
		BtnAmbience.setValue(0);
		String sLinesLabels[] = {"Amb Off", "Amb On"};
		BtnAmbience.setLabel(sLinesLabels);
		
		SldR = new Slider(75, iHeight+5, 90, 20);
		SldR.setValue(0.7);
		SldR.setLabel("Red");
		
		SldG = new Slider(175, iHeight+5, 90, 20);
		SldG.setValue(0.7);
		SldG.setLabel("Green");

		SldB = new Slider(275, iHeight+5, 90, 20);
		SldB.setValue(0.7);
		SldB.setLabel("Blue");
		
		BtnShine = new Button(375, iHeight+5, 90, 20);
		BtnShine.setValue(5);
		String sShineLabels[] = {"P =  1","P =  2","P =  4","P =  8","P = 16","P = 32"};
		BtnShine.setLabel(sShineLabels);

		AddLight(-2,2,1,1,1,1);
//		AddLight(2,-3,1, .8,.8,.8);
//		AddLight(1,1,1,1,1,1);

		for (int k=0; k<3; k++)
			Triangle[k] = new VertexNode();
		for (int k=0; k<4; k++)
			Vertices[k] = new VertexNode();
		for (int k=0; k<4; k++)
		{
			Trap1[k] = new VertexNode();
			Trap2[k] = new VertexNode();
		}
	}
	
	public void DrawShade(Graphics g, ParamSurface Surface)
		//should send this a matrix of the xyz coords instead of the actual shape
		//this will help generalize it
		//also, change the shapes so that they store xyz in a 2D matrix
	{
		for (int i=1; i<EPSILON_INV; i++)
			//get the surface normals of the polygons
		{
			for (int j=0; j<EPSILON_INV; j++)
			{
				u[X] = Surface.getShape()[(i-1)%EPSILON_INV][j][X] - Surface.getShape()[i][j][X];
				u[Y] = Surface.getShape()[(i-1)%EPSILON_INV][j][Y] - Surface.getShape()[i][j][Y];
				u[Z] = Surface.getShape()[(i-1)%EPSILON_INV][j][Z] - Surface.getShape()[i][j][Z];
								
				v[X] = Surface.getShape()[i][(j+1)%EPSILON_INV][X] - Surface.getShape()[i][j][X];
				v[Y] = Surface.getShape()[i][(j+1)%EPSILON_INV][Y] - Surface.getShape()[i][j][Y];
				v[Z] = Surface.getShape()[i][(j+1)%EPSILON_INV][Z] - Surface.getShape()[i][j][Z];

				dSurfaceNormal[i-1][j] = CrossProduct(u,v);
			}
		}
			//the last row		
			for (int j=0; j<EPSILON_INV; j++)
			{
				u[X] = Surface.getShape()[EPSILON_INV-1][j][X] - Surface.getShape()[EPSILON_INV][j][X];
				u[Y] = Surface.getShape()[EPSILON_INV-1][j][Y] - Surface.getShape()[EPSILON_INV][j][Y];
				u[Z] = Surface.getShape()[EPSILON_INV-1][j][Z] - Surface.getShape()[EPSILON_INV][j][Z];
								
				v[X] = Surface.getShape()[EPSILON_INV-1][(j+1)%EPSILON_INV][X] - Surface.getShape()[EPSILON_INV][j][X];
				v[Y] = Surface.getShape()[EPSILON_INV-1][(j+1)%EPSILON_INV][Y] - Surface.getShape()[EPSILON_INV][j][Y];
				v[Z] = Surface.getShape()[EPSILON_INV-1][(j+1)%EPSILON_INV][Z] - Surface.getShape()[EPSILON_INV][j][Z];

				dSurfaceNormal[EPSILON_INV-1][j] = CrossProduct(u,v);
			}



		for (int i=1; i<EPSILON_INV; i++)
			//get the surface normals of the vertices by getting the average
			//of the surface normals of the polygons surrounding it
			//normalize the surface normals
		{
			for (int j=0; j<EPSILON_INV; j++)
			{
				dVertexNormal[i][j][X] = dSurfaceNormal[i-1][j][X] + 
										 dSurfaceNormal[i-1][Mod((j-1),EPSILON_INV)][X] +
										 dSurfaceNormal[i][Mod((j-1),EPSILON_INV)][X] + 
										 dSurfaceNormal[i][j][X];
													
				dVertexNormal[i][j][Y] = dSurfaceNormal[i-1][j][Y] + 
										 dSurfaceNormal[i-1][Mod((j-1),EPSILON_INV)][Y] +
										 dSurfaceNormal[i][Mod((j-1),EPSILON_INV)][Y] + 
										 dSurfaceNormal[i][j][Y];
													
				dVertexNormal[i][j][Z] = dSurfaceNormal[i-1][j][Z] + 
										 dSurfaceNormal[i-1][Mod((j-1),EPSILON_INV)][Z] +
										 dSurfaceNormal[i][Mod((j-1),EPSILON_INV)][Z] + 
										 dSurfaceNormal[i][j][Z];

				dNormalized[i][j] = Normalize(dVertexNormal[i][j]);
			}
		}

		for (int j=0; j<EPSILON_INV; j++)
		{
			dNormalized[0][j][X] = 0;
			dNormalized[0][j][Y] = 1;
			dNormalized[0][j][Z] = 0;
			
			dNormalized[EPSILON_INV][j][X] =  0;
			dNormalized[EPSILON_INV][j][Y] = -1;
			dNormalized[EPSILON_INV][j][Z] =  0;
		}

		for (int i=1; i<(EPSILON_INV+1); i++)
			//for each polygon
		{
			for (int j=0; j<(EPSILON_INV); j++)
			{
				
				Vertices[0].setXYZ(Surface.getShape()[i-1][j]);
				Vertices[0].setRGB(Shade(dNormalized[i-1][j]));
				
				Vertices[1].setXYZ(Surface.getShape()[i-1][Mod((j-1),EPSILON_INV)]);
				Vertices[1].setRGB(Shade(dNormalized[i-1][Mod((j-1),EPSILON_INV)]));
				
				Vertices[2].setXYZ(Surface.getShape()[i][Mod((j-1),EPSILON_INV)]);
				Vertices[2].setRGB(Shade(dNormalized[i][Mod((j-1),EPSILON_INV)]));
				
				Vertices[3].setXYZ(Surface.getShape()[i][j]);
				Vertices[3].setRGB(Shade(dNormalized[i][j]));
				
				for (int k=0; k<4; k++)
				{
					double dTemp[] = new double[3];
				
					//get px, py, pz
//					dTemp[X] = (focus * Vertices[k].getXYZ()[X]) / (focus - Vertices[k].getXYZ()[Z]);
//					dTemp[Y] = (focus * Vertices[k].getXYZ()[Y]) / (focus - Vertices[k].getXYZ()[Z]);

					dTemp[X] = (focus * Vertices[k].getXYZ()[X]) / (Vertices[k].getXYZ()[Z]);
					dTemp[Y] = (focus * Vertices[k].getXYZ()[Y]) / (Vertices[k].getXYZ()[Z]);
					dTemp[Z] = 1 / Vertices[k].getXYZ()[Z];
					
					//viewport
					dTemp[X] = dTemp[X] + iWidth/2;
					dTemp[Y] = dTemp[Y] + iHeight/2;
					
					//these vertices contain RGB values and px, py, pz
					Vertices[k].setXYZ(dTemp);
				}
				
				//first triangle
				Triangle[0].Copy(Vertices[0]);
				Triangle[1].Copy(Vertices[2]);
				Triangle[2].Copy(Vertices[1]);
				
				ScanConvert(g, Triangle);
				
				Triangle[0].Copy(Vertices[0]);
				Triangle[1].Copy(Vertices[3]);
				Triangle[2].Copy(Vertices[2]);
								
				ScanConvert(g, Triangle);
			}
		}
	}
		
	public void ScanConvert(Graphics g, VertexNode[] Triangle)
		//this takes a triangle, splits it into trapezoids
		//lerps to get individual pixel stuffs, etc, you know
	{
		//iTemp[0] has the max, iTemp[1] has the mid, iTemp[2] has the min
		iTopMidBot = MaxMin(Triangle);
		
		//we have three separate thingers so we have to make a new vertexnode
		t = (Triangle[iTopMidBot[MID]].getXYZ()[Y] - Triangle[iTopMidBot[BOT]].getXYZ()[Y]) / 
				(Triangle[iTopMidBot[TOP]].getXYZ()[Y] - Triangle[iTopMidBot[BOT]].getXYZ()[Y]);

		NewNode.Copy(VertexLerp(t, Triangle[iTopMidBot[BOT]], Triangle[iTopMidBot[TOP]]));
		
		if (NewNode.getXYZ()[X] < Triangle[iTopMidBot[MID]].getXYZ()[X])
		{
			Trap1[0].Copy(Triangle[iTopMidBot[TOP]]);
			Trap1[1].Copy(NewNode);
			Trap1[2].Copy(Triangle[iTopMidBot[MID]]);
			Trap1[3].Copy(Triangle[iTopMidBot[TOP]]);

			Trap2[0].Copy(NewNode);
			Trap2[1].Copy(Triangle[iTopMidBot[BOT]]);
			Trap2[2].Copy(Triangle[iTopMidBot[BOT]]);
			Trap2[3].Copy(Triangle[iTopMidBot[MID]]);
		}
		else
		{
			Trap1[0].Copy(Triangle[iTopMidBot[TOP]]);
			Trap1[1].Copy(Triangle[iTopMidBot[MID]]);
			Trap1[2].Copy(NewNode);
			Trap1[3].Copy(Triangle[iTopMidBot[TOP]]);

			Trap2[0].Copy(Triangle[iTopMidBot[MID]]);
			Trap2[1].Copy(Triangle[iTopMidBot[BOT]]);
			Trap2[2].Copy(Triangle[iTopMidBot[BOT]]);
			Trap2[3].Copy(NewNode);
		}				

		DrawTrapezoid(Trap1);
		DrawTrapezoid(Trap2);
	}
	
	public void DrawPoly(Graphics g, VertexNode[] Poly)
	{
		int x[] = new int[Poly.length];
		int y[] = new int[Poly.length];
		for (int i=0; i< Poly.length; i++)
		{
			x[i] = (int)Poly[i].getXYZ()[X];
			y[i] = (int)Poly[i].getXYZ()[Y];
		}
		Color temp = new Color((int)(255*Poly[0].getRGB()[R]), (int)(255*Poly[0].getRGB()[G]), (int)(255*Poly[0].getRGB()[B]));
		g.setColor(temp);
		
		if (BtnAmbience.getValue()==0)
			g.fillPolygon(x, y, Poly.length);
		else
			g.drawPolygon(x, y, Poly.length);
	}
	
	public void DrawTrapezoid(VertexNode[] Trap)
	{
		int top = (int)Math.ceil(Trap[0].getXYZ()[Y]);
		int bottom = (int)Math.ceil(Trap[1].getXYZ()[Y]);
		
		double ti, tj;
		for (int i = bottom; i < top; i++)
		{
			//find t between absolute top and current scanline
			//interpolate to get left and right
			ti = (i - Trap[1].getXYZ()[Y]) / (Trap[0].getXYZ()[Y] - Trap[1].getXYZ()[Y]);
			
			LeftNode.Copy(VertexLerp(ti, Trap[1], Trap[0]));
			RightNode.Copy(VertexLerp(ti, Trap[2], Trap[3]));
			//do it again between left and right
			
			int left = (int)Math.ceil(LeftNode.getXYZ()[X]);
			int right = (int)Math.ceil(RightNode.getXYZ()[X]);
			
			for (int j=left; j<right; j++)
			{
				VertexNode PixelNode = new VertexNode();;
				tj = (j - LeftNode.getXYZ()[X]) / (RightNode.getXYZ()[X] - LeftNode.getXYZ()[X]);
					
				PixelNode.Copy(VertexLerp(tj, LeftNode, RightNode));
				
				if ((i >= 0) && (i<iHeight) && (j>=0) && (j<iWidth))
					if ((PixelNode.getXYZ()[Z] < ZBuffer[i][j]))
					{
						FrameBuffer[i][j] = pack((int)(255*PixelNode.getRGB()[R]),(int)(255*PixelNode.getRGB()[G]),(int)(255*PixelNode.getRGB()[B]));
						ZBuffer[i][j] = PixelNode.getXYZ()[Z];
					}
			}			
		}		
	}
	
	public VertexNode VertexLerp(double t, VertexNode A, VertexNode B)
	{
		VertexNode NewNode = new VertexNode();
		double XYZ[] = new double[3];
		double RGB[] = new double[3];

		XYZ[X] = lerp(t, A.getXYZ()[X], B.getXYZ()[X]);
		XYZ[Y] = lerp(t, A.getXYZ()[Y], B.getXYZ()[Y]);
		XYZ[Z] = lerp(t, A.getXYZ()[Z], B.getXYZ()[Z]);
		
		RGB[R] = lerp(t, A.getRGB()[R], B.getRGB()[R]);
		RGB[G] = lerp(t, A.getRGB()[G], B.getRGB()[G]);
		RGB[this.B] = lerp(t, A.getRGB()[this.B], B.getRGB()[this.B]);
		
		NewNode.setXYZ(XYZ);
		NewNode.setRGB(RGB);
		
		return NewNode;	
	}
	
	public double[] Shade(double[] dNormal)
	//only do specular or diffuse, not both
	{
		double RGB[] = {0,0,0};
		
//		RGB = Gouraud(RGB, dNormal);
		RGB = Phong(RGB, dNormal);

		return RGB;
	}
	
	public double[] Gouraud(double[] RGB, double[] dNormal)
	{
		return AddDiffuse(AddAmbience(RGB), dNormal);
	}
//	
	public double[] Phong(double[] RGB, double[] dNormal)
	{
		if (BtnAmbience.getValue() == 0)
			RGB = AddAmbience(RGB);
//		RGB = AddDiffuse(RGB, dNormal);
		RGB = AddSpecular(RGB, dNormal);
		return RGB;
	}
	
	public double[] AddAmbience(double[] RGB)
	{
		return VectorAddVector(RGB, dAmbience);
	}
	
	public double[] AddDiffuse(double[] RGB, double[] dNormal)
	{
		double dTempRGB[] = RGB;
		for (int i=0; i<iNumLights; i++)
			for (int j=0; j<3; j++)
				dTempRGB[j] += dDiffuse[j] * DotProduct(dNormal, (dLight[i]));
		return dTempRGB;
	}
	
	public double[] AddSpecular(double RGB[], double[] dNormal)
	//do this so it does diffuse as well
	{
		double dTempRGB[] = RGB;
		double dSpecular = 1;
		double dDiffusion;
		
		for(int i=0; i<iNumLights; i++)
		{
			dDiffusion = Math.min(1,DotProduct(dNormal, Normalize(dLight[i])));
							
			dSpecular = Math.min(0,DotProduct(getReflection(dNormal),Normalize(dLight[i])));
			dSpecular = Math.pow(dSpecular, 32);
			
			dTempRGB[R] += dLight[i][R+3] *( dDiffusion*SldR.getValue() + dSpecular*dSpecularClr[R]);
			dTempRGB[G] += dLight[i][G+3] *( dDiffusion*SldG.getValue() + dSpecular*dSpecularClr[G]);
			dTempRGB[B] += dLight[i][B+3] *( dDiffusion*SldB.getValue() + dSpecular*dSpecularClr[B]);

		}
		return dTempRGB;
	}
	
	public double[] AddBoring (double[] RGB, double[] dNormal)
	{
		for(int i=0; i<RGB.length; i++)
			RGB[i] = 0.5 + 0.5 * dNormal[i];
		return RGB;
	}
	
	public void AddLight(double xNew, double yNew, double zNew, double rNew, double gNew, double bNew)
	{
		dLight[iNumLights][X] = xNew;
		dLight[iNumLights][Y] = yNew;
		dLight[iNumLights][Z] = zNew;
		
		dLight[iNumLights][3] = rNew;
		dLight[iNumLights][4] = gNew;
		dLight[iNumLights][5] = bNew;
		iNumLights++;
	}
	
	public double[] getReflection(double[] dNormal)
	{
		double dTemp[] = new double[3];
				
		dTemp[X] = (2 * DotProduct(dNormal, Normalize(dEye)) * dNormal[X] - Normalize(dEye)[X]);
		dTemp[Y] = (2 * DotProduct(dNormal, Normalize(dEye)) * dNormal[Y] - Normalize(dEye)[Y]);
		dTemp[Z] = (2 * DotProduct(dNormal, Normalize(dEye)) * dNormal[Z] - Normalize(dEye)[Z]);
		
		return dTemp;
	}
	
	public double[] VectorAddVector(double[] dVector1, double[] dVector2)
	{
		for (int i=0; i<3; i++)
			dVector1[i] += dVector2[i];
		return dVector1;
	}
	int i=0;
	
	public double[] VectorMultiplyConstant(double dMult, double[] dVector)
	{
		for (int i=0; i<3; i++)
			dVector[i] *= dMult;
		return dVector;
	}

	public double[] VectorMultiplyVector(double[] dVectorA, double[] dVectorB)
	{
		double[] dVectorC = new double[3];
		for (int i=0; i<3; i++)
		{
			dVectorC[i] = dVectorA[i]*dVectorB[0] + dVectorA[i]*dVectorB[1] + dVectorA[i]*dVectorB[2];
			System.out.println(dVectorA[i]);
		}	
		return dVectorC;
	}
	
	public double[] Normalize(double[] dVector)
	{
		double dLength = Math.sqrt(DotProduct(dVector, dVector));
		dVector[X] = dVector[X] / dLength;
		dVector[Y] = dVector[Y] / dLength;
		dVector[Z] = dVector[Z] / dLength;
		
		return dVector;
	}
	
	public double[] CrossProduct(double[] u, double[] v)
	{
		double dCross[] = new double[3];
		dCross[X] = u[Y]*v[Z] - u[Z]*v[Y];
		dCross[Y] = u[Z]*v[X] - u[X]*v[Z];
		dCross[Z] = u[X]*v[Y] - u[Y]*v[X];
		
		return dCross;
	}
	
	public double DotProduct(double[] u, double[] v)
	{
		return (u[X]*v[X] + u[Y]*v[Y] +u[Z]*v[Z]);
	}
	
	public double lerp(double t, double a, double b)
	{
		return a + t * (b - a);
	}
	
	public void CameraView(int iSign, ParamSurface Surface)
	{
		if (iSign == PRE_DRAW)
		{
			Matrix3D.IdentityMatrix(mStack[mTop]);
			push();	
				Surface.Rotate(mStack[mTop], -CameraX, Y);
				Surface.Transform(mStack[mTop], Surface.getShape());
			pop();
			push();
				Surface.Rotate(mStack[mTop], CameraY, X);
				Surface.Transform(mStack[mTop], Surface.getShape());
			pop();
		}
		else if (iSign == POST_DRAW)
		{
			Matrix3D.IdentityMatrix(MatrixTemp);
			push();
				Surface.Rotate(mStack[mTop], -CameraY, X);
				Surface.Transform(mStack[mTop], Surface.getShape());
			pop();
			push();
				Surface.Rotate(mStack[mTop], CameraX, Y);
				Surface.Transform(mStack[mTop], Surface.getShape());
			pop();
		}
	}
	
	public int[] MaxMin(VertexNode[] Triangle)
	{
		int iIndexMax = 0, iIndexMin = 0, iIndexMid = 0;
		double dMax, dMin;
		
		dMax = Math.max(Triangle[0].getXYZ()[Y], Triangle[1].getXYZ()[Y]);
		dMax = Math.max(dMax, Triangle[2].getXYZ()[Y]);

		dMin = Math.min(Triangle[0].getXYZ()[Y], Triangle[1].getXYZ()[Y]);
		dMin = Math.min(dMin, Triangle[2].getXYZ()[Y]);
		
		for (int i=0; i<Triangle.length; i++)
		{
			if (Triangle[i].getXYZ()[Y] == dMax)
				iIndexMax = i;
			else if (Triangle[i].getXYZ()[Y] == dMin)
				iIndexMin = i;
			else
				iIndexMid = i;
		}
		
		int iTemp[] = {iIndexMax, iIndexMid, iIndexMin};
		return iTemp;
	}

	public void push()
	{
		Matrix3D.Copy(mStack[mTop], mStack[mTop+1]);
		mTop++;
	}
	
	public void pop()
	{
		mTop--;
	}
	
	public int Mod(int iNum, int iMod)
	{
		if(iNum > 0)
			iNum = iNum%iMod;
		else
			while (iNum < 0)
				iNum += iMod;
		return iNum;		
	}

	public boolean mouseDrag(Event e, int x, int y)
	{
		SldR.drag(x, y);
		SldG.drag(x, y);
		SldB.drag(x, y);
		{		
			x = iX - x;
			y = iY - y;
			CameraX = (CameraX + ((double)x / 360.0))%(2*Math.PI);
			CameraY = (CameraY + ((double)y / 360.0))%(2*Math.PI);
		}
		return true;	
	}

	public boolean mouseUp(Event e, int x, int y)
	{
		BtnAmbience.up(x,y);
		BtnShine.up(x,y);
		return true;
	}
	
	public boolean mouseDown(Event e, int x, int y)
	{
		BtnAmbience.down(x,y);
		BtnShine.down(x,y);
		SldR.down(x, y);
		SldG.down(x, y);
		SldB.down(x, y);
		iX = x;
		iY = y;
		return true;			
	}
}
