diff --git a/engine/src/core/com/jme3/material/RenderState.java b/engine/src/core/com/jme3/material/RenderState.java index d11d624e3..27be6eda6 100644 --- a/engine/src/core/com/jme3/material/RenderState.java +++ b/engine/src/core/com/jme3/material/RenderState.java @@ -125,11 +125,34 @@ public class RenderState implements Cloneable, Savable { */ Back, /** - * Cull both front and back faces. + * Cull both front and back faces. */ FrontAndBack } + + public enum StencilOperation { + Keep, //keep the current value + Zero, //set the value to 0 + Replace, //sets the buffer to + Increment, + IncrementWrap, + Decrement, + DecrementWrap, + Invert + } + + public enum StencilFunction { + Never, + Less, + LessEqual, + Greater, + GreaterEqual, + Equal, + NotEqual, + Always + } + static { NULL.cullMode = FaceCullMode.Off; NULL.depthTest = false; @@ -147,7 +170,7 @@ public class RenderState implements Cloneable, Savable { ADDITIONAL.applyAlphaFallOff = false; ADDITIONAL.applyPolyOffset = false; } - + boolean pointSprite = false; boolean applyPointSprite = true; boolean wireframe = false; @@ -170,6 +193,15 @@ public class RenderState implements Cloneable, Savable { boolean applyPolyOffset = true; float offsetFactor = 0; float offsetUnits = 0; + boolean stencilTest = false; + StencilOperation frontStencilStencilFailOperation = StencilOperation.Keep; + StencilOperation frontStencilDepthFailOperation = StencilOperation.Keep; + StencilOperation frontStencilDepthPassOperation = StencilOperation.Keep; + StencilOperation backStencilStencilFailOperation = StencilOperation.Keep; + StencilOperation backStencilDepthFailOperation = StencilOperation.Keep; + StencilOperation backStencilDepthPassOperation = StencilOperation.Keep; + StencilFunction frontStencilFunction = StencilFunction.Always; + StencilFunction backStencilFunction = StencilFunction.Always; public void write(JmeExporter ex) throws IOException { OutputCapsule oc = ex.getCapsule(this); @@ -185,6 +217,15 @@ public class RenderState implements Cloneable, Savable { oc.write(offsetEnabled, "offsetEnabled", false); oc.write(offsetFactor, "offsetFactor", 0); oc.write(offsetUnits, "offsetUnits", 0); + oc.write(stencilTest, "stencilTest", false); + oc.write(frontStencilStencilFailOperation, "frontStencilStencilFailOperation", StencilOperation.Keep); + oc.write(frontStencilDepthFailOperation, "frontStencilDepthFailOperation", StencilOperation.Keep); + oc.write(frontStencilDepthPassOperation, "frontStencilDepthPassOperation", StencilOperation.Keep); + oc.write(backStencilStencilFailOperation, "frontStencilStencilFailOperation", StencilOperation.Keep); + oc.write(backStencilDepthFailOperation, "backStencilDepthFailOperation", StencilOperation.Keep); + oc.write(backStencilDepthPassOperation, "backStencilDepthPassOperation", StencilOperation.Keep); + oc.write(frontStencilFunction, "frontStencilFunction", StencilFunction.Always); + oc.write(backStencilFunction, "backStencilFunction", StencilFunction.Always); } public void read(JmeImporter im) throws IOException { @@ -201,6 +242,15 @@ public class RenderState implements Cloneable, Savable { offsetEnabled = ic.readBoolean("offsetEnabled", false); offsetFactor = ic.readFloat("offsetFactor", 0); offsetUnits = ic.readFloat("offsetUnits", 0); + stencilTest = ic.readBoolean("stencilTest", false); + frontStencilStencilFailOperation = ic.readEnum("frontStencilStencilFailOperation", StencilOperation.class, StencilOperation.Keep); + frontStencilDepthFailOperation = ic.readEnum("frontStencilDepthFailOperation", StencilOperation.class, StencilOperation.Keep); + frontStencilDepthPassOperation = ic.readEnum("frontStencilDepthPassOperation", StencilOperation.class, StencilOperation.Keep); + backStencilStencilFailOperation = ic.readEnum("backStencilStencilFailOperation", StencilOperation.class, StencilOperation.Keep); + backStencilDepthFailOperation = ic.readEnum("backStencilDepthFailOperation", StencilOperation.class, StencilOperation.Keep); + backStencilDepthPassOperation = ic.readEnum("backStencilDepthPassOperation", StencilOperation.class, StencilOperation.Keep); + frontStencilFunction = ic.readEnum("frontStencilFunction", StencilFunction.class, StencilFunction.Always); + backStencilFunction = ic.readEnum("backStencilFunction", StencilFunction.class, StencilFunction.Always); } @Override @@ -282,6 +332,41 @@ public class RenderState implements Cloneable, Savable { offsetUnits = units; } + public void setStencil(boolean enabled, + StencilOperation _frontStencilStencilFailOperation, + StencilOperation _frontStencilDepthFailOperation, + StencilOperation _frontStencilDepthPassOperation, + StencilOperation _backStencilStencilFailOperation, + StencilOperation _backStencilDepthFailOperation, + StencilOperation _backStencilDepthPassOperation, + StencilFunction _frontStencilFunction, + StencilFunction _backStencilFunction){ + + stencilTest = enabled; + this.frontStencilStencilFailOperation = _frontStencilStencilFailOperation; + this.frontStencilDepthFailOperation = _frontStencilDepthFailOperation; + this.frontStencilDepthPassOperation = _frontStencilDepthPassOperation; + this.backStencilStencilFailOperation = _backStencilStencilFailOperation; + this.backStencilDepthFailOperation = _backStencilDepthFailOperation; + this.backStencilDepthPassOperation = _backStencilDepthPassOperation; + this.frontStencilFunction = _frontStencilFunction; + this.backStencilFunction = _backStencilFunction; + } + + public boolean isStencilTest() { + return stencilTest; + } + + public StencilOperation getFrontStencilStencilFailOperation(){ return frontStencilStencilFailOperation; } + public StencilOperation getFrontStencilDepthFailOperation(){ return frontStencilDepthFailOperation; } + public StencilOperation getFrontStencilDepthPassOperation(){ return frontStencilDepthPassOperation; } + public StencilOperation getBackStencilStencilFailOperation(){ return backStencilStencilFailOperation; } + public StencilOperation getBackStencilDepthFailOperation(){ return backStencilDepthFailOperation; } + public StencilOperation getBackStencilDepthPassOperation(){ return backStencilDepthPassOperation; } + + public StencilFunction getFrontStencilFunction(){ return frontStencilFunction; } + public StencilFunction getBackStencilFunction(){ return backStencilFunction; } + public void setFaceCullMode(FaceCullMode cullMode) { applyCullMode = true; this.cullMode = cullMode; diff --git a/engine/src/core/com/jme3/renderer/RenderContext.java b/engine/src/core/com/jme3/renderer/RenderContext.java index b1c1b745d..3333af300 100644 --- a/engine/src/core/com/jme3/renderer/RenderContext.java +++ b/engine/src/core/com/jme3/renderer/RenderContext.java @@ -42,7 +42,7 @@ import com.jme3.texture.Texture; * internally to reduce state changes. NOTE: This class is specific to OpenGL. */ public class RenderContext { - + /** * If back-face culling is enabled. */ @@ -139,6 +139,19 @@ public class RenderContext { public int boundTextureUnit = 0; + /** + * Stencil Buffer state + */ + public boolean stencilTest = false; + public RenderState.StencilOperation frontStencilStencilFailOperation = RenderState.StencilOperation.Keep; + public RenderState.StencilOperation frontStencilDepthFailOperation = RenderState.StencilOperation.Keep; + public RenderState.StencilOperation frontStencilDepthPassOperation = RenderState.StencilOperation.Keep; + public RenderState.StencilOperation backStencilStencilFailOperation = RenderState.StencilOperation.Keep; + public RenderState.StencilOperation backStencilDepthFailOperation = RenderState.StencilOperation.Keep; + public RenderState.StencilOperation backStencilDepthPassOperation = RenderState.StencilOperation.Keep; + public RenderState.StencilFunction frontStencilFunction = RenderState.StencilFunction.Always; + public RenderState.StencilFunction backStencilFunction = RenderState.StencilFunction.Always; + /** * Vertex attribs currently bound and enabled. If a slot is null, then * it is disabled. diff --git a/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java b/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java index 7d8e80be6..a591dd6fa 100644 --- a/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java +++ b/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java @@ -324,6 +324,9 @@ public class LwjglGL1Renderer implements GL1Renderer { context.blendMode = state.getBlendMode(); } + if(state.isStencilTest()) + throw new UnsupportedOperationException("OpenGL 1.1 doesn't support two sided stencil operations."); + } public void setViewPort(int x, int y, int w, int h) { @@ -520,7 +523,7 @@ public class LwjglGL1Renderer implements GL1Renderer { if (!FastMath.isPowerOfTwo(img.getWidth()) || !FastMath.isPowerOfTwo(img.getHeight()) || img.getWidth() != img.getHeight()){ - + // Resize texture to Power-of-2 size MipMapGenerator.resizeToPowerOf2(img); @@ -685,7 +688,7 @@ public class LwjglGL1Renderer implements GL1Renderer { public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) { if (count > 1) throw new UnsupportedOperationException(); - + glDrawArrays(convertElementMode(mode), 0, vertCount); } @@ -800,7 +803,7 @@ public class LwjglGL1Renderer implements GL1Renderer { drawElements(elMode, fmt, indexData); - + curOffset += elementLength; }*/ } else { @@ -810,7 +813,7 @@ public class LwjglGL1Renderer implements GL1Renderer { } } - + public void clearVertexAttribs() { for (int i = 0; i < 16; i++) { @@ -895,7 +898,7 @@ public class LwjglGL1Renderer implements GL1Renderer { } statistics.onMeshDrawn(mesh, lod); - + // if (!dynamic) { // dealing with a static object, generate display list // renderMeshDisplayList(mesh); diff --git a/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java b/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java index 95ee90957..43eef1e38 100644 --- a/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java +++ b/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java @@ -170,7 +170,7 @@ public class LwjglRenderer implements Renderer { return caps; } - @SuppressWarnings("fallthrough") + @SuppressWarnings("fallthrough") public void initialize() { ContextCapabilities ctxCaps = GLContext.getCapabilities(); if (ctxCaps.OpenGL20) { @@ -471,7 +471,7 @@ public class LwjglRenderer implements Renderer { if (state.isDepthTest() && !context.depthTestEnabled) { glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); + glDepthFunc(GL_LESS); context.depthTestEnabled = true; } else if (!state.isDepthTest() && context.depthTestEnabled) { glDisable(GL_DEPTH_TEST); @@ -603,6 +603,97 @@ public class LwjglRenderer implements Renderer { context.blendMode = state.getBlendMode(); } + if(context.stencilTest!=state.isStencilTest() + || context.frontStencilStencilFailOperation!=state.getFrontStencilStencilFailOperation() + || context.frontStencilDepthFailOperation!=state.getFrontStencilDepthFailOperation() + || context.frontStencilDepthPassOperation!=state.getFrontStencilDepthPassOperation() + || context.backStencilStencilFailOperation!=state.getBackStencilStencilFailOperation() + || context.backStencilDepthFailOperation!=state.getBackStencilDepthFailOperation() + || context.backStencilDepthPassOperation!=state.getBackStencilDepthPassOperation() + || context.frontStencilFunction!=state.getFrontStencilFunction() + || context.backStencilFunction!=state.getBackStencilFunction() + ){ + + context.frontStencilStencilFailOperation=state.getFrontStencilStencilFailOperation(); //terrible looking, I know + context.frontStencilDepthFailOperation=state.getFrontStencilDepthFailOperation(); + context.frontStencilDepthPassOperation=state.getFrontStencilDepthPassOperation(); + context.backStencilStencilFailOperation=state.getBackStencilStencilFailOperation(); + context.backStencilDepthFailOperation=state.getBackStencilDepthFailOperation(); + context.backStencilDepthPassOperation=state.getBackStencilDepthPassOperation(); + context.frontStencilFunction=state.getFrontStencilFunction(); + context.backStencilFunction=state.getBackStencilFunction(); + + if(state.isStencilTest()){ + glEnable(GL_STENCIL_TEST); + glStencilOpSeparate(GL_FRONT, + glStencilOpFromStencilOp(state.getFrontStencilStencilFailOperation()), + glStencilOpFromStencilOp(state.getFrontStencilDepthFailOperation()), + glStencilOpFromStencilOp(state.getFrontStencilDepthPassOperation()) + ); + glStencilOpSeparate(GL_BACK, + glStencilOpFromStencilOp(state.getBackStencilStencilFailOperation()), + glStencilOpFromStencilOp(state.getBackStencilDepthFailOperation()), + glStencilOpFromStencilOp(state.getBackStencilDepthPassOperation()) + ); + glStencilFuncSeparate(GL_FRONT, + glStencilFuncFromStencilFunc(state.getFrontStencilFunction()), + 0,Integer.MAX_VALUE + ); + glStencilFuncSeparate(GL_BACK, + glStencilFuncFromStencilFunc(state.getBackStencilFunction()), + 0,Integer.MAX_VALUE + ); + }else{ + glDisable(GL_STENCIL_TEST); + } + } + + } + + private int glStencilOpFromStencilOp(RenderState.StencilOperation s){ + switch(s){ + case Keep: + return GL_KEEP; + case Zero: + return GL_ZERO; + case Replace: + return GL_REPLACE; + case Increment: + return GL_INCR; + case IncrementWrap: + return GL_INCR_WRAP; + case Decrement: + return GL_DECR; + case DecrementWrap: + return GL_DECR_WRAP; + case Invert: + return GL_INVERT; + default: + throw new UnsupportedOperationException("Unrecognized front stencil operation: " + s); + } //end switch + } + + private int glStencilFuncFromStencilFunc(RenderState.StencilFunction s){ + switch(s){ + case Never: + return GL_NEVER; + case Less: + return GL_LESS; + case LessEqual: + return GL_LEQUAL; + case Greater: + return GL_GREATER; + case GreaterEqual: + return GL_GEQUAL; + case Equal: + return GL_EQUAL; + case NotEqual: + return GL_NOTEQUAL; + case Always: + return GL_ALWAYS; + default: + throw new UnsupportedOperationException("Unrecognized front stencil functin: " + s); + } //end switch } /*********************************************************************\ @@ -1524,7 +1615,7 @@ public class LwjglRenderer implements Renderer { } } - @SuppressWarnings("fallthrough") + @SuppressWarnings("fallthrough") private void setupTextureParams(Texture tex) { Image image = tex.getImage(); int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1);