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 7ff4a992a..ba7214e58 100644 --- a/jme3-core/src/main/java/com/jme3/material/RenderState.java +++ b/jme3-core/src/main/java/com/jme3/material/RenderState.java @@ -120,6 +120,92 @@ public class RenderState implements Cloneable, Savable { */ Always,} + /** + * BlendEquation specifies the blending equation to combine + * pixels. + */ + public enum BlendEquation { + /** + * Sets the blend equation so that the source and destination data are + * added. (Default) Clamps to [0,1] Useful for things like antialiasing + * and transparency. + */ + Add, + /** + * Sets the blend equation so that the source and destination data are + * subtracted (Src - Dest). Clamps to [0,1] Falls back to Add if + * supportsSubtract is false. + */ + Subtract, + /** + * Same as Subtract, but the order is reversed (Dst - Src). Clamps to + * [0,1] Falls back to Add if supportsSubtract is false. + */ + ReverseSubtract, + /** + * Sets the blend equation so that each component of the result color is + * the minimum of the corresponding components of the source and + * destination colors. This and Max are useful for applications that + * analyze image data (image thresholding against a constant color, for + * example). Falls back to Add if supportsMinMax is false. + */ + Min, + /** + * Sets the blend equation so that each component of the result color is + * the maximum of the corresponding components of the source and + * destination colors. This and Min are useful for applications that + * analyze image data (image thresholding against a constant color, for + * example). Falls back to Add if supportsMinMax is false. + */ + Max + } + + /** + * BlendEquationAlpha specifies the blending equation to + * combine pixels for the alpha component. + */ + public enum BlendEquationAlpha { + /** + * Sets the blend equation to be the same as the one defined by + * {@link #blendEquation}. + * + */ + InheritColor, + /** + * Sets the blend equation so that the source and destination data are + * added. (Default) Clamps to [0,1] Useful for things like antialiasing + * and transparency. + */ + Add, + /** + * Sets the blend equation so that the source and destination data are + * subtracted (Src - Dest). Clamps to [0,1] Falls back to Add if + * supportsSubtract is false. + */ + Subtract, + /** + * Same as Subtract, but the order is reversed (Dst - Src). Clamps to + * [0,1] Falls back to Add if supportsSubtract is false. + */ + ReverseSubtract, + /** + * Sets the blend equation so that the result alpha is the minimum of + * the source alpha and destination alpha. This and Max are useful for + * applications that analyze image data (image thresholding against a + * constant color, for example). Falls back to Add if supportsMinMax is + * false. + */ + Min, + /** + * sSets the blend equation so that the result alpha is the maximum of + * the source alpha and destination alpha. This and Min are useful for + * applications that analyze image data (image thresholding against a + * constant color, for example). Falls back to Add if supportsMinMax is + * false. + */ + Max + } + /** * BlendMode specifies the blending operation to use. * @@ -282,6 +368,8 @@ public class RenderState implements Cloneable, Savable { ADDITIONAL.applyDepthWrite = false; ADDITIONAL.applyDepthTest = false; ADDITIONAL.applyColorWrite = false; + ADDITIONAL.applyBlendEquation = false; + ADDITIONAL.applyBlendEquationAlpha = false; ADDITIONAL.applyBlendMode = false; ADDITIONAL.applyAlphaTest = false; ADDITIONAL.applyAlphaFallOff = false; @@ -299,6 +387,10 @@ public class RenderState implements Cloneable, Savable { boolean applyDepthTest = true; boolean colorWrite = true; boolean applyColorWrite = true; + BlendEquation blendEquation = BlendEquation.Add; + boolean applyBlendEquation = true; + BlendEquationAlpha blendEquationAlpha = BlendEquationAlpha.InheritColor; + boolean applyBlendEquationAlpha = true; BlendMode blendMode = BlendMode.Off; boolean applyBlendMode = true; boolean alphaTest = false; @@ -352,6 +444,8 @@ public class RenderState implements Cloneable, Savable { oc.write(backStencilDepthPassOperation, "backStencilDepthPassOperation", StencilOperation.Keep); oc.write(frontStencilFunction, "frontStencilFunction", TestFunction.Always); oc.write(backStencilFunction, "backStencilFunction", TestFunction.Always); + oc.write(blendEquation, "blendEquation", BlendEquation.Add); + oc.write(blendEquationAlpha, "blendEquationAlpha", BlendEquationAlpha.InheritColor); oc.write(depthFunc, "depthFunc", TestFunction.LessOrEqual); oc.write(alphaFunc, "alphaFunc", TestFunction.Greater); oc.write(lineWidth, "lineWidth", 1); @@ -363,6 +457,8 @@ public class RenderState implements Cloneable, Savable { oc.write(applyDepthWrite, "applyDepthWrite", true); oc.write(applyDepthTest, "applyDepthTest", true); oc.write(applyColorWrite, "applyColorWrite", true); + oc.write(applyBlendEquation, "applyBlendEquation", true); + oc.write(applyBlendEquationAlpha, "applyBlendEquationAlpha", true); oc.write(applyBlendMode, "applyBlendMode", true); oc.write(applyAlphaTest, "applyAlphaTest", true); oc.write(applyAlphaFallOff, "applyAlphaFallOff", true); @@ -396,6 +492,8 @@ public class RenderState implements Cloneable, Savable { backStencilDepthPassOperation = ic.readEnum("backStencilDepthPassOperation", StencilOperation.class, StencilOperation.Keep); frontStencilFunction = ic.readEnum("frontStencilFunction", TestFunction.class, TestFunction.Always); backStencilFunction = ic.readEnum("backStencilFunction", TestFunction.class, TestFunction.Always); + blendEquation = ic.readEnum("blendEquation", BlendEquation.class, BlendEquation.Add); + blendEquationAlpha = ic.readEnum("blendEquationAlpha", BlendEquationAlpha.class, BlendEquationAlpha.InheritColor); depthFunc = ic.readEnum("depthFunc", TestFunction.class, TestFunction.LessOrEqual); alphaFunc = ic.readEnum("alphaFunc", TestFunction.class, TestFunction.Greater); lineWidth = ic.readFloat("lineWidth", 1); @@ -407,6 +505,8 @@ public class RenderState implements Cloneable, Savable { applyDepthWrite = ic.readBoolean("applyDepthWrite", true); applyDepthTest = ic.readBoolean("applyDepthTest", true); applyColorWrite = ic.readBoolean("applyColorWrite", true); + applyBlendEquation = ic.readBoolean("applyBlendEquation", true); + applyBlendEquationAlpha = ic.readBoolean("applyBlendEquationAlpha", true); applyBlendMode = ic.readBoolean("applyBlendMode", true); applyAlphaTest = ic.readBoolean("applyAlphaTest", true); applyAlphaFallOff = ic.readBoolean("applyAlphaFallOff", true); @@ -433,8 +533,8 @@ public class RenderState implements Cloneable, Savable { } /** - * returns true if the given renderState is equall to this one - * @param o the renderState to compate to + * returns true if the given renderState is equal to this one + * @param o the renderState to compare to * @return true if the renderStates are equal */ @Override @@ -475,6 +575,14 @@ public class RenderState implements Cloneable, Savable { return false; } + if (blendEquation != rs.blendEquation) { + return false; + } + + if (blendEquationAlpha != rs.blendEquationAlpha) { + return false; + } + if (blendMode != rs.blendMode) { return false; } @@ -663,6 +771,61 @@ public class RenderState implements Cloneable, Savable { cachedHashCode = -1; } + /** + * Set the blending equation. + *

+ * When blending is enabled, (blendMode is not + * {@link BlendMode#Off}) the input pixel will be blended with the pixel + * already in the color buffer. The blending equation is determined by the + * {@link BlendEquation}. For example, the mode {@link BlendMode#Additive} + * and {@link BlendEquation#Add} will add the input pixel's color to the + * color already in the color buffer: + *
+ * Result = Source Color + Destination Color + *
+ * However, the mode {@link BlendMode#Additive} + * and {@link BlendEquation#Subtract} will subtract the input pixel's color to the + * color already in the color buffer: + *
+ * Result = Source Color - Destination Color + * + * @param blendEquation The blend equation to use. + */ + public void setBlendEquation(BlendEquation blendEquation) { + applyBlendEquation = true; + this.blendEquation = blendEquation; + cachedHashCode = -1; + } + + /** + * Set the blending equation for the alpha component. + *

+ * When blending is enabled, (blendMode is not + * {@link BlendMode#Off}) the input pixel will be blended with the pixel + * already in the color buffer. The blending equation is determined by the + * {@link BlendEquation} and can be overrode for the alpha component using + * the {@link BlendEquationAlpha} . For example, the mode + * {@link BlendMode#Additive} and {@link BlendEquationAlpha#Add} will add + * the input pixel's alpha to the alpha component already in the color + * buffer: + *
+ * Result = Source Alpha + Destination Alpha + *
+ * However, the mode {@link BlendMode#Additive} and + * {@link BlendEquationAlpha#Subtract} will subtract the input pixel's alpha + * to the alpha component already in the color buffer: + *
+ * Result = Source Alpha - Destination Alpha + * + * @param blendEquationAlpha The blend equation to use for the alpha + * component. + */ + public void setBlendEquationAlpha(BlendEquationAlpha blendEquationAlpha) { + applyBlendEquationAlpha = true; + this.blendEquationAlpha = blendEquationAlpha; + cachedHashCode = -1; + } + /** * Enable depth testing. * @@ -991,6 +1154,24 @@ public class RenderState implements Cloneable, Savable { return backStencilFunction; } + /** + * Retrieve the blend equation. + * + * @return the blend equation. + */ + public BlendEquation getBlendEquation() { + return blendEquation; + } + + /** + * Retrieve the blend equation used for the alpha component. + * + * @return the blend equation for the alpha component. + */ + public BlendEquationAlpha getBlendEquationAlpha() { + return blendEquationAlpha; + } + /** * Retrieve the blend mode. * @@ -1165,6 +1346,14 @@ public class RenderState implements Cloneable, Savable { return applyBlendMode; } + public boolean isApplyBlendEquation() { + return applyBlendEquation; + } + + public boolean isApplyBlendEquationAlpha() { + return applyBlendEquationAlpha; + } + public boolean isApplyColorWrite() { return applyColorWrite; } @@ -1219,6 +1408,8 @@ public class RenderState implements Cloneable, Savable { hash = 79 * hash + (this.depthFunc != null ? this.depthFunc.hashCode() : 0); hash = 79 * hash + (this.colorWrite ? 1 : 0); hash = 79 * hash + (this.blendMode != null ? this.blendMode.hashCode() : 0); + hash = 79 * hash + (this.blendEquation != null ? this.blendEquation.hashCode() : 0); + hash = 79 * hash + (this.blendEquationAlpha != null ? this.blendEquationAlpha.hashCode() : 0); hash = 79 * hash + (this.alphaTest ? 1 : 0); hash = 79 * hash + (this.alphaFunc != null ? this.alphaFunc.hashCode() : 0); hash = 79 * hash + Float.floatToIntBits(this.alphaFallOff); @@ -1302,6 +1493,16 @@ public class RenderState implements Cloneable, Savable { } else { state.colorWrite = colorWrite; } + if (additionalState.applyBlendEquation) { + state.blendEquation = additionalState.blendEquation; + } else { + state.blendEquation = blendEquation; + } + if (additionalState.applyBlendEquationAlpha) { + state.blendEquationAlpha = additionalState.blendEquationAlpha; + } else { + state.blendEquation = blendEquation; + } if (additionalState.applyBlendMode) { state.blendMode = additionalState.blendMode; } else { @@ -1389,6 +1590,8 @@ public class RenderState implements Cloneable, Savable { backStencilDepthPassOperation = state.backStencilDepthPassOperation; frontStencilFunction = state.frontStencilFunction; backStencilFunction = state.backStencilFunction; + blendEquationAlpha = state.blendEquationAlpha; + blendEquation = state.blendEquation; depthFunc = state.depthFunc; alphaFunc = state.alphaFunc; lineWidth = state.lineWidth; @@ -1399,6 +1602,8 @@ public class RenderState implements Cloneable, Savable { applyDepthWrite = true; applyDepthTest = true; applyColorWrite = true; + applyBlendEquation = true; + applyBlendEquationAlpha = true; applyBlendMode = true; applyAlphaTest = true; applyAlphaFallOff = true; @@ -1424,6 +1629,9 @@ public class RenderState implements Cloneable, Savable { + "\napplyDepthTest=" + applyDepthTest + "\ncolorWrite=" + colorWrite + "\napplyColorWrite=" + applyColorWrite + + "\nblendEquation=" + blendEquation + + "\napplyBlendEquation=" + applyBlendEquation + + "\napplyBlendEquationAlpha=" + applyBlendEquationAlpha + "\nblendMode=" + blendMode + "\napplyBlendMode=" + applyBlendMode + "\nalphaTest=" + alphaTest diff --git a/jme3-core/src/main/java/com/jme3/renderer/RenderContext.java b/jme3-core/src/main/java/com/jme3/renderer/RenderContext.java index 5be184c94..ac44e438d 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/RenderContext.java +++ b/jme3-core/src/main/java/com/jme3/renderer/RenderContext.java @@ -110,6 +110,16 @@ public class RenderContext { */ public RenderState.BlendMode blendMode = RenderState.BlendMode.Off; + /** + * @see RenderState#setBlendEquation(com.jme3.material.RenderState.BlendEquation) + */ + public RenderState.BlendEquation blendEquation = RenderState.BlendEquation.Add; + + /** + * @see RenderState#setBlendEquationAlpha(com.jme3.material.RenderState.BlendEquationAlpha) + */ + public RenderState.BlendEquationAlpha blendEquationAlpha = RenderState.BlendEquationAlpha.InheritColor; + /** * @see RenderState#setWireframe(boolean) */ @@ -270,6 +280,8 @@ public class RenderContext { polyOffsetUnits = 0; pointSize = 1; blendMode = RenderState.BlendMode.Off; + blendEquation = RenderState.BlendEquation.Add; + blendEquationAlpha = RenderState.BlendEquationAlpha.InheritColor; wireframe = false; boundShaderProgram = 0; boundShader = null; diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL2.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL2.java index abf6a77d7..d6e53b14d 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL2.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL2.java @@ -71,8 +71,14 @@ public interface GL2 extends GL { public static final int GL_TEXTURE_WRAP_R = 0x8072; public static final int GL_VERTEX_PROGRAM_POINT_SIZE = 0x8642; public static final int GL_UNSIGNED_INT_8_8_8_8 = 0x8035; + public static final int GL_FUNC_ADD = 0x8006; + public static final int GL_FUNC_SUBTRACT = 0x800A; + public static final int GL_FUNC_REVERSE_SUBTRACT = 0x800B; + public static final int GL_MIN = 0x8007; + public static final int GL_MAX = 0x8008; public void glAlphaFunc(int func, float ref); + public void glBlendEquationSeparate(int colorMode, int alphaMode); public void glPointSize(float size); public void glPolygonMode(int face, int mode); public void glDrawBuffer(int mode); diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugDesktop.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugDesktop.java index fd18cc7ff..3c39c8036 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugDesktop.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugDesktop.java @@ -100,4 +100,9 @@ public class GLDebugDesktop extends GLDebugES implements GL2, GL3, GL4 { gl3.glFramebufferTextureLayer(param1, param2, param3, param4, param5); checkError(); } + + public void glBlendEquationSeparate(int colorMode, int alphaMode) { + gl2.glBlendEquationSeparate(colorMode, alphaMode); + checkError(); + } } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java index 7ef77a2b6..73e55b0e2 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java @@ -746,6 +746,18 @@ public final class GLRenderer implements Renderer { throw new UnsupportedOperationException("Unrecognized blend mode: " + state.getBlendMode()); } + + if (gl2 != null && (state.getBlendEquation() != context.blendEquation || state.getBlendEquationAlpha() != context.blendEquationAlpha)) { + int colorMode = convertBlendEquation(state.getBlendEquation()); + int alphaMode; + if (state.getBlendEquationAlpha() == RenderState.BlendEquationAlpha.InheritColor) { + alphaMode = colorMode; + } else { + alphaMode = convertBlendEquationAlpha(state.getBlendEquationAlpha()); + } + gl2.glBlendEquationSeparate(colorMode, alphaMode); + context.blendEquation = state.getBlendEquation(); + } } context.blendMode = state.getBlendMode(); @@ -796,6 +808,41 @@ public final class GLRenderer implements Renderer { } } + private int convertBlendEquation(RenderState.BlendEquation blendEquation) { + switch (blendEquation) { + case Add: + return GL2.GL_FUNC_ADD; + case Subtract: + return GL2.GL_FUNC_SUBTRACT; + case ReverseSubtract: + return GL2.GL_FUNC_REVERSE_SUBTRACT; + case Min: + return GL2.GL_MIN; + case Max: + return GL2.GL_MAX; + default: + throw new UnsupportedOperationException("Unrecognized blend operation: " + blendEquation); + } + } + + private int convertBlendEquationAlpha(RenderState.BlendEquationAlpha blendEquationAlpha) { + //Note: InheritColor mode should already be handled, that is why it does not belong the the switch case. + switch (blendEquationAlpha) { + case Add: + return GL2.GL_FUNC_ADD; + case Subtract: + return GL2.GL_FUNC_SUBTRACT; + case ReverseSubtract: + return GL2.GL_FUNC_REVERSE_SUBTRACT; + case Min: + return GL2.GL_MIN; + case Max: + return GL2.GL_MAX; + default: + throw new UnsupportedOperationException("Unrecognized alpha blend operation: " + blendEquationAlpha); + } + } + private int convertStencilOperation(StencilOperation stencilOp) { switch (stencilOp) { case Keep: diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java index 8b0f831c5..9657df3f2 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java @@ -36,6 +36,7 @@ import com.jme3.material.logic.SinglePassLightingLogic; import com.jme3.material.logic.DefaultTechniqueDefLogic; import com.jme3.asset.*; import com.jme3.material.*; +import com.jme3.material.RenderState.BlendEquation; import com.jme3.material.RenderState.BlendMode; import com.jme3.material.RenderState.FaceCullMode; import com.jme3.material.TechniqueDef.LightMode; @@ -452,6 +453,10 @@ public class J3MLoader implements AssetLoader { renderState.setDepthTest(parseBoolean(split[1])); }else if (split[0].equals("Blend")){ renderState.setBlendMode(BlendMode.valueOf(split[1])); + }else if (split[0].equals("BlendEquation")){ + renderState.setBlendEquation(BlendEquation.valueOf(split[1])); + }else if (split[0].equals("BlendEquationAlpha")){ + renderState.setBlendEquationAlpha(RenderState.BlendEquationAlpha.valueOf(split[1])); }else if (split[0].equals("AlphaTestFalloff")){ renderState.setAlphaTest(true); renderState.setAlphaFallOff(Float.parseFloat(split[1])); diff --git a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL.java b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL.java index 2ca2fb266..8b222d751 100644 --- a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL.java +++ b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL.java @@ -78,6 +78,11 @@ public class JoglGL implements GL, GL2, GL3, GL4 { GLContext.getCurrentGL().glBindTexture(param1, param2); } + @Override + public void glBlendEquationSeparate(int colorMode, int alphaMode){ + GLContext.getCurrentGL().glBlendEquationSeparate(colorMode, alphaMode); + } + @Override public void glBlendFunc(int param1, int param2) { GLContext.getCurrentGL().glBlendFunc(param1, param2); diff --git a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java index 6f640042f..abc419c41 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java +++ b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java @@ -50,6 +50,10 @@ public final class LwjglGL implements GL, GL2, GL3, GL4 { GL11.glBindTexture(param1, param2); } + public void glBlendEquationSeparate(int colorMode, int alphaMode){ + GL20.glBlendEquationSeparate(colorMode,alphaMode); + } + public void glBlendFunc(int param1, int param2) { GL11.glBlendFunc(param1, param2); } diff --git a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java index 25de6b148..10358c009 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java @@ -79,6 +79,10 @@ public class LwjglGL implements GL, GL2, GL3, GL4 { GL11.glBindTexture(param1, param2); } + public void glBlendEquationSeparate(int colorMode, int alphaMode){ + GL20.glBlendEquationSeparate(colorMode,alphaMode); + } + public void glBlendFunc(int param1, int param2) { GL11.glBlendFunc(param1, param2); }