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); } }