/** * Copyright (c) 2003-2009, Xith3D Project Group all rights reserved. * * Portions based on the Java3D interface, Copyright by Sun Microsystems. * Many thanks to the developers of Java3D and Sun Microsystems for their * innovation and design. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the 'Xith3D Project Group' nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A * RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE */ package org.xith3d.render.lwjgl; import java.nio.FloatBuffer; import org.jagatoo.logging.ProfileTimer; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.openmali.FastMath; import org.openmali.vecmath2.Point3f; import org.xith3d.effects.EffectFactory; import org.xith3d.effects.shadows.VolumeShadowFactory; import org.xith3d.effects.shadows.occluder.Occluder; import org.xith3d.render.OpenGLCapabilities; import org.xith3d.render.OpenGLStatesCache; import org.xith3d.render.preprocessing.RenderBin; import org.xith3d.scenegraph.Light; import org.xith3d.scenegraph.Node; import org.xith3d.scenegraph.View; import org.xith3d.utility.logging.X3DLog; /** * Handles volume shadow rendering. * * @author David Yazel * @author Marvin Froehlich (aka Qudus) */ public class VolumeShadowRenderPeer implements ShadowRenderPeer.ShadowRenderPeerInterface { private final FloatBuffer floatBuffer4x4 = BufferUtils.createFloatBuffer( 16 ); private float[] trans = new float[ 16 ]; public final int initShadows( View view, Light light, RenderBin shadowBin, RenderPeerImpl renderPeer, long frameId ) { return ( 0 ); } private final int drawObjectShadow( OpenGLStatesCache statesCache, OpenGLCapabilities glCaps, Occluder occluder ) { occluder.getWorldTransform().getColumnMajor( trans ); floatBuffer4x4.clear(); floatBuffer4x4.put( trans ).flip(); floatBuffer4x4.rewind(); GL11.glLoadMatrix( floatBuffer4x4 ); if ( !statesCache.enabled || statesCache.normalsArrayEnabled ) { GL11.glDisableClientState( GL11.GL_NORMAL_ARRAY ); statesCache.normalsArrayEnabled = false; } if ( !statesCache.enabled || statesCache.colorsArrayEnabled ) { GL11.glDisableClientState( GL11.GL_COLOR_ARRAY ); statesCache.colorsArrayEnabled = false; } for ( int i = 0; i < glCaps.getMaxTextureUnits(); i++ ) { final int tuMaskValue = FastMath.pow( 2, i ); if ( !statesCache.enabled || ( statesCache.texCoordArraysEnableMask & tuMaskValue ) != 0 ) { ShapeAtomPeer.selectClientTextureUnit( i, statesCache, false ); GL11.glDisableClientState( GL11.GL_TEXTURE_COORD_ARRAY ); statesCache.texCoordArraysEnableMask &= ~tuMaskValue; } } if ( !statesCache.enabled || !statesCache.coordsArrayEnabled ) { GL11.glEnableClientState( GL11.GL_VERTEX_ARRAY ); statesCache.coordsArrayEnabled = true; } GL11.glVertexPointer( 3, 0, occluder.getBuffer().getCoordinatesData().getBuffer() ); GL11.glDrawArrays( GL11.GL_TRIANGLES, 0, occluder.getBuffer().getValidVertexCount() ); return ( occluder.getBuffer().getValidVertexCount() / 3 ); } /** * Draws the shadow volumes in the ShadowBin. * * @param view * @param light * @param shadowBin * @param renderPeer * @param frameId * * @return the number of triangles rendered */ public final int drawShadows( View view, Light light, RenderBin shadowBin, RenderPeerImpl renderPeer, long frameId ) { final OpenGLStatesCache statesCache = renderPeer.getStatesCache(); final OpenGLCapabilities glCaps = renderPeer.getCanvasPeer().getOpenGLCapabilities(); final VolumeShadowFactory shadowFactory = (VolumeShadowFactory)EffectFactory.getInstance().getShadowFactory(); final Point3f lightSourcePos = shadowFactory.getLightSourcePosition(); int numTriangles = 0; ProfileTimer.startProfile( X3DLog.LOG_CHANNEL, "ShadowShaderPeer::drawShadows" ); final int FULLMASK = 0xffffffff; final int STENCIL_VAL = 128; final RenderBin bin = shadowBin; // determine edges for ( int i = 0; i < bin.size(); i++ ) { final Node node = bin.getAtom( i ).getNode(); final Occluder occluder = (Occluder)node.getShadowAttachment(); occluder.determineVisibleEdges( lightSourcePos ); } GL11.glPushAttrib( GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_ENABLE_BIT | GL11.GL_POLYGON_BIT | GL11.GL_STENCIL_BUFFER_BIT ); GL11.glPushMatrix(); GL11.glLoadIdentity(); GL11.glDisable( GL11.GL_LIGHTING ); // turn off Lighting statesCache.lightingEnabled = false; GL11.glDisable( GL11.GL_TEXTURE_2D ); statesCache.texture2DEnabled[ statesCache.currentServerTextureUnit ] = false; GL11.glDisable( GL12.GL_TEXTURE_3D ); statesCache.texture3DEnabled[ statesCache.currentServerTextureUnit ] = false; GL11.glDisable( GL11.GL_BLEND ); statesCache.blendingEnabled = false; GL11.glStencilFunc( GL11.GL_NEVER, 0xff, FULLMASK ); GL11.glDepthMask( true ); GL11.glDepthFunc( GL11.GL_LEQUAL ); GL11.glEnable( GL11.GL_DEPTH_TEST ); statesCache.depthTestEnabled = true; GL11.glPopMatrix(); GL11.glColor3f( 1f, 0f, 0f ); GL11.glClearStencil( STENCIL_VAL ); GL11.glClear( GL11.GL_STENCIL_BUFFER_BIT ); GL11.glDisable( GL11.GL_LIGHTING ); // turn off Lighting statesCache.lightingEnabled = false; GL11.glFrontFace( GL11.GL_CCW ); GL11.glEnable( GL11.GL_CULL_FACE ); statesCache.cullFaceEnabled = true;; GL11.glCullFace( GL11.GL_FRONT ); GL11.glDepthMask( false ); // turn off writing to the Depth-Buffer GL11.glDepthFunc( GL11.GL_LEQUAL ); GL11.glEnable( GL11.GL_STENCIL_TEST ); // turn on Stencil-Buffer testing statesCache.stencilTestEnabled = true; GL11.glColorMask( false, false, false, false ); // don't draw into the Color-Buffer GL11.glStencilFunc( GL11.GL_ALWAYS, STENCIL_VAL, FULLMASK ); // First Pass. Increase Stencil Value In The Shadow GL11.glStencilOp( GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_INCR ); for ( int i = 0; i < bin.size(); i++ ) { final Node node = bin.getAtom( i ).getNode(); final Occluder occluder = (Occluder)node.getShadowAttachment(); numTriangles += drawObjectShadow( statesCache, glCaps, occluder ); } // Second Pass. Decrease Stencil Value In The Shadow GL11.glCullFace( GL11.GL_BACK ); GL11.glStencilOp( GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_DECR ); GL11.glColor3f( 0.5f, 0.5f, 0f ); for ( int i = 0; i < bin.size(); i++ ) { final Node node = bin.getAtom( i ).getNode(); final Occluder occluder = (Occluder)node.getShadowAttachment(); drawObjectShadow( statesCache, glCaps, occluder ); } GL11.glColorMask( ( statesCache.colorWriteMask & 1 ) != 0, ( statesCache.colorWriteMask & 2 ) != 0, ( statesCache.colorWriteMask & 4 ) != 0, ( statesCache.colorWriteMask & 8 ) != 0 ); // reset color-mask // Draw A Shadowing RectanGL11.gle Covering The Entire Screen GL11.glColor4f( 0.0f, 0.0f, 0.0f, 0.4f ); GL11.glEnable( GL11.GL_BLEND ); statesCache.blendingEnabled = true; GL11.glBlendFunc( GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA ); // get rid of the view matrix, only have the projection matrix GL11.glMatrixMode( GL11.GL_PROJECTION ); GL11.glLoadIdentity(); view.getProjection().getColumnMajor( trans ); floatBuffer4x4.clear(); floatBuffer4x4.put( trans ).flip(); floatBuffer4x4.rewind(); GL11.glLoadMatrix( floatBuffer4x4 ); GL11.glMatrixMode( GL11.GL_MODELVIEW ); GL11.glPushMatrix(); GL11.glLoadIdentity(); GL11.glDepthMask( false ); //GL11.glEnable( GL11.GL_STENCIL_TEST ); // turn on Stencil-Buffer testing GL11.glStencilFunc( GL11.GL_NOTEQUAL, STENCIL_VAL, FULLMASK ); GL11.glStencilOp( GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP ); GL11.glCullFace( GL11.GL_BACK ); GL11.glDisable( GL11.GL_CULL_FACE ); statesCache.cullFaceEnabled = false; GL11.glAlphaFunc( GL11.GL_ALWAYS, 0f ); GL11.glBegin( GL11.GL_TRIANGLE_STRIP ); GL11.glVertex3f( -100f, 100f, -2f ); GL11.glVertex3f( -100f, -1000f, -2f ); GL11.glVertex3f( +100f, 100f, -2f ); GL11.glVertex3f( +100f, -1000f, -2f ); GL11.glEnd(); GL11.glPopMatrix(); GL11.glPopAttrib(); ProfileTimer.endProfile(); return ( numTriangles ); } }