From 55b8188d20acd72e750e7a6c9800067a62ea2a19 Mon Sep 17 00:00:00 2001 From: "rem..om" Date: Fri, 23 Mar 2012 09:46:52 +0000 Subject: [PATCH] Material has now a isEqual method that compares materialDef, material params and material additional render state git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9256 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../src/core/com/jme3/material/Material.java | 120 +++++++++--- .../core/com/jme3/material/RenderState.java | 177 +++++++++++++----- .../material/TestMaterialCompare.java | 109 +++++++++++ 3 files changed, 335 insertions(+), 71 deletions(-) create mode 100644 engine/src/test/jme3test/material/TestMaterialCompare.java diff --git a/engine/src/core/com/jme3/material/Material.java b/engine/src/core/com/jme3/material/Material.java index 8d43853b6..dd20d8b32 100644 --- a/engine/src/core/com/jme3/material/Material.java +++ b/engine/src/core/com/jme3/material/Material.java @@ -69,7 +69,6 @@ public class Material implements Asset, Cloneable, Savable, Comparable // Version #2: Fixed issue with RenderState.apply*** flags not getting exported public static final int SAVABLE_VERSION = 2; - private static final Logger logger = Logger.getLogger(Material.class.getName()); private static final RenderState additiveLight = new RenderState(); private static final RenderState depthOnly = new RenderState(); @@ -105,8 +104,8 @@ public class Material implements Asset, Cloneable, Savable, Comparable this.def = def; // Load default values from definition (if any) - for (MatParam param : def.getMaterialParams()){ - if (param.getValue() != null){ + for (MatParam param : def.getMaterialParams()) { + if (param.getValue() != null) { setParam(param.getName(), param.getVarType(), param.getValue()); } } @@ -133,14 +132,14 @@ public class Material implements Asset, Cloneable, Savable, Comparable public String getAssetName() { return key != null ? key.getName() : null; } - + /** * @return the name of the material (not the same as the asset name), the returned value can be null */ public String getName() { - return name; - } - + return name; + } + /** * This method sets the name of the material. * The name is not the same as the asset name. @@ -148,8 +147,8 @@ public class Material implements Asset, Cloneable, Savable, Comparable * @param name the name of the material */ public void setName(String name) { - this.name = name; - } + this.name = name; + } public void setKey(AssetKey key) { this.key = key; @@ -204,12 +203,12 @@ public class Material implements Asset, Cloneable, Savable, Comparable @Override public boolean equals(Object obj) { - if(obj instanceof Material){ - return ((Material)obj).compareTo(this) == 0; + if (obj instanceof Material) { + return ((Material) obj).compareTo(this) == 0; } return super.equals(obj); } - + /** * Clones this material. The result is returned. */ @@ -232,10 +231,85 @@ public class Material implements Asset, Cloneable, Savable, Comparable return mat; } catch (CloneNotSupportedException ex) { - throw new AssertionError(); + throw new AssertionError(ex); } } + /** + * Compares two materials and returns true if they are equal. + * This methods compare definition, params, additional render states + * The difference with the equals method is that it's truely comaring the objects. (equals compare the sort ids for geometry sorting) + * @param mat the material to cmpare to this material + * @return true if the materials are equal. + */ + public boolean isEqual(Material mat) { + //early exit if the material are the same object + if (this == mat) { + return true; + } + + + //comparing matrial definition + if (this.getMaterialDef() != mat.getMaterialDef()) { + return false; + } + + //early exit if the size of the params is different + if (paramValues.size() != mat.paramValues.size()) { + return false; + } + + //comparing params + for (String paramKey : paramValues.keySet()) { + MatParam paramOrig = getParam(paramKey); + MatParam param = mat.getParam(paramKey); + //this param does not exist in compared mat + if (param == null) { + return false; + } + + //params exist in both materials but they not of the same type + if (param.type != paramOrig.type) { + return false; + } + + if (param instanceof MatParamTexture) { + MatParamTexture tex = (MatParamTexture) param; + MatParamTexture texOrig = (MatParamTexture) paramOrig; + //comparing textures + if (getTextureId(tex) != getTextureId(texOrig)) { + return false; + } + } else { + if (!param.getValue().equals(paramOrig.getValue())) { + return false; + } + } + } + + + //comparing additional render states + if (additionalState == null) { + if (mat.additionalState != null) { + return false; + } + } else { + if (!additionalState.equals(mat.additionalState)) { + return false; + } + } + + + return true; + } + + private int getTextureId(MatParamTexture param) { + if (param.getTextureValue() != null && param.getTextureValue().getImage() != null) { + return param.getTextureValue().getImage().getId(); + } + return -1; + } + /** * Returns the currently active technique. *

@@ -386,7 +460,7 @@ public class Material implements Asset, Cloneable, Savable, Comparable if (type != null && paramDef.getVarType() != type) { logger.log(Level.WARNING, "Material parameter being set: {0} with " - + "type {1} doesn''t match definition types {2}", new Object[]{name, type.name(), paramDef.getVarType()} ); + + "type {1} doesn''t match definition types {2}", new Object[]{name, type.name(), paramDef.getVarType()}); } return newName; @@ -785,7 +859,7 @@ public class Material implements Asset, Cloneable, Savable, Comparable //We transform the spot directoin in view space here to save 5 varying later in the lighting shader //one vec4 less and a vec4 that becomes a vec3 //the downside is that spotAngleCos decoding happen now in the frag shader. - tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(),0); + tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0); rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec); tmpLightDirection.set(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos); @@ -1064,10 +1138,10 @@ public class Material implements Asset, Cloneable, Savable, Comparable boolean guessRenderStateApply = false; int ver = ic.getSavableVersion(Material.class); - if (ver < 1){ + if (ver < 1) { applyDefaultValues = true; } - if (ver < 2){ + if (ver < 2) { guessRenderStateApply = true; } if (im.getFormatVersion() == 0) { @@ -1117,24 +1191,24 @@ public class Material implements Asset, Cloneable, Savable, Comparable param.setName(checkSetParam(param.getVarType(), param.getName())); paramValues.put(param.getName(), param); } - - if (applyDefaultValues){ + + if (applyDefaultValues) { // compatability with old versions where default vars were // not available - for (MatParam param : def.getMaterialParams()){ - if (param.getValue() != null && paramValues.get(param.getName()) == null){ + for (MatParam param : def.getMaterialParams()) { + if (param.getValue() != null && paramValues.get(param.getName()) == null) { setParam(param.getName(), param.getVarType(), param.getValue()); } } } - if (guessRenderStateApply && additionalState != null){ + if (guessRenderStateApply && additionalState != null) { // Try to guess values of "apply" render state based on defaults // if value != default then set apply to true additionalState.applyPolyOffset = additionalState.offsetEnabled; additionalState.applyAlphaFallOff = additionalState.alphaTest; additionalState.applyAlphaTest = additionalState.alphaTest; additionalState.applyBlendMode = additionalState.blendMode != BlendMode.Off; - additionalState.applyColorWrite = !additionalState.colorWrite; + additionalState.applyColorWrite = !additionalState.colorWrite; additionalState.applyCullMode = additionalState.cullMode != FaceCullMode.Back; additionalState.applyDepthTest = !additionalState.depthTest; additionalState.applyDepthWrite = !additionalState.depthWrite; diff --git a/engine/src/core/com/jme3/material/RenderState.java b/engine/src/core/com/jme3/material/RenderState.java index 37897fd58..23e368a95 100644 --- a/engine/src/core/com/jme3/material/RenderState.java +++ b/engine/src/core/com/jme3/material/RenderState.java @@ -59,13 +59,11 @@ public class RenderState implements Cloneable, Savable { * */ public static final RenderState DEFAULT = new RenderState(); - /** * The NULL render state is identical to the {@link RenderState#DEFAULT} * render state except that depth testing and face culling are disabled. */ public static final RenderState NULL = new RenderState(); - /** * The ADDITIONAL render state is identical to the * {@link RenderState#DEFAULT} render state except that all apply @@ -232,13 +230,11 @@ public class RenderState implements Cloneable, Savable { * Replace the value in the stencil buffer with the reference value. */ Replace, - /** * Increment the value in the stencil buffer, clamp once reaching * the maximum value. */ Increment, - /** * Increment the value in the stencil buffer and wrap to 0 when * reaching the maximum value. @@ -253,7 +249,6 @@ public class RenderState implements Cloneable, Savable { * value when reaching 0. */ DecrementWrap, - /** * Does a bitwise invert of the value in the stencil buffer. */ @@ -277,39 +272,28 @@ public class RenderState implements Cloneable, Savable { ADDITIONAL.applyAlphaFallOff = false; ADDITIONAL.applyPolyOffset = false; } - boolean pointSprite = false; boolean applyPointSprite = true; - boolean wireframe = false; boolean applyWireFrame = true; - FaceCullMode cullMode = FaceCullMode.Back; boolean applyCullMode = true; - boolean depthWrite = true; boolean applyDepthWrite = true; - boolean depthTest = true; boolean applyDepthTest = true; - boolean colorWrite = true; boolean applyColorWrite = true; - BlendMode blendMode = BlendMode.Off; boolean applyBlendMode = true; - boolean alphaTest = false; boolean applyAlphaTest = true; - float alphaFallOff = 0; boolean applyAlphaFallOff = true; - float offsetFactor = 0; float offsetUnits = 0; boolean offsetEnabled = false; boolean applyPolyOffset = true; - boolean stencilTest = false; boolean applyStencilTest = false; StencilOperation frontStencilStencilFailOperation = StencilOperation.Keep; @@ -320,7 +304,7 @@ public class RenderState implements Cloneable, Savable { StencilOperation backStencilDepthPassOperation = StencilOperation.Keep; TestFunction frontStencilFunction = TestFunction.Always; TestFunction backStencilFunction = TestFunction.Always; - + public void write(JmeExporter ex) throws IOException { OutputCapsule oc = ex.getCapsule(this); oc.write(pointSprite, "pointSprite", false); @@ -344,19 +328,19 @@ public class RenderState implements Cloneable, Savable { oc.write(backStencilDepthPassOperation, "backStencilDepthPassOperation", StencilOperation.Keep); oc.write(frontStencilFunction, "frontStencilFunction", TestFunction.Always); oc.write(backStencilFunction, "backStencilFunction", TestFunction.Always); - + // Only "additional render state" has them set to false by default - oc.write(applyPointSprite, "applyPointSprite", true); - oc.write(applyWireFrame, "applyWireFrame", true); - oc.write(applyCullMode, "applyCullMode", true); - oc.write(applyDepthWrite, "applyDepthWrite", true); - oc.write(applyDepthTest, "applyDepthTest", true); - oc.write(applyColorWrite, "applyColorWrite", true); - oc.write(applyBlendMode, "applyBlendMode", true); - oc.write(applyAlphaTest, "applyAlphaTest", true); + oc.write(applyPointSprite, "applyPointSprite", true); + oc.write(applyWireFrame, "applyWireFrame", true); + oc.write(applyCullMode, "applyCullMode", true); + oc.write(applyDepthWrite, "applyDepthWrite", true); + oc.write(applyDepthTest, "applyDepthTest", true); + oc.write(applyColorWrite, "applyColorWrite", true); + oc.write(applyBlendMode, "applyBlendMode", true); + oc.write(applyAlphaTest, "applyAlphaTest", true); oc.write(applyAlphaFallOff, "applyAlphaFallOff", true); - oc.write(applyPolyOffset, "applyPolyOffset", true); - + oc.write(applyPolyOffset, "applyPolyOffset", true); + } public void read(JmeImporter im) throws IOException { @@ -382,17 +366,17 @@ 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); - - applyPointSprite = ic.readBoolean("applyPointSprite", true); - applyWireFrame = ic.readBoolean("applyWireFrame", true); - applyCullMode = ic.readBoolean("applyCullMode", true); - applyDepthWrite = ic.readBoolean("applyDepthWrite", true); - applyDepthTest = ic.readBoolean("applyDepthTest", true); - applyColorWrite = ic.readBoolean("applyColorWrite", true); - applyBlendMode = ic.readBoolean("applyBlendMode", true); - applyAlphaTest = ic.readBoolean("applyAlphaTest", true); + + applyPointSprite = ic.readBoolean("applyPointSprite", true); + applyWireFrame = ic.readBoolean("applyWireFrame", true); + applyCullMode = ic.readBoolean("applyCullMode", true); + applyDepthWrite = ic.readBoolean("applyDepthWrite", true); + applyDepthTest = ic.readBoolean("applyDepthTest", true); + applyColorWrite = ic.readBoolean("applyColorWrite", true); + applyBlendMode = ic.readBoolean("applyBlendMode", true); + applyAlphaTest = ic.readBoolean("applyAlphaTest", true); applyAlphaFallOff = ic.readBoolean("applyAlphaFallOff", true); - applyPolyOffset = ic.readBoolean("applyPolyOffset", true); + applyPolyOffset = ic.readBoolean("applyPolyOffset", true); } /** @@ -409,6 +393,103 @@ public class RenderState implements Cloneable, Savable { } } + /** + * returns true if the given renderState is equall to this one + * @param o the renderState to compate to + * @return true if the renderStates are equal + */ + @Override + public boolean equals(Object o) { + if (o == null) { + return false; + } + if (!(o instanceof RenderState)) { + return false; + } + RenderState rs = (RenderState) o; + if (pointSprite != rs.pointSprite) { + return false; + } + + if (wireframe != rs.wireframe) { + return false; + } + + if (cullMode != rs.cullMode) { + return false; + } + + if (depthWrite != rs.depthWrite) { + return false; + } + + if (depthTest != rs.depthTest) { + return false; + } + + if (colorWrite != rs.colorWrite) { + return false; + } + + if (blendMode != rs.blendMode) { + return false; + } + + if (alphaTest != rs.alphaTest) { + return false; + } + + if (alphaFallOff != rs.alphaFallOff) { + return false; + } + + if (offsetEnabled != rs.offsetEnabled) { + return false; + } + + if (offsetFactor != rs.offsetFactor) { + return false; + } + + if (offsetUnits != rs.offsetUnits) { + return false; + } + + if (stencilTest != rs.stencilTest) { + return false; + } + + if (stencilTest) { + if (frontStencilStencilFailOperation != rs.frontStencilStencilFailOperation) { + return false; + } + if (frontStencilDepthFailOperation != rs.frontStencilDepthFailOperation) { + return false; + } + if (frontStencilDepthPassOperation != rs.frontStencilDepthPassOperation) { + return false; + } + if (backStencilStencilFailOperation != rs.backStencilStencilFailOperation) { + return false; + } + if (backStencilDepthFailOperation != rs.backStencilDepthFailOperation) { + return false; + } + + if (backStencilDepthPassOperation != rs.backStencilDepthPassOperation) { + return false; + } + if (frontStencilFunction != rs.frontStencilFunction) { + return false; + } + if (backStencilFunction != rs.backStencilFunction) { + return false; + } + } + + return true; + } + /** * Enables point sprite mode. * @@ -1033,29 +1114,29 @@ public class RenderState implements Cloneable, Savable { state.offsetFactor = offsetFactor; state.offsetUnits = offsetUnits; } - if (additionalState.applyStencilTest){ + if (additionalState.applyStencilTest) { state.stencilTest = additionalState.stencilTest; state.frontStencilStencilFailOperation = additionalState.frontStencilStencilFailOperation; - state.frontStencilDepthFailOperation = additionalState.frontStencilDepthFailOperation; - state.frontStencilDepthPassOperation = additionalState.frontStencilDepthPassOperation; + state.frontStencilDepthFailOperation = additionalState.frontStencilDepthFailOperation; + state.frontStencilDepthPassOperation = additionalState.frontStencilDepthPassOperation; state.backStencilStencilFailOperation = additionalState.backStencilStencilFailOperation; - state.backStencilDepthFailOperation = additionalState.backStencilDepthFailOperation; - state.backStencilDepthPassOperation = additionalState.backStencilDepthPassOperation; + state.backStencilDepthFailOperation = additionalState.backStencilDepthFailOperation; + state.backStencilDepthPassOperation = additionalState.backStencilDepthPassOperation; state.frontStencilFunction = additionalState.frontStencilFunction; state.backStencilFunction = additionalState.backStencilFunction; - }else{ + } else { state.stencilTest = stencilTest; state.frontStencilStencilFailOperation = frontStencilStencilFailOperation; - state.frontStencilDepthFailOperation = frontStencilDepthFailOperation; - state.frontStencilDepthPassOperation = frontStencilDepthPassOperation; + state.frontStencilDepthFailOperation = frontStencilDepthFailOperation; + state.frontStencilDepthPassOperation = frontStencilDepthPassOperation; state.backStencilStencilFailOperation = backStencilStencilFailOperation; - state.backStencilDepthFailOperation = backStencilDepthFailOperation; - state.backStencilDepthPassOperation = backStencilDepthPassOperation; + state.backStencilDepthFailOperation = backStencilDepthFailOperation; + state.backStencilDepthPassOperation = backStencilDepthPassOperation; state.frontStencilFunction = frontStencilFunction; state.backStencilFunction = backStencilFunction; diff --git a/engine/src/test/jme3test/material/TestMaterialCompare.java b/engine/src/test/jme3test/material/TestMaterialCompare.java new file mode 100644 index 000000000..57b33cbe2 --- /dev/null +++ b/engine/src/test/jme3test/material/TestMaterialCompare.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * 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 'jMonkeyEngine' 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) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package jme3test.material; + +import com.jme3.app.SimpleApplication; +import com.jme3.material.Material; +import com.jme3.material.RenderState.BlendMode; +import com.jme3.math.ColorRGBA; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class TestMaterialCompare extends SimpleApplication { + + public static void main(String[] args) { + TestMaterialCompare app = new TestMaterialCompare(); + app.start(); + } + + @Override + public void simpleInitApp() { + + Logger.getLogger("com.jme3").setLevel(Level.SEVERE); + + //clonned mats + Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat1.setName("mat1"); + mat1.setColor("Color", ColorRGBA.Blue); + + Material mat2 = mat1.clone(); + mat2.setName("mat2"); + testMats(mat1,mat2,true); + + //clonned mat with different additional render state + Material mat3 = mat1.clone();; + mat3.setName("mat3"); + mat3.getAdditionalRenderState().setBlendMode(BlendMode.ModulateX2); + testMats(mat1,mat3,false); + + //two separately loaded materials + Material mat4 = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m"); + mat4.setName("mat4"); + Material mat5 = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m"); + mat5.setName("mat5"); + testMats(mat4,mat5,true); + + //two materials created the same way + Material mat6 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat6.setName("mat6"); + mat6.setColor("Color", ColorRGBA.Blue); + testMats(mat1,mat6,true); + + //changing a material param + mat6.setColor("Color", ColorRGBA.Green); + testMats(mat1,mat6,false); + + + } + + private void testMats(Material mat1, Material mat2, boolean expected) { + if (mat2.isEqual(mat1)) { + System.out.print(mat1.getName() + " equals " + mat2.getName()); + if(expected){ + System.out.println(" success"); + }else{ + System.out.println(" fail"); + } + }else{ + System.out.print(mat1.getName() + " is not equal " + mat2.getName()); + if(!expected){ + System.out.println(" success"); + }else{ + System.out.println(" fail"); + } + } + } + + @Override + public void simpleUpdate(float tpf) { + } +}