瀏覽代碼

Changes the way a shader is generated to prepare to the new node declaration

Nehon 7 年之前
父節點
當前提交
7055de4531

+ 111 - 72
jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java

@@ -36,8 +36,9 @@ import com.jme3.material.ShaderGenerationInfo;
 import com.jme3.material.plugins.ConditionParser;
 import com.jme3.shader.Shader.ShaderType;
 
-import java.util.ArrayList;
 import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
  * This shader Generator can generate Vertex and Fragment shaders from
@@ -51,6 +52,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
      * the indentation characters 1à tabulation characters
      */
     private final static String INDENTCHAR = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+    private final static Logger log = Logger.getLogger(Glsl100ShaderGenerator.class.getName());
 
     protected ShaderNodeVariable inPosTmp;
 
@@ -116,21 +118,16 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
         }
     }
 
-    /**
-     * {@inheritDoc}
-     *
-     * if the declaration contains no code nothing is done, else it's appended
-     */
-    @Override
-    protected void generateDeclarativeSection(StringBuilder source, ShaderNode shaderNode, String nodeSource, ShaderGenerationInfo info) {
-        if (nodeSource.replaceAll("\\n", "").trim().length() > 0) {
-            nodeSource = updateDefinesName(nodeSource, shaderNode);
+
+    protected void generateDeclarationSection(StringBuilder source) {
+        for (String defName : declaredNodes.keySet()) {
+            NodeDeclaration nd = declaredNodes.get(defName);
             source.append("\n");
             unIndent();
-            startCondition(shaderNode.getCondition(), source);
-            source.append(nodeSource);
+            startCondition(nd.condition, source);
+            source.append(nd.source);
             source.append("\n");
-            endCondition(shaderNode.getCondition(), source);
+            endCondition(nd.condition, source);
             indent();
         }
     }
@@ -235,81 +232,112 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
     @Override
     protected void generateNodeMainSection(StringBuilder source, ShaderNode shaderNode, String nodeSource, ShaderGenerationInfo info) {
 
-        nodeSource = updateDefinesName(nodeSource, shaderNode);
+
         source.append("\n");
-        comment(source, shaderNode, "Begin");
+        comment(source, shaderNode, "");
         startCondition(shaderNode.getCondition(), source);
 
-        final List<String> declaredInputs = new ArrayList<>();
-
-        // Decalring variables with default values first
         final ShaderNodeDefinition definition = shaderNode.getDefinition();
 
-        for (final ShaderNodeVariable var : definition.getInputs()) {
-
-            if (var.getType().startsWith("sampler")) {
-                continue;
-            }
-
-            final String fullName = shaderNode.getName() + "_" + var.getName();
-
-            final ShaderNodeVariable variable = new ShaderNodeVariable(var.getType(), shaderNode.getName(),
-                    var.getName(), var.getMultiplicity());
-
-            if (!isVarying(info, variable)) {
-                declareVariable(source, variable, var.getDefaultValue(), true, null);
+        StringBuilder b = new StringBuilder();
+        appendIndent(b);
+        b.append(definition.getName()).append("(");
+        boolean isFirst = true;
+        for (ShaderNodeVariable v : definition.getParams()) {
+            if (!isFirst) {
+                b.append(", ");
             }
+            if (definition.getInputs().contains(v)) {
 
-            nodeSource = replaceVariableName(nodeSource, variable);
-            declaredInputs.add(fullName);
-        }
-
-        for (VariableMapping mapping : shaderNode.getInputMapping()) {
+                List<VariableMapping> maps = shaderNode.getInputMapping(v.getName());
 
-            final ShaderNodeVariable rightVariable = mapping.getRightVariable();
-            final ShaderNodeVariable leftVariable = mapping.getLeftVariable();
+                boolean declared = false;
+                for (VariableMapping m : maps) {
+                    // map varyings to their inputs, as the code may not do the mapping.
+                    if (isVarying(info, m.getLeftVariable())) {
+                        map(m, source, false);
+                        declared = true;
+                    }
+                }
 
-            String newName = shaderNode.getName() + "_" + leftVariable.getName();
-            boolean isDeclared = declaredInputs.contains(newName);
-            //Variables fed with a sampler matparam or world param are replaced by the matparam itself
-            //It avoids issue with samplers that have to be uniforms.
-            if (rightVariable != null && isWorldOrMaterialParam(rightVariable) && rightVariable.getType().startsWith("sampler")) {
-                nodeSource = replace(nodeSource, leftVariable, rightVariable.getPrefix() + rightVariable.getName());
+                if (maps.isEmpty()) {
+                    //no mapping found
+                    if (v.getDefaultValue() != null) {
+                        // if there is a default value append it to the function call
+                        b.append(v.getDefaultValue());
+                    } else {
+                        // no default value, construct a variable with the proper type and dummy value and raise a warning
+                        b.append(getConstructor(v.getType()));
+                        log.log(Level.WARNING, "No input defined for variable " + v.getName() + " on shader node " + shaderNode.getName());
+                    }
+                } else if (maps.size() == 1 && !declared) {
+                    // one mapping for this variable, directly append the
+                    // other variable from the mapping to the function call
+                    VariableMapping m = maps.get(0);
+                    ShaderNodeVariable v2 = m.getRightVariable();
+                    b.append(getAppendableNameSpace(v2))
+                            .append(v2.getPrefix())
+                            .append(v2.getName());
+                    if (m.getRightSwizzling().length() > 0) {
+                        b.append(".");
+                        b.append(m.getRightSwizzling());
+                    }
+                } else {
+                    // 2 possible cases here
+                    // the variable is a varrying: we can append it directly
+                    // or
+                    // several mappings with different conditions: we have to declare the variable and
+                    // map it properly before appending the variable in the function call
+                    for (VariableMapping mapping : maps) {
+                        map(mapping, source, true);
+                    }
+                    b.append(shaderNode.getName())
+                            .append("_")
+                            .append(v.getName());
+                }
             } else {
-
-                if (leftVariable.getType().startsWith("sampler")) {
-                    throw new IllegalArgumentException("a Sampler must be a uniform");
+                // outputs
+                String name = shaderNode.getName() + "_" + v.getName();
+                // if the output is not a varying (already declared) we declare it)
+                if (!isVarying(info, name)) {
+                    appendIndent(source);
+                    source.append(v.getType()).append(" ").append(name).append(";\n");
                 }
-                map(mapping, source, !isDeclared);
-            }
-
-            if (!isDeclared) {
-                nodeSource = replace(nodeSource, leftVariable, newName);
-                declaredInputs.add(newName);
+                // append the variable to the function call
+                b.append(shaderNode.getName())
+                        .append("_")
+                        .append(v.getName());
             }
+            isFirst = false;
         }
 
+        b.append(");\n");
 
-
-        for (ShaderNodeVariable var : definition.getOutputs()) {
-            ShaderNodeVariable v = new ShaderNodeVariable(var.getType(), shaderNode.getName(), var.getName(), var.getMultiplicity());
-            if (!declaredInputs.contains(shaderNode.getName() + "_" + var.getName())) {
-                if (!isVarying(info, v)) {
-                    declareVariable(source, v);
-                }
-                nodeSource = replaceVariableName(nodeSource, v);
-            }
-        }
-        
-        source.append(nodeSource);
-   
+        // Map any output to global output.
         for (VariableMapping mapping : shaderNode.getOutputMapping()) {
-            map(mapping, source, true);
+            map(mapping, b, false);
         }
+        source.append(b);
+
         endCondition(shaderNode.getCondition(), source);
-        comment(source, shaderNode, "End");
     }
 
+    /**
+     * Returns a proper constructor call for a given type
+     * @param type
+     * @return
+     */
+    private String getConstructor(String type) {
+        if (type.startsWith("i") || type.startsWith("u")) {
+            return type + "(0)";
+        }
+        if (type.equals("boolean") || type.startsWith("u")) {
+            return "false";
+        }
+        return type + "(0.0)";
+    }
+
+
     /**
      * declares a variable, embed in a conditional block if needed
      * @param source the StringBuilder to use
@@ -510,6 +538,17 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
         return isVarying;
     }
 
+    protected boolean isVarying(ShaderGenerationInfo info, String variableName) {
+        for (ShaderNodeVariable shaderNodeVariable : info.getVaryings()) {
+            String name = shaderNodeVariable.getNameSpace() + "_" + shaderNodeVariable.getName();
+            if (name.equals(variableName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
     /**
      * Appends a comment to the generated code
      * @param source the StringBuilder to use 
@@ -554,10 +593,10 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
         String[] lines = nodeSource.split("\\n");
         ConditionParser parser = new ConditionParser();
         for (String line : lines) {
-
-            if (line.trim().startsWith("#if")) {
-                List<String> params = parser.extractDefines(line.trim());
-                String l = line.trim().replaceAll("defined", "").replaceAll("#if ", "").replaceAll("#ifdef", "");//parser.getFormattedExpression();
+            line = line.trim();
+            if (line.startsWith("#if")) {
+                List<String> params = parser.extractDefines(line);
+                String l = line.replaceAll("defined", "").replaceAll("#if ", "").replaceAll("#ifdef", "");
                 boolean match = false;
                 for (String param : params) {
                     for (VariableMapping map : shaderNode.getInputMapping()) {

+ 89 - 46
jme3-core/src/main/java/com/jme3/shader/ShaderGenerator.java

@@ -34,6 +34,7 @@ package com.jme3.shader;
 import com.jme3.asset.AssetManager;
 import com.jme3.material.ShaderGenerationInfo;
 import com.jme3.material.TechniqueDef;
+import com.jme3.material.plugins.ConditionParser;
 import com.jme3.shader.Shader.ShaderType;
 import com.jme3.shader.plugins.ShaderAssetKey;
 
@@ -72,7 +73,15 @@ public abstract class ShaderGenerator {
      */
     Pattern extensions = Pattern.compile("(#extension.*\\s+)");
 
-    private Map<String, String> imports = new LinkedHashMap<>();
+    /**
+     * a set of imports to append to the shader source
+     */
+    private Set<String> imports = new HashSet<>();
+
+    /**
+     * The nodes function and their condition to be declared in the shader
+     */
+    protected Map<String, NodeDeclaration> declaredNodes = new LinkedHashMap<>();
 
     /**
      * Build a shaderGenerator
@@ -155,12 +164,6 @@ public abstract class ShaderGenerator {
 
         generateEndOfMainSection(source, info, type);
 
-        //insert imports backward
-        int insertIndex = sourceDeclaration.length();
-        for (String importSource : imports.values()) {
-            sourceDeclaration.insert(insertIndex, importSource);
-        }
-
         sourceDeclaration.append(source);
 
         return moveExtensionsUp(sourceDeclaration);
@@ -195,56 +198,92 @@ public abstract class ShaderGenerator {
      * @param type the Shader type
      */
     protected void generateDeclarationAndMainBody(List<ShaderNode> shaderNodes, StringBuilder sourceDeclaration, StringBuilder source, ShaderGenerationInfo info, Shader.ShaderType type) {
+        declaredNodes.clear();
         for (ShaderNode shaderNode : shaderNodes) {
             if (info.getUnusedNodes().contains(shaderNode.getName())) {
                 continue;
             }
+
             if (shaderNode.getDefinition().getType() == type) {
                 int index = findShaderIndexFromVersion(shaderNode, type);
                 String shaderPath = shaderNode.getDefinition().getShadersPath().get(index);
                 Map<String, String> sources = (Map<String, String>) assetManager.loadAsset(new ShaderAssetKey(shaderPath, false));
                 String loadedSource = sources.get("[main]");
                 for (String name : sources.keySet()) {
-                    if (!name.equals("[main]")) {
-                        imports.put(name, sources.get(name));
+                    if (!name.equals("[main]") && !imports.contains(name)) {
+                        imports.add(name);
+                        // append the imported file in place if it hasn't been imported already.
+                        sourceDeclaration.append(sources.get(name));
                     }
                 }
-                appendNodeDeclarationAndMain(loadedSource, sourceDeclaration, source, shaderNode, info, shaderPath);
+                // Nodes are functions added to the declaration part of the shader
+                // Multiple nodes may use the same definition and we don't want to declare it several times.
+                // Also nodes can have #ifdef conditions so we need to properly merge this conditions to declare the Node function.
+                NodeDeclaration nd = declaredNodes.get(shaderNode.getDefinition().getName());
+                loadedSource =  functionize(loadedSource, shaderNode.getDefinition());
+                if(nd == null){
+                    nd = new NodeDeclaration(shaderNode.getCondition(),  loadedSource);
+                    declaredNodes.put(shaderNode.getDefinition().getName(), nd);
+                } else {
+                    nd.condition = ConditionParser.mergeConditions(nd.condition, shaderNode.getCondition(), "||");
+                }
+
+                generateNodeMainSection(source, shaderNode, loadedSource, info);
             }
         }
+
+        generateDeclarationSection(sourceDeclaration);
+
     }
 
     /**
-     * Appends declaration and main part of a node to the shader declaration and
-     * main part. the loadedSource is split by "void main(){" to split
-     * declaration from main part of the node source code.The trailing "}" is
-     * removed from the main part. Each part is then respectively passed to
-     * generateDeclarativeSection and generateNodeMainSection.
-     *
-     * @see ShaderGenerator#generateDeclarativeSection
-     * @see ShaderGenerator#generateNodeMainSection
-     *
-     * @param loadedSource the actual source code loaded for this node.
-     * @param shaderPath path to the shader file
-     * @param sourceDeclaration the Shader declaration part string builder.
-     * @param source the Shader main part StringBuilder.
-     * @param shaderNode the shader node.
-     * @param info the ShaderGenerationInfo.
+     * Tuns old style shader node code into a proper function so that it can be appended to the declarative sectio.
+     * Note that this only needed for nodes coming from a j3sn file.
+     * @param source
+     * @param def
+     * @return
      */
-    protected void appendNodeDeclarationAndMain(String loadedSource, StringBuilder sourceDeclaration, StringBuilder source, ShaderNode shaderNode, ShaderGenerationInfo info, String shaderPath) {
-        if (loadedSource.length() > 1) {
-            loadedSource = loadedSource.substring(0, loadedSource.lastIndexOf("}"));
-            String[] sourceParts = loadedSource.split("\\s*void\\s*main\\s*\\(\\s*\\)\\s*\\{");
-            if(sourceParts.length<2){
-                throw new IllegalArgumentException("Syntax error in "+ shaderPath +". Cannot find 'void main(){' in \n"+ loadedSource);
+    public static String functionize(String source, ShaderNodeDefinition def){
+        StringBuffer signature = new StringBuffer();
+        def.setReturnType("void");
+        signature.append("void ").append(def.getName()).append("(");
+        boolean addParam = false;
+        if(def.getParams().isEmpty()){
+            addParam = true;
+        }
+
+        boolean isFirst = true;
+        for (ShaderNodeVariable v : def.getInputs()) {
+            if(!isFirst){
+                signature.append(", ");
+            }
+            String qualifier;
+            qualifier = "const in";
+            signature.append(qualifier).append(" ").append(v.getType()).append(" ").append(v.getName());
+            isFirst = false;
+            if(addParam) {
+                def.getParams().add(v);
             }
-            generateDeclarativeSection(sourceDeclaration, shaderNode, sourceParts[0], info);
-            generateNodeMainSection(source, shaderNode, sourceParts[1], info);
-        } else {
-            //if source is empty, we still call generateNodeMainSection so that mappings can be done.
-            generateNodeMainSection(source, shaderNode, loadedSource, info);
         }
 
+        for (ShaderNodeVariable v : def.getOutputs()) {
+            if(def.getInputs().contains(v)){
+                continue;
+            }
+            if(!isFirst){
+                signature.append(", ");
+            }
+            signature.append("out ").append(v.getType()).append(" ").append(v.getName());
+            isFirst = false;
+            if(addParam) {
+                def.getParams().add(v);
+            }
+        }
+        signature.append("){");
+
+        source = source.replaceAll("\\s*void\\s*main\\s*\\(\\s*\\)\\s*\\{", signature.toString());
+
+        return source;
     }
 
     /**
@@ -287,19 +326,12 @@ public abstract class ShaderGenerator {
     protected abstract void generateVaryings(StringBuilder source, ShaderGenerationInfo info, ShaderType type);
 
     /**
-     * Appends the given shaderNode declarative part to the shader declarative
-     * part. If needed the shader type can be determined by fetching the
-     * shaderNode's definition type.
-     *
-     * @see ShaderNode#getDefinition()
-     * @see ShaderNodeDefinition#getType()
+     * Appends the shaderNodes function to the shader declarative
+     * part.
      * 
-     * @param nodeDecalarationSource the declaration part of the node
      * @param source the StringBuilder to append generated code.
-     * @param shaderNode the shaderNode.
-     * @param info the ShaderGenerationInfo.
      */
-    protected abstract void generateDeclarativeSection(StringBuilder source, ShaderNode shaderNode, String nodeDecalarationSource, ShaderGenerationInfo info);
+    protected abstract void generateDeclarationSection(StringBuilder source);
 
     /**
      * generates the start of the shader main section. this method is
@@ -363,4 +395,15 @@ public abstract class ShaderGenerator {
         }
         return index;
     }    
+
+    protected class NodeDeclaration{
+        String condition;
+        String source;
+
+        public NodeDeclaration(String condition, String source) {
+            this.condition = condition;
+            this.source = source;
+        }
+    }
+
 }

+ 15 - 0
jme3-core/src/main/java/com/jme3/shader/ShaderNode.java

@@ -143,6 +143,21 @@ public class ShaderNode implements Savable, Cloneable {
         return inputMapping;
     }
 
+    /**
+     * Returns a list of variable mapping for the given node input.
+     *
+     * @return the input mappings.
+     */
+    public List<VariableMapping> getInputMapping(String varName) {
+        List<VariableMapping> list = new ArrayList<>();
+        for (VariableMapping v : inputMapping) {
+            if (v.getLeftVariable().getName().equals(varName)){
+                list.add(v);
+            }
+        }
+        return list;
+    }
+
     /**
      * Sets the input mappings.
      *

+ 23 - 4
jme3-core/src/main/java/com/jme3/shader/ShaderNodeDefinition.java

@@ -57,8 +57,10 @@ public class ShaderNodeDefinition implements Savable {
     private String documentation;
     private List<ShaderNodeVariable> inputs = new ArrayList<ShaderNodeVariable>();
     private List<ShaderNodeVariable> outputs = new ArrayList<ShaderNodeVariable>();
+    private List<ShaderNodeVariable> params = new ArrayList<>();
     private String path = null;
     private boolean noOutput = false;
+    private String returnType;
 
     /**
      * creates a ShaderNodeDefinition
@@ -183,8 +185,22 @@ public class ShaderNodeDefinition implements Savable {
     public void setPath(String path) {
         this.path = path;
     }
-    
-    
+
+    public String getReturnType() {
+        return returnType;
+    }
+
+    public void setReturnType(String returnType) {
+        this.returnType = returnType;
+    }
+
+    public List<ShaderNodeVariable> getParams() {
+        return params;
+    }
+
+    public void setParams(List<ShaderNodeVariable> params) {
+        this.params = params;
+    }
 
     /**
      * jme serialization (not used)
@@ -194,14 +210,15 @@ public class ShaderNodeDefinition implements Savable {
      */
     @Override
     public void write(JmeExporter ex) throws IOException {
-        OutputCapsule oc = (OutputCapsule) ex.getCapsule(this);
+        OutputCapsule oc = ex.getCapsule(this);
         oc.write(name, "name", "");
         String[] str = new String[shadersLanguage.size()];
         oc.write(shadersLanguage.toArray(str), "shadersLanguage", null);
         oc.write(shadersPath.toArray(str), "shadersPath", null);
         oc.write(type, "type", null);
         oc.writeSavableArrayList((ArrayList) inputs, "inputs", new ArrayList<ShaderNodeVariable>());
-        oc.writeSavableArrayList((ArrayList) outputs, "inputs", new ArrayList<ShaderNodeVariable>());
+        oc.writeSavableArrayList((ArrayList) outputs, "outputs", new ArrayList<ShaderNodeVariable>());
+        oc.writeSavableArrayList((ArrayList) params, "params", new ArrayList<ShaderNodeVariable>());
     }
 
     public List<String> getShadersLanguage() {
@@ -250,6 +267,8 @@ public class ShaderNodeDefinition implements Savable {
         type = ic.readEnum("type", Shader.ShaderType.class, null);
         inputs = (List<ShaderNodeVariable>) ic.readSavableArrayList("inputs", new ArrayList<ShaderNodeVariable>());
         outputs = (List<ShaderNodeVariable>) ic.readSavableArrayList("outputs", new ArrayList<ShaderNodeVariable>());
+        params = (List<ShaderNodeVariable>) ic.readSavableArrayList("params", new ArrayList<ShaderNodeVariable>());
+        im.getAssetManager();
     }
 
     /**

+ 6 - 6
jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/HardwareSkinning/basicGpuSkinning.vert

@@ -1,8 +1,8 @@
+#import "Common/ShaderLib/Skinning.glsllib"
 
-void main(){        
-        modModelPosition = (mat4(0.0) +
-            boneMatrices[int(boneIndex.x)] * boneWeight.x +
-            boneMatrices[int(boneIndex.y)] * boneWeight.y +
-            boneMatrices[int(boneIndex.z)] * boneWeight.z +
-            boneMatrices[int(boneIndex.w)] * boneWeight.w) * vec4(modelPosition.xyz,1.0);
+void main(){
+    #ifdef NUM_BONES
+        modModelPosition = modelPosition;
+        Skinning_Compute(modModelPosition);
+    #endif
 }

+ 7 - 9
jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/HardwareSkinning/fullGpuSkinning.vert

@@ -1,12 +1,10 @@
+#import "Common/ShaderLib/Skinning.glsllib"
 
 void main(){
-        modModelPosition = (mat4(0.0) +
-            boneMatrices[int(boneIndex.x)] * boneWeight.x +
-            boneMatrices[int(boneIndex.y)] * boneWeight.y +
-            boneMatrices[int(boneIndex.z)] * boneWeight.z +
-            boneMatrices[int(boneIndex.w)] * boneWeight.w) * modelPosition;
-
-        mat3 rotMat = mat3(mat[0].xyz, mat[1].xyz, mat[2].xyz);
-        modModelTangent = rotMat * modelTangent;
-        modModelNormal = rotMat * modelNormal;
+    #ifdef NUM_BONES
+        modModelPosition = modelPosition;
+        modModelNormal = modelNormal;
+        modModelTangents = modelTangents;
+        Skinning_Compute(modModelPosition, modModelNormal, modModelTangents);
+    #endif
 }

+ 22 - 0
jme3-core/src/plugins/java/com/jme3/material/plugins/ConditionParser.java

@@ -104,6 +104,28 @@ public class ConditionParser {
         return defines;
     }
 
+    /**
+     * merges 2 condition with the given operator
+     *
+     * @param condition1 the first condition
+     * @param condition2 the second condition
+     * @param operator the operator ("&&" or "||&)
+     * @return the merged condition
+     */
+    public static String mergeConditions(String condition1, String condition2, String operator) {
+        if (condition1 != null) {
+            if (condition2 == null) {
+                return condition1;
+            } else {
+                String mergedCondition = "(" + condition1 + ") " + operator + " (" + condition2 + ")";
+                return mergedCondition;
+            }
+        } else {
+            return condition2;
+        }
+    }
+
+
     /**
      *
      * @return the formatted expression previously updated by extractDefines

+ 1 - 21
jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java

@@ -580,7 +580,7 @@ public class ShaderNodeLoaderDelegate {
                         multiplicity = multiplicity.toUpperCase();
                         left.setMultiplicity(multiplicity);
                         // only declare the variable if the define is defined.
-                        left.setCondition(mergeConditions(left.getCondition(), "defined(" + multiplicity + ")", "||"));
+                        left.setCondition(ConditionParser.mergeConditions(left.getCondition(), "defined(" + multiplicity + ")", "||"));
                     } else {
                         throw new MatParseException("Wrong multiplicity for variable" + left.getName() + ". " +
                                 multiplicity + " should be an int or a declared material parameter.", statement);
@@ -1057,26 +1057,6 @@ public class ShaderNodeLoaderDelegate {
         }
     }
 
-    /**
-     * merges 2 condition with the given operator
-     *
-     * @param condition1 the first condition
-     * @param condition2 the second condition
-     * @param operator the operator ("&&" or "||&)
-     * @return the merged condition
-     */
-    public String mergeConditions(String condition1, String condition2, String operator) {
-        if (condition1 != null) {
-            if (condition2 == null) {
-                return condition1;
-            } else {
-                String mergedCondition = "(" + condition1 + ") " + operator + " (" + condition2 + ")";
-                return mergedCondition;
-            }
-        } else {
-            return condition2;
-        }
-    }
 
     /**
      * Searches a variable in a list from its name and merges the conditions of the

+ 1 - 1
jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java

@@ -4,7 +4,7 @@ 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.math.*;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.shape.Box;
 import com.jme3.shader.Shader;