package classwork;

import javax.media.opengl.*;
import jocode.*;

/**
 * GLART_12_copyframe.java
 * <P>
 * Copy the framebuffer to a texture.
 * <P>
 * There may be times that you want to make a copy of the screen or part of the screen.  For instance
 * you can generate a texture dynamically using openGL drawing commands, or
 * you may want to have a scene animated within another scene, like a movie playing in a larger
 * scene, or a reflection on a mirror.  One technique to accomplish this is to render to the framebuffer,
 * then save the framebuffer to a texture.
 * Since the framebuffer contents and the texture are stored on the graphics card,
 * this operation is relatively fast.
 * <P>
 * This demmo shows how to save the entire screen image to a texture, then draw that screen image back to
 * the framebuffer.
 * <P>
 * Normally when rendering a scene in OpenGL you'll clear the framebuffer (glClear()) and draw the
 * entire scene in each call to render().  In this case I wanted to leave faint trails behind as geometry
 * moved, so I did not call glClear().  This may cause odd flickering at times because OpenGL does not
 * guarantee that the framebuffer won't change betweem renders.  In fact, the framebuffer may contain
 * the remnants of some other graphical operration, as the graphics card optimizes resources on the fly.
 * The solution is to save an image of the screen at the end of each render(), annd write that image
 * into the framebuffer at the beginnning of each render().  This way the framebuffer contents can change
 * between renders and it won't matter, we'll just write back our last screen before  we render anything
 * else.
 * <P>
 * The functions:
 * <P>
 * glCopyTexSubImage()
 * makeTextureForScreen()
 * frameSave()
 * frameDraw()
 * <P>
 * See init() for the calls to create a texture to hold the screen image.
 * See render() for the calls to save and restore the screen image.
 */
public class GLART_12_copyframe extends JOApp {
    float rotation = 0f;
    float rotation2 = 0f;
    float rotationAmount = .08f;
    // some color values
    float R = 1f;
    float G = 1f;
    float B = 1f;
    // For copying screen image to a texture
    int screenTextureSize = 1024;		// how large should texture be to hold screen
    // will hold screen image as texture
    int screenTextureHandle;

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

    /**
     * Initialize the environment
     */
    public void setup(){
        // allocate a texture big enough to hold screen image
        // the texure will be square, the dimensions will be some power of two, as
        // large or larger than the width of the screen.
        screenTextureHandle = makeTextureForScreen(getWidth(),getHeight());

        // Random value for rotation increment: .05 - 1.0
        rotationAmount = (float) (.05 + (Math.random()*.05));

        // 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)getWidth() / (float)getHeight(), // 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);

        // No need for depth, composition is flat
        gl.glDisable(GL.GL_DEPTH_TEST);
        gl.glDisable(GL.GL_CULL_FACE);

        // set the background color
        gl.glClearColor(.1f, .1f, .82f, 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);

        // need textures on for frameDraw() to work
        gl.glEnable(GL.GL_TEXTURE_2D);

        // capture the screen now to initialize the texture image
        // It's a good idea to call glClear() before capturing the screen for the first time.
        frameCopy(screenTextureHandle);
    }

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

    	// to leave trails behind drawing DON'T call glClear()
    	
        // Reset the Modelview matrix
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();

        // draw the screen image back to the framebuffer before we draw anything else
        // frameDraw() will switch the Projection matrix to ortho mode to draw the screen image
        frameDraw(screenTextureHandle);

        // 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(2,0,0);

        // need textures on for frameDraw() to work
        gl.glDisable(GL.GL_TEXTURE_2D);

        // draw a red quad
    	gl.glColor4f(R,0f,0f,.03f);
    	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,G,0f,.03f);
    	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,B,.03f);
    	gl.glPushMatrix();
    	{
        	gl.glRotatef(rotation2, 1,0,1);
    		gl.glTranslatef(.5f,-.5f,0);
    		gl.glRotatef(rotation2, 0,0,1);
    		drawQuad();
    	}
    	gl.glPopMatrix();

        // need textures on for frameCopy() to work
        gl.glEnable(GL.GL_TEXTURE_2D);

        // save the screen image to a texture
    	// do this after you've drawn everything you want to save
        frameCopy(screenTextureHandle);
    }

    /**
     * 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();
    }

    public void reshape(int newDisplayWidth, int newDisplayHeight) {
    	setViewport(0,0,newDisplayWidth,newDisplayHeight);
        // Define perspective
        glu.gluPerspective(
            45.0f,        // Field Of View
            (float)getWidth() / (float)getHeight(), // aspect ratio
            0.1f,         // near Z clipping plane
            100.0f);      // far Z clipping plane
    }
}
