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);
}