/**
 * Lesson9.java
 *
 * Author: Darren Hodges
 * Date: 21/12/1999
 *
 * Port of the NeHe OpenGL Tutorial (Lesson 9: "Moving Bitmaps In 3D Space")
 * to Java using the GL4Java interface to OpenGL.
 *
 */


import java.applet.*;
import java.awt.*;
import java.awt.event.*;

//GL4Java classes
import gl4java.GLContext;
import gl4java.awt.GLAnimCanvas;
import gl4java.utils.textures.*;


public class Lesson9 extends Applet
{
	//Our rendering canvas
   	//We are using GLAnimCanvas because we want the canvas
   	//to be constantly redrawn
    renderCanvas canvas = null;


	/**
     * void init()
     *
     * Initialise the applet.
     */
	public void init()
	{   
        //We will use BorderLayout to layout the applet components
        setLayout(new BorderLayout());
        
        //Create our canvas and add it to the center of the applet
        canvas = new renderCanvas(getSize().width, getSize().height);
	canvas.requestFocus();
        add("Center", canvas);
	}
	
	
	/**
     * void start()
     *
     * Start the applet.
     */
	public void start()
    {
        //Start animating the canvas
        canvas.start();
    }

	
	/**
     * void stop()
     *
     * Stop the applet.
     */
    public void stop()
    {
        //Stop animating the canvas
        canvas.stop();
    }
    
    
    /**
     * void destroy()
     *
     * Destroy the applet.
     */
    public void destroy()
    {
        //Stop animating the canvas
        canvas.stop();
        //Destroy the canvas
        canvas.destroy();
    }



    private class renderCanvas extends GLAnimCanvas
    	implements KeyListener, MouseListener
    {   
    	boolean twinkle = true; //Twinkling stars
    	boolean tp = false; //T Pressed?
    	final int NUM = 50; //NUMber of stars to draw
    	
    	Star[] star = new Star[NUM]; //Array of stars
    	
    	float zoom = -15.0f; 	//Viewing Distance Away From Stars
		float tilt = 90.0f;	 	//Tilt The View
		float spin = 0.0f;		//Spin Twinkling Stars

		int loop = 0; //General loop Variable
	
		int[] texture = new int[1];	//Storage For One Texture
		
		
        /**
         * renderCanvas(int w, int h)
         *
         * Constructor.
         */
        public renderCanvas(int w, int h)
        {
            super(w, h);
            
            //Registers this canvas to process keyboard events
        	addKeyListener(this);
                addMouseListener(this);    
        }
    
        
        /**
         * void preInit()
         *
         * Called just BEFORE the GL-Context is created.
         */
        public void preInit()
        {
            //We want double buffering
            doubleBuffer = true;
            //But we dont want stereo view
            stereoView = false;
        }
    
    
        /**
         * void LoadGLTextures()
         *
         * Load textures.
         */
        public void LoadGLTextures()
    	{
    		PngTextureLoader texLoader = new PngTextureLoader(gl, glu);
    		texLoader.readTexture(getCodeBase(), "data/star.png");
    		
    		if(texLoader.isOk())
    		{
				//Create Texture
				gl.glGenTextures(1, texture);
				gl.glBindTexture(GL_TEXTURE_2D, texture[0]);
			
				gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
				gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			
				gl.glTexImage2D(GL_TEXTURE_2D,
						 		0,
						 		3,
						 		texLoader.getImageWidth(),
						 		texLoader.getImageHeight(),
						 		0,
						 		GL_RGB,
						 		GL_UNSIGNED_BYTE,
						 		texLoader.getTexture());
    		}
    	}
    	
        
        /**
         * void init()
         *
         * Called just AFTER the GL-Context is created.
         */
        public void init()
        {
            //Load The Texture(s)
            LoadGLTextures();
			//Enable Texture Mapping
			gl.glEnable(GL_TEXTURE_2D);
            
            //This Will Clear The Background Color To Black
			gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

			//Enables Clearing Of The Depth Buffer
			gl.glClearDepth(1.0);
			
			//Enables Smooth Color Shading
			gl.glShadeModel(GL_SMOOTH);
			
			//Select The Projection Matrix
			gl.glMatrixMode(GL_PROJECTION);
			
			//Reset The Projection Matrix
			gl.glLoadIdentity();
			
			//Calculate The Aspect Ratio Of The Window
			glu.gluPerspective(45.0f, (float)getSize().width / (float)getSize().height, 0.1f, 100.0f);
			//Select The Modelview Matrix
			gl.glMatrixMode(GL_MODELVIEW);
			
			//Set The Blending Function For Translucency
			gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE);
			//Enable Blending
			gl.glEnable(GL_BLEND);
			
			//Create A Loop That Goes Through All The Stars
			for(loop = 0; loop < NUM; loop++)
			{
				star[loop] = new Star();
				star[loop].angle = 0.0f; //Start All The Stars At Angle Zero
				star[loop].dist = ((float)(loop) / NUM) * 5.0f; //Calculate Distance From The Center
				star[loop].r = (byte)(256 * Math.random()); //Give star[loop] A Random Red Intensity
				star[loop].g = (byte)(256 * Math.random()); //Give star[loop] A Random Green Intensity
				star[loop].b = (byte)(256 * Math.random()); //Give star[loop] A Random Blue Intensity
			}
        }
    
    
        /**
         * void destroy()
         *
         * Destroy the canvas.
         */
        public void destroy()
        {
            //Destroy the GLContext
            cvsDispose();
        }
    
		
		/**
		 * void reshape(int width, int height)
		 *
		 * Called after the first paint command.
		 */
        public void reshape(int width, int height)
        {
            //Reset The Current Viewport And Perspective Transformation
			gl.glViewport(0, 0, width, height);

			//Select The Projection Matrix
			gl.glMatrixMode(GL_PROJECTION);
			//Reset The Projection Matrix
			gl.glLoadIdentity();
			//Calculate The Aspect Ratio Of The Window
			glu.gluPerspective(45.0f, (float)getSize().width / (float)getSize().height, 0.1f, 100.0f);
			//Select The Modelview Matrix
			gl.glMatrixMode(GL_MODELVIEW);
        }

        
        /**
         * void display()
         *
         * Draw to the canvas.
         */
        public void display()
        {
            //Ensure GL is initialised correctly
            if (glj.gljMakeCurrent(true) == false)
            	return;
            
            //Clear The Screen And The Depth Buffer
			gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			
			//Select Our Texture
			gl.glBindTexture(GL_TEXTURE_2D, texture[0]);
			
			//Loop Through All The Stars
			for(loop = 0; loop < NUM; loop++)
			{
				gl.glLoadIdentity(); //Reset The View Before We Draw Each Star
				gl.glTranslatef(0.0f, 0.0f, zoom); //Zoom Into The Screen (Using The Value In 'zoom')
				gl.glRotatef(tilt, 1.0f, 0.0f, 0.0f); //Tilt The View (Using The Value In 'tilt')
				gl.glRotatef(star[loop].angle, 0.0f, 1.0f, 0.0f); //Rotate To The Current Stars Angle
				gl.glTranslatef(star[loop].dist, 0.0f, 0.0f); //Move Forward On The X Plane
				gl.glRotatef(-star[loop].angle, 0.0f, 1.0f, 0.0f); //Cancel The Current Stars Angle
				gl.glRotatef(-tilt, 1.0f, 0.0f, 0.0f); //Cancel The Screen Tilt
				
				//Twinkling Stars Enabled
				if(twinkle)
				{
					//Assign A Color Using Bytes
					gl.glColor4ub(star[(NUM - loop) - 1].r, star[(NUM - loop) - 1].g, star[(NUM - loop) - 1].b, (byte)255);
					gl.glBegin(GL_QUADS); //Begin Drawing The Textured Quad
						gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-1.0f, -1.0f, 0.0f);
						gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(1.0f, -1.0f, 0.0f);
						gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(1.0f, 1.0f, 0.0f);
						gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-1.0f, 1.0f, 0.0f);
					gl.glEnd(); //Done Drawing The Textured Quad
				}
				
				gl. glRotatef(spin,0.0f,0.0f,1.0f); //Rotate The Star On The Z Axis
				//Assign A Color Using Bytes
				gl.glColor4ub(star[loop].r, star[loop].g, star[loop].b, (byte)255);
				gl.glBegin(GL_QUADS); //Begin Drawing The Textured Quad
					gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-1.0f, -1.0f, 0.0f);
					gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(1.0f,-1.0f, 0.0f);
					gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(1.0f, 1.0f, 0.0f);
					gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-1.0f, 1.0f, 0.0f);
				gl.glEnd(); //Done Drawing The Textured Quad
					
				spin += 0.01f; //Used To Spin The Stars
				star[loop].angle += (float)(loop / (float)NUM); //Changes The Angle Of A Star
				star[loop].dist -= 0.01f; //Changes The Distance Of A Star
					
				//Is The Star In The Middle Yet?
				if(star[loop].dist < 0.0f)
				{
					star[loop].dist += 5.0f;		//Move The Star 5 Units From The Center
					star[loop].r = (byte)(256 * Math.random());	//Give It A New Red Value
					star[loop].g = (byte)(256 * Math.random());	//Give It A New Green Value
					star[loop].b = (byte)(256 * Math.random());	//Give It A New Blue Value
				}
			}
            
            //Swap buffers
            glj.gljSwap();
            glj.gljFree();
        }
        
        
        /**
     	 * void keyTyped(KeyEvent e)
     	 *
     	 * Invoked when a key has been typed. This event occurs when a key press is followed by a key release. 
     	 */
    	public void keyTyped(KeyEvent e)
		{
		}


 		/**
 	 	 * void keyPressed(KeyEvent e)
 	 	 *
 	 	 * Invoked when a key has been pressed.
 	 	 */
 		public void keyPressed(KeyEvent e)
		{
			switch(e.getKeyCode())
			{
				//Switch ON/OFF twinkle when T is pressed
				case KeyEvent.VK_T:
				{
					if(!tp)
					{
						tp = true;
						//Toggle twinkle
						twinkle = !twinkle;
					}
						
					break;
				}
				
				//Tilt screen up when user presses UP
				case KeyEvent.VK_UP:
				{
					//Tilt The Screen Up
					tilt -= 0.5f;
					break;
				}
				
				//Tilt screen down when user presses DOWN
				case KeyEvent.VK_DOWN:
				{
					//Tilt The Screen Down
					tilt += 0.5f;
					break;
				}
				
				//Zoom out when user presses PG_UP
				case KeyEvent.VK_PAGE_UP:
				{
					//Zoom out
					zoom -= 0.2f;
					break;
				}
				
				//Zoom in when user presses PG_DOWN
				case KeyEvent.VK_PAGE_DOWN:
				{
					//Zoom in
					zoom += 0.2f;
					break;
				}
			}
		}


		/**
	 	 * void keyReleased(KeyEvent e)
	 	 *
	 	 * Invoked when a key has been released. 
	 	 */
		public void keyReleased(KeyEvent e)
		{
			switch(e.getKeyCode())
			{
				//Key has been released
				case KeyEvent.VK_T:
				{
					tp = false;
					break;
				}
			}
		}

        // Methods required for the implementation of MouseListener
        public void mouseEntered( MouseEvent evt )
        {
            Component comp = evt.getComponent();
	    if( comp.equals(this ) )
	    {
		requestFocus();
	    }
        }

        public void mouseExited( MouseEvent evt )
        { }
        public void mousePressed( MouseEvent evt )
        { }
        public void mouseReleased( MouseEvent evt )                                     
	{ }
        public void mouseClicked( MouseEvent evt )
        { 
            Component comp = evt.getComponent();
	    if( comp.equals(this ) )
	    {
		requestFocus();
	    }
	}

    }
    
    
    
    public class Star
	{
		byte r, g, b; //Stars colour
		float dist; //Stars distance from center
		float angle; //Stars current angle
		
			
		/**
		 * Star()
		 *
		 * Constructor.
		 */
		public Star()
		{
			r = g = b = 0;
			dist = angle = 0.0f;
		}
	}
}
