package classwork;

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

/**
 * GLART_5_texture_samples.java
 *
 * Demo six texture examples.
 *
 */
public class GLART_5_texture_samples  extends JOApp {
    private float rotation = 0f;

    // texture handles (a number that refers to an allocated texture)
    int myTextureHandle = 0;
    int bgTextureHandle = 0;
    int screenTextureHandle = 0;

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

    /**
     * Initialize OpenGL
     *
     */
    public void setup() {
        // Select the Projection Matrix (controls perspective)
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();    // Reset

        gl.glOrtho(
        			-4f, 4f,   //X: left side, right side
        			-3f, 3f,   //Y: bottom, top
					-9f, 9f    //Z: near, far
					);

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

        // enable texturing
        gl.glEnable(GL.GL_TEXTURE_2D);

        // set the background color
        gl.glClearColor(.2f, .4f, .5f, 1);

        // Prepare a texture from the image
        myTextureHandle = makeTexture("images/velazquez_texture_256.jpg");

        // Prepare a texture from the image
        bgTextureHandle = makeTexture("images/screen_bg.png");

        // Allocate a blank texture same size as window
        screenTextureHandle = makeTexture(getWidth(), getHeight());
    }

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

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

        // draw the background image
		renderBackground(bgTextureHandle);

        // Select and reset the Modelview Matrix (controls model orientation)
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();

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

        // "select" our texture
    	gl.glBindTexture(GL.GL_TEXTURE_2D,myTextureHandle);

		// upper left
		gl.glPushMatrix();
		{
			gl.glTranslatef(-2.5f, 1.1f, 0);
			renderTexturePlain();
		}
		gl.glPopMatrix();

		// upper middle
		gl.glPushMatrix();
		{
			gl.glTranslatef(0, 1.1f, 0);
			renderTextureTiled();
		}
		gl.glPopMatrix();

		// upper right
		gl.glPushMatrix();
		{
			gl.glTranslatef(2.5f, 1.1f, 0);
			renderTextureZoomed();
		}
		gl.glPopMatrix();

		// lower left
		gl.glPushMatrix();
		{
			gl.glTranslatef(-2.5f, -1.6f, 0);
			this.renderTextureMirrored();
		}
		gl.glPopMatrix();

		// lower middle
		gl.glPushMatrix();
		{
			gl.glTranslatef(0, -1.6f, 0);
			renderTextureTransformed();
		}
		gl.glPopMatrix();

		// lower right
		gl.glPushMatrix();
		{
			gl.glTranslatef(2.5f, -1.6f, 0);
			renderTextureScreen();
		}
		gl.glPopMatrix();

		// copy the screen to a texture (used in renderTextureScreen())
		captureScreen(screenTextureHandle);
    }

	/**
	 *  draw a large texture over the entire screen area
	 *  assumes that the texture passed in is 1024x1024
	 */
	public void renderBackground(int txtrHandle) {
		// make this the "current" texture
        gl.glBindTexture(GL.GL_TEXTURE_2D,txtrHandle);

        // switch to projection matrix, set ortho mode
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glPushMatrix();           // preserve previous perspective view
        gl.glLoadIdentity();         // reset matrix
		gl.glOrtho( 0, 800f,  0, 600f,  -3f, 3f );  // ortho mode, same size as screen

		// switch back to model view
        gl.glMatrixMode(GL.GL_MODELVIEW);
	    gl.glPushMatrix();	      	// preserve the current Modelview Matrix
		gl.glLoadIdentity();			// clear the Modelview Matrix

		// draw a quad as big as the texture (NOT as big as screen!)
		gl.glBegin(GL.GL_QUADS);
		{
			// lower left
			gl.glTexCoord2f(0f, 0f);
			gl.glVertex3f( 0f, 0f, -2f);
			// lower right
			gl.glTexCoord2f(1f, 0f);
			gl.glVertex3f( 800, 0, -2);
			// upper right
			gl.glTexCoord2f(1f, 1f);
			gl.glVertex3f( 800, 600, -2);
			// upper left
			gl.glTexCoord2f(0f, 1f);
			gl.glVertex3f( 0, 600, -2);
		}
		gl.glEnd();

		// return to previous modelview matrix
		gl.glPopMatrix();

		// select projection matrix and pop back to previous projection
	    gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glPopMatrix();
	}

	/**
	 *  Draw texture tiled once onto quad:  glTexCoord values range from 0 to 1
	 */
	public void renderTexturePlain() {
        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 texture tiled 4x:  glTexCoord values range from 0 to 4
	 */
	public void renderTextureTiled() {
		float tileFactor = 4;  // rotation/100f;
        gl.glBegin(GL.GL_QUADS);
        {
            gl.glTexCoord2f(0, 0);
            gl.glVertex3f(-1.0f,-1.0f, 0.0f);         // Bottom Left

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

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

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

	/**
	 *  Draw texture "zoomed in": glTexCoord values range from .3 to .7
	 */
	public void renderTextureZoomed() {
        gl.glBegin(GL.GL_QUADS);
        {
            gl.glTexCoord2f(.3f, .3f);
            gl.glVertex3f(-1.0f,-1.0f, 0.0f);         // Bottom Left

            gl.glTexCoord2f(.7f, .3f);
            gl.glVertex3f( 1.0f,-1.0f, 0.0f);         // Bottom Right

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

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

	/**
	 *  Draw texture mirrored:  glTexCoord values range from 1 to 0
	 */
	public void renderTextureMirrored() {
        gl.glBegin(GL.GL_QUADS);
        {
            gl.glTexCoord2f(1, 0);
            gl.glVertex3f(-1.0f,-1.0f, 0.0f);         // Bottom Left

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

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

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

	/**
	 *  Draw texture rotated.
	 */
	public void renderTextureTransformed() {
		// switch to Texture Matrix and rotate the texture space
		gl.glMatrixMode(GL.GL_TEXTURE);
		gl.glPushMatrix();      // preserve previous texture matrix
		gl.glLoadIdentity();
		gl.glTranslatef(.5f,.5f,0f);       // shift coord system to center of image
		gl.glRotatef(rotation, 0, 0, 1);   // rotate around center
		gl.glTranslatef(-.5f,-.5f,0f);     // shift back

		// switch back to modelview and draw the quad
		gl.glMatrixMode(GL.GL_MODELVIEW);
        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();

		// switch to Texture Matrix and return to previous state
		gl.glMatrixMode(GL.GL_TEXTURE);
        gl.glPopMatrix();

        // return to modelview matrix
        gl.glMatrixMode(GL.GL_MODELVIEW);
	}

	/**
	 *  Draw screen image textured on quad.
	 *  The screen image is smaller (800x600) than the texture dimensions (1024x1024).
	 *  If we use 0-1 for our U and V range we'll see the extra blank space as well as the screen.
	 *  To map only to screen area we'll calculate the UV position of the right and top edge of the screen image.
	 *
	 *  See captureScreen().
	 */
	public void renderTextureScreen() {
        // "select" texture that has screen image
    	gl.glBindTexture(GL.GL_TEXTURE_2D, screenTextureHandle);

    	// just for fun, rotate the quad
		gl.glRotatef(rotation,  0, .6f, 1);
		gl.glRotatef(rotation*.2f, 0, 0, 1);

    	// draw a quad with texture coordinates
        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();
	}

	/**
	 * calls glCopyTexSubImage2D() to copy pixels from the framebuffer into a texture.
	 * The texture must be large enough to hold the screen image (ie. a 1024x1024
	 * texture can hold a screen 1024x768 or smaller).
	 */
	public void captureScreen(int screenTexture) {
        // "select" the texture that will hold screen image
    	gl.glBindTexture(GL.GL_TEXTURE_2D, screenTexture);

        // Copy framebuffer into texture
        gl.glCopyTexSubImage2D(
				GL.GL_TEXTURE_2D,             // always GL_TEXTURE_2D
        		0,                              // texture detail level: always 0
        		0,0,                            // x,y offset into texture
        		0,0,                            // lower left of screen area to copy
        		getWidth(),getHeight()    // width,height of screen area to copy
        		);
	}
}
