/*********************************************************************************************/
/*                                                                                           */
/*  Java 3D Engine Basics Tutorials - Tut x.x                                                */
/*  Auth: bkenwright@xbdev.net                                                               */
/*                                                                                           */
/*                                                                                           */
/*********************************************************************************************/

import java.awt.image.*;
import java.awt.*;
import java.applet.*;

/*********************************************************************************************/
/*                                                                                           */
/*  class Triangle                                                                           */
/*  We need a way of representing our triangles...so that we can have a number of them       */
/*  and not have to deal with large numbers of arrays of values and things.  By putting      */
/*  them in a class each triangle is all nice and tidy.                                      */
/*                                                                                           */
/*********************************************************************************************/

class Triangle
{
   float x0, y0, z0;
   float x1, y1, z1;
   float x2, y2, z2;
   Color c;
   
   // Average z
   float avg_z;
   
   // Transformed coords
   float tx0, ty0, tz0;
   float tx1, ty1, tz1;
   float tx2, ty2, tz2;
   Color tc;
   
   
   public Triangle(){} // default constructor
   public Triangle( float x0_, float y0_, float z0_,
                    float x1_, float y1_, float z1_,
                    float x2_, float y2_, float z2_,
                    Color c_  )
   {
       tx0=x0=x0_; ty0=y0=y0_; tz0=z0=z0_;
       tx1=x1=x1_; ty1=y1=y1_; tz1=z1=z1_;
       tx2=x2=x2_; ty2=y2=y2_; tz2=z2=z2_;
       tc = c = c_;
   }// End of Triangle(..) constructor
  
   public void Translate( float x, float y, float z )
   {
   			tx0 += x;  ty0 += y;  tz0 += z;
   			tx1 += x;  ty1 += y;  tz1 += z;
   			tx2 += x;  ty2 += y;  tz2 += z;
   }// End of Translate(..)
   
   public void ResetCoords()
   {
     tx0=x0; ty0=y0; tz0=z0;
     tx1=x1; ty1=y1; tz1=z1;
     tx2=x2; ty2=y2; tz2=z2;
     
   }// End ResetCoords()
   
   public void RotateX( float angle )
   {
      float cosA = (float)Math.cos(angle);
      float sinA = (float)Math.sin(angle);
      
      float temp_x, temp_y, temp_z;
      
      temp_x =  tx0;
      temp_y =  ty0*cosA - tz0*sinA;
      temp_z =  ty0*sinA + tz0*cosA;
      tx0 = temp_x;  ty0 = temp_y;  tz0 = temp_z;
      
      temp_x =  tx1;
      temp_y =  ty1*cosA - tz1*sinA;
      temp_z =  ty1*sinA + tz1*cosA;
      tx1 = temp_x;  ty1 = temp_y;  tz1 = temp_z;
      
      temp_x =  tx2;
      temp_y =  ty2*cosA - tz2*sinA;
      temp_z =  ty2*sinA + tz2*cosA;
      tx2 = temp_x;  ty2 = temp_y;  tz2 = temp_z;
      
   }// End RotateX(..)
   
  public void RotateY( float angle )
   {
      float cosA = (float)Math.cos(angle);
      float sinA = (float)Math.sin(angle);
      
      float temp_x, temp_y, temp_z;
      
      temp_x =   tx0*cosA + tz0*sinA;
      temp_y =   ty0;
      temp_z =  -tx0*sinA + tz0*cosA;
      tx0 = temp_x;  ty0 = temp_y;  tz0 = temp_z;
      
      temp_x =   tx1*cosA + tz1*sinA;
      temp_y =   ty1;
      temp_z =  -tx1*sinA + tz1*cosA;
      tx1 = temp_x;  ty1 = temp_y;  tz1 = temp_z;
      
      temp_x =   tx2*cosA + tz2*sinA;
      temp_y =   ty2;
      temp_z =  -tx2*sinA + tz2*cosA;
      tx2 = temp_x;  ty2 = temp_y;  tz2 = temp_z;
			 
   }// End RotateY(..)
   
   public void RotateZ( float angle )
   {
      float cosA = (float)Math.cos(angle);
      float sinA = (float)Math.sin(angle);
      
      float temp_x =  tx0*cosA - ty0*sinA;
      float temp_y =  tx0*sinA + ty0*cosA;
      float temp_z =  tz0;
      tx0 = temp_x;  ty0 = temp_y;  tz0 = temp_z;
      
      temp_x =  tx1*cosA - ty1*sinA;
      temp_y =  tx1*sinA + ty1*cosA;
      temp_z =  tz1;
      tx1 = temp_x;  ty1 = temp_y;  tz1 = temp_z;
      
      temp_x =  tx2*cosA - ty2*sinA;
      temp_y =  tx2*sinA + ty2*cosA;
      temp_z =  tz2;
      tx2 = temp_x;  ty2 = temp_y;  tz2 = temp_z;
   }// End RotateZ(..)
   
   
}// End of class triangle



/*********************************************************************************************/
/*                                                                                           */
/*  Program Entry Point                                                                      */
/*                                                                                           */
/*********************************************************************************************/

public class render_order extends Applet 
{
  Image myImage;
  Graphics offScreen;
  
  int mouse_x_left=0;
  int mouse_y_left=0;
  
  int mouse_x_right=0;
  int mouse_y_right=0;
  


  
  int m_NumTris = 2;
  Triangle[] m_tri;

  public void init() 
  {
  	 Dimension appletSize = this.getSize();
	   myImage = createImage(appletSize.width,appletSize.height);
   	 offScreen = myImage.getGraphics();
   	 
   	 m_tri = new Triangle[m_NumTris];
   	 m_tri[0] = new Triangle(-1.0f, -1.0f, -0.5f,
			                       0.0f,  1.0f,  -0.5f,
													   1.0f, -1.0f,  -0.5f,
													   Color.red);
													  
		 m_tri[1] = new Triangle(-1.0f, -1.0f, 0.5f,
			                       0.0f,  1.0f, 0.5f,
													   1.0f, -1.0f, 0.5f,
													   Color.blue);
													 
   	 
  }// End of init(..)

  public void RenderTriangle(Triangle t)
  {
     ScreenPerspective( myImage,
                         t.tx0, t.ty0, t.tz0,
                         t.tx1, t.ty1, t.tz1,
                         t.tx2, t.ty2, t.tz2,
                         t.tc);
  }
  
  void SortRenderOrder(Triangle t[], int iNumTris)
  {
     // First lets generate an average z for each triangle
     float average_z;
     for( int i=0; i<iNumTris; i++ )
     {
        average_z = t[i].tz0 + t[i].tz1 + t[i].tz2;
        average_z /= 3.0f;
        t[i].avg_z = average_z;
     }// End for loop
     
     // Now lets do a bruit force sort, so each triangle is in order
     // of our average z
     for( int outer=0; outer<iNumTris; outer++ )
     {
        for( int inner=0; inner<iNumTris; inner++ )
        {
            if( t[outer].avg_z > t[inner].avg_z )
            {
               Triangle temp = t[outer];
               t[outer] = t[inner];
               t[inner] = temp;
            }// End if(..)
        }// End inner for loop
     }// End outer for loop
     
  }// SortRenderOrder(..)
  
  void RenderAllTriangles(Triangle t[], int iNumTris)
  {
     for(int i=0; i<iNumTris; i++)
     {
        RenderTriangle( t[i] );
     }// End for loop
  }// End of RenderAllTriangles(..)
  
  void ResetAllCoords(Triangle t[], int iNumTris)
  {
  	 for(int i=0; i<iNumTris; i++)
     {
        t[i].ResetCoords();  
     }// End for loop
  }// End of ResetAllCoords(..)
  
  float m_AngleX = 0.0f;
  float m_AngleY = 0.0f;
  float m_AngleZ = 0.0f;
  
  public void paint(Graphics g) 
  {
     // Clear screen
		 offScreen.setColor(Color.white);
		 offScreen.fillRect(0,0,this.getSize().width,this.getSize().height);
			
			Dimension appletSize = this.getSize();
		  int width  = appletSize.width;
		  int height = appletSize.height;


      ResetAllCoords( m_tri, m_NumTris );		      

      // Lets rotate one of the tiangles
      m_tri[0].RotateY(m_AngleY);
      m_tri[1].RotateY(m_AngleY);
      m_AngleY += 0.2f;
      
      m_tri[0].Translate(0.0f, 0.0f, 3.0f);
      m_tri[1].Translate(0.0f, 0.0f, 3.0f);

      SortRenderOrder( m_tri, m_NumTris );
			RenderAllTriangles( m_tri, m_NumTris );


		             
  	 //offScreen.drawLine( mouse_x_left+10,  mouse_y_left+10,
		 //            mouse_x_right+10, mouse_y_right+10);
  	 
	   g.drawImage(myImage,0,0,this);
  }// End of paint(..)


  
  public void ScreenPerspective( Image image,
	                               float x0, float y0, float z0,
	                               float x1, float y1, float z1,
	                               float x2, float y2, float z2,
	                               Color c )
  {
			    // This is where we'll put our final values that we calculate
			    // we only have an x and a y, as we'll convert our x,y and z into
			    // a screen coordinate square which has all the sizing and things
			    // done to it already

		
		      // Get the screen size of our applet
		      Dimension appletSize = this.getSize();
		      int width  = appletSize.width;
		      int height = appletSize.height;
		
		      
		      //if( (z0<1) && (z1<1) && (z2<1) )
		      //   return;
		         
		      //if( z0 < 1 ) z0 = 1;
		      //if( z1 < 1 ) z1 = 1;
		      //if( z2 < 1 ) z2 = 1;
		      
		         
		      // Something worth noting - we have a pole at z=0...as when we
		      // create a perspective ..which is x_per = d*x/z for example, if
		      // z is 0 or very close to zero, we'll get an infinit number...so
		      // if we have a z value less than 1 we'll just round z to 1 and it
		      // will get clipped later down the line.
		      
		      // Whats this pTan stuff for?...well we can't just clip the z value
		      // to 1...as what if its on a really steep angle...so what we do, is
		      // we work out a new value for x and z and then clip it to the near
		      // clip plane :)
		      
		      // Add some clipping code so Z is clipped to the near view plane
		      // ++ //
		
					// From x,y,z to x,y for the screen.
					// -1-Perspective Conversion

		      // Focul point of 1 (Normalised view plane 90 degrees)
					float d = 1; 
					float Perspective_x0 = -d*x0 / z0;
					float Perspective_x1 = -d*x1 / z1;
					float Perspective_x2 = -d*x2 / z2;
					
					// its between -1 to 1...so we need to scale it to 0 to 1...and scale to
					// the size of the screen.
					Perspective_x0 += 1;
					Perspective_x1 += 1;
					Perspective_x2 += 1; 
					// So now its between 0 and 2 and not -1 to 1;
					Perspective_x0 *= 0.5;
					Perspective_x1 *= 0.5;
					Perspective_x2 *= 0.5;
					// -2- Now we scale it to the size of the screen
					float Screen_x0 = (width-1)*Perspective_x0;
					float Screen_x1 = (width-1)*Perspective_x1;
					float Screen_x2 = (width-1)*Perspective_x2;
					
					// Add some clipping to make sure its on the screen here
					// ++ //
		              
					// Now we do the same for the y value to convert it
		      // to screen coordinates … its exactly the same as for the x above,
		      // but I've combined all the stages into a single line for each
		      // one

					// Note y1 is the top of square, and y2 is the bottom of our square
					
					float Screen_y0 = ((-d*y0 / z0)+1)*0.5f *(height-1);
					float Screen_y1 = ((-d*y1 / z1)+1)*0.5f *(height-1);
					float Screen_y2 = ((-d*y2 / z2)+1)*0.5f *(height-1);

          // Debug information
					//System.out.println( "x0: " + Screen_x0 + "  y0: " + Screen_y0 );
					//System.out.println( "x1: " + Screen_x1 + "  y1: " + Screen_y1 );
					//System.out.println( "x2: " + Screen_x2 + "  y2: " + Screen_y2 );
					//System.out.println("");

					// Render our data
			    drawTriangle( image,
			                  Screen_x0, Screen_y0,
			                  Screen_x1, Screen_y1,
			                  Screen_x2, Screen_y2,
			                  c );            

  }// End ScreenPerspective(...)


	public void drawTriangle( Image image,
	                          float x0, float y0,
	                          float x1, float y1,
	                          float x2, float y2,
	                          Color c )
	{
	   //First we sort our points out into y order...where it goes
	   // 0
	   // 2
	   // 1
	   float t; // temp variable
	   if( y1 < y0 )
	   {
	      t  = y0;
	      y0 = y1;
	      y1 = t;
	      
	      t  = x0;
	      x0 = x1;
	      x1 = t;
	   }
		 
		 if( y2 < y0 )
	   {
	      t  = y0;
	      y0 = y2;
	      y2 = t;
	      
	      t  = x0;
	      x0 = x2;
	      x2 = t;
	   }  
		 
		if( y1 < y2 )
	   {
	      t  = y1;
	      y1 = y2;
	      y2 = t;
	      
	      t  = x1;
	      x1 = x2;
	      x2 = t;
	   }     
	   
	   // Next part...
	   // Render top of triangle
	   float x_left  = x0;
	   float x_right = x0;
	   
	   float dy_top    = y2 - y0;
	   float dy_bottom = y1 - y2;
	   
	   float dx_left  = x2 - x0;   // to the middle then we change it
	   float dx_right = x1 - x0;   // all the way to the bottom :)
	   
	   //int dx_left_dir  = dx_left  < 0 ? -1 : 1;
	   //int dx_right_dir = dx_right < 0 ? -1 : 1;
	   
	   //dx_left  *= dx_left_dir;
	   //dx_right *= dx_right_dir;
	   
	   //dx_left  = dx_left  / dy_top;
	   //dx_right = dx_right / dy_top;
	   
		 dx_left  = (x2 - x0) / (y2 - y0);
	   dx_right = (x1 - x0) / (y1 - y0);
	   
	   int s = 0;
	   if( dx_left > dx_right )
	   {
	      t = dx_left;
	      dx_left = dx_right;
	      dx_right = t;
	      s=1;
	   }//End if
	   
	   for(float y=y0; y<y2; y++)
	   {
	        for(float x=x_left; x<x_right; x++)
	        {
	   			   setPixel(image, (int)x, (int)y, c );
		  		}// End inner for loop
		  		x_left  += dx_left;
		  		x_right += dx_right;
	   
	   }// End outer for loop
	

	   // Now for the bottom of the triangle
		 if( s == 0 )
	   {
	      dx_left = (x1 - x2) / (y1 - y2);
	   }
	   else
	   {
	      dx_right = (x1 - x2) / (y1 - y2);
	   }
	   
	   
	   for(float y=y2; y<y1; y++)
	   {
	      for(float x=x_left; x<x_right; x++)
	      {
	         setPixel(image, (int)x, (int)y, c);
	      }
	      x_left  += dx_left;
	      x_right += dx_right;
	   }// End outer for loop
	
	
	}//End of drawTriangle(..)

  public void drawLine(Image image, int x0, int y0, int x1, int y1, Color c)
  {
  		float sx = (float)x0;
  		float sy = (float)y0;
  		float ex = (float)x1;
  		float ey = (float)y1;
  		
  		float dx = ex-sx;
  		float dy = ey-sy;	
  		
  		int xdir = dx > 0 ? 1 : -1;
  		int ydir = dy > 0 ? 1 : -1;
  		
  		dx = dx*xdir;
  		dy = dy*ydir;
  		
  		
  		float incx =  dx / dy;
  		float incy =  dy / dx;
  		
  		incx *= xdir;
  		incy *= ydir;
  		
  		if( dy > dx )
  		{
		  		for( int y=0; y<dy; y++)
		  		{
		  				setPixel(image, (int)sx, (int)sy, Color.blue );
		  				sx += incx;
		  				sy += ydir;

		  		}// End for loop(..)
  		}
  		else
  		{
  		    for( int x=0; x<dx; x++)
		  		{
		  				setPixel(image, (int)sx, (int)sy, Color.blue );
		  				sy += incy;
		  				sx += xdir;

		  		}// End for loop(..)
  		}
  		
  }//End of drawline(..)
  
  public void setPixel(Image image, int x, int y, Color color ) 
  {
     Graphics g = image.getGraphics( );
     g.setColor( color );
     g.fillRect( x, y, 1, 1 );
     g.dispose( );
  }// End of setPixel(..)


/*********************************************************************************************/
/*                                                                                           */
/*  Capture event when mouse is pressed                                                      */
/*    Parameters:                                                                            */
/*    evt is The event...                                                                    */
/*    x is the x mouse position                                                              */
/*    y is the y mouse position                                                              */
/*    return whether or not we handled the event                                             */
/*                                                                                           */
/*********************************************************************************************/

	public boolean mouseDown(Event evt, int x, int y)
  {
  	  if(evt.id == Event.MOUSE_DOWN)
      {
          // Right mouse button
      		if( evt.metaDown() == true )
      		{
      				System.out.println( "Right MouseDown" );
      				mouse_x_right = x;
     					mouse_y_right = y;
     					
     					//m_x0 = x;
     					//m_y0 = y;
      		}
      		else // right mouse button
      		{
      				System.out.println( "Left MouseDown" );
      				mouse_x_left = x;
     					mouse_y_left = y;
     					
     					//m_x1 = x;
     					//m_y1 = y;
      		}
      }// End if
    
		 repaint();
		 return true;
	}// End of mouseDown(..)

/*********************************************************************************************/
/*                                                                                           */
/*  Capture mouse release                                                                    */
/*    Parameters:                                                                            */
/*    evt is The event...                                                                    */
/*    x is the x mouse position                                                              */
/*    y is the y mouse position                                                              */
/*    return whether or not we handled the event                                             */
/*                                                                                           */
/*********************************************************************************************/

	public boolean mouseUp(Event evt, int x, int y)
	{

		return false;
	}// End of mouseUp(..)
	
/*********************************************************************************************/
/*                                                                                           */
/*  Capture mouse drags                                                                      */
/*    Parameters:                                                                            */
/*    evt is The event...                                                                    */
/*    x is the x mouse position                                                              */
/*    y is the y mouse position                                                              */
/*    return whether or not we handled the event                                             */
/*                                                                                           */
/*********************************************************************************************/

	public boolean mouseDrag(Event evt, int x, int y)
	{
  	return true;
	}// End of mouseDrag(..)


}// End of our Applet


