Переглянути джерело

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
rem..om 13 роки тому
батько
коміт
55b8188d20

+ 97 - 23
engine/src/core/com/jme3/material/Material.java

@@ -69,7 +69,6 @@ public class Material implements Asset, Cloneable, Savable, Comparable<Material>
 
     // 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<Material>
         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<Material>
     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<Material>
      * @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<Material>
 
     @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<Material>
 
             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.
      * <p>
@@ -386,7 +460,7 @@ public class Material implements Asset, Cloneable, Savable, Comparable<Material>
 
         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<Material>
                     //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<Material>
         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<Material>
             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;

+ 129 - 48
engine/src/core/com/jme3/material/RenderState.java

@@ -59,13 +59,11 @@ public class RenderState implements Cloneable, Savable {
      * </ul>
      */
     public static final RenderState DEFAULT = new RenderState();
-
     /**
      * The <code>NULL</code> 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 <code>ADDITIONAL</code> 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;

+ 109 - 0
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) {
+    }
+}