Переглянути джерело

One can now define several versions for the shader in a Technique in a J3md file, ie:
VertexShader GLSL150 GLSL110 : "path/to/shader/file.vert"
FragmentShader GLSL150 GLSL110 : "path/to/shader/file.frag"

Versions must be separated with spaces. They will be matched together when creating the technique so they have to follow the same order for different shaders.

Nehon 8 роки тому
батько
коміт
12a2f0f63c

+ 33 - 1
jme3-core/src/main/java/com/jme3/material/ShaderGenerationInfo.java

@@ -48,7 +48,7 @@ import java.util.List;
  *
  * @author Nehon
  */
-public class ShaderGenerationInfo implements Savable {
+public class ShaderGenerationInfo implements Savable, Cloneable {
 
     /**
      * the list of attributes of the vertex shader
@@ -187,4 +187,36 @@ public class ShaderGenerationInfo implements Savable {
         vertexGlobal = (ShaderNodeVariable) ic.readSavable("vertexGlobal", null);
 
     }
+
+    @Override
+    protected Object clone() throws CloneNotSupportedException {
+        ShaderGenerationInfo clone = (ShaderGenerationInfo) super.clone();
+
+        for (ShaderNodeVariable attribute : attributes) {
+            clone.attributes.add((ShaderNodeVariable) attribute.clone());
+        }
+
+        for (ShaderNodeVariable uniform : vertexUniforms) {
+            clone.vertexUniforms.add((ShaderNodeVariable) uniform.clone());
+        }
+
+        clone.vertexGlobal = (ShaderNodeVariable) vertexGlobal.clone();
+
+
+        for (ShaderNodeVariable varying : varyings) {
+            clone.varyings.add((ShaderNodeVariable) varying.clone());
+        }
+
+        for (ShaderNodeVariable uniform : fragmentUniforms) {
+            clone.fragmentUniforms.add((ShaderNodeVariable) uniform.clone());
+        }
+
+        for (ShaderNodeVariable globals : fragmentGlobals) {
+            clone.fragmentGlobals.add((ShaderNodeVariable) globals.clone());
+        }
+
+        clone.unusedNodes.addAll(unusedNodes);
+
+        return clone;
+    }
 }

+ 55 - 4
jme3-core/src/main/java/com/jme3/material/TechniqueDef.java

@@ -39,6 +39,7 @@ import com.jme3.shader.*;
 import com.jme3.shader.Shader.ShaderType;
 
 import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
 import java.util.*;
 
 /**
@@ -46,7 +47,7 @@ import java.util.*;
  *
  * @author Kirill Vainer
  */
-public class TechniqueDef implements Savable {
+public class TechniqueDef implements Savable, Cloneable {
 
     /**
      * Version #1: Separate shader language for each shader source.
@@ -539,7 +540,7 @@ public class TechniqueDef implements Savable {
     public void setShaderFile(EnumMap<Shader.ShaderType, String> shaderNames, EnumMap<Shader.ShaderType, String> shaderLanguages) {
         requiredCaps.clear();
 
-        int maxCap = 0;
+        weight = 0;
         for (Shader.ShaderType shaderType : shaderNames.keySet()) {
             String language = shaderLanguages.get(shaderType);
             String shaderFile = shaderNames.get(shaderType);
@@ -549,7 +550,7 @@ public class TechniqueDef implements Savable {
 
             Caps cap = Caps.valueOf(language);
             requiredCaps.add(cap);
-            maxCap = Math.max(maxCap, cap.ordinal());
+            weight = Math.max(weight, cap.ordinal());
 
             if (shaderType.equals(Shader.ShaderType.Geometry)) {
                 requiredCaps.add(Caps.GeometryShader);
@@ -557,7 +558,6 @@ public class TechniqueDef implements Savable {
                 requiredCaps.add(Caps.TesselationShader);
             }
         }
-        weight = maxCap;
     }
 
     /**
@@ -776,4 +776,55 @@ public class TechniqueDef implements Savable {
     public void setLightSpace(LightSpace lightSpace) {
         this.lightSpace = lightSpace;
     }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        //cannot use super.clone because of the final fields instance that would be shared by the clones.
+        TechniqueDef clone = new TechniqueDef(name, sortId);
+
+        clone.noRender = noRender;
+        clone.lightMode = lightMode;
+        clone.shadowMode = shadowMode;
+        clone.lightSpace = lightSpace;
+        clone.usesNodes = usesNodes;
+        clone.shaderPrologue = shaderPrologue;
+
+        clone.setShaderFile(shaderNames, shaderLanguages);
+
+        clone.defineNames = new ArrayList<>(defineNames.size());
+        clone.defineNames.addAll(defineNames);
+
+        clone.defineTypes = new ArrayList<>(defineTypes.size());
+        clone.defineTypes.addAll(defineTypes);
+
+        clone.paramToDefineId = new HashMap<>(paramToDefineId.size());
+        clone.paramToDefineId.putAll(paramToDefineId);
+
+        if (shaderNodes != null) {
+            for (ShaderNode shaderNode : shaderNodes) {
+                clone.shaderNodes.add((ShaderNode) shaderNode.clone());
+            }
+            clone.shaderGenerationInfo = (ShaderGenerationInfo) shaderGenerationInfo.clone();
+        }
+
+        if (renderState != null) {
+            clone.setRenderState(renderState.clone());
+        }
+        if (forcedRenderState != null) {
+            clone.setForcedRenderState(forcedRenderState.clone());
+        }
+
+        try {
+            clone.logic = logic.getClass().getConstructor(TechniqueDef.class).newInstance(clone);
+        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
+            e.printStackTrace();
+        }
+
+        if (worldBinds != null) {
+            clone.worldBinds = new ArrayList<>(worldBinds.size());
+            clone.worldBinds.addAll(worldBinds);
+        }
+
+        return clone;
+    }
 }

+ 21 - 1
jme3-core/src/main/java/com/jme3/shader/ShaderNode.java

@@ -54,7 +54,7 @@ import java.util.List;
  *
  * @author Nehon
  */
-public class ShaderNode implements Savable {
+public class ShaderNode implements Savable, Cloneable {
 
     private String name;
     private ShaderNodeDefinition definition;
@@ -212,4 +212,24 @@ public class ShaderNode implements Savable {
     public String toString() {
         return "\nShaderNode{" + "\nname=" + name + ", \ndefinition=" + definition.getName() + ", \ncondition=" + condition + ", \ninputMapping=" + inputMapping + ", \noutputMapping=" + outputMapping + '}';
     }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        ShaderNode clone = (ShaderNode) super.clone();
+
+        //No need to clone the definition.
+        clone.definition = definition;
+
+        clone.inputMapping = new ArrayList<>();
+        for (VariableMapping variableMapping : inputMapping) {
+            clone.inputMapping.add((VariableMapping) variableMapping.clone());
+        }
+
+        clone.outputMapping = new ArrayList<>();
+        for (VariableMapping variableMapping : outputMapping) {
+            clone.outputMapping.add((VariableMapping) variableMapping.clone());
+        }
+
+        return clone;
+    }
 }

+ 5 - 7
jme3-core/src/main/java/com/jme3/shader/ShaderNodeVariable.java

@@ -243,11 +243,6 @@ public class ShaderNodeVariable implements Savable, Cloneable {
         return "\n" + type + ' ' + (nameSpace != null ? (nameSpace + '.') : "") + name;
     }
 
-    @Override
-    public ShaderNodeVariable clone() {
-        return new ShaderNodeVariable(type, nameSpace, name);
-    }
-
     /**
      *
      * @return true if this variable is a shader output
@@ -281,6 +276,9 @@ public class ShaderNodeVariable implements Savable, Cloneable {
     public void setMultiplicity(String multiplicity) {
         this.multiplicity = multiplicity;
     }
-    
-    
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
 }

+ 11 - 1
jme3-core/src/main/java/com/jme3/shader/VariableMapping.java

@@ -43,7 +43,7 @@ import java.io.IOException;
  *
  * @author Nehon
  */
-public class VariableMapping implements Savable {
+public class VariableMapping implements Savable, Cloneable {
 
     private ShaderNodeVariable leftVariable;
     private ShaderNodeVariable rightVariable;
@@ -195,4 +195,14 @@ public class VariableMapping implements Savable {
     public String toString() {
         return "\n{" + leftVariable.toString() + (leftSwizzling.length() > 0 ? ("." + leftSwizzling) : "") + " = " + rightVariable.getType() + " " + rightVariable.getNameSpace() + "." + rightVariable.getName() + (rightSwizzling.length() > 0 ? ("." + rightSwizzling) : "") + " : " + condition + "}";
     }
+
+    @Override
+    protected Object clone() throws CloneNotSupportedException {
+        VariableMapping clone = (VariableMapping) super.clone();
+
+        clone.leftVariable = (ShaderNodeVariable) leftVariable.clone();
+        clone.rightVariable = (ShaderNodeVariable) rightVariable.clone();
+
+        return clone;
+    }
 }

+ 62 - 37
jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java

@@ -43,21 +43,18 @@ import com.jme3.material.logic.StaticPassLightingLogic;
 import com.jme3.math.ColorRGBA;
 import com.jme3.math.Vector2f;
 import com.jme3.math.Vector3f;
-import com.jme3.shader.DefineList;
-import com.jme3.shader.Shader;
-import com.jme3.shader.VarType;
+import com.jme3.shader.*;
 import com.jme3.texture.Texture;
 import com.jme3.texture.Texture2D;
 import com.jme3.texture.image.ColorSpace;
 import com.jme3.util.PlaceholderAssets;
 import com.jme3.util.blockparser.BlockLanguageParser;
 import com.jme3.util.blockparser.Statement;
+import com.jme3.util.clone.Cloner;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.EnumMap;
-import java.util.List;
+import java.util.*;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.regex.Matcher;
@@ -69,6 +66,7 @@ public class J3MLoader implements AssetLoader {
    // private ErrorLogger errors;
     private ShaderNodeLoaderDelegate nodesLoaderDelegate;
     boolean isUseNodes = false;
+    int langSize = 0;
 
     private AssetManager assetManager;
     private AssetKey key;
@@ -79,13 +77,13 @@ public class J3MLoader implements AssetLoader {
     private RenderState renderState;
     private ArrayList<String> presetDefines = new ArrayList<String>();
 
-    private EnumMap<Shader.ShaderType, String> shaderLanguages;
+    private List<EnumMap<Shader.ShaderType, String>> shaderLanguages;
     private EnumMap<Shader.ShaderType, String> shaderNames;
 
     private static final String whitespacePattern = "\\p{javaWhitespace}+";
 
     public J3MLoader() {
-        shaderLanguages = new EnumMap<>(Shader.ShaderType.class);
+        shaderLanguages = new ArrayList<>();// EnumMap<>(Shader.ShaderType.class);
         shaderNames = new EnumMap<>(Shader.ShaderType.class);
     }
 
@@ -97,20 +95,29 @@ public class J3MLoader implements AssetLoader {
             throw new IOException("Shader statement syntax incorrect" + statement);
         }
         String[] typeAndLang = split[0].split(whitespacePattern);
-        if (typeAndLang.length != 2) {
-            throw new IOException("Shader statement syntax incorrect: " + statement);
-        }
 
         for (Shader.ShaderType shaderType : Shader.ShaderType.values()) {
             if (typeAndLang[0].equals(shaderType.toString() + "Shader")) {
-                readShaderDefinition(shaderType, split[1].trim(), typeAndLang[1]);
+
+                readShaderDefinition(shaderType, split[1].trim(), Arrays.copyOfRange(typeAndLang, 1, typeAndLang.length));
             }
         }
     }
 
-    private void readShaderDefinition(Shader.ShaderType shaderType, String name, String language) {
+
+    private void readShaderDefinition(Shader.ShaderType shaderType, String name, String... languages) {
         shaderNames.put(shaderType, name);
-        shaderLanguages.put(shaderType, language);
+
+        if (langSize != 0 && langSize != languages.length) {
+            throw new AssetLoadException("Technique " + technique.getName() + " must have the same number of languages for each shader type.");
+        }
+        langSize = languages.length;
+        for (int i = 0; i < languages.length; i++) {
+            if (i >= shaderLanguages.size()) {
+                shaderLanguages.add(new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class));
+            }
+            shaderLanguages.get(i).put(shaderType, languages[i]);
+        }
     }
 
     // LightMode <MODE>
@@ -608,6 +615,7 @@ public class J3MLoader implements AssetLoader {
     private void readTechnique(Statement techStat) throws IOException{
         isUseNodes = false;
         String[] split = techStat.getLine().split(whitespacePattern);
+        Cloner cloner = new Cloner();
 
         String name;
         if (split.length == 1) {
@@ -625,6 +633,30 @@ public class J3MLoader implements AssetLoader {
             readTechniqueStatement(statement);
         }
 
+        technique.setShaderPrologue(createShaderPrologue(presetDefines));
+
+        switch (technique.getLightMode()) {
+            case Disable:
+                technique.setLogic(new DefaultTechniqueDefLogic(technique));
+                break;
+            case MultiPass:
+                technique.setLogic(new MultiPassLightingLogic(technique));
+                break;
+            case SinglePass:
+                technique.setLogic(new SinglePassLightingLogic(technique));
+                break;
+            case StaticPass:
+                technique.setLogic(new StaticPassLightingLogic(technique));
+                break;
+            case SinglePassAndImageBased:
+                technique.setLogic(new SinglePassAndImageBasedLightingLogic(technique));
+                break;
+            default:
+                throw new UnsupportedOperationException();
+        }
+
+        List<TechniqueDef> techniqueDefs = new ArrayList<>();
+
         if(isUseNodes){
             nodesLoaderDelegate.computeConditions();
             
@@ -634,43 +666,36 @@ public class J3MLoader implements AssetLoader {
             // Not sure if this is needed anymore, since shader caching
             // is now done by TechniqueDef.
             technique.setShaderFile(technique.hashCode() + "", technique.hashCode() + "", "GLSL100", "GLSL100");
+            techniqueDefs.add(technique);
         }else if (shaderNames.containsKey(Shader.ShaderType.Vertex) && shaderNames.containsKey(Shader.ShaderType.Fragment)) {
-            technique.setShaderFile(shaderNames, shaderLanguages);
+            if (shaderLanguages.size() > 1) {
+                for (int i = 1; i < shaderLanguages.size(); i++) {
+                    TechniqueDef td = cloner.clone(technique);
+                    td.setShaderFile(shaderNames, shaderLanguages.get(i));
+                    techniqueDefs.add(td);
+                }
+            }
+            technique.setShaderFile(shaderNames, shaderLanguages.get(0));
+            techniqueDefs.add(technique);
+
         } else {
             technique = null;
             shaderLanguages.clear();
             shaderNames.clear();
             presetDefines.clear();
+            langSize = 0;
             logger.log(Level.WARNING, "Fixed function technique was ignored");
             logger.log(Level.WARNING, "Fixed function technique ''{0}'' was ignored for material {1}",
                     new Object[]{name, key});
             return;
         }
-        
-        technique.setShaderPrologue(createShaderPrologue(presetDefines));
-        
-        switch (technique.getLightMode()) {
-            case Disable:
-                technique.setLogic(new DefaultTechniqueDefLogic(technique));
-                break;
-            case MultiPass:
-                technique.setLogic(new MultiPassLightingLogic(technique));
-                break;
-            case SinglePass:
-                technique.setLogic(new SinglePassLightingLogic(technique));
-                break;
-            case StaticPass:
-                technique.setLogic(new StaticPassLightingLogic(technique));
-                break;
-            case SinglePassAndImageBased:
-                technique.setLogic(new SinglePassAndImageBasedLightingLogic(technique));
-                break;
-            default:
-                throw new UnsupportedOperationException();
+
+        for (TechniqueDef techniqueDef : techniqueDefs) {
+            materialDef.addTechniqueDef(techniqueDef);
         }
 
-        materialDef.addTechniqueDef(technique);
         technique = null;
+        langSize = 0;
         shaderLanguages.clear();
         shaderNames.clear();
         presetDefines.clear();