diff --git a/jme3-core/src/main/java/com/jme3/material/RenderState.java b/jme3-core/src/main/java/com/jme3/material/RenderState.java index c49744db1e..7f21e8abfa 100644 --- a/jme3-core/src/main/java/com/jme3/material/RenderState.java +++ b/jme3-core/src/main/java/com/jme3/material/RenderState.java @@ -1511,6 +1511,10 @@ public int contentHashCode() { hash = 79 * hash + (this.backStencilDepthPassOperation != null ? this.backStencilDepthPassOperation.hashCode() : 0); hash = 79 * hash + (this.frontStencilFunction != null ? this.frontStencilFunction.hashCode() : 0); hash = 79 * hash + (this.backStencilFunction != null ? this.backStencilFunction.hashCode() : 0); + hash = 79 * hash + (this.frontStencilMask); + hash = 79 * hash + (this.frontStencilReference); + hash = 79 * hash + (this.backStencilMask); + hash = 79 * hash + (this.backStencilReference); hash = 79 * hash + Float.floatToIntBits(this.lineWidth); hash = 79 * hash + this.sfactorRGB.hashCode(); @@ -1623,6 +1627,11 @@ public RenderState copyMergedTo(RenderState additionalState, RenderState state) state.frontStencilFunction = additionalState.frontStencilFunction; state.backStencilFunction = additionalState.backStencilFunction; + + state.frontStencilMask = additionalState.frontStencilMask; + state.frontStencilReference = additionalState.frontStencilMask; + state.backStencilMask = additionalState.backStencilMask; + state.backStencilReference = additionalState.backStencilMask; } else { state.stencilTest = stencilTest; @@ -1636,6 +1645,11 @@ public RenderState copyMergedTo(RenderState additionalState, RenderState state) state.frontStencilFunction = frontStencilFunction; state.backStencilFunction = backStencilFunction; + + state.frontStencilMask = frontStencilMask; + state.frontStencilReference = frontStencilMask; + state.backStencilMask = backStencilMask; + state.backStencilReference = backStencilMask; } if (additionalState.applyLineWidth) { state.lineWidth = additionalState.lineWidth; @@ -1665,6 +1679,10 @@ public void set(RenderState state) { backStencilDepthPassOperation = state.backStencilDepthPassOperation; frontStencilFunction = state.frontStencilFunction; backStencilFunction = state.backStencilFunction; + frontStencilMask = state.frontStencilMask; + frontStencilReference = state.frontStencilReference; + backStencilMask = state.backStencilMask; + backStencilReference = state.backStencilReference; blendEquationAlpha = state.blendEquationAlpha; blendEquation = state.blendEquation; depthFunc = state.depthFunc; diff --git a/jme3-examples/src/main/java/jme3test/stencil/TestStencilOutline.java b/jme3-examples/src/main/java/jme3test/stencil/TestStencilOutline.java new file mode 100644 index 0000000000..406b15621e --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/stencil/TestStencilOutline.java @@ -0,0 +1,151 @@ +package jme3test.stencil; + +import com.jme3.app.SimpleApplication; +import com.jme3.asset.AssetManager; +import com.jme3.material.Material; +import com.jme3.material.RenderState; +import com.jme3.math.ColorRGBA; +import com.jme3.post.FilterPostProcessor; +import com.jme3.post.filters.BloomFilter; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.renderer.queue.OpaqueComparator; +import com.jme3.renderer.queue.RenderQueue; +import com.jme3.scene.Geometry; +import com.jme3.scene.control.AbstractControl; +import com.jme3.scene.shape.Box; +import com.jme3.system.AppSettings; +import com.jme3.texture.Image; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class TestStencilOutline extends SimpleApplication { + + class OutlineComparator extends OpaqueComparator { + @Override + public int compare(Geometry o1, Geometry o2) { + boolean ol1 = o1.getUserData("Outline") != null; + boolean ol2 = o2.getUserData("Outline") != null; + if (ol1 == ol2) { + return super.compare(o1, o2); + } else { + if (ol1) { + return 1; + } else { + return -1; + } + } + } + } + + class OutlineControl extends AbstractControl { + private Material outlineMaterial; + + public OutlineControl(AssetManager assetManager, ColorRGBA colorRGBA) { + outlineMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + outlineMaterial.setColor("Color", colorRGBA); + outlineMaterial.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Front); + outlineMaterial.getAdditionalRenderState().setDepthFunc(RenderState.TestFunction.Always); + outlineMaterial.getAdditionalRenderState().setStencil(true, + RenderState.StencilOperation.Keep, //front triangle fails stencil test + RenderState.StencilOperation.Keep, //front triangle fails depth test + RenderState.StencilOperation.Keep, //front triangle passes depth test + RenderState.StencilOperation.Keep, //back triangle fails stencil test + RenderState.StencilOperation.Keep, //back triangle fails depth test + RenderState.StencilOperation.Keep, //back triangle passes depth test + RenderState.TestFunction.NotEqual, //front triangle stencil test function + RenderState.TestFunction.NotEqual); //back triangle stencil test function + outlineMaterial.getAdditionalRenderState().setFrontStencilReference(1); + outlineMaterial.getAdditionalRenderState().setBackStencilReference(1); + outlineMaterial.getAdditionalRenderState().setFrontStencilMask(0xFF); + outlineMaterial.getAdditionalRenderState().setBackStencilMask(0xFF); + } + + @Override + protected void controlUpdate(float v) { + + } + + @Override + protected void controlRender(RenderManager renderManager, ViewPort viewPort) { + if (spatial instanceof Geometry) { + Geometry geometry= (Geometry) spatial; + Geometry clone = geometry.clone(); + clone.scale(1.1f); + clone.setUserData("Outline", true); + clone.setMaterial(outlineMaterial); + clone.updateGeometricState(); + viewPort.getQueue().addToQueue(clone, RenderQueue.Bucket.Opaque); + } + } + } + + @Override + public void update() { + super.update(); + } + + @Override + public void simpleInitApp() { + flyCam.setMoveSpeed(24); + flyCam.setZoomSpeed(-5); + viewPort.getQueue().setGeometryComparator(RenderQueue.Bucket.Opaque, new OutlineComparator()); + viewPort.setClearFlags(true,true,true); + Material boxMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + boxMat.getAdditionalRenderState().setStencil(true, + RenderState.StencilOperation.Keep, //front triangle fails stencil test + RenderState.StencilOperation.Replace, //front triangle fails depth test + RenderState.StencilOperation.Replace, //front triangle passes depth test + RenderState.StencilOperation.Keep, //back triangle fails stencil test + RenderState.StencilOperation.Replace, //back triangle fails depth test + RenderState.StencilOperation.Replace, //back triangle passes depth test + RenderState.TestFunction.Always, //front triangle stencil test function + RenderState.TestFunction.Always); //back triangle stencil test function + boxMat.getAdditionalRenderState().setFrontStencilReference(1); + boxMat.getAdditionalRenderState().setBackStencilReference(1); + boxMat.getAdditionalRenderState().setFrontStencilMask(0xFF); + boxMat.getAdditionalRenderState().setBackStencilMask(0xFF); + boxMat.setTexture("ColorMap", assetManager.loadTexture("Common/Textures/MissingTexture.png")); + + Material floorMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + floorMat.setTexture("ColorMap", assetManager.loadTexture("Common/Textures/MissingTexture.png")); + + Geometry floor = new Geometry("Floor", new Box(10f, 0, 10f)); + floor.setMaterial(floorMat); + rootNode.attachChild(floor); + + Geometry box1 = new Geometry("Box1", new Box(0.5f, 0.5f, 0.5f)); + box1.setLocalTranslation(3, 1.5f, 0); + box1.setLocalScale(4); + box1.addControl(new OutlineControl(assetManager, ColorRGBA.Blue)); + box1.setMaterial(boxMat); + Geometry box2 = new Geometry("Box2", new Box(0.5f, 0.5f, 0.5f)); + box2.setLocalTranslation(-3, 1.5f, 0); + box2.setLocalScale(3); + box2.addControl(new OutlineControl(assetManager,ColorRGBA.Red)); + box2.setMaterial(boxMat); + + rootNode.attachChild(box1); + rootNode.attachChild(box2); + + //This is to make sure a depth stencil format is used in the TestChooser app. + FilterPostProcessor postProcessor=new FilterPostProcessor(assetManager); + postProcessor.setFrameBufferDepthFormat(Image.Format.Depth24Stencil8); + viewPort.addProcessor(postProcessor); + postProcessor.addFilter(new BloomFilter()); + } + + + public static void main(String[] args) { + Logger.getLogger("").setLevel(Level.FINEST); + TestStencilOutline app = new TestStencilOutline(); + AppSettings settings = new AppSettings(true); + settings.setGraphicsDebug(true); + settings.setDepthBits(24); + settings.setStencilBits(8); + app.setSettings(settings); + app.setShowSettings(false); + app.start(); + } +}