package classwork;

import javax.media.opengl.*;
import java.awt.event.*;
import jocode.*;

/**
 * GLART_12_pbuffer.java
 * <P>
 * Render into a Frame buffer object (or "FBO") and save the contents to a file.
 * <P>
 * FBOs function just like the normal OpenGL framebuffer but are not drawn
 * to the video screen.  They can be larger than the screen, up to the memory
 * limits of the graphics card.  FBOs are often used to generate textures
 * dynamically. A texture can be attached to the FBO and gl rendering
 * commands will draw into the texture.
 * <P>
 * This demo creates an FBO that is several times larger than the screen,
 * draws a simple image into it and saves the image to a PNG file.
 * <P>
 * Hit  F1  to save the image to screen_capture.png and pbuffer_capture.png.
 * <P>
 * NOTE: screenshots from large FBOs can consume a lot of memory.  Be sure to 
 * allocate more memory to java.  Add the parameters to the JRE when java runs:<BR>
 *        -Xms128m -Xmx128m   <BR>
 * This will force java to run with 128 megabytes of memory.
 * <P>
 * @see JOFrameBuffer.java  class that manages FBO
 */
public class GLART_12_FBO extends JOApp {
    float rotation = 0f;
    float rotation2 = 0f;
    float rotationAmount = .08f;
    // JOFrameBuffer class wraps a "framebuffer object", a virtual
    // screen that can be rendered to just like the Display.
    JOFrameBuffer FBO;
    // Set the FBO size relative to the screen.  Most OpenGL rendering
    // will scale to fit the FBO dimensions.
    float fboScale = 2f;
    // flag: when true, we'll capture the current screen from the Pbuffer
    boolean bufferCapture = false;


    /**
     * Main function just creates and runs the application.
     */
    public static void main(String args[]) {
    	GLART_12_FBO app = new GLART_12_FBO();
    	windowTitle = "FBO Demo";
	    displayWidth = 800;
	    displayHeight = 600;
	    //app.fullScreen = true;
        app.run();
    }

    /**
     * Initialize the environment
     */
    public void setup() {
        // Random value for rotation increment: .05 - 1.0
        rotationAmount = (float) (.05 + (Math.random()*.05));

        // Create a frame buffer object.  May be larger than screen
        FBO = new JOFrameBuffer( (int) (getWidth()*fboScale),
        					(int) (getHeight()*fboScale) );

        // "Select" the FBO, so rendering commands will go to the offscreen frame buffer
        FBO.activate();

        // Initialize the OpenGL context FOR THE FBO
        initGL(getWidth(), getHeight());

        // rendering commands will now go the screen
        FBO.deactivate();

        // Initialize the OpenGL context FOR THE default framebuffer
        initGL(getWidth(), getHeight());

        // Select the Projection Matrix (controls perspective)
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();    // Reset The Projection Matrix

        // Define perspective
        glu.gluPerspective(
            45.0f,        // Field Of View
            (float)displayWidth / (float)displayHeight, // aspect ratio
            0.1f,         // near Z clipping plane
            100.0f);      // far Z clipping plane

        // Select The Modelview Matrix (controls model orientation)
        gl.glMatrixMode(GL.GL_MODELVIEW);
    }

    /**
     * Initialize OpenGL
     *
     */
    public void initGL(int displayWidth, int displayHeight) {
        // No need for depth, composition is flat
        gl.glDisable(GL.GL_DEPTH_TEST);

        // set the background color
        gl.glClearColor(.1f, .1f, .12f, 1);
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

        // To create transparencies (alpha blending)
        gl.glEnable(GL.GL_BLEND);
        gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
    }

    /**
     * Render the scene.
     */
    public void draw() {
    	rotation += .08f;
    	rotation2 += rotationAmount;

    	renderFrame();

    	// Save default framebuffer and FBO pixels to PNG image
    	// OpenGL scales most rendering operations to fit the current
    	// screen proportions, except for glLineWidth() and glPointSize(),
        // which are absolute sizes and do not scale.  Before drawing
    	// to the FBO scale up the lineWidth.
    	if (bufferCapture) {
    		// save screen
    		screenShot("screen_capture.png");
    		// select Pbuffer
    		FBO.activate();
    		// adjust the line width to match pbuffer scale
    		gl.glLineWidth(fboScale);
    		// draw the scene into pbuffer
    		renderFrame();
    		// further drawing will go to screen
    		FBO.deactivate();
    		// save the pbuffer to PNG
    		FBO.screenShot("screen_capture_fbo.png");
    		// reset the line width to normal scale
    		gl.glLineWidth(1);
    		// lower the flag
    		bufferCapture = false;
    	}
    }

    /**
     * Render the scene.
     */
    public void renderFrame() {
    	gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

        // Reset the Modelview matrix
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();

        // be sure we're in modelview mode (frameDraw switches to Projection matrix)
        gl.glMatrixMode(GL.GL_MODELVIEW);

        // Place the 'eye'
        glu.gluLookAt(
            0f, 0f, 5f,   // eye position
            0f, 0f, 0f,    // target to look at
            0f, 1f, 0f);   // which way is up

        // rotate scene
      	gl.glRotatef(rotation*3.3f, 0,0,1);
    	gl.glTranslatef(.5f,0,0);

    	// draw a red quad
    	gl.glColor4f(1,0f,0f,.7f);
    	gl.glPushMatrix();
    	{
        	gl.glRotatef(rotation*.7f, 0,1,1);
    		gl.glTranslatef(0,.5f,0);
    		gl.glRotatef(rotation2, 0,0,1);
    		drawQuad();
    	}
    	gl.glPopMatrix();

    	// rotate more
    	gl.glRotatef(rotation2*4.7f, 0,0,1);
    	gl.glTranslatef(1,0,0);

    	// draw a green quad
    	gl.glColor4f(0f,1,0f,.7f);
    	gl.glPushMatrix();
    	{
        	gl.glRotatef(rotation2*2f, 0,0,1);
    		gl.glTranslatef(-.5f,-.5f,0);
    		gl.glRotatef(rotation2, 0,0,1);
    		drawQuad();
    	}
    	gl.glPopMatrix();

    	// draw a blue quad
    	gl.glColor4f(0f,0f,1,.7f);
    	gl.glPushMatrix();
    	{
        	gl.glRotatef(rotation2, 1,0,1);
    		gl.glTranslatef(.5f,-.5f,0);
    		gl.glRotatef(rotation2, 0,0,1);
    		drawQuadLine();
    	}
    	gl.glPopMatrix();

    	// draw lines
    	gl.glPushMatrix();
    	{
        	gl.glRotatef(rotation2*2.1f, 1,0,1);
        	gl.glColor4f(0f,.5f,1,.7f);
    		drawQuadLine();
        	gl.glRotatef(2f, 1,0,1);
        	gl.glColor4f(0f,.6f,1,.7f);
    		drawQuadLine();
        	gl.glRotatef(3f, 1,0,1);
        	gl.glColor4f(0f,.8f,1,.7f);
    		drawQuadLine();
    	}
    	gl.glPopMatrix();
    }

    /**
     * draw a 1x1 square
     */
    public void drawQuad() {
        gl.glBegin(GL.GL_QUADS);
        {
            gl.glTexCoord2f(0, 0);
            gl.glVertex3f(-1.0f,-1.0f, 0.0f);         // Bottom Left

            gl.glTexCoord2f(1, 0);
            gl.glVertex3f( 1.0f,-1.0f, 0.0f);         // Bottom Right

            gl.glTexCoord2f(1, 1);
            gl.glVertex3f( 1.0f, 1.0f, 0.0f);         // Top Right

            gl.glTexCoord2f(0, 1);
            gl.glVertex3f(-1.0f, 1.0f, 0.0f);         // Top left
        }
        gl.glEnd();
    }

    /**
     * draw a 1x1 square
     */
    public void drawQuadLine() {
        gl.glBegin(GL.GL_LINE_STRIP);
        {
            gl.glTexCoord2f(0, 0);
            gl.glVertex3f(-1.0f,-1.0f, 0.0f);         // Bottom Left

            gl.glTexCoord2f(1, 0);
            gl.glVertex3f( 1.0f,-1.0f, 0.0f);         // Bottom Right

            gl.glTexCoord2f(1, 1);
            gl.glVertex3f( 1.0f, 1.0f, 0.0f);         // Top Right

            gl.glTexCoord2f(0, 1);
            gl.glVertex3f(-1.0f, 1.0f, 0.0f);         // Top left

            gl.glTexCoord2f(0, 0);
            gl.glVertex3f(-1.0f,-1.0f, 0.0f);         // Bottom Left
        }
        gl.glEnd();
    }

    /**
     */
    public void keyUp(int keycode) {
        // set flag to save screen (see render())
        if (keycode == KeyEvent.VK_F1) {
            bufferCapture = true;
        }
    }

    /**
     * Cleanup all the resources.
     *
     */
    public void exit() {
        //if (FBO != null) {
        //	FBO.cleanup();
        //}
        super.exit();
    }
}
