From cb04548fd553664e615d1ca1d711482d8dad9b24 Mon Sep 17 00:00:00 2001 From: Nehon Date: Sat, 14 Jan 2017 15:25:20 +0100 Subject: [PATCH] Techniques are now weighted and sorted depending on their LightMode and GLSL version --- .../main/java/com/jme3/material/Material.java | 12 +- .../java/com/jme3/material/MaterialDef.java | 28 ++- .../java/com/jme3/material/TechniqueDef.java | 23 ++- .../material/TestTechniqueDefOrdering.java | 167 ++++++++++++++++++ 4 files changed, 217 insertions(+), 13 deletions(-) create mode 100644 jme3-core/src/test/java/com/jme3/material/TestTechniqueDefOrdering.java 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 15a996b58..1f03bc9ae 100644 --- a/jme3-core/src/main/java/com/jme3/material/Material.java +++ b/jme3-core/src/main/java/com/jme3/material/Material.java @@ -704,15 +704,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { * @throws UnsupportedOperationException If no candidate technique supports * the system capabilities. */ - public void selectTechnique(String name, RenderManager renderManager) { + public void selectTechnique(String name, final RenderManager renderManager) { // check if already created Technique tech = techniques.get(name); // When choosing technique, we choose one that // supports all the caps. if (tech == null) { EnumSet rendererCaps = renderManager.getRenderer().getCaps(); - List techDefs = def.getTechniqueDefs(name); - + List techDefs = def.getSortedTechniqueDefs(name, renderManager); if (techDefs == null || techDefs.isEmpty()) { throw new IllegalArgumentException( String.format("The requested technique %s is not available on material %s", name, def.getName())); @@ -724,10 +723,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { // 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; - } + break; } lastTech = techDef; } @@ -779,7 +775,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { } private int updateShaderMaterialParameters(Renderer renderer, Shader shader, - SafeArrayList worldOverrides, SafeArrayList forcedOverrides) { + SafeArrayList worldOverrides, SafeArrayList forcedOverrides) { int unit = 0; if (worldOverrides != 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 d780c5111..bd01b8d06 100644 --- a/jme3-core/src/main/java/com/jme3/material/MaterialDef.java +++ b/jme3-core/src/main/java/com/jme3/material/MaterialDef.java @@ -32,11 +32,10 @@ package com.jme3.material; import com.jme3.asset.AssetManager; -import com.jme3.export.*; +import com.jme3.renderer.RenderManager; import com.jme3.shader.VarType; import com.jme3.texture.image.ColorSpace; -import java.io.IOException; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; @@ -56,6 +55,7 @@ public class MaterialDef{ private Map> techniques; private Map matParams; + private TechDefComparator comparator = new TechDefComparator(); /** * Serialization only. Do not use. @@ -188,6 +188,17 @@ public class MaterialDef{ return techniques.get(name); } + public List getSortedTechniqueDefs(String name, RenderManager rm) { + List techDefs = getTechniqueDefs(name); + if (techDefs == null) { + return null; + } + //Sorting the techdef depending on their weight (depending on their glsl version) and on the preferred light mode) + comparator.rm = rm; + Collections.sort(techDefs, comparator); + return techDefs; + } + /** * * @return the list of all the technique definitions names. @@ -196,4 +207,17 @@ public class MaterialDef{ return techniques.keySet(); } + public static class TechDefComparator implements Comparator { + + RenderManager rm; + + @Override + public int compare(TechniqueDef o1, TechniqueDef o2) { + float o1Weight = o1.getWeight() + (o1.getLightMode() == rm.getPreferredLightMode() ? 10f : 0); + float o2Weight = o2.getWeight() + (o2.getLightMode() == rm.getPreferredLightMode() ? 10f : 0); + return (int) Math.signum(o2Weight - o1Weight); + } + } + + } 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 9ad4ece48..62a0a0cd3 100644 --- a/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java +++ b/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java @@ -31,9 +31,9 @@ */ package com.jme3.material; -import com.jme3.material.logic.TechniqueDefLogic; import com.jme3.asset.AssetManager; import com.jme3.export.*; +import com.jme3.material.logic.TechniqueDefLogic; import com.jme3.renderer.Caps; import com.jme3.shader.*; import com.jme3.shader.Shader.ShaderType; @@ -161,6 +161,9 @@ public class TechniqueDef implements Savable { //The space in which the light should be transposed before sending to the shader. private LightSpace lightSpace; + //used to find the best fit technique + private float weight = 0; + /** * Creates a new technique definition. *

@@ -341,6 +344,8 @@ public class TechniqueDef implements Savable { requiredCaps.add(vertCap); Caps fragCap = Caps.valueOf(fragLanguage); requiredCaps.add(fragCap); + + weight = Math.max(vertCap.ordinal(), fragCap.ordinal()); } /** @@ -534,6 +539,7 @@ public class TechniqueDef implements Savable { public void setShaderFile(EnumMap shaderNames, EnumMap shaderLanguages) { requiredCaps.clear(); + int maxCap = 0; for (Shader.ShaderType shaderType : shaderNames.keySet()) { String language = shaderLanguages.get(shaderType); String shaderFile = shaderNames.get(shaderType); @@ -541,8 +547,9 @@ public class TechniqueDef implements Savable { this.shaderLanguages.put(shaderType, language); this.shaderNames.put(shaderType, shaderFile); - Caps vertCap = Caps.valueOf(language); - requiredCaps.add(vertCap); + Caps cap = Caps.valueOf(language); + requiredCaps.add(cap); + maxCap = Math.max(maxCap, cap.ordinal()); if (shaderType.equals(Shader.ShaderType.Geometry)) { requiredCaps.add(Caps.GeometryShader); @@ -550,6 +557,7 @@ public class TechniqueDef implements Savable { requiredCaps.add(Caps.TesselationShader); } } + weight = maxCap; } /** @@ -600,6 +608,15 @@ public class TechniqueDef implements Savable { return shaderNames.get(shaderType); } + /** + * returns the weight of the technique def + * + * @return + */ + public float getWeight() { + return weight; + } + /** * Adds a new world parameter by the given name. * diff --git a/jme3-core/src/test/java/com/jme3/material/TestTechniqueDefOrdering.java b/jme3-core/src/test/java/com/jme3/material/TestTechniqueDefOrdering.java new file mode 100644 index 000000000..494d20b31 --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/material/TestTechniqueDefOrdering.java @@ -0,0 +1,167 @@ +package com.jme3.material; + +import com.jme3.renderer.RenderManager; +import com.jme3.shader.Shader; +import com.jme3.system.NullRenderer; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.assertEquals; + +/** + * Created by Nehon on 14/01/2017. + */ +public class TestTechniqueDefOrdering { + + @Test + public void order() { + + RenderManager rm = new RenderManager(new NullRenderer()); + rm.setPreferredLightMode(TechniqueDef.LightMode.MultiPass); + MaterialDef.TechDefComparator comp = new MaterialDef.TechDefComparator(); + comp.rm = rm; + + + //random case + List defs = new ArrayList<>(); + TechniqueDef def = new TechniqueDef("tech", 1); + def.setShaderFile("", "", "GLSL100", "GLSL100"); + def.setLightMode(TechniqueDef.LightMode.SinglePass); + defs.add(def); + def = new TechniqueDef("tech2", 1); + def.setShaderFile("", "", "GLSL150", "GLSL150"); + def.setLightMode(TechniqueDef.LightMode.MultiPass); + defs.add(def); + def = new TechniqueDef("tech3", 1); + def.setShaderFile("", "", "GLSL110", "GLSL110"); + defs.add(def); + def = new TechniqueDef("tech4", 1); + def.setShaderFile("", "", "GLSL120", "GLSL120"); + defs.add(def); + def = new TechniqueDef("tech5", 1); + def.setShaderFile("", "", "GLSL130", "GLSL130"); + defs.add(def); + + Collections.sort(defs, comp); + + assertEquals(defs.get(0).getName(), "tech2"); + assertEquals(defs.get(1).getName(), "tech5"); + assertEquals(defs.get(2).getName(), "tech4"); + assertEquals(defs.get(3).getName(), "tech3"); + assertEquals(defs.get(4).getName(), "tech"); + + + //Test the unshaded material case: 2 disabled : 150 and 100 + defs = new ArrayList<>(); + def = new TechniqueDef("unshaded", 1); + def.setShaderFile("", "", "GLSL100", "GLSL100"); + defs.add(def); + def = new TechniqueDef("unshaded2", 1); + def.setShaderFile("", "", "GLSL150", "GLSL150"); + defs.add(def); + Collections.sort(defs, comp); + + assertEquals(defs.get(0).getName(), "unshaded2"); + assertEquals(defs.get(1).getName(), "unshaded"); + + //Test the lighting material case: 2 singlepass : 150 and 100, 2 multipass : 150 and 100 + defs = new ArrayList<>(); + def = new TechniqueDef("lighting1", 1); + def.setShaderFile("", "", "GLSL100", "GLSL100"); + def.setLightMode(TechniqueDef.LightMode.MultiPass); + defs.add(def); + def = new TechniqueDef("lighting2", 1); + def.setShaderFile("", "", "GLSL150", "GLSL150"); + def.setLightMode(TechniqueDef.LightMode.MultiPass); + defs.add(def); + def = new TechniqueDef("lighting3", 1); + def.setShaderFile("", "", "GLSL100", "GLSL100"); + def.setLightMode(TechniqueDef.LightMode.SinglePass); + defs.add(def); + def = new TechniqueDef("lighting4", 1); + def.setShaderFile("", "", "GLSL150", "GLSL150"); + def.setLightMode(TechniqueDef.LightMode.SinglePass); + defs.add(def); + Collections.sort(defs, comp); + + assertEquals(defs.get(0).getName(), "lighting2"); + assertEquals(defs.get(1).getName(), "lighting1"); + assertEquals(defs.get(2).getName(), "lighting4"); + assertEquals(defs.get(3).getName(), "lighting3"); + + //switching preferred lighting mode + rm.setPreferredLightMode(TechniqueDef.LightMode.SinglePass); + Collections.sort(defs, comp); + + assertEquals(defs.get(0).getName(), "lighting4"); + assertEquals(defs.get(1).getName(), "lighting3"); + assertEquals(defs.get(2).getName(), "lighting2"); + assertEquals(defs.get(3).getName(), "lighting1"); + + + //test setting source through the enumMaps method with random cases + rm.setPreferredLightMode(TechniqueDef.LightMode.MultiPass); + defs = new ArrayList<>(); + def = new TechniqueDef("lighting1", 1); + EnumMap em = new EnumMap<>(Shader.ShaderType.class); + em.put(Shader.ShaderType.Vertex, ""); + em.put(Shader.ShaderType.Fragment, ""); + em.put(Shader.ShaderType.Geometry, ""); + EnumMap l = new EnumMap<>(Shader.ShaderType.class); + l.put(Shader.ShaderType.Vertex, "GLSL100"); + l.put(Shader.ShaderType.Fragment, "GLSL100"); + l.put(Shader.ShaderType.Geometry, "GLSL100"); + def.setShaderFile(em, l); + def.setLightMode(TechniqueDef.LightMode.SinglePass); + defs.add(def); + + def = new TechniqueDef("lighting2", 1); + em = new EnumMap<>(Shader.ShaderType.class); + em.put(Shader.ShaderType.Vertex, ""); + em.put(Shader.ShaderType.Fragment, ""); + em.put(Shader.ShaderType.Geometry, ""); + l = new EnumMap<>(Shader.ShaderType.class); + l.put(Shader.ShaderType.Vertex, "GLSL100"); + l.put(Shader.ShaderType.Fragment, "GLSL100"); + l.put(Shader.ShaderType.Geometry, "GLSL100"); + def.setShaderFile(em, l); + def.setLightMode(TechniqueDef.LightMode.MultiPass); + defs.add(def); + + def = new TechniqueDef("lighting3", 1); + em = new EnumMap<>(Shader.ShaderType.class); + em.put(Shader.ShaderType.Vertex, ""); + em.put(Shader.ShaderType.Fragment, ""); + em.put(Shader.ShaderType.Geometry, ""); + l = new EnumMap<>(Shader.ShaderType.class); + l.put(Shader.ShaderType.Vertex, "GLSL150"); + l.put(Shader.ShaderType.Fragment, "GLSL150"); + l.put(Shader.ShaderType.Geometry, "GLSL150"); + def.setShaderFile(em, l); + def.setLightMode(TechniqueDef.LightMode.MultiPass); + defs.add(def); + + def = new TechniqueDef("lighting4", 1); + em = new EnumMap<>(Shader.ShaderType.class); + em.put(Shader.ShaderType.Vertex, ""); + em.put(Shader.ShaderType.Fragment, ""); + em.put(Shader.ShaderType.Geometry, ""); + l = new EnumMap<>(Shader.ShaderType.class); + l.put(Shader.ShaderType.Vertex, "GLSL130"); + l.put(Shader.ShaderType.Fragment, "GLSL130"); + l.put(Shader.ShaderType.Geometry, "GLSL110"); + def.setShaderFile(em, l); + def.setLightMode(TechniqueDef.LightMode.MultiPass); + defs.add(def); + + Collections.sort(defs, comp); + + assertEquals(defs.get(0).getName(), "lighting3"); + assertEquals(defs.get(1).getName(), "lighting4"); + assertEquals(defs.get(2).getName(), "lighting2"); + assertEquals(defs.get(3).getName(), "lighting1"); + + + } +}