
import org.lwjgl.opengl.*;
import org.lwjgl.util.glu.*;
import org.lwjgl.input.Keyboard;

/**
 * Flowers drawn with a fake shadow.  Uses glColor() to set vert
 * colors, no lighting, shadow is fudged by drawing flowers
 * in a darker color below the full color flower.
 */
public class GLART_5_flowers {
    private boolean done = false;
    private final String windowTitle = "Flowers with fudged shadow";
    private DisplayMode displayMode;
 	float rotation = 0f;
 	int[] randomX = new int[20];
 	int[] randomY = new int[20];
 	float[] randomS = new float[20];


    /**
     * Main function just creates and runs the application.
     */
    public static void main(String args[]) {
    	GLART_5_flowers app = new GLART_5_flowers();
        app.run();
    }

    /**
     * Initialize the app, then sit in a render loop until done==true.
     */
    public void run() {
        try {
            init();
            while (!done) {
                mainloop();
                render();
                Display.update();   // update video display
            }
            cleanup();
        }
        catch (Exception e) {
            e.printStackTrace();
            System.exit(0);
        }
    }

    /**
     * Initialize the environment
     * @throws Exception
     */
    private void init() throws Exception {
        initDisplay();
        initGL();

        randomX = randomInts(randomX, displayMode.getWidth());
        randomY = randomInts(randomY, displayMode.getHeight());
        randomS = randomFloats(randomS, 1);
    }

    /**
     * Create an OpenGL display, in this case a fullscreen window.
     * @throws Exception
     */
    private void initDisplay() throws Exception {
        // set to full screen, no chrome
        Display.setFullscreen(false);
        // get all possible display resolutions
        DisplayMode d[] = Display.getAvailableDisplayModes();
        // find a resolution we like
        for (int i = 0; i < d.length; i++) {
            if (d[i].getWidth() == 800
                && d[i].getHeight() == 600
                && d[i].getBitsPerPixel() == 32) {
                displayMode = d[i];
                break;
            }
        }
        // set the display to the resolution we picked
        Display.setDisplayMode(displayMode);
        Display.setTitle(windowTitle);
        // Synchronize display updates with the video refresh rate.
        // Graphics cards usually refresh video screen about
        // 60 times per second.  If opengl tries to update screen
        // faster than 60 x per sec, then you'll see "tearing" on
        // the screen (two animation frames overlapping) on screen.
        Display.setVSyncEnabled(true);
        // create the window
        Display.create(new PixelFormat(0, 24, 0));
    }

    /**
     * Initialize OpenGL
     *
     */
    private void initGL() {
        // Select the Projection Matrix (controls perspective)
        GL11.glMatrixMode(GL11.GL_PROJECTION);

        // Reset The Projection Matrix
        GL11.glLoadIdentity();

        // Define perspective
        GLU.gluPerspective(
            45.0f,        // Field Of View
            (float)displayMode.getWidth() / (float)displayMode.getHeight(), // aspect ratio
            0.1f,         // near Z clipping plane
            10000.0f);      // far Z clipping plane

        // Select The Modelview Matrix (controls model orientation)
        GL11.glMatrixMode(GL11.GL_MODELVIEW);

        // turn depth testing on
        GL11.glEnable(GL11.GL_DEPTH_TEST);

        // green background
        GL11.glClearColor(.1f,.4f,0f,1f);
    }

    /**
     * Handle keyboard input.  Just check for escape key or user
     * clicking to close the window.
     */
    private void mainloop() {
        if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {   // Escape is pressed
            done = true;
        }
        if(Display.isCloseRequested()) {                // Window is closed
            done = true;
        }
    }

    /**
     * Render the scene.
     */
    private void render() {
    	int w = displayMode.getWidth();
    	int h = displayMode.getHeight();
    	rotation += .05f;

        // Clear screen and depth buffer
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

        // Reset the Modelview matrix
        // this resets the coordinate system to center of screen
        GL11.glLoadIdentity();

        // Where is the 'eye'
        GLU.gluLookAt(
            0f, -600f, 600f,   // eye position
            0f, -100f, 0f,    // target to look at
            0f, 1f, 0f);   // which way is up

		// rotate scene
        GL11.glRotatef(rotation, 0, 0, 1);

        // draw a bunch of flowers
        for (int i=0; i < randomX.length; i++) {
        	// preserve the current coordinate system
        	GL11.glPushMatrix();
        	{
        		// shift to a random x,y position
	            GL11.glTranslatef(randomX[i]-400, randomY[i]-300,  randomS[i]);

        		// scale up a random amount
	            GL11.glScalef(.6f+randomS[i], .6f+randomS[i],  .6f+randomS[i]);

            	// draw dark flower below
	            GL11.glPushMatrix();
	            {
	            	GL11.glTranslatef(25,40,-130);
	            	drawFlowerShadow(250,2,2);
	            }
	            GL11.glPopMatrix();

	            // draw flower in color above
	            drawFlower(250,2,2);
        	}
        	GL11.glPopMatrix();
    		// glPopMatrix will return to previous coord system
    		// in effect clearing the translate we did above.
        }
    }

    //============================================================

    public float random(float upperbound) {
    	return (float)Math.random()*((float)upperbound);
    }

    //Draws a flower centered at 0,0.  Use translate() to place circle at desired coords.
    //r radius of flower petals, nodes*2 number of petals, stepSize definition
    public void drawFlower(float r, int nodes, int stepSize) {
        int s = 0;
        int e = 360;
        float ts, tc, rs, rc;
        float x, y, z;

        //Draw Center of Flower
        s = 0;
        e = 360;
        GL11.glBegin(GL11.GL_QUAD_STRIP);
        {
            ts = (float) Math.sin(Math.toRadians(s));
            tc = (float) Math.cos(Math.toRadians(s));
            GL11.glColor3f(1f,1f,0f);
            GL11.glVertex3f(tc*r*.1f, ts*r*.08f, 1f);
            GL11.glColor3f(.8f,.7f,0f);
            GL11.glVertex3f(0, 0, 1f);
            while ( (s = ( (s + stepSize) / stepSize) * stepSize) < e)
            {
                ts = (float) Math.sin(Math.toRadians(s));
                tc = (float) Math.cos(Math.toRadians(s));
                GL11.glColor3f(1f,1f,0f);
                GL11.glVertex3f(tc*r*.1f, ts*r*.08f, 1f);
                GL11.glColor3f(.8f,.7f,0f);
                GL11.glVertex3f(0, 0, 1f);
            }
            ts = (float) Math.sin(Math.toRadians(s));
            tc = (float) Math.cos(Math.toRadians(s));
            GL11.glColor3f(1f,1f,0f);
            GL11.glVertex3f(tc*r*.1f, ts*r*.08f, 1f);
            GL11.glColor3f(.8f,.7f,0f);
            GL11.glVertex3f(0, 0, 1f);
        }
        GL11.glEnd();

        //Draw Petals
        s = 0;
        e = 360;
        r /= 2f;
        GL11.glColor3f(1f,1f,1f);
        GL11.glBegin(GL11.GL_POLYGON);
        {
            while ( (s = ( (s + stepSize) / stepSize) * stepSize) < e)
            {
                ts = (float) Math.sin(Math.toRadians(s));
                tc = (float) Math.cos(Math.toRadians(s));
                rs = (float) Math.sin(Math.toRadians(s*nodes));
                rc = (float) Math.cos(Math.toRadians(s*nodes));
                x = tc*(rc*rs)*r;
                y = ts*(rc*rs)*r;
                z = (float)Math.abs((x*y)*.01f);
                GL11.glColor3f(z, z, 1f);
                GL11.glVertex3f(x, y, -z);
            }
        }
        GL11.glEnd();
    }//END DrawFlower

    public void drawFlowerShadow(float r, int nodes, int stepSize) {
        int s = 0;
        int e = 360;
        float ts, tc, rs, rc;
        float x, y, z;

        GL11.glColor3f(.10f,.28f,.13f);

        //Draw Center of Flower
        s = 0;
        e = 360;
        GL11.glBegin(GL11.GL_POLYGON);
        {
            ts = (float) Math.sin(Math.toRadians(s));
            tc = (float) Math.cos(Math.toRadians(s));
            GL11.glVertex3f(tc*r*.1f, ts*r*.08f, 1f);
            while ( (s = ( (s + stepSize) / stepSize) * stepSize) < e)
            {
                ts = (float) Math.sin(Math.toRadians(s));
                tc = (float) Math.cos(Math.toRadians(s));
                GL11.glVertex3f(tc*r*.1f, ts*r*.08f, 1f);
            }
            ts = (float) Math.sin(Math.toRadians(s));
            tc = (float) Math.cos(Math.toRadians(s));
            GL11.glVertex3f(tc*r*.1f, ts*r*.08f, 1f);
        }
        GL11.glEnd();

        //Draw Petals
        s = 0;
        e = 360;
        r /= 2f;
        GL11.glBegin(GL11.GL_POLYGON);
        {
            while ( (s = ( (s + stepSize) / stepSize) * stepSize) < e)
            {
                ts = (float) Math.sin(Math.toRadians(s));
                tc = (float) Math.cos(Math.toRadians(s));
                rs = (float) Math.sin(Math.toRadians(s*nodes));
                rc = (float) Math.cos(Math.toRadians(s*nodes));
                x = tc*(rc*rs)*r;
                y = ts*(rc*rs)*r;
                GL11.glVertex3f(x, y, 0);
            }
        }
        GL11.glEnd();
    }//END DrawFlower

    public int[] randomInts(int[] array, int upperBound) {
    	for (int i=0; i<array.length; i++) {
    		array[i] = (int)random(upperBound);
    	}
    	return array;
    }

    public float[] randomFloats(float[] array, float upperBound) {
    	for (int i=0; i<array.length; i++) {
    		array[i] = (float)random(upperBound);
    	}
    	return array;
    }

    /**
     * Cleanup all the resources.
     *
     */
    private void cleanup() {
        Display.destroy();
    }
}


