diff --git a/jme3-core/src/main/java/com/jme3/material/Material.java b/jme3-core/src/main/java/com/jme3/material/Material.java index 59d67f5fe..adc7669e2 100644 --- a/jme3-core/src/main/java/com/jme3/material/Material.java +++ b/jme3-core/src/main/java/com/jme3/material/Material.java @@ -785,12 +785,36 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { sortingId = -1; } - private void updateShaderMaterialParameters(Renderer renderer, Shader shader) { + private void updateShaderMaterialParameters(Renderer renderer, Shader shader, ArrayList overrides) { int unit = 0; + + for (MatParamOverride override : overrides) { + VarType type = override.getVarType(); + + MatParam paramDef = def.getMaterialParam(override.getName()); + if (paramDef == null || paramDef.getVarType() != type) { + continue; + } + + Uniform uniform = shader.getUniform(override.getPrefixedName()); + if (type.isTextureType()) { + renderer.setTexture(unit, (Texture) override.getValue()); + uniform.setValue(VarType.Int, unit); + unit++; + } else { + uniform.setValue(type, override.getValue()); + } + } + for (int i = 0; i < paramValues.size(); i++) { MatParam param = paramValues.getValue(i); VarType type = param.getVarType(); Uniform uniform = shader.getUniform(param.getPrefixedName()); + + if (uniform.isSetByCurrentMaterial()) { + continue; + } + if (type.isTextureType()) { renderer.setTexture(unit, (Texture) param.getValue()); uniform.setValue(VarType.Int, unit); @@ -799,8 +823,9 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { uniform.setValue(type, param.getValue()); } } + } - + private void updateRenderState(RenderManager renderManager, Renderer renderer, TechniqueDef techniqueDef) { if (renderManager.getForcedRenderState() != null) { renderer.applyRenderState(renderManager.getForcedRenderState()); @@ -835,7 +860,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { } Shader shader = technique.makeCurrent(renderManager, null, null, rendererCaps); - updateShaderMaterialParameters(renderer, shader); + updateShaderMaterialParameters(renderer, shader, null); renderManager.getRenderer().setShader(shader); } @@ -951,7 +976,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { renderManager.updateUniformBindings(shader); // Set material parameters - updateShaderMaterialParameters(renderer, shader); + updateShaderMaterialParameters(renderer, shader, geometry.getWorldOverrides()); // Clear any uniforms not changed by material. resetUniformsNotSetByCurrent(shader); diff --git a/jme3-core/src/main/java/com/jme3/material/Technique.java b/jme3-core/src/main/java/com/jme3/material/Technique.java index 8f26a300d..9dee91241 100644 --- a/jme3-core/src/main/java/com/jme3/material/Technique.java +++ b/jme3-core/src/main/java/com/jme3/material/Technique.java @@ -44,7 +44,6 @@ import com.jme3.shader.VarType; import com.jme3.util.ListMap; import java.util.ArrayList; import java.util.EnumSet; -import java.util.List; /** * Represents a technique instance. @@ -53,6 +52,7 @@ public final class Technique { private final TechniqueDef def; private final Material owner; + private final DefineList paramDefines; private final DefineList dynamicDefines; /** @@ -65,6 +65,7 @@ public final class Technique { public Technique(Material owner, TechniqueDef def) { this.owner = owner; this.def = def; + this.paramDefines = def.createDefineList(); this.dynamicDefines = def.createDefineList(); } @@ -83,13 +84,14 @@ public final class Technique { * Called by the material to tell the technique a parameter was modified. * Specify null for value if the param is to be cleared. */ - void notifyParamChanged(String paramName, VarType type, Object value) { + final void notifyParamChanged(String paramName, VarType type, Object value) { Integer defineId = def.getShaderParamDefineId(paramName); + if (defineId == null) { return; } - dynamicDefines.set(defineId, type, value); + paramDefines.set(defineId, type, value); } /** @@ -98,9 +100,9 @@ public final class Technique { * The technique updates dynamic defines based on the * currently set material parameters. */ - void notifyTechniqueSwitched() { + final void notifyTechniqueSwitched() { ListMap paramMap = owner.getParamsMap(); - dynamicDefines.clear(); + paramDefines.clear(); for (int i = 0; i < paramMap.size(); i++) { MatParam param = paramMap.getValue(i); notifyParamChanged(param.getName(), param.getVarType(), param.getValue()); @@ -122,18 +124,19 @@ public final class Technique { TechniqueDefLogic logic = def.getLogic(); AssetManager assetManager = owner.getMaterialDef().getAssetManager(); - // TODO: remove allocation - DefineList combinedDefines = def.createDefineList(); - combinedDefines.setAll(dynamicDefines); + dynamicDefines.clear(); + dynamicDefines.setAll(paramDefines); for (MatParamOverride override : overrides) { Integer defineId = def.getShaderParamDefineId(override.name); if (defineId != null) { - combinedDefines.set(defineId, override.type, override.value); + if (def.getDefineIdType(defineId) == override.type) { + dynamicDefines.set(defineId, override.type, override.value); + } } } - return logic.makeCurrent(assetManager, renderManager, rendererCaps, lights, combinedDefines); + return logic.makeCurrent(assetManager, renderManager, rendererCaps, lights, dynamicDefines); } /** @@ -163,8 +166,10 @@ public final class Technique { } /** - * @deprecated Preset defines are precompiled into - * {@link TechniqueDef#getShaderPrologue()}, whereas + * @return nothing. + * + * @deprecated Preset defines are precompiled into + * {@link TechniqueDef#getShaderPrologue()}, whereas * dynamic defines are available via {@link #getParamDefines()}. */ @Deprecated diff --git a/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java b/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java index 93add2c7b..6f499b271 100644 --- a/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java +++ b/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java @@ -340,6 +340,15 @@ public class TechniqueDef implements Savable { return paramToDefineId.get(paramName); } + /** + * Get the type of a particular define. + * + * @param defineId The define ID to lookup. + * @return The type of the define, or null if not found. + */ + public VarType getDefineIdType(int defineId) { + return defineId < defineTypes.size() ? defineTypes.get(defineId) : null; + } /** * Adds a define linked to a material parameter. @@ -388,6 +397,28 @@ public class TechniqueDef implements Savable { defineTypes.add(defineType); return defineId; } + + /** + * Get the names of all defines declared on this technique definition. + * + * The defines are returned in order of declaration. + * + * @return the names of all defines declared. + */ + public String[] getDefineNames() { + return defineNames.toArray(new String[0]); + } + + /** + * Get the types of all defines declared on this technique definition. + * + * The types are returned in order of declaration. + * + * @return the types of all defines declared. + */ + public VarType[] getDefineTypes() { + return defineTypes.toArray(new VarType[0]); + } /** * Create a define list with the size matching the number diff --git a/jme3-core/src/main/java/com/jme3/shader/Uniform.java b/jme3-core/src/main/java/com/jme3/shader/Uniform.java index 9580d8b72..f167359ee 100644 --- a/jme3-core/src/main/java/com/jme3/shader/Uniform.java +++ b/jme3-core/src/main/java/com/jme3/shader/Uniform.java @@ -70,6 +70,30 @@ public class Uniform extends ShaderVariable { */ protected boolean setByCurrentMaterial = false; + @Override + public int hashCode() { + int hash = 5; + hash = 31 * hash + (this.value != null ? this.value.hashCode() : 0); + hash = 31 * hash + (this.varType != null ? this.varType.hashCode() : 0); + hash = 31 * hash + (this.binding != null ? this.binding.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + final Uniform other = (Uniform) obj; + if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) { + return false; + } + return this.binding == other.binding && this.varType == other.varType; + } + @Override public String toString(){ StringBuilder sb = new StringBuilder(); diff --git a/jme3-core/src/test/java/com/jme3/material/TechniqueDefMatParamOverrideTest.java b/jme3-core/src/test/java/com/jme3/material/TechniqueDefMatParamOverrideTest.java new file mode 100644 index 000000000..f527103a0 --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/material/TechniqueDefMatParamOverrideTest.java @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2009-2016 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 com.jme3.material; + +import com.jme3.asset.AssetManager; +import com.jme3.light.LightList; +import com.jme3.math.Matrix4f; +import com.jme3.renderer.RenderManager; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Box; +import com.jme3.shader.Shader; +import com.jme3.shader.Uniform; +import com.jme3.shader.VarType; +import java.util.Arrays; +import java.util.HashSet; +import org.junit.Assert; +import org.junit.Test; + +import static com.jme3.scene.MPOTestUtils.*; +import com.jme3.shader.DefineList; +import com.jme3.system.NullRenderer; +import com.jme3.system.TestUtil; +import com.jme3.texture.Image.Format; +import com.jme3.texture.Texture; +import com.jme3.texture.Texture2D; +import java.util.HashMap; +import java.util.Map; + +public class TechniqueDefMatParamOverrideTest { + + private static final HashSet IGNORED_UNIFORMS = new HashSet( + Arrays.asList(new String[]{"m_ParallaxHeight", "m_Shininess"})); + + @Test + public void testBoolMpoOnly() { + material("Common/MatDefs/Light/Lighting.j3md"); + inputMpo(mpoBool("UseMaterialColors", true)); + outDefines(def("MATERIAL_COLORS", VarType.Boolean, true)); + outUniforms(uniform("UseMaterialColors", VarType.Boolean, true)); + } + + @Test + public void testBoolMpOnly() { + material("Common/MatDefs/Light/Lighting.j3md"); + inputMp(mpoBool("UseMaterialColors", true)); + outDefines(def("MATERIAL_COLORS", VarType.Boolean, true)); + outUniforms(uniform("UseMaterialColors", VarType.Boolean, true)); + } + + @Test + public void testBoolOverrideFalse() { + material("Common/MatDefs/Light/Lighting.j3md"); + inputMp(mpoBool("UseMaterialColors", true)); + inputMpo(mpoBool("UseMaterialColors", false)); + outDefines(); + outUniforms(uniform("UseMaterialColors", VarType.Boolean, false)); + } + + @Test + public void testBoolOverrideTrue() { + material("Common/MatDefs/Light/Lighting.j3md"); + inputMp(mpoBool("UseMaterialColors", false)); + inputMpo(mpoBool("UseMaterialColors", true)); + outDefines(def("MATERIAL_COLORS", VarType.Boolean, true)); + outUniforms(uniform("UseMaterialColors", VarType.Boolean, true)); + } + + @Test + public void testFloatMpoOnly() { + material("Common/MatDefs/Light/Lighting.j3md"); + inputMpo(mpoFloat("AlphaDiscardThreshold", 3.12f)); + outDefines(def("DISCARD_ALPHA", VarType.Float, 3.12f)); + outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 3.12f)); + } + + @Test + public void testFloatMpOnly() { + material("Common/MatDefs/Light/Lighting.j3md"); + inputMp(mpoFloat("AlphaDiscardThreshold", 3.12f)); + outDefines(def("DISCARD_ALPHA", VarType.Float, 3.12f)); + outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 3.12f)); + } + + @Test + public void testFloatOverride() { + material("Common/MatDefs/Light/Lighting.j3md"); + inputMp(mpoFloat("AlphaDiscardThreshold", 3.12f)); + inputMpo(mpoFloat("AlphaDiscardThreshold", 2.79f)); + outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f)); + outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f)); + } + + @Test + public void testIntMpoOnly() { + material("Common/MatDefs/Light/Lighting.j3md"); + inputMpo(mpoInt("NumberOfBones", 1234)); + outDefines(def("NUM_BONES", VarType.Int, 1234)); + outUniforms(uniform("NumberOfBones", VarType.Int, 1234)); + } + + @Test + public void testIntMpOnly() { + material("Common/MatDefs/Light/Lighting.j3md"); + inputMp(mpoInt("NumberOfBones", 1234)); + outDefines(def("NUM_BONES", VarType.Int, 1234)); + outUniforms(uniform("NumberOfBones", VarType.Int, 1234)); + } + + @Test + public void testIntOverride() { + material("Common/MatDefs/Light/Lighting.j3md"); + inputMp(mpoInt("NumberOfBones", 1234)); + inputMpo(mpoInt("NumberOfBones", 4321)); + outDefines(def("NUM_BONES", VarType.Int, 4321)); + outUniforms(uniform("NumberOfBones", VarType.Int, 4321)); + } + + @Test + public void testMatrixArray() { + Matrix4f[] matrices = new Matrix4f[]{ + new Matrix4f() + }; + + material("Common/MatDefs/Light/Lighting.j3md"); + inputMpo(mpoMatrix4Array("BoneMatrices", matrices)); + outDefines(); + outUniforms(uniform("BoneMatrices", VarType.Matrix4Array, matrices)); + } + + @Test + public void testNonExistentParameter() { + material("Common/MatDefs/Light/Lighting.j3md"); + inputMpo(mpoInt("NonExistent", 4321)); + outDefines(); + outUniforms(); + } + + @Test + public void testWrongType() { + material("Common/MatDefs/Light/Lighting.j3md"); + inputMpo(mpoInt("UseMaterialColors", 4321)); + outDefines(); + outUniforms(); + } + + @Test + public void testParamOnly() { + material("Common/MatDefs/Light/Lighting.j3md"); + inputMpo(mpoFloat("ShadowMapSize", 3.12f)); + outDefines(); + outUniforms(uniform("ShadowMapSize", VarType.Float, 3.12f)); + } + + @Test + public void testRemove() { + material("Common/MatDefs/Light/Lighting.j3md"); + + reset(); + inputMp(mpoInt("NumberOfBones", 1234)); + outDefines(def("NUM_BONES", VarType.Int, 1234)); + outUniforms(uniform("NumberOfBones", VarType.Int, 1234)); + + reset(); + inputMpo(mpoInt("NumberOfBones", 4321)); + outDefines(def("NUM_BONES", VarType.Int, 4321)); + outUniforms(uniform("NumberOfBones", VarType.Int, 4321)); + + reset(); + geometry.clearMatParamOverrides(); + outDefines(def("NUM_BONES", VarType.Int, 1234)); + outUniforms(uniform("NumberOfBones", VarType.Int, 1234)); + + reset(); + geometry.getMaterial().clearParam("NumberOfBones"); + outDefines(); + outUniforms(); + + reset(); + inputMpo(mpoInt("NumberOfBones", 4321)); + outDefines(def("NUM_BONES", VarType.Int, 4321)); + outUniforms(uniform("NumberOfBones", VarType.Int, 4321)); + + reset(); + inputMp(mpoInt("NumberOfBones", 1234)); + outDefines(def("NUM_BONES", VarType.Int, 4321)); + outUniforms(uniform("NumberOfBones", VarType.Int, 4321)); + } + + public void testRemoveOverride() { + material("Common/MatDefs/Light/Lighting.j3md"); + + reset(); + inputMp(mpoInt("NumberOfBones", 1234)); + outDefines(def("NUM_BONES", VarType.Int, 1234)); + outUniforms(uniform("NumberOfBones", VarType.Int, 1234)); + + reset(); + inputMpo(mpoInt("NumberOfBones", 4321)); + outDefines(def("NUM_BONES", VarType.Int, 4321)); + outUniforms(uniform("NumberOfBones", VarType.Int, 4321)); + + reset(); + geometry.clearMatParamOverrides(); + outDefines(def("NUM_BONES", VarType.Int, 1234)); + outUniforms(uniform("NumberOfBones", VarType.Int, 1234)); + } + + @Test + public void testRemoveMpoOnly() { + material("Common/MatDefs/Light/Lighting.j3md"); + + reset(); + inputMpo(mpoInt("NumberOfBones", 4321)); + outDefines(def("NUM_BONES", VarType.Int, 4321)); + outUniforms(uniform("NumberOfBones", VarType.Int, 4321)); + + reset(); + geometry.clearMatParamOverrides(); + outDefines(); + outUniforms(); + } + + @Test + public void testTextureMpoOnly() { + material("Common/MatDefs/Light/Lighting.j3md"); + Texture2D tex = new Texture2D(128, 128, Format.RGBA8); + + inputMpo(mpoTexture2D("DiffuseMap", tex)); + outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex)); + outUniforms(uniform("DiffuseMap", VarType.Int, 0)); + outTextures(tex); + } + + @Test + public void testTextureOverride() { + material("Common/MatDefs/Light/Lighting.j3md"); + Texture2D tex1 = new Texture2D(128, 128, Format.RGBA8); + Texture2D tex2 = new Texture2D(128, 128, Format.RGBA8); + + inputMp(mpoTexture2D("DiffuseMap", tex1)); + inputMpo(mpoTexture2D("DiffuseMap", tex2)); + + outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex2)); + outUniforms(uniform("DiffuseMap", VarType.Int, 0)); + outTextures(tex2); + } + + @Test + public void testRemoveTexture() { + material("Common/MatDefs/Light/Lighting.j3md"); + Texture2D tex = new Texture2D(128, 128, Format.RGBA8); + + reset(); + inputMpo(mpoTexture2D("DiffuseMap", tex)); + outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex)); + outUniforms(uniform("DiffuseMap", VarType.Int, 0)); + outTextures(tex); + + reset(); + geometry.clearMatParamOverrides(); + outDefines(); + outUniforms(); + outTextures(); + } + + @Test + public void testRemoveTextureOverride() { + material("Common/MatDefs/Light/Lighting.j3md"); + Texture2D tex1 = new Texture2D(128, 128, Format.RGBA8); + Texture2D tex2 = new Texture2D(128, 128, Format.RGBA8); + + reset(); + inputMp(mpoTexture2D("DiffuseMap", tex1)); + outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex1)); + outUniforms(uniform("DiffuseMap", VarType.Int, 0)); + outTextures(tex1); + + reset(); + inputMpo(mpoTexture2D("DiffuseMap", tex2)); + outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex2)); + outUniforms(uniform("DiffuseMap", VarType.Int, 0)); + outTextures(tex2); + + reset(); + geometry.clearMatParamOverrides(); + outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex1)); + outUniforms(uniform("DiffuseMap", VarType.Int, 0)); + outTextures(tex1); + } + + private static final class Define { + + public String name; + public VarType type; + public Object value; + + @Override + public int hashCode() { + int hash = 3; + hash = 89 * hash + this.name.hashCode(); + hash = 89 * hash + this.type.hashCode(); + hash = 89 * hash + this.value.hashCode(); + return hash; + } + + @Override + public boolean equals(Object obj) { + final Define other = (Define) obj; + return this.name.equals(other.name) && this.type.equals(other.type) && this.value.equals(other.value); + } + } + + private final Geometry geometry = new Geometry("geometry", new Box(1, 1, 1)); + private final LightList lightList = new LightList(geometry); + + private final NullRenderer renderer = new NullRenderer() { + @Override + public void setShader(Shader shader) { + TechniqueDefMatParamOverrideTest.this.usedShader = shader; + evaluated = true; + } + + @Override + public void setTexture(int unit, Texture texture) { + TechniqueDefMatParamOverrideTest.this.usedTextures[unit] = texture; + } + }; + private final RenderManager renderManager = new RenderManager(renderer); + + private boolean evaluated = false; + private Shader usedShader = null; + private final Texture[] usedTextures = new Texture[32]; + + private void inputMp(MatParam... params) { + if (evaluated) { + throw new IllegalStateException(); + } + Material mat = geometry.getMaterial(); + for (MatParam param : params) { + mat.setParam(param.getName(), param.getVarType(), param.getValue()); + } + } + + private void inputMpo(MatParamOverride... overrides) { + if (evaluated) { + throw new IllegalStateException(); + } + for (MatParamOverride override : overrides) { + geometry.addMatParamOverride(override); + } + geometry.updateGeometricState(); + } + + private void reset() { + evaluated = false; + usedShader = null; + Arrays.fill(usedTextures, null); + } + + private Define def(String name, VarType type, Object value) { + Define d = new Define(); + d.name = name; + d.type = type; + d.value = value; + return d; + } + + private Uniform uniform(String name, VarType type, Object value) { + Uniform u = new Uniform(); + u.setName("m_" + name); + u.setValue(type, value); + return u; + } + + private void material(String path) { + AssetManager assetManager = TestUtil.createAssetManager(); + geometry.setMaterial(new Material(assetManager, path)); + } + + private void evaluateTechniqueDef() { + Assert.assertFalse(evaluated); + Material mat = geometry.getMaterial(); + mat.render(geometry, lightList, renderManager); + Assert.assertTrue(evaluated); + } + + private void outTextures(Texture... textures) { + for (int i = 0; i < usedTextures.length; i++) { + if (i < textures.length) { + Assert.assertSame(textures[i], usedTextures[i]); + } else { + Assert.assertNull(usedTextures[i]); + } + } + } + + private void outDefines(Define... expectedDefinesArray) { + Map nameToDefineMap = new HashMap(); + for (Define define : expectedDefinesArray) { + nameToDefineMap.put(define.name, define); + } + + if (!evaluated) { + evaluateTechniqueDef(); + } + + Material mat = geometry.getMaterial(); + Technique tech = mat.getActiveTechnique(); + TechniqueDef def = tech.getDef(); + DefineList actualDefines = tech.getDynamicDefines(); + + String[] defineNames = def.getDefineNames(); + VarType[] defineTypes = def.getDefineTypes(); + + Assert.assertEquals(defineNames.length, defineTypes.length); + + for (int index = 0; index < defineNames.length; index++) { + String name = defineNames[index]; + VarType type = defineTypes[index]; + Define expectedDefine = nameToDefineMap.remove(name); + Object expectedValue = null; + + if (expectedDefine != null) { + Assert.assertEquals(expectedDefine.type, type); + expectedValue = expectedDefine.value; + } + + switch (type) { + case Boolean: + if (expectedValue != null) { + Assert.assertEquals((boolean) (Boolean) expectedValue, actualDefines.getBoolean(index)); + } else { + Assert.assertEquals(false, actualDefines.getBoolean(index)); + } + break; + case Int: + if (expectedValue != null) { + Assert.assertEquals((int) (Integer) expectedValue, actualDefines.getInt(index)); + } else { + Assert.assertEquals(0, actualDefines.getInt(index)); + } + break; + case Float: + if (expectedValue != null) { + Assert.assertEquals((float) (Float) expectedValue, actualDefines.getFloat(index), 0f); + } else { + Assert.assertEquals(0f, actualDefines.getFloat(index), 0f); + } + break; + default: + if (expectedValue != null) { + Assert.assertEquals(1, actualDefines.getInt(index)); + } else { + Assert.assertEquals(0, actualDefines.getInt(index)); + } + break; + } + } + + Assert.assertTrue(nameToDefineMap.isEmpty()); + } + + private void outUniforms(Uniform... uniforms) { + HashSet actualUniforms = new HashSet(); + + for (Uniform uniform : usedShader.getUniformMap().values()) { + if (uniform.getName().startsWith("m_") + && !IGNORED_UNIFORMS.contains(uniform.getName())) { + actualUniforms.add(uniform); + } + } + + HashSet expectedUniforms = new HashSet(Arrays.asList(uniforms)); + + if (!expectedUniforms.equals(actualUniforms)) { + System.out.println(expectedUniforms + " != " + actualUniforms); + Assert.fail("Uniform lists must match"); + } + } +}