// (c) Tuomas J. Lukka package gzz.gfx.gl; import gzz.vob.*; /** Simple encapsulation of stenciling methods for OpenGL. * XXX Make recursive by using stencils greater than 1, * and making this an object, attached to a GLVobMap (?). */ public class Stencil { private static Vob initStencil; private static Vob initOutside; private static Vob initBackplane; private static Vob exitBackplane; private static Vob initContents_depth; private static Vob initContents_nodepth; private static Vob initZero; private static Vob exit; private static void init() { if(initStencil != null) return; initStencil = GZZGL.createCallList( " PushAttrib ENABLE_BIT STENCIL_BUFFER_BIT COLOR_BUFFER_BIT\n"+ " Enable STENCIL_TEST\n"+ " StencilFunc ALWAYS 1 255\n"+ " StencilOp REPLACE REPLACE REPLACE\n"+ // XXX " StencilMask 255\n"+ " ColorMask 1 1 1 1\n"+ " Enable DEPTH_TEST\n" ); initOutside = GZZGL.createCallList( " StencilFunc EQUAL 0 255\n"+ " StencilMask 0\n" ); initBackplane = GZZGL.createCallList( " PushAttrib ENABLE_BIT STENCIL_BUFFER_BIT COLOR_BUFFER_BIT\n"+ " PushMatrix \n"+ " Translate 0 0 10000 \n"+ " StencilFunc EQUAL 1 255\n"+ " Disable DEPTH_TEST\n" ); exitBackplane = GZZGL.createCallList( " PopMatrix\n" + " PopAttrib\n" ); initContents_depth = GZZGL.createCallList( " StencilFunc EQUAL 1 255\n" ); initContents_nodepth = GZZGL.createCallList( " StencilFunc EQUAL 1 255\n"+ " DepthMask 0\n"+ " Disable DEPTH_TEST\n" ); initZero = GZZGL.createCallList( " StencilFunc EQUAL 1 255\n"+ " StencilOp ZERO ZERO ZERO\n"+ // XXX " StencilMask 1\n"+ " ColorMask 0 0 0 0\n" ); exit = GZZGL.createCallList( " PopAttrib\n" ); } /** Draw something stenciled * Assumes that stencil buffer is all zeroes when called; leaves * it zeroed if contract of drawOverStencil is met. * @param vs The VobScene to draw into * @param drawStencil The routine which causes exactly those * fragments to be drawn that need to be in the * stencil. * @param drawOverStencil (may be null): the routine to draw * at least those fragments in the stencil and * possibly more: e.g. if drawStencil uses * textures, this can draw the same polygons without * to save time. * @param drawOutside (may be null): if desired, something that * will be rendered only in the area OUTSIDE the * stencil * @param drawContents (may be null): the contents of the stencil. */ static public void drawStenciled(VobScene vs, Runnable drawStencil, Runnable drawOverStencil, Runnable drawOutside, Runnable drawContents, boolean needDepth) { if(drawOverStencil == null) drawOverStencil = drawStencil; GLVobMap vm = (GLVobMap)vs.map; // First, draw the stencil vm.put(initStencil); drawStencil.run(); // Draw the outside if(drawOutside != null) { vm.put(initOutside); drawOutside.run(); } // If depth is needed, draw the "backplane" if(needDepth) { vm.put(initBackplane); drawOverStencil.run(); vm.put(exitBackplane); } // Draw the contents if(drawContents != null) { if(needDepth) vm.put(initContents_depth); else vm.put(initContents_nodepth); drawContents.run(); } vm.put(initZero); drawOverStencil.run(); vm.put(exit); // Zero stencil buffer } }