package classwork; 

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

/**
 * Use the stencil buffer to mask out an area of a scene.
 *
 * Hold SPACE down to turn mask off
 *
 */
public class GLART_10_Stencil extends JOApp {
    float rotateModelY = 0;
    JOModel obj;
    int texture;
    JOImage flagImage;


    /**
     * Create and runs the application.
     */
    public static void main(String args[]) {
    	GLART_10_Stencil app = new GLART_10_Stencil();
    	windowTitle = "Stencil Demo";
    	displayWidth = 1024;
    	displayHeight = 600;
        app.run();
    }

    /**
     * Initialize the app.
     */
    public void setup() {
        // load model
        obj = new JOModel("models/cow.obj");

        flagImage = new JOImage("images/Flag_of_the_United_States.png");
        
        // load a texture
        texture = makeTexture("images/grass_1_512.jpg");

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

        // Define perspective
        glu.gluPerspective(
            30.0f,        // Field Of View
            (float)getWidth() / (float)getHeight(), // aspect ratio
            0.01f,         // near Z clipping plane
            1000.0f);      // far Z clipping plane

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

        // make sure OpenGL correctly layers objects
        gl.glEnable(GL.GL_DEPTH_TEST);

        // turn on texturing
        gl.glEnable(GL.GL_TEXTURE_2D);

        // OpenGL won't draw backward facing triangles ("back faces")
        gl.glEnable(GL.GL_CULL_FACE);

        // set the background color
        gl.glClearColor(.3f, .3f, .7f, 1);
    }

    /**
     * Render the scene.
     */
    public void draw() {
    	rotateModelY += .05;

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

        // reset the coordinate system to center of screen
        gl.glLoadIdentity();

        // Place the viewpoint
        glu.gluLookAt(
			0, 4, 10,
			0, 2, 0,
			0f, 1f, 0f);

    	drawImageFullScreen(flagImage);
    	
        ///////////////////////////////////////////////
        // Step 1: make a mask from the rotating model

        // Clear stencil buffer
        clearMask();

        // prepare to draw a mask into the stencil buffer
        beginMask(1);

        // Render the object that will be our stencil shape.
        // Color and depth buffer are disabled by beginMask(),
        // only the stencil buffer will be modified.
        drawObject();

        // turn the color and depth buffers back on, "freeze" the stencil buffer
        endMask();

        ///////////////////////////////////////////////
        // Step 2: activate the mask and draw scene into mask area

        // While stencil test is enabled, only draw to areas where the
        // stencil buffer has a 1.  This will draw only inside the object silhouette.
        if(!JOApp.isKeyDown(KeyEvent.VK_SPACE)) {
        	activateMask(1);
        }

        // draw a sphere into the stencil area
        gl.glPushMatrix();
        {
        	gl.glTranslatef(0,2f,0);   // move up a litte (off the floor)
        	gl.glScalef(3.3f,2.5f,3.3f);  // scale it down a little
        	gl.glRotatef(-rotateModelY, 0, 1, 0); // around vertical axis
        	gl.glBindTexture(GL.GL_TEXTURE_2D, texture);
        	renderSphere();
        }
        gl.glPopMatrix();

        // just to be safe, disable the stencil test when we're done
        gl.glDisable(GL.GL_STENCIL_TEST);
    }


    /**
     * draw the model
     */
	public void drawObject() {
		gl.glPushMatrix();
		{
			gl.glTranslatef(0,2,0);   // move up a litte (off the floor)
			gl.glScalef(.5f,.5f,.5f);  // scale it down a little
			gl.glRotatef(rotateModelY*2f, 0, 1, 0); // around vertical axis
			obj.renderTextured(texture);
		}
		gl.glPopMatrix();
	}

    /////////////////////////////////////////////////////////////////
    // Stencil functions
    /////////////////////////////////////////////////////////////////

    /**
     * clear the stencil buffer
     */
	public static void clearMask() {
		gl.glClear(GL.GL_STENCIL_BUFFER_BIT);
	}

	/**
	 *  Begin creating a mask.  This function turns off the color and depth buffers
	 *  so all subsequent drawing will go only into the stencil buffer.
	 *  To use:
	 *          beginMask(1);
	 *          renderModel();
	 *          endMask();
	 */
	public static void beginMask(int maskvalue) {
		// turn off writing to the color buffer and depth buffer
		gl.glColorMask(false, false, false, false);
		gl.glDepthMask(false);

		// enable stencil buffer
		gl.glEnable(GL.GL_STENCIL_TEST);

		// set the stencil test to ALWAYS pass
		gl.glStencilFunc(GL.GL_ALWAYS, maskvalue, 0xFFFFFFFF);
		// REPLACE the stencil buffer value with maskvalue whereever we draw
		gl.glStencilOp(GL.GL_REPLACE, GL.GL_REPLACE, GL.GL_REPLACE);
	}

	/**
	 *  End the mask.  Freeze the stencil buffer and activate the color and depth buffers.
	 */
	public static void endMask() {
		// don't let future drawing modify the contents of the stencil buffer
		gl.glStencilOp(GL.GL_KEEP, GL.GL_KEEP, GL.GL_KEEP);

		// turn the color and depth buffers back on
		gl.glColorMask(true, true, true, true);
		gl.glDepthMask(true);
	}

	/**
	 *  Restrict rendering to the masked area.
	 *  To use:
	 *          GLStencil.beginMask(1);
	 *          renderModel();
	 *          GLStencil.endMask();
	 */
	public static void activateMask(int maskvalue) {
		// enable stencil buffer
		gl.glEnable(GL.GL_STENCIL_TEST);

		// until stencil test is disabled, only write to areas where the
		// stencil buffer equals the mask value
		gl.glStencilFunc(GL.GL_EQUAL, maskvalue, 0xFFFFFFFF);
	}
}
