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 49d98a72c..1fe8bac06 100644 --- a/jme3-core/src/main/java/com/jme3/material/Material.java +++ b/jme3-core/src/main/java/com/jme3/material/Material.java @@ -774,32 +774,43 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { sortingId = -1; } - private void updateShaderMaterialParameters(Renderer renderer, Shader shader, List overrides) { - int unit = 0; + private int applyOverrides(Renderer renderer, Shader shader, List overrides, int unit) { + for (MatParamOverride override : overrides) { + VarType type = override.getVarType(); - if (overrides != null) { - for (MatParamOverride override : overrides) { - VarType type = override.getVarType(); + MatParam paramDef = def.getMaterialParam(override.getName()); - MatParam paramDef = def.getMaterialParam(override.getName()); - if (paramDef == null || paramDef.getVarType() != type || !override.isEnabled()) { - continue; - } + if (paramDef == null || paramDef.getVarType() != type || !override.isEnabled()) { + continue; + } - Uniform uniform = shader.getUniform(override.getPrefixedName()); - if (override.getValue() != null) { - if (type.isTextureType()) { - renderer.setTexture(unit, (Texture) override.getValue()); - uniform.setValue(VarType.Int, unit); - unit++; - } else { - uniform.setValue(type, override.getValue()); - } + Uniform uniform = shader.getUniform(override.getPrefixedName()); + + if (override.getValue() != null) { + if (type.isTextureType()) { + renderer.setTexture(unit, (Texture) override.getValue()); + uniform.setValue(VarType.Int, unit); + unit++; } else { - uniform.clearValue(); + uniform.setValue(type, override.getValue()); } + } else { + uniform.clearValue(); } } + return unit; + } + + private void updateShaderMaterialParameters(Renderer renderer, Shader shader, + List worldOverrides, List forcedOverrides) { + + int unit = 0; + if (worldOverrides != null) { + unit = applyOverrides(renderer, shader, worldOverrides, unit); + } + if (forcedOverrides != null) { + unit = applyOverrides(renderer, shader, forcedOverrides, unit); + } for (int i = 0; i < paramValues.size(); i++) { MatParam param = paramValues.getValue(i); @@ -854,8 +865,8 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { return; } - Shader shader = technique.makeCurrent(renderManager, null, null, rendererCaps); - updateShaderMaterialParameters(renderer, shader, null); + Shader shader = technique.makeCurrent(renderManager, null, null, null, rendererCaps); + updateShaderMaterialParameters(renderer, shader, null, null); renderManager.getRenderer().setShader(shader); } @@ -962,7 +973,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { List overrides = geometry.getWorldMatParamOverrides(); // Select shader to use - Shader shader = technique.makeCurrent(renderManager, overrides, lights, rendererCaps); + Shader shader = technique.makeCurrent(renderManager, overrides, renderManager.getForcedMatParams(), lights, rendererCaps); // Begin tracking which uniforms were changed by material. clearUniformsSetByCurrent(shader); @@ -971,7 +982,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { renderManager.updateUniformBindings(shader); // Set material parameters - updateShaderMaterialParameters(renderer, shader, geometry.getWorldMatParamOverrides()); + updateShaderMaterialParameters(renderer, shader, overrides, renderManager.getForcedMatParams()); // 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 e6a8efdde..3a4fa71da 100644 --- a/jme3-core/src/main/java/com/jme3/material/Technique.java +++ b/jme3-core/src/main/java/com/jme3/material/Technique.java @@ -110,6 +110,20 @@ public final class Technique { } } + private void applyOverrides(DefineList defineList, List overrides) { + for (MatParamOverride override : overrides) { + if (!override.isEnabled()) { + continue; + } + Integer defineId = def.getShaderParamDefineId(override.name); + if (defineId != null) { + if (def.getDefineIdType(defineId) == override.type) { + defineList.set(defineId, override.type, override.value); + } + } + } + } + /** * Called by the material to determine which shader to use for rendering. * @@ -120,7 +134,8 @@ public final class Technique { * @param rendererCaps The renderer capabilities which the shader should support. * @return A compatible shader. */ - Shader makeCurrent(RenderManager renderManager, List overrides, + Shader makeCurrent(RenderManager renderManager, List worldOverrides, + List forcedOverrides, LightList lights, EnumSet rendererCaps) { TechniqueDefLogic logic = def.getLogic(); AssetManager assetManager = owner.getMaterialDef().getAssetManager(); @@ -128,18 +143,11 @@ public final class Technique { dynamicDefines.clear(); dynamicDefines.setAll(paramDefines); - if (overrides != null) { - for (MatParamOverride override : overrides) { - if (!override.isEnabled()) { - continue; - } - Integer defineId = def.getShaderParamDefineId(override.name); - if (defineId != null) { - if (def.getDefineIdType(defineId) == override.type) { - dynamicDefines.set(defineId, override.type, override.value); - } - } - } + if (worldOverrides != null) { + applyOverrides(dynamicDefines, worldOverrides); + } + if (forcedOverrides != null) { + applyOverrides(dynamicDefines, forcedOverrides); } return logic.makeCurrent(assetManager, renderManager, rendererCaps, lights, dynamicDefines); diff --git a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java index 42afc55b4..45b118926 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java +++ b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java @@ -34,6 +34,7 @@ package com.jme3.renderer; import com.jme3.light.DefaultLightFilter; import com.jme3.light.LightFilter; import com.jme3.light.LightList; +import com.jme3.material.MatParamOverride; import com.jme3.material.Material; import com.jme3.material.MaterialDef; import com.jme3.material.RenderState; @@ -82,6 +83,7 @@ public class RenderManager { private Material forcedMaterial = null; private String forcedTechnique = null; private RenderState forcedRenderState = null; + private final List forcedOverrides = new ArrayList<>(); private int viewX, viewY, viewWidth, viewHeight; private Matrix4f orthoMatrix = new Matrix4f(); private LightList filteredLightList = new LightList(null); @@ -92,6 +94,7 @@ public class RenderManager { private TechniqueDef.LightMode preferredLightMode = TechniqueDef.LightMode.MultiPass; private int singlePassLightBatchSize = 1; + /** * Create a high-level rendering interface over the * low-level rendering interface. @@ -426,6 +429,44 @@ public class RenderManager { this.forcedTechnique = forcedTechnique; } + /** + * Adds a forced material parameter to use when rendering geometries. + *

+ * The provided parameter takes precedence over parameters set on the + * material or any overrides that exist in the scene graph that have the + * same name. + * + * @param override The override to add + * @see MatParamOverride + * @see #removeForcedMatParam(com.jme3.material.MatParamOverride) + */ + public void addForcedMatParam(MatParamOverride override) { + forcedOverrides.add(override); + } + + /** + * Remove a forced material parameter previously added. + * + * @param override The override to remove. + * @see #addForcedMatParam(com.jme3.material.MatParamOverride) + */ + public void removeForcedMatParam(MatParamOverride override) { + forcedOverrides.remove(override); + } + + /** + * Get the forced material parameters applied to rendered geometries. + *

+ * Forced parameters can be added via + * {@link #addForcedMatParam(com.jme3.material.MatParamOverride)} or removed + * via {@link #removeForcedMatParam(com.jme3.material.MatParamOverride)}. + * + * @return The forced material parameters. + */ + public List getForcedMatParams() { + return forcedOverrides; + } + /** * Enable or disable alpha-to-coverage. *

diff --git a/jme3-core/src/test/java/com/jme3/material/MaterialMatParamOverrideTest.java b/jme3-core/src/test/java/com/jme3/material/MaterialMatParamOverrideTest.java index ac8a3650b..defd850ba 100644 --- a/jme3-core/src/test/java/com/jme3/material/MaterialMatParamOverrideTest.java +++ b/jme3-core/src/test/java/com/jme3/material/MaterialMatParamOverrideTest.java @@ -53,6 +53,7 @@ import com.jme3.system.TestUtil; import com.jme3.texture.Image.Format; import com.jme3.texture.Texture; import com.jme3.texture.Texture2D; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.junit.Before; @@ -126,6 +127,22 @@ public class MaterialMatParamOverrideTest { outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f)); } + @Test + public void testForcedOverride() { + material("Common/MatDefs/Light/Lighting.j3md"); + inputMp(mpoFloat("AlphaDiscardThreshold", 3.12f)); + inputMpo(mpoFloat("AlphaDiscardThreshold", 2.79f)); + inputFpo(mpoFloat("AlphaDiscardThreshold", 1.23f)); + outDefines(def("DISCARD_ALPHA", VarType.Float, 1.23f)); + outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 1.23f)); + + reset(); + root.clearMatParamOverrides(); + root.updateGeometricState(); + outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f)); + outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f)); + } + @Test public void testChildOverridesParent() { material("Common/MatDefs/Light/Lighting.j3md"); @@ -439,10 +456,22 @@ public class MaterialMatParamOverrideTest { root.updateGeometricState(); } + private void inputFpo(MatParamOverride... overrides) { + if (evaluated) { + throw new IllegalStateException(); + } + for (MatParamOverride override : overrides) { + renderManager.addForcedMatParam(override); + } + } + private void reset() { evaluated = false; usedShader = null; Arrays.fill(usedTextures, null); + for (MatParamOverride override : new ArrayList<>(renderManager.getForcedMatParams())) { + renderManager.removeForcedMatParam(override); + } } private Define def(String name, VarType type, Object value) {