Merge branch 'master' into PBRisComing

# Conflicts:
#	jme3-core/src/main/java/com/jme3/material/Material.java
define_list_fix
Nehon 9 years ago
commit 6a9c655be3
  1. 22
      jme3-core/src/main/java/com/jme3/material/MatParamTexture.java
  2. 167
      jme3-core/src/main/java/com/jme3/material/Material.java
  3. 47
      jme3-core/src/main/java/com/jme3/material/MaterialDef.java
  4. 34
      jme3-core/src/main/java/com/jme3/material/Technique.java
  5. 15
      jme3-core/src/main/java/com/jme3/material/TechniqueDef.java
  6. 48
      jme3-core/src/main/java/com/jme3/renderer/RenderManager.java
  7. 2
      jme3-core/src/main/java/com/jme3/scene/Spatial.java
  8. 2
      jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java
  9. 2
      jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java
  10. 74
      jme3-core/src/test/java/com/jme3/material/MaterialMatParamOverrideTest.java
  11. 15
      jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java
  12. 7
      jme3-core/src/test/java/com/jme3/renderer/OpaqueComparatorTest.java
  13. 10
      jme3-core/src/test/resources/same-name-technique.j3md
  14. 2
      jme3-core/src/tools/java/jme3tools/shadercheck/ShaderCheck.java
  15. 3
      jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java

@ -35,7 +35,6 @@ import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule; import com.jme3.export.OutputCapsule;
import com.jme3.renderer.Renderer;
import com.jme3.shader.VarType; import com.jme3.shader.VarType;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.texture.image.ColorSpace; import com.jme3.texture.image.ColorSpace;
@ -44,13 +43,11 @@ import java.io.IOException;
public class MatParamTexture extends MatParam { public class MatParamTexture extends MatParam {
private Texture texture; private Texture texture;
private int unit;
private ColorSpace colorSpace; 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); super(type, name, texture);
this.texture = texture; this.texture = texture;
this.unit = unit;
this.colorSpace = colorSpace; this.colorSpace = colorSpace;
} }
@ -92,31 +89,18 @@ public class MatParamTexture extends MatParam {
this.colorSpace = colorSpace; this.colorSpace = colorSpace;
} }
public void setUnit(int unit) {
this.unit = unit;
}
public int getUnit() {
return unit;
}
@Override @Override
public void write(JmeExporter ex) throws IOException { public void write(JmeExporter ex) throws IOException {
super.write(ex); super.write(ex);
OutputCapsule oc = ex.getCapsule(this); OutputCapsule oc = ex.getCapsule(this);
oc.write(unit, "texture_unit", -1); oc.write(0, "texture_unit", -1);
oc.write(texture, "texture", null); // For backwards compatibility
// For backwards compat
oc.write(texture, "texture", null);
} }
@Override @Override
public void read(JmeImporter im) throws IOException { public void read(JmeImporter im) throws IOException {
super.read(im); super.read(im);
InputCapsule ic = im.getCapsule(this); InputCapsule ic = im.getCapsule(this);
unit = ic.readInt("texture_unit", -1);
texture = (Texture) value; texture = (Texture) value;
//texture = (Texture) ic.readSavable("texture", null);
} }
} }

@ -93,7 +93,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
private ListMap<String, MatParam> paramValues = new ListMap<String, MatParam>(); private ListMap<String, MatParam> paramValues = new ListMap<String, MatParam>();
private Technique technique; private Technique technique;
private HashMap<String, Technique> techniques = new HashMap<String, Technique>(); private HashMap<String, Technique> techniques = new HashMap<String, Technique>();
private int nextTexUnit = 0;
private RenderState additionalState = null; private RenderState additionalState = null;
private RenderState mergedRenderState = new RenderState(); private RenderState mergedRenderState = new RenderState();
private boolean transparent = false; 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 // E.g. if user chose custom technique for one material but
// uses default technique for other material, the materials // uses default technique for other material, the materials
// are not equal. // are not equal.
String thisDefName = this.technique != null ? this.technique.getDef().getName() : "Default"; String thisDefName = this.technique != null
String otherDefName = other.technique != null ? other.technique.getDef().getName() : "Default"; ? 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)) { if (!thisDefName.equals(otherDefName)) {
return false; return false;
} }
@ -510,16 +515,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
paramValues.remove(name); paramValues.remove(name);
if (matParam instanceof MatParamTexture) { 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; sortingId = -1;
} }
if (technique != null) { if (technique != null) {
@ -562,13 +557,13 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
+ "Linear using texture.getImage.setColorSpace().", + "Linear using texture.getImage.setColorSpace().",
new Object[]{value.getName(), value.getImage().getColorSpace().name(), name}); 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 { } else {
val.setTextureValue(value); val.setTextureValue(value);
} }
if (technique != null) { if (technique != null) {
technique.notifyParamChanged(name, type, nextTexUnit - 1); technique.notifyParamChanged(name, type, value);
} }
// need to recompute sort ID // 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. * Select the technique to use for rendering this material.
* <p> * <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) * Any candidate technique for selection (either default or named)
* must be verified to be compatible with the system, for that, the * must be verified to be compatible with the system, for that, the
* <code>renderManager</code> is queried for capabilities. * <code>renderManager</code> is queried for capabilities.
* *
* @param name The name of the technique to select, pass "Default" to * @param name The name of the technique to select, pass
* select one of the default techniques. * {@link TechniqueDef#DEFAULT_TECHNIQUE_NAME} to select one of the default
* techniques.
* @param renderManager The {@link RenderManager render manager} * @param renderManager The {@link RenderManager render manager}
* to query for capabilities. * to query for capabilities.
* *
* @throws IllegalArgumentException If "Default" is passed and no default * @throws IllegalArgumentException If no technique exists with the given
* techniques are available on the material definition, or if a name * name.
* is passed but there's no technique by that name.
* @throws UnsupportedOperationException If no candidate technique supports * @throws UnsupportedOperationException If no candidate technique supports
* the system capabilities. * the system capabilities.
*/ */
@ -731,46 +721,30 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
// supports all the caps. // supports all the caps.
if (tech == null) { if (tech == null) {
EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps(); EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps();
if (name.equals("Default")) { List<TechniqueDef> techDefs = def.getTechniqueDefs(name);
List<TechniqueDef> techDefs = def.getDefaultTechniques();
if (techDefs == null || techDefs.isEmpty()) {
throw new IllegalArgumentException("No default techniques are available on material '" + def.getName() + "'");
}
TechniqueDef lastTech = null; if (techDefs == null || techDefs.isEmpty()) {
for (TechniqueDef techDef : techDefs) { throw new IllegalArgumentException(
if (rendererCaps.containsAll(techDef.getRequiredCaps())) { String.format("The requested technique %s is not available on material %s", name, def.getName()));
// 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 (!rendererCaps.containsAll(techDef.getRequiredCaps())) { TechniqueDef lastTech = null;
throw new UnsupportedOperationException("The explicitly chosen technique '" + name + "' on material '" + def.getName() + "'\n" for (TechniqueDef techDef : techDefs) {
+ "requires caps " + techDef.getRequiredCaps() + " which are not " if (rendererCaps.containsAll(techDef.getRequiredCaps())) {
+ "supported by the video renderer"); // 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;
tech = new Technique(this, techDef); }
techniques.put(name, tech); 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) { } else if (technique == tech) {
// attempting to switch to an already // attempting to switch to an already
@ -785,32 +759,43 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
sortingId = -1; sortingId = -1;
} }
private int updateShaderMaterialParameters(Renderer renderer, Shader shader, List<MatParamOverride> overrides) { private int applyOverrides(Renderer renderer, Shader shader, List<MatParamOverride> overrides, int unit) {
int unit = 0; for (MatParamOverride override : overrides) {
VarType type = override.getVarType();
if (overrides != null) { MatParam paramDef = def.getMaterialParam(override.getName());
for (MatParamOverride override : overrides) {
VarType type = override.getVarType();
MatParam paramDef = def.getMaterialParam(override.getName()); if (paramDef == null || paramDef.getVarType() != type || !override.isEnabled()) {
if (paramDef == null || paramDef.getVarType() != type || !override.isEnabled()) { continue;
continue; }
}
Uniform uniform = shader.getUniform(override.getPrefixedName()); Uniform uniform = shader.getUniform(override.getPrefixedName());
if (override.getValue() != null) {
if (type.isTextureType()) { if (override.getValue() != null) {
renderer.setTexture(unit, (Texture) override.getValue()); if (type.isTextureType()) {
uniform.setValue(VarType.Int, unit); renderer.setTexture(unit, (Texture) override.getValue());
unit++; uniform.setValue(VarType.Int, unit);
} else { unit++;
uniform.setValue(type, override.getValue());
}
} else { } 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++) { for (int i = 0; i < paramValues.size(); i++) {
MatParam param = paramValues.getValue(i); MatParam param = paramValues.getValue(i);
@ -857,7 +842,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
*/ */
public void preload(RenderManager renderManager) { public void preload(RenderManager renderManager) {
if (technique == null) { if (technique == null) {
selectTechnique("Default", renderManager); selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
} }
TechniqueDef techniqueDef = technique.getDef(); TechniqueDef techniqueDef = technique.getDef();
Renderer renderer = renderManager.getRenderer(); Renderer renderer = renderManager.getRenderer();
@ -867,8 +852,8 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
return; return;
} }
Shader shader = technique.makeCurrent(renderManager, null, null, rendererCaps); Shader shader = technique.makeCurrent(renderManager, null, null, null, rendererCaps);
updateShaderMaterialParameters(renderer, shader, null); updateShaderMaterialParameters(renderer, shader, null, null);
renderManager.getRenderer().setShader(shader); renderManager.getRenderer().setShader(shader);
} }
@ -957,7 +942,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
*/ */
public void render(Geometry geometry, LightList lights, RenderManager renderManager) { public void render(Geometry geometry, LightList lights, RenderManager renderManager) {
if (technique == null) { if (technique == null) {
selectTechnique("Default", renderManager); selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
} }
TechniqueDef techniqueDef = technique.getDef(); TechniqueDef techniqueDef = technique.getDef();
@ -975,7 +960,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
List<MatParamOverride> overrides = geometry.getWorldMatParamOverrides(); List<MatParamOverride> overrides = geometry.getWorldMatParamOverrides();
// Select shader to use // 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. // Begin tracking which uniforms were changed by material.
clearUniformsSetByCurrent(shader); clearUniformsSetByCurrent(shader);
@ -984,9 +969,10 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
renderManager.updateUniformBindings(shader); renderManager.updateUniformBindings(shader);
// Set material parameters // Set material parameters
//TODO RRemove the unit when texture units are handled in the Uniform //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. // Clear any uniforms not changed by material.
resetUniformsNotSetByCurrent(shader); resetUniformsNotSetByCurrent(shader);
@ -1081,11 +1067,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
MatParam param = entry.getValue(); MatParam param = entry.getValue();
if (param instanceof MatParamTexture) { if (param instanceof MatParamTexture) {
MatParamTexture texVal = (MatParamTexture) param; MatParamTexture texVal = (MatParamTexture) param;
if (nextTexUnit < texVal.getUnit() + 1) {
nextTexUnit = texVal.getUnit() + 1;
}
// the texture failed to load for this param // the texture failed to load for this param
// do not add to param values // do not add to param values
if (texVal.getTextureValue() == null || texVal.getTextureValue().getImage() == null) { if (texVal.getTextureValue() == null || texVal.getTextureValue().getImage() == null) {

@ -32,6 +32,7 @@
package com.jme3.material; package com.jme3.material;
import com.jme3.asset.AssetManager; import com.jme3.asset.AssetManager;
import com.jme3.renderer.Caps;
import com.jme3.shader.VarType; import com.jme3.shader.VarType;
import com.jme3.texture.image.ColorSpace; import com.jme3.texture.image.ColorSpace;
import java.util.*; import java.util.*;
@ -51,8 +52,7 @@ public class MaterialDef {
private String assetName; private String assetName;
private AssetManager assetManager; private AssetManager assetManager;
private List<TechniqueDef> defaultTechs; private Map<String, List<TechniqueDef>> techniques;
private Map<String, TechniqueDef> techniques;
private Map<String, MatParam> matParams; private Map<String, MatParam> matParams;
/** /**
@ -70,9 +70,8 @@ public class MaterialDef {
public MaterialDef(AssetManager assetManager, String name){ public MaterialDef(AssetManager assetManager, String name){
this.assetManager = assetManager; this.assetManager = assetManager;
this.name = name; this.name = name;
techniques = new HashMap<String, TechniqueDef>(); techniques = new HashMap<String, List<TechniqueDef>>();
matParams = new HashMap<String, MatParam>(); matParams = new HashMap<String, MatParam>();
defaultTechs = new ArrayList<TechniqueDef>();
logger.log(Level.FINE, "Loaded material definition: {0}", name); logger.log(Level.FINE, "Loaded material definition: {0}", name);
} }
@ -135,7 +134,7 @@ public class MaterialDef {
* @see ColorSpace * @see ColorSpace
*/ */
public void addMaterialParamTexture(VarType type, String name, ColorSpace 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. * 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. * @param technique The technique definition to add.
*/ */
public void addTechniqueDef(TechniqueDef technique) { public void addTechniqueDef(TechniqueDef technique) {
if (technique.getName().equals("Default")) { List<TechniqueDef> list = techniques.get(technique.getName());
defaultTechs.add(technique); if (list == null) {
} else { list = new ArrayList<>();
techniques.put(technique.getName(), technique); techniques.put(technique.getName(), list);
} }
list.add(technique);
} }
/** /**
* Returns a list of all default techniques. * Returns technique definitions with the given name.
* *
* @return a list of all default techniques. * @param name The name of the technique definitions to find
*/ *
public List<TechniqueDef> getDefaultTechniques(){ * @return The technique definitions, or null if cannot be found.
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.
*/ */
public TechniqueDef getTechniqueDef(String name) { public List<TechniqueDef> getTechniqueDefs(String name) {
return techniques.get(name); return techniques.get(name);
} }
} }

@ -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. * 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. * @param rendererCaps The renderer capabilities which the shader should support.
* @return A compatible shader. * @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) { LightList lights, EnumSet<Caps> rendererCaps) {
TechniqueDefLogic logic = def.getLogic(); TechniqueDefLogic logic = def.getLogic();
AssetManager assetManager = owner.getMaterialDef().getAssetManager(); AssetManager assetManager = owner.getMaterialDef().getAssetManager();
@ -128,18 +143,11 @@ public final class Technique {
dynamicDefines.clear(); dynamicDefines.clear();
dynamicDefines.setAll(paramDefines); dynamicDefines.setAll(paramDefines);
if (overrides != null) { if (worldOverrides != null) {
for (MatParamOverride override : overrides) { applyOverrides(dynamicDefines, worldOverrides);
if (!override.isEnabled()) { }
continue; if (forcedOverrides != null) {
} applyOverrides(dynamicDefines, forcedOverrides);
Integer defineId = def.getShaderParamDefineId(override.name);
if (defineId != null) {
if (def.getDefineIdType(defineId) == override.type) {
dynamicDefines.set(defineId, override.type, override.value);
}
}
}
} }
return logic.makeCurrent(assetManager, renderManager, rendererCaps, lights, dynamicDefines); return logic.makeCurrent(assetManager, renderManager, rendererCaps, lights, dynamicDefines);

@ -53,6 +53,14 @@ public class TechniqueDef implements Savable {
*/ */
public static final int SAVABLE_VERSION = 1; 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. * Describes light rendering mode.
*/ */
@ -113,7 +121,7 @@ public class TechniqueDef implements Savable {
Legacy Legacy
} }
private EnumSet<Caps> requiredCaps = EnumSet.noneOf(Caps.class); private final EnumSet<Caps> requiredCaps = EnumSet.noneOf(Caps.class);
private String name; private String name;
private int sortId; private int sortId;
@ -153,7 +161,7 @@ public class TechniqueDef implements Savable {
public TechniqueDef(String name, int sortId){ public TechniqueDef(String name, int sortId){
this(); this();
this.sortId = sortId; 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. * 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 * @return the name of this technique
*/ */

@ -34,6 +34,7 @@ package com.jme3.renderer;
import com.jme3.light.DefaultLightFilter; import com.jme3.light.DefaultLightFilter;
import com.jme3.light.LightFilter; import com.jme3.light.LightFilter;
import com.jme3.light.LightList; import com.jme3.light.LightList;
import com.jme3.material.MatParamOverride;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.material.MaterialDef; import com.jme3.material.MaterialDef;
import com.jme3.material.RenderState; import com.jme3.material.RenderState;
@ -82,6 +83,7 @@ public class RenderManager {
private Material forcedMaterial = null; private Material forcedMaterial = null;
private String forcedTechnique = null; private String forcedTechnique = null;
private RenderState forcedRenderState = null; private RenderState forcedRenderState = null;
private final List<MatParamOverride> forcedOverrides = new ArrayList<>();
private int viewX, viewY, viewWidth, viewHeight; private int viewX, viewY, viewWidth, viewHeight;
private Matrix4f orthoMatrix = new Matrix4f(); private Matrix4f orthoMatrix = new Matrix4f();
private LightList filteredLightList = new LightList(null); private LightList filteredLightList = new LightList(null);
@ -92,6 +94,7 @@ public class RenderManager {
private TechniqueDef.LightMode preferredLightMode = TechniqueDef.LightMode.MultiPass; private TechniqueDef.LightMode preferredLightMode = TechniqueDef.LightMode.MultiPass;
private int singlePassLightBatchSize = 1; private int singlePassLightBatchSize = 1;
/** /**
* Create a high-level rendering interface over the * Create a high-level rendering interface over the
* low-level rendering interface. * low-level rendering interface.
@ -426,6 +429,44 @@ public class RenderManager {
this.forcedTechnique = forcedTechnique; 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. * Enable or disable alpha-to-coverage.
* <p> * <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 //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 //else the geom is not rendered
if (forcedTechnique != null) { if (forcedTechnique != null) {
if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) { MaterialDef matDef = g.getMaterial().getMaterialDef();
tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default"; if (matDef.getTechniqueDefs(forcedTechnique) != null) {
tmpTech = g.getMaterial().getActiveTechnique() != null
? g.getMaterial().getActiveTechnique().getDef().getName()
: TechniqueDef.DEFAULT_TECHNIQUE_NAME;
g.getMaterial().selectTechnique(forcedTechnique, this); g.getMaterial().selectTechnique(forcedTechnique, this);
//saving forcedRenderState for future calls //saving forcedRenderState for future calls
RenderState tmpRs = forcedRenderState; RenderState tmpRs = forcedRenderState;

@ -593,8 +593,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
worldOverrides.addAll(localOverrides); worldOverrides.addAll(localOverrides);
} else { } else {
assert (parent.refreshFlags & RF_MATPARAM_OVERRIDE) == 0; assert (parent.refreshFlags & RF_MATPARAM_OVERRIDE) == 0;
worldOverrides.addAll(localOverrides);
worldOverrides.addAll(parent.worldOverrides); worldOverrides.addAll(parent.worldOverrides);
worldOverrides.addAll(localOverrides);
} }
} }

@ -587,7 +587,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
for (int i = 0; i < l.size(); i++) { for (int i = 0; i < l.size(); i++) {
Material mat = l.get(i).getMaterial(); Material mat = l.get(i).getMaterial();
//checking if the material has the post technique and adding it to the material cache //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)) { if (!matCache.contains(mat)) {
matCache.add(mat); matCache.add(mat);
} }

@ -533,7 +533,7 @@ public class PssmShadowRenderer implements SceneProcessor {
for (int i = 0; i < l.size(); i++) { for (int i = 0; i < l.size(); i++) {
Material mat = l.get(i).getMaterial(); Material mat = l.get(i).getMaterial();
//checking if the material has the post technique and adding it to the material cache //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)) { if (!matCache.contains(mat)) {
matCache.add(mat); matCache.add(mat);
} }

@ -46,14 +46,17 @@ import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import static com.jme3.scene.MPOTestUtils.*; import static com.jme3.scene.MPOTestUtils.*;
import com.jme3.scene.Node;
import com.jme3.shader.DefineList; import com.jme3.shader.DefineList;
import com.jme3.system.NullRenderer; import com.jme3.system.NullRenderer;
import com.jme3.system.TestUtil; import com.jme3.system.TestUtil;
import com.jme3.texture.Image.Format; import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D; import com.jme3.texture.Texture2D;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.junit.Before;
/** /**
* Validates how {@link MatParamOverride MPOs} work on the material level. * Validates how {@link MatParamOverride MPOs} work on the material level.
@ -124,6 +127,33 @@ public class MaterialMatParamOverrideTest {
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f)); 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 @Test
public void testMpoDisable() { public void testMpoDisable() {
material("Common/MatDefs/Light/Lighting.j3md"); material("Common/MatDefs/Light/Lighting.j3md");
@ -222,7 +252,7 @@ public class MaterialMatParamOverrideTest {
reset(); reset();
geometry.clearMatParamOverrides(); geometry.clearMatParamOverrides();
geometry.updateGeometricState(); root.updateGeometricState();
outDefines(def("NUM_BONES", VarType.Int, 1234)); outDefines(def("NUM_BONES", VarType.Int, 1234));
outUniforms(uniform("NumberOfBones", VarType.Int, 1234)); outUniforms(uniform("NumberOfBones", VarType.Int, 1234));
@ -272,7 +302,7 @@ public class MaterialMatParamOverrideTest {
reset(); reset();
geometry.clearMatParamOverrides(); geometry.clearMatParamOverrides();
geometry.updateGeometricState(); root.updateGeometricState();
outDefines(); outDefines();
outUniforms(); outUniforms();
} }
@ -315,7 +345,7 @@ public class MaterialMatParamOverrideTest {
reset(); reset();
geometry.clearMatParamOverrides(); geometry.clearMatParamOverrides();
geometry.updateGeometricState(); root.updateGeometricState();
outDefines(); outDefines();
outUniforms(); outUniforms();
outTextures(); outTextures();
@ -341,7 +371,7 @@ public class MaterialMatParamOverrideTest {
reset(); reset();
geometry.clearMatParamOverrides(); geometry.clearMatParamOverrides();
geometry.updateGeometricState(); root.updateGeometricState();
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex1)); outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex1));
outUniforms(uniform("DiffuseMap", VarType.Int, 0)); outUniforms(uniform("DiffuseMap", VarType.Int, 0));
outTextures(tex1); 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); private final LightList lightList = new LightList(geometry);
@Before
public void setUp() {
root.attachChild(geometry);
}
private final NullRenderer renderer = new NullRenderer() { private final NullRenderer renderer = new NullRenderer() {
@Override @Override
public void setShader(Shader shader) { public void setShader(Shader shader) {
@ -407,13 +443,35 @@ public class MaterialMatParamOverrideTest {
for (MatParamOverride override : overrides) { for (MatParamOverride override : overrides) {
geometry.addMatParamOverride(override); 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() { private void reset() {
evaluated = false; evaluated = false;
usedShader = null; usedShader = null;
Arrays.fill(usedTextures, null); Arrays.fill(usedTextures, null);
for (MatParamOverride override : new ArrayList<>(renderManager.getForcedMatParams())) {
renderManager.removeForcedMatParam(override);
}
} }
private Define def(String name, VarType type, Object value) { private Define def(String name, VarType type, Object value) {
@ -520,6 +578,10 @@ public class MaterialMatParamOverrideTest {
} }
private void outUniforms(Uniform... uniforms) { private void outUniforms(Uniform... uniforms) {
if (!evaluated) {
evaluateTechniqueDef();
}
HashSet<Uniform> actualUniforms = new HashSet<>(); HashSet<Uniform> actualUniforms = new HashSet<>();
for (Uniform uniform : usedShader.getUniformMap().values()) { for (Uniform uniform : usedShader.getUniformMap().values()) {

@ -7,8 +7,11 @@ import com.jme3.asset.TextureKey;
import com.jme3.material.MatParamTexture; import com.jme3.material.MatParamTexture;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.material.MaterialDef; import com.jme3.material.MaterialDef;
import com.jme3.renderer.Caps;
import com.jme3.shader.VarType; import com.jme3.shader.VarType;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import java.io.IOException;
import java.util.EnumSet;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -18,6 +21,7 @@ import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.junit.Assert.*;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
@ -51,6 +55,15 @@ public class J3MLoaderTest {
j3MLoader = new J3MLoader(); 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 @Test
public void oldStyleTextureParameters_shouldBeSupported() throws Exception { public void oldStyleTextureParameters_shouldBeSupported() throws Exception {
when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/texture-parameters-oldstyle.j3m")); 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) { 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); final TextureKey textureKey = new TextureKey(path, flipY);
textureKey.setGenerateMips(true); textureKey.setGenerateMips(true);

@ -33,6 +33,7 @@ package com.jme3.renderer;
import com.jme3.asset.AssetManager; import com.jme3.asset.AssetManager;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.material.TechniqueDef;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.renderer.queue.GeometryList; import com.jme3.renderer.queue.GeometryList;
import com.jme3.renderer.queue.OpaqueComparator; import com.jme3.renderer.queue.OpaqueComparator;
@ -92,7 +93,7 @@ public class OpaqueComparatorTest {
String techniqueName = materials[i].getActiveTechnique().getDef().getName(); String techniqueName = materials[i].getActiveTechnique().getDef().getName();
clonedMaterial.selectTechnique(techniqueName, renderManager); clonedMaterial.selectTechnique(techniqueName, renderManager);
} else { } else {
clonedMaterial.selectTechnique("Default", renderManager); clonedMaterial.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
} }
geom.setMaterial(clonedMaterial); geom.setMaterial(clonedMaterial);
@ -156,7 +157,7 @@ public class OpaqueComparatorTest {
Material lightingMatGlow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); Material lightingMatGlow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
lightingMatDefault.setName("TechDefault"); lightingMatDefault.setName("TechDefault");
lightingMatDefault.selectTechnique("Default", renderManager); lightingMatDefault.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
lightingPostShadow.setName("TechPostShad"); lightingPostShadow.setName("TechPostShad");
lightingPostShadow.selectTechnique("PostShadow", renderManager); lightingPostShadow.selectTechnique("PostShadow", renderManager);
@ -272,7 +273,7 @@ public class OpaqueComparatorTest {
tex2.getImage().setId(3); tex2.getImage().setId(3);
matBase1.setName("BASE"); matBase1.setName("BASE");
matBase1.selectTechnique("Default", renderManager); matBase1.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
matBase1.setBoolean("UseVertexColor", true); matBase1.setBoolean("UseVertexColor", true);
matBase1.setTexture("DiffuseMap", texBase); matBase1.setTexture("DiffuseMap", texBase);

@ -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
}
}

@ -38,7 +38,7 @@ public class ShaderCheck {
MaterialDef def = (MaterialDef) assetManager.loadAsset(matdefName); MaterialDef def = (MaterialDef) assetManager.loadAsset(matdefName);
EnumSet<Caps> rendererCaps = EnumSet.noneOf(Caps.class); EnumSet<Caps> rendererCaps = EnumSet.noneOf(Caps.class);
rendererCaps.add(Caps.GLSL100); rendererCaps.add(Caps.GLSL100);
for (TechniqueDef techDef : def.getDefaultTechniques()) { for (TechniqueDef techDef : def.getTechniqueDefs(TechniqueDef.DEFAULT_TECHNIQUE_NAME)) {
DefineList defines = techDef.createDefineList(); DefineList defines = techDef.createDefineList();
Shader shader = techDef.getShader(assetManager, rendererCaps, defines); Shader shader = techDef.getShader(assetManager, rendererCaps, defines);
for (Validator validator : validators) { for (Validator validator : validators) {

@ -3,6 +3,7 @@ package jme3test.material;
import com.jme3.app.SimpleApplication; import com.jme3.app.SimpleApplication;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.material.Technique; import com.jme3.material.Technique;
import com.jme3.material.TechniqueDef;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.scene.Geometry; import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Box;
@ -27,7 +28,7 @@ public class TestShaderNodes extends SimpleApplication {
Texture tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); Texture tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg");
Material mat = new Material(assetManager, "Common/MatDefs/Misc/UnshadedNodes.j3md"); 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(); Technique t = mat.getActiveTechnique();
// for (Shader.ShaderSource shaderSource : t.getShader().getSources()) { // for (Shader.ShaderSource shaderSource : t.getShader().getSources()) {

Loading…
Cancel
Save