diff --git a/jme3-core/src/main/java/com/jme3/material/MatParamTexture.java b/jme3-core/src/main/java/com/jme3/material/MatParamTexture.java index 78c8ef33c..cb800e75b 100644 --- a/jme3-core/src/main/java/com/jme3/material/MatParamTexture.java +++ b/jme3-core/src/main/java/com/jme3/material/MatParamTexture.java @@ -35,7 +35,6 @@ import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; -import com.jme3.renderer.Renderer; import com.jme3.shader.VarType; import com.jme3.texture.Texture; import com.jme3.texture.image.ColorSpace; @@ -44,13 +43,11 @@ import java.io.IOException; public class MatParamTexture extends MatParam { private Texture texture; - private int unit; private ColorSpace colorSpace; - public MatParamTexture(VarType type, String name, Texture texture, int unit, ColorSpace colorSpace) { + public MatParamTexture(VarType type, String name, Texture texture, ColorSpace colorSpace) { super(type, name, texture); this.texture = texture; - this.unit = unit; this.colorSpace = colorSpace; } @@ -92,31 +89,18 @@ public class MatParamTexture extends MatParam { this.colorSpace = colorSpace; } - public void setUnit(int unit) { - this.unit = unit; - } - - public int getUnit() { - return unit; - } - - @Override public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule oc = ex.getCapsule(this); - oc.write(unit, "texture_unit", -1); - - // For backwards compat - oc.write(texture, "texture", null); + oc.write(0, "texture_unit", -1); + oc.write(texture, "texture", null); // For backwards compatibility } @Override public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule ic = im.getCapsule(this); - unit = ic.readInt("texture_unit", -1); texture = (Texture) value; - //texture = (Texture) ic.readSavable("texture", null); } } \ No newline at end of file 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 0c5030732..f73748fc9 100644 --- a/jme3-core/src/main/java/com/jme3/material/Material.java +++ b/jme3-core/src/main/java/com/jme3/material/Material.java @@ -93,7 +93,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { private ListMap paramValues = new ListMap(); private Technique technique; private HashMap techniques = new HashMap(); - private int nextTexUnit = 0; private RenderState additionalState = null; private RenderState mergedRenderState = new RenderState(); private boolean transparent = false; @@ -264,8 +263,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { // E.g. if user chose custom technique for one material but // uses default technique for other material, the materials // are not equal. - String thisDefName = this.technique != null ? this.technique.getDef().getName() : "Default"; - String otherDefName = other.technique != null ? other.technique.getDef().getName() : "Default"; + String thisDefName = this.technique != null + ? this.technique.getDef().getName() + : TechniqueDef.DEFAULT_TECHNIQUE_NAME; + + String otherDefName = other.technique != null + ? other.technique.getDef().getName() + : TechniqueDef.DEFAULT_TECHNIQUE_NAME; + if (!thisDefName.equals(otherDefName)) { return false; } @@ -510,16 +515,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { paramValues.remove(name); if (matParam instanceof MatParamTexture) { - int texUnit = ((MatParamTexture) matParam).getUnit(); - nextTexUnit--; - for (MatParam param : paramValues.values()) { - if (param instanceof MatParamTexture) { - MatParamTexture texParam = (MatParamTexture) param; - if (texParam.getUnit() > texUnit) { - texParam.setUnit(texParam.getUnit() - 1); - } - } - } sortingId = -1; } if (technique != null) { @@ -562,13 +557,13 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { + "Linear using texture.getImage.setColorSpace().", new Object[]{value.getName(), value.getImage().getColorSpace().name(), name}); } - paramValues.put(name, new MatParamTexture(type, name, value, nextTexUnit++, null)); + paramValues.put(name, new MatParamTexture(type, name, value, null)); } else { val.setTextureValue(value); } if (technique != null) { - technique.notifyParamChanged(name, type, nextTexUnit - 1); + technique.notifyParamChanged(name, type, value); } // need to recompute sort ID @@ -704,23 +699,18 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { /** * Select the technique to use for rendering this material. *

- * If name is "Default", then one of the - * {@link MaterialDef#getDefaultTechniques() default techniques} - * on the material will be selected. Otherwise, the named technique - * will be found in the material definition. - *

* Any candidate technique for selection (either default or named) * must be verified to be compatible with the system, for that, the * renderManager is queried for capabilities. * - * @param name The name of the technique to select, pass "Default" to - * select one of the default techniques. + * @param name The name of the technique to select, pass + * {@link TechniqueDef#DEFAULT_TECHNIQUE_NAME} to select one of the default + * techniques. * @param renderManager The {@link RenderManager render manager} * to query for capabilities. * - * @throws IllegalArgumentException If "Default" is passed and no default - * techniques are available on the material definition, or if a name - * is passed but there's no technique by that name. + * @throws IllegalArgumentException If no technique exists with the given + * name. * @throws UnsupportedOperationException If no candidate technique supports * the system capabilities. */ @@ -731,46 +721,30 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { // supports all the caps. if (tech == null) { EnumSet rendererCaps = renderManager.getRenderer().getCaps(); - if (name.equals("Default")) { - List techDefs = def.getDefaultTechniques(); - if (techDefs == null || techDefs.isEmpty()) { - throw new IllegalArgumentException("No default techniques are available on material '" + def.getName() + "'"); - } + List techDefs = def.getTechniqueDefs(name); - TechniqueDef lastTech = null; - for (TechniqueDef techDef : techDefs) { - if (rendererCaps.containsAll(techDef.getRequiredCaps())) { - // use the first one that supports all the caps - tech = new Technique(this, techDef); - techniques.put(name, tech); - if(tech.getDef().getLightMode() == renderManager.getPreferredLightMode() || - tech.getDef().getLightMode() == LightMode.Disable){ - break; - } - } - lastTech = techDef; - } - if (tech == null) { - throw new UnsupportedOperationException("No default technique on material '" + def.getName() + "'\n" - + " is supported by the video hardware. The caps " - + lastTech.getRequiredCaps() + " are required."); - } - - } else { - // create "special" technique instance - TechniqueDef techDef = def.getTechniqueDef(name); - if (techDef == null) { - throw new IllegalArgumentException("For material " + def.getName() + ", technique not found: " + name); - } + if (techDefs == null || techDefs.isEmpty()) { + throw new IllegalArgumentException( + String.format("The requested technique %s is not available on material %s", name, def.getName())); + } - if (!rendererCaps.containsAll(techDef.getRequiredCaps())) { - throw new UnsupportedOperationException("The explicitly chosen technique '" + name + "' on material '" + def.getName() + "'\n" - + "requires caps " + techDef.getRequiredCaps() + " which are not " - + "supported by the video renderer"); + TechniqueDef lastTech = null; + for (TechniqueDef techDef : techDefs) { + if (rendererCaps.containsAll(techDef.getRequiredCaps())) { + // use the first one that supports all the caps + tech = new Technique(this, techDef); + techniques.put(name, tech); + if (tech.getDef().getLightMode() == renderManager.getPreferredLightMode() + || tech.getDef().getLightMode() == LightMode.Disable) { + break; + } } - - tech = new Technique(this, techDef); - techniques.put(name, tech); + lastTech = techDef; + } + if (tech == null) { + throw new UnsupportedOperationException("No default technique on material '" + def.getName() + "'\n" + + " is supported by the video hardware. The caps " + + lastTech.getRequiredCaps() + " are required."); } } else if (technique == tech) { // attempting to switch to an already @@ -785,32 +759,43 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { sortingId = -1; } - private int 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 int 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); @@ -857,7 +842,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { */ public void preload(RenderManager renderManager) { if (technique == null) { - selectTechnique("Default", renderManager); + selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); } TechniqueDef techniqueDef = technique.getDef(); Renderer renderer = renderManager.getRenderer(); @@ -867,8 +852,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); } @@ -957,7 +942,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { */ public void render(Geometry geometry, LightList lights, RenderManager renderManager) { if (technique == null) { - selectTechnique("Default", renderManager); + selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); } TechniqueDef techniqueDef = technique.getDef(); @@ -975,7 +960,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); @@ -984,9 +969,10 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { renderManager.updateUniformBindings(shader); // Set material parameters + //TODO RRemove the unit when texture units are handled in the Uniform - int unit = updateShaderMaterialParameters(renderer, shader, geometry.getWorldMatParamOverrides()); - + int unit = updateShaderMaterialParameters(renderer, shader, overrides, renderManager.getForcedMatParams()); + // Clear any uniforms not changed by material. resetUniformsNotSetByCurrent(shader); @@ -1081,11 +1067,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { MatParam param = entry.getValue(); if (param instanceof MatParamTexture) { MatParamTexture texVal = (MatParamTexture) param; - - if (nextTexUnit < texVal.getUnit() + 1) { - nextTexUnit = texVal.getUnit() + 1; - } - // the texture failed to load for this param // do not add to param values if (texVal.getTextureValue() == null || texVal.getTextureValue().getImage() == null) { diff --git a/jme3-core/src/main/java/com/jme3/material/MaterialDef.java b/jme3-core/src/main/java/com/jme3/material/MaterialDef.java index 47d672f3b..fa205460e 100644 --- a/jme3-core/src/main/java/com/jme3/material/MaterialDef.java +++ b/jme3-core/src/main/java/com/jme3/material/MaterialDef.java @@ -32,6 +32,7 @@ package com.jme3.material; import com.jme3.asset.AssetManager; +import com.jme3.renderer.Caps; import com.jme3.shader.VarType; import com.jme3.texture.image.ColorSpace; import java.util.*; @@ -51,8 +52,7 @@ public class MaterialDef { private String assetName; private AssetManager assetManager; - private List defaultTechs; - private Map techniques; + private Map> techniques; private Map matParams; /** @@ -70,9 +70,8 @@ public class MaterialDef { public MaterialDef(AssetManager assetManager, String name){ this.assetManager = assetManager; this.name = name; - techniques = new HashMap(); + techniques = new HashMap>(); matParams = new HashMap(); - defaultTechs = new ArrayList(); logger.log(Level.FINE, "Loaded material definition: {0}", name); } @@ -135,7 +134,7 @@ public class MaterialDef { * @see ColorSpace */ public void addMaterialParamTexture(VarType type, String name, ColorSpace colorSpace) { - matParams.put(name, new MatParamTexture(type, name, null , 0, colorSpace)); + matParams.put(name, new MatParamTexture(type, name, null, colorSpace)); } /** @@ -164,40 +163,26 @@ public class MaterialDef { /** * Adds a new technique definition to this material definition. - *

- * If the technique name is "Default", it will be added - * to the list of {@link MaterialDef#getDefaultTechniques() default techniques}. - * + * * @param technique The technique definition to add. */ public void addTechniqueDef(TechniqueDef technique) { - if (technique.getName().equals("Default")) { - defaultTechs.add(technique); - } else { - techniques.put(technique.getName(), technique); + List list = techniques.get(technique.getName()); + if (list == null) { + list = new ArrayList<>(); + techniques.put(technique.getName(), list); } + list.add(technique); } /** - * Returns a list of all default techniques. - * - * @return a list of all default techniques. - */ - public List getDefaultTechniques(){ - return defaultTechs; - } - - /** - * Returns a technique definition with the given name. - * This does not include default techniques which can be - * retrieved via {@link MaterialDef#getDefaultTechniques() }. - * - * @param name The name of the technique definition to find - * - * @return The technique definition, or null if cannot be found. + * Returns technique definitions with the given name. + * + * @param name The name of the technique definitions to find + * + * @return The technique definitions, or null if cannot be found. */ - public TechniqueDef getTechniqueDef(String name) { + public List getTechniqueDefs(String name) { return techniques.get(name); } - } 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 28ef9e87b..5881adf9a 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/material/TechniqueDef.java b/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java index a1cbc3abb..f218809e6 100644 --- a/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java +++ b/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java @@ -53,6 +53,14 @@ public class TechniqueDef implements Savable { */ public static final int SAVABLE_VERSION = 1; + /** + * The default technique name. + * + * The technique with this name is selected if no specific technique is + * requested by the user. Currently set to "Default". + */ + public static final String DEFAULT_TECHNIQUE_NAME = "Default"; + /** * Describes light rendering mode. */ @@ -113,7 +121,7 @@ public class TechniqueDef implements Savable { Legacy } - private EnumSet requiredCaps = EnumSet.noneOf(Caps.class); + private final EnumSet requiredCaps = EnumSet.noneOf(Caps.class); private String name; private int sortId; @@ -153,7 +161,7 @@ public class TechniqueDef implements Savable { public TechniqueDef(String name, int sortId){ this(); this.sortId = sortId; - this.name = name == null ? "Default" : name; + this.name = name == null ? TechniqueDef.DEFAULT_TECHNIQUE_NAME : name; } /** @@ -178,7 +186,8 @@ public class TechniqueDef implements Savable { /** * Returns the name of this technique as specified in the J3MD file. - * Default techniques have the name "Default". + * Default + * techniques have the name {@link #DEFAULT_TECHNIQUE_NAME}. * * @return the name of this technique */ 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 1a11d7339..0860d69f5 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. *

@@ -539,8 +580,11 @@ public class RenderManager { //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null //else the geom is not rendered if (forcedTechnique != null) { - if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) { - tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default"; + MaterialDef matDef = g.getMaterial().getMaterialDef(); + if (matDef.getTechniqueDefs(forcedTechnique) != null) { + tmpTech = g.getMaterial().getActiveTechnique() != null + ? g.getMaterial().getActiveTechnique().getDef().getName() + : TechniqueDef.DEFAULT_TECHNIQUE_NAME; g.getMaterial().selectTechnique(forcedTechnique, this); //saving forcedRenderState for future calls RenderState tmpRs = forcedRenderState; diff --git a/jme3-core/src/main/java/com/jme3/scene/Spatial.java b/jme3-core/src/main/java/com/jme3/scene/Spatial.java index 7870d5849..452ccce47 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Spatial.java +++ b/jme3-core/src/main/java/com/jme3/scene/Spatial.java @@ -593,8 +593,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab worldOverrides.addAll(localOverrides); } else { assert (parent.refreshFlags & RF_MATPARAM_OVERRIDE) == 0; - worldOverrides.addAll(localOverrides); worldOverrides.addAll(parent.worldOverrides); + worldOverrides.addAll(localOverrides); } } diff --git a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java index b55c6b2d2..891c3b402 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java @@ -587,7 +587,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable for (int i = 0; i < l.size(); i++) { Material mat = l.get(i).getMaterial(); //checking if the material has the post technique and adding it to the material cache - if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) { + if (mat.getMaterialDef().getTechniqueDefs(postTechniqueName) != null) { if (!matCache.contains(mat)) { matCache.add(mat); } diff --git a/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java index e06b5c337..ed84f9f0c 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java @@ -533,7 +533,7 @@ public class PssmShadowRenderer implements SceneProcessor { for (int i = 0; i < l.size(); i++) { Material mat = l.get(i).getMaterial(); //checking if the material has the post technique and adding it to the material cache - if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) { + if (mat.getMaterialDef().getTechniqueDefs(postTechniqueName) != null) { if (!matCache.contains(mat)) { matCache.add(mat); } 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 846059e6e..defd850ba 100644 --- a/jme3-core/src/test/java/com/jme3/material/MaterialMatParamOverrideTest.java +++ b/jme3-core/src/test/java/com/jme3/material/MaterialMatParamOverrideTest.java @@ -46,14 +46,17 @@ import org.junit.Assert; import org.junit.Test; import static com.jme3.scene.MPOTestUtils.*; +import com.jme3.scene.Node; 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.ArrayList; import java.util.HashMap; import java.util.Map; +import org.junit.Before; /** * Validates how {@link MatParamOverride MPOs} work on the material level. @@ -124,6 +127,33 @@ 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"); + + inputParentMpo(mpoFloat("AlphaDiscardThreshold", 3.12f)); + inputMpo(mpoFloat("AlphaDiscardThreshold", 2.79f)); + + outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f)); + outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f)); + } + @Test public void testMpoDisable() { material("Common/MatDefs/Light/Lighting.j3md"); @@ -222,7 +252,7 @@ public class MaterialMatParamOverrideTest { reset(); geometry.clearMatParamOverrides(); - geometry.updateGeometricState(); + root.updateGeometricState(); outDefines(def("NUM_BONES", VarType.Int, 1234)); outUniforms(uniform("NumberOfBones", VarType.Int, 1234)); @@ -272,7 +302,7 @@ public class MaterialMatParamOverrideTest { reset(); geometry.clearMatParamOverrides(); - geometry.updateGeometricState(); + root.updateGeometricState(); outDefines(); outUniforms(); } @@ -315,7 +345,7 @@ public class MaterialMatParamOverrideTest { reset(); geometry.clearMatParamOverrides(); - geometry.updateGeometricState(); + root.updateGeometricState(); outDefines(); outUniforms(); outTextures(); @@ -341,7 +371,7 @@ public class MaterialMatParamOverrideTest { reset(); geometry.clearMatParamOverrides(); - geometry.updateGeometricState(); + root.updateGeometricState(); outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex1)); outUniforms(uniform("DiffuseMap", VarType.Int, 0)); outTextures(tex1); @@ -369,9 +399,15 @@ public class MaterialMatParamOverrideTest { } } - private final Geometry geometry = new Geometry("geometry", new Box(1, 1, 1)); + private final Geometry geometry = new Geometry("Geometry", new Box(1, 1, 1)); + private final Node root = new Node("Root Node"); private final LightList lightList = new LightList(geometry); + @Before + public void setUp() { + root.attachChild(geometry); + } + private final NullRenderer renderer = new NullRenderer() { @Override public void setShader(Shader shader) { @@ -407,13 +443,35 @@ public class MaterialMatParamOverrideTest { for (MatParamOverride override : overrides) { geometry.addMatParamOverride(override); } - geometry.updateGeometricState(); + root.updateGeometricState(); + } + + private void inputParentMpo(MatParamOverride... overrides) { + if (evaluated) { + throw new IllegalStateException(); + } + for (MatParamOverride override : overrides) { + root.addMatParamOverride(override); + } + 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) { @@ -520,6 +578,10 @@ public class MaterialMatParamOverrideTest { } private void outUniforms(Uniform... uniforms) { + if (!evaluated) { + evaluateTechniqueDef(); + } + HashSet actualUniforms = new HashSet<>(); for (Uniform uniform : usedShader.getUniformMap().values()) { diff --git a/jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java b/jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java index f45eb9bdf..9f9a1fded 100644 --- a/jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java +++ b/jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java @@ -7,8 +7,11 @@ import com.jme3.asset.TextureKey; import com.jme3.material.MatParamTexture; import com.jme3.material.Material; import com.jme3.material.MaterialDef; +import com.jme3.renderer.Caps; import com.jme3.shader.VarType; import com.jme3.texture.Texture; +import java.io.IOException; +import java.util.EnumSet; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -18,6 +21,7 @@ import org.mockito.runners.MockitoJUnitRunner; import static org.mockito.Matchers.any; import static org.mockito.Mockito.verify; +import static org.junit.Assert.*; import static org.mockito.Mockito.when; /** @@ -51,6 +55,15 @@ public class J3MLoaderTest { j3MLoader = new J3MLoader(); } + @Test + public void multipleSameNamedTechniques_shouldBeSupported() throws IOException { + when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/same-name-technique.j3md")); + MaterialDef def = (MaterialDef) j3MLoader.load(assetInfo); + assertEquals(2, def.getTechniqueDefs("Test").size()); + assertEquals(EnumSet.of(Caps.GLSL150), def.getTechniqueDefs("Test").get(0).getRequiredCaps()); + assertEquals(EnumSet.of(Caps.GLSL100), def.getTechniqueDefs("Test").get(1).getRequiredCaps()); + } + @Test public void oldStyleTextureParameters_shouldBeSupported() throws Exception { when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/texture-parameters-oldstyle.j3m")); @@ -107,7 +120,7 @@ public class J3MLoaderTest { } private TextureKey setupMockForTexture(final String paramName, final String path, final boolean flipY, final Texture texture) { - when(materialDef.getMaterialParam(paramName)).thenReturn(new MatParamTexture(VarType.Texture2D, paramName, texture, 0, null)); + when(materialDef.getMaterialParam(paramName)).thenReturn(new MatParamTexture(VarType.Texture2D, paramName, texture, null)); final TextureKey textureKey = new TextureKey(path, flipY); textureKey.setGenerateMips(true); diff --git a/jme3-core/src/test/java/com/jme3/renderer/OpaqueComparatorTest.java b/jme3-core/src/test/java/com/jme3/renderer/OpaqueComparatorTest.java index 84555c9f6..ee4d279fd 100644 --- a/jme3-core/src/test/java/com/jme3/renderer/OpaqueComparatorTest.java +++ b/jme3-core/src/test/java/com/jme3/renderer/OpaqueComparatorTest.java @@ -33,6 +33,7 @@ package com.jme3.renderer; import com.jme3.asset.AssetManager; import com.jme3.material.Material; +import com.jme3.material.TechniqueDef; import com.jme3.math.ColorRGBA; import com.jme3.renderer.queue.GeometryList; import com.jme3.renderer.queue.OpaqueComparator; @@ -92,7 +93,7 @@ public class OpaqueComparatorTest { String techniqueName = materials[i].getActiveTechnique().getDef().getName(); clonedMaterial.selectTechnique(techniqueName, renderManager); } else { - clonedMaterial.selectTechnique("Default", renderManager); + clonedMaterial.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); } geom.setMaterial(clonedMaterial); @@ -156,7 +157,7 @@ public class OpaqueComparatorTest { Material lightingMatGlow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); lightingMatDefault.setName("TechDefault"); - lightingMatDefault.selectTechnique("Default", renderManager); + lightingMatDefault.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); lightingPostShadow.setName("TechPostShad"); lightingPostShadow.selectTechnique("PostShadow", renderManager); @@ -272,7 +273,7 @@ public class OpaqueComparatorTest { tex2.getImage().setId(3); matBase1.setName("BASE"); - matBase1.selectTechnique("Default", renderManager); + matBase1.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); matBase1.setBoolean("UseVertexColor", true); matBase1.setTexture("DiffuseMap", texBase); diff --git a/jme3-core/src/test/resources/same-name-technique.j3md b/jme3-core/src/test/resources/same-name-technique.j3md new file mode 100644 index 000000000..f3fde1876 --- /dev/null +++ b/jme3-core/src/test/resources/same-name-technique.j3md @@ -0,0 +1,10 @@ +MaterialDef Test Material { + Technique Test { + VertexShader GLSL150 : test150.vert + FragmentShader GLSL150 : test150.frag + } + Technique Test { + VertexShader GLSL100 : test.vert + FragmentShader GLSL100 : test.frag + } +} diff --git a/jme3-core/src/tools/java/jme3tools/shadercheck/ShaderCheck.java b/jme3-core/src/tools/java/jme3tools/shadercheck/ShaderCheck.java index d41dde064..98d379b38 100644 --- a/jme3-core/src/tools/java/jme3tools/shadercheck/ShaderCheck.java +++ b/jme3-core/src/tools/java/jme3tools/shadercheck/ShaderCheck.java @@ -38,7 +38,7 @@ public class ShaderCheck { MaterialDef def = (MaterialDef) assetManager.loadAsset(matdefName); EnumSet rendererCaps = EnumSet.noneOf(Caps.class); rendererCaps.add(Caps.GLSL100); - for (TechniqueDef techDef : def.getDefaultTechniques()) { + for (TechniqueDef techDef : def.getTechniqueDefs(TechniqueDef.DEFAULT_TECHNIQUE_NAME)) { DefineList defines = techDef.createDefineList(); Shader shader = techDef.getShader(assetManager, rendererCaps, defines); for (Validator validator : validators) { diff --git a/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java b/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java index 3b8088223..a580f54ed 100644 --- a/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java +++ b/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java @@ -3,6 +3,7 @@ package jme3test.material; import com.jme3.app.SimpleApplication; import com.jme3.material.Material; import com.jme3.material.Technique; +import com.jme3.material.TechniqueDef; import com.jme3.math.ColorRGBA; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; @@ -27,7 +28,7 @@ public class TestShaderNodes extends SimpleApplication { Texture tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); Material mat = new Material(assetManager, "Common/MatDefs/Misc/UnshadedNodes.j3md"); - mat.selectTechnique("Default", renderManager); + mat.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); Technique t = mat.getActiveTechnique(); // for (Shader.ShaderSource shaderSource : t.getShader().getSources()) {