Explorar o código

Merge branch 'master' into PBRisComing

# Conflicts:
#	jme3-core/src/main/java/com/jme3/material/Material.java
Nehon %!s(int64=9) %!d(string=hai) anos
pai
achega
6a9c655be3

+ 3 - 19
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);
     }
 }

+ 74 - 93
jme3-core/src/main/java/com/jme3/material/Material.java

@@ -93,7 +93,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
     private ListMap<String, MatParam> paramValues = new ListMap<String, MatParam>();
     private Technique technique;
     private HashMap<String, Technique> techniques = new HashMap<String, Technique>();
-    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.
      * <p>
-     * If <code>name</code> 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.
-     * <p>
      * Any candidate technique for selection (either default or named)
      * must be verified to be compatible with the system, for that, the
      * <code>renderManager</code> 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<Caps> rendererCaps = renderManager.getRenderer().getCaps();
-            if (name.equals("Default")) {
-                List<TechniqueDef> techDefs = def.getDefaultTechniques();
-                if (techDefs == null || techDefs.isEmpty()) {
-                    throw new IllegalArgumentException("No default techniques are available on material '" + def.getName() + "'");
-                }
+            List<TechniqueDef> 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<MatParamOverride> overrides) {
-        int unit = 0;
+    private int applyOverrides(Renderer renderer, Shader shader, List<MatParamOverride> 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<MatParamOverride> worldOverrides, List<MatParamOverride> 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<MatParamOverride> 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) {

+ 16 - 31
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<TechniqueDef> defaultTechs;
-    private Map<String, TechniqueDef> techniques;
+    private Map<String, List<TechniqueDef>> techniques;
     private Map<String, MatParam> matParams;
 
     /**
@@ -70,9 +70,8 @@ public class MaterialDef {
     public MaterialDef(AssetManager assetManager, String name){
         this.assetManager = assetManager;
         this.name = name;
-        techniques = new HashMap<String, TechniqueDef>();
+        techniques = new HashMap<String, List<TechniqueDef>>();
         matParams = new HashMap<String, MatParam>();
-        defaultTechs = new ArrayList<TechniqueDef>();
         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.
-     * <p>
-     * 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<TechniqueDef> 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<TechniqueDef> 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<TechniqueDef> getTechniqueDefs(String name) {
         return techniques.get(name);
     }
-
 }

+ 21 - 13
jme3-core/src/main/java/com/jme3/material/Technique.java

@@ -110,6 +110,20 @@ public final class Technique {
         }
     }
 
+    private void applyOverrides(DefineList defineList, List<MatParamOverride> 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<MatParamOverride> overrides,
+    Shader makeCurrent(RenderManager renderManager, List<MatParamOverride> worldOverrides,
+            List<MatParamOverride> forcedOverrides,
             LightList lights, EnumSet<Caps> 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);

+ 12 - 3
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<Caps> requiredCaps = EnumSet.noneOf(Caps.class);
+    private final EnumSet<Caps> 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
      */

+ 46 - 2
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<MatParamOverride> 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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<MatParamOverride> getForcedMatParams() {
+        return forcedOverrides;
+    }
+
     /**
      * Enable or disable alpha-to-coverage. 
      * <p>
@@ -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;

+ 1 - 1
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);
         }
     }
 

+ 1 - 1
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);
                 }

+ 1 - 1
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);
                 }

+ 68 - 6
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<Uniform> actualUniforms = new HashSet<>();
 
         for (Uniform uniform : usedShader.getUniformMap().values()) {

+ 14 - 1
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);

+ 4 - 3
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);
         

+ 10 - 0
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
+    }
+}

+ 1 - 1
jme3-core/src/tools/java/jme3tools/shadercheck/ShaderCheck.java

@@ -38,7 +38,7 @@ public class ShaderCheck {
         MaterialDef def = (MaterialDef) assetManager.loadAsset(matdefName);
         EnumSet<Caps> 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) {

+ 2 - 1
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()) {