Browse Source

Shader Nodes implementation :
Core system

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10432 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

rem..om 12 years ago
parent
commit
55d065ab78
25 changed files with 3803 additions and 140 deletions
  1. 112 0
      engine/src/core-plugins/com/jme3/material/plugins/ConditionParser.java
  2. 65 33
      engine/src/core-plugins/com/jme3/material/plugins/J3MLoader.java
  3. 90 0
      engine/src/core-plugins/com/jme3/material/plugins/MatParseException.java
  4. 85 0
      engine/src/core-plugins/com/jme3/material/plugins/ShaderNodeDefinitionLoader.java
  5. 937 0
      engine/src/core-plugins/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java
  6. 16 0
      engine/src/core/com/jme3/asset/AssetManager.java
  7. 1 0
      engine/src/core/com/jme3/asset/Desktop.cfg
  8. 32 1
      engine/src/core/com/jme3/asset/DesktopAssetManager.java
  9. 89 0
      engine/src/core/com/jme3/asset/ShaderNodeDefinitionKey.java
  10. 5 5
      engine/src/core/com/jme3/material/Material.java
  11. 190 0
      engine/src/core/com/jme3/material/ShaderGenerationInfo.java
  12. 29 16
      engine/src/core/com/jme3/material/Technique.java
  13. 44 1
      engine/src/core/com/jme3/material/TechniqueDef.java
  14. 38 10
      engine/src/core/com/jme3/renderer/queue/GeometryList.java
  15. 572 0
      engine/src/core/com/jme3/shader/Glsl100ShaderGenerator.java
  16. 146 0
      engine/src/core/com/jme3/shader/Glsl150ShaderGenerator.java
  17. 290 0
      engine/src/core/com/jme3/shader/ShaderGenerator.java
  18. 215 0
      engine/src/core/com/jme3/shader/ShaderNode.java
  19. 253 0
      engine/src/core/com/jme3/shader/ShaderNodeDefinition.java
  20. 234 0
      engine/src/core/com/jme3/shader/ShaderNodeVariable.java
  21. 70 3
      engine/src/core/com/jme3/shader/ShaderUtils.java
  22. 42 27
      engine/src/core/com/jme3/shader/UniformBinding.java
  23. 30 22
      engine/src/core/com/jme3/shader/VarType.java
  24. 196 0
      engine/src/core/com/jme3/shader/VariableMapping.java
  25. 22 22
      engine/src/core/com/jme3/util/blockparser/Statement.java

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

@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.material.plugins;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * An utility class that allows to parse a define condition in a glsl language
+ * style.
+ *
+ * extractDefines is able to get a list of defines in an expression and update
+ * the formatter expression with upercased defines
+ *
+ * @author Nehon
+ */
+public class ConditionParser {
+
+    private String formattedExpression = "";
+
+    public static void main(String argv[]) {
+        ConditionParser parser = new ConditionParser();
+        List<String> defines = parser.extractDefines("(LightMap && SeparateTexCoord) || !ColorMap");
+
+        for (String string : defines) {
+            System.err.println(string);
+        }
+        System.err.println(parser.formattedExpression);
+
+        defines = parser.extractDefines("#if (defined(LightMap) && defined(SeparateTexCoord)) || !defined(ColorMap)");
+
+        for (String string : defines) {
+            System.err.println(string);
+        }
+        System.err.println(parser.formattedExpression);
+
+
+//        System.err.println(parser.getFormattedExpression());
+//        
+//        parser.parse("ShaderNode.var.xyz");
+//        parser.parse("var.xyz");
+//        parser.parse("ShaderNode.var");
+//        parser.parse("var");
+    }
+
+    /**
+     * parse a condition and returns the list of defines of this condition.
+     * additionally this methods updates the formattedExpression with uppercased
+     * defines names
+     * 
+     * supported expression syntax example: 
+     * "(LightMap && SeparateTexCoord) || !ColorMap"
+     * "#if (defined(LightMap) && defined(SeparateTexCoord)) || !defined(ColorMap)"
+     * "#ifdef LightMap"
+     * "#ifdef (LightMap && SeparateTexCoord) || !ColorMap"
+     *
+     * @param expression the expression to parse
+     * @return the list of defines
+     */
+    public List<String> extractDefines(String expression) {
+        List<String> defines = new ArrayList<String>();
+        expression = expression.replaceAll("#ifdef", "").replaceAll("#if", "").replaceAll("defined", "");
+        Pattern pattern = Pattern.compile("(\\w+)");
+        formattedExpression = expression;
+        Matcher m = pattern.matcher(expression);
+        while (m.find()) {
+            String match = m.group();
+            defines.add(match);
+            formattedExpression = formattedExpression.replaceAll(match, "defined(" + match.toUpperCase() + ")");
+        }
+        return defines;
+    }
+
+    /**
+     * 
+     * @return the formatted expression previously updated by extractDefines
+     */
+    public String getFormattedExpression() {
+        return formattedExpression;
+    }
+}

+ 65 - 33
engine/src/core-plugins/com/jme3/material/plugins/J3MLoader.java

@@ -56,6 +56,9 @@ import java.util.logging.Logger;
 public class J3MLoader implements AssetLoader {
 
     private static final Logger logger = Logger.getLogger(J3MLoader.class.getName());
+   // private ErrorLogger errors;
+    private ShaderNodeLoaderDelegate nodesLoaderDelegate;
+    boolean isUseNodes = false;
     
     private AssetManager assetManager;
     private AssetKey key;
@@ -64,7 +67,7 @@ public class J3MLoader implements AssetLoader {
     private Material material;
     private TechniqueDef technique;
     private RenderState renderState;
-
+    
     private String vertLanguage;
     private String fragLanguage;
     
@@ -76,13 +79,6 @@ public class J3MLoader implements AssetLoader {
     public J3MLoader(){
     }
 
-    private void throwIfNequal(String expected, String got) throws IOException {
-        if (expected == null)
-            throw new IOException("Expected a statement, got '"+got+"'!");
-
-        if (!expected.equals(got))
-            throw new IOException("Expected '"+expected+"', got '"+got+"'!");
-    }
 
     // <TYPE> <LANG> : <SOURCE>
     private void readShaderStatement(String statement) throws IOException {
@@ -311,8 +307,8 @@ public class J3MLoader implements AssetLoader {
         return word != null && word.equals("On");
     }
 
-    private void readRenderStateStatement(String statement) throws IOException{
-        String[] split = statement.split(whitespacePattern);
+    private void readRenderStateStatement(Statement statement) throws IOException{
+        String[] split = statement.getLine().split(whitespacePattern);
         if (split[0].equals("Wireframe")){
             renderState.setWireframe(parseBoolean(split[1]));
         }else if (split[0].equals("FaceCull")){
@@ -334,15 +330,15 @@ public class J3MLoader implements AssetLoader {
             renderState.setColorWrite(parseBoolean(split[1]));
         }else if (split[0].equals("PointSprite")){
             renderState.setPointSprite(parseBoolean(split[1]));
-        }else{
-            throwIfNequal(null, split[0]);
+        } else {
+            throw new MatParseException(null, split[0], statement);
         }
     }
 
     private void readAdditionalRenderState(List<Statement> renderStates) throws IOException{
         renderState = material.getAdditionalRenderState();
         for (Statement statement : renderStates){
-            readRenderStateStatement(statement.getLine());
+            readRenderStateStatement(statement);
         }
         renderState = null;
     }
@@ -350,7 +346,7 @@ public class J3MLoader implements AssetLoader {
     private void readRenderState(List<Statement> renderStates) throws IOException{
         renderState = new RenderState();
         for (Statement statement : renderStates){
-            readRenderStateStatement(statement.getLine());
+            readRenderStateStatement(statement);
         }
         technique.setRenderState(renderState);
         renderState = null;
@@ -359,7 +355,7 @@ public class J3MLoader implements AssetLoader {
     private void readForcedRenderState(List<Statement> renderStates) throws IOException{
         renderState = new RenderState();
         for (Statement statement : renderStates){
-            readRenderStateStatement(statement.getLine());
+            readRenderStateStatement(statement);
         }
         technique.setForcedRenderState(renderState);
         renderState = null;
@@ -384,9 +380,9 @@ public class J3MLoader implements AssetLoader {
         }
 
     }
-
+    
     private void readTechniqueStatement(Statement statement) throws IOException{
-        String[] split = statement.getLine().split("[ \\{]");
+        String[] split = statement.getLine().split("[ \\{]");       
         if (split[0].equals("VertexShader") ||
             split[0].equals("FragmentShader")){
             readShaderStatement(statement.getLine());
@@ -400,13 +396,28 @@ public class J3MLoader implements AssetLoader {
             readRenderState(statement.getContents());
         }else if (split[0].equals("ForcedRenderState")){  
             readForcedRenderState(statement.getContents());
-        }else if (split[0].equals("Defines")){
-            readDefines(statement.getContents());
-        }else{
-            throwIfNequal(null, split[0]);
+        }else if (split[0].equals("Defines")){           
+            readDefines(statement.getContents());         
+        } else if (split[0].equals("ShaderNodesDefinitions")) {
+            initNodesLoader();
+            if (isUseNodes) {
+                nodesLoaderDelegate.readNodesDefinitions(statement.getContents());
+            }
+        } else if (split[0].equals("VertexShaderNodes")) {
+            initNodesLoader();
+            if (isUseNodes) {
+                nodesLoaderDelegate.readVertexShaderNodes(statement.getContents());
+            }
+        } else if (split[0].equals("FragmentShaderNodes")) {
+            initNodesLoader();
+            if (isUseNodes) {                
+                nodesLoaderDelegate.readFragmentShaderNodes(statement.getContents());
+            }
+        } else {
+            throw new MatParseException(null, split[0], statement);
         }
     }
-
+    
     private void readTransparentStatement(String statement) throws IOException{
         String[] split = statement.split(whitespacePattern);
         if (split.length != 2){
@@ -448,6 +459,7 @@ public class J3MLoader implements AssetLoader {
     }
 
     private void loadFromRoot(List<Statement> roots) throws IOException{
+        isUseNodes = false;
         if (roots.size() == 2){
             Statement exception = roots.get(0);
             String line = exception.getLine();
@@ -459,7 +471,7 @@ public class J3MLoader implements AssetLoader {
         }else if (roots.size() != 1){
             throw new IOException("Too many roots in J3M/J3MD file");
         }
-        
+               
         boolean extending = false;
         Statement materialStat = roots.get(0);
         String materialName = materialStat.getLine();
@@ -476,32 +488,33 @@ public class J3MLoader implements AssetLoader {
         String[] split = materialName.split(":", 2);
         
         if (materialName.equals("")){
-            throw new IOException("Material name cannot be empty");
+            throw new MatParseException("Material name cannot be empty", materialStat);         
         }
 
         if (split.length == 2){
             if (!extending){
-                throw new IOException("Must use 'Material' when extending.");
+                throw new MatParseException("Must use 'Material' when extending.", materialStat); 
             }
 
             String extendedMat = split[1].trim();
 
             MaterialDef def = (MaterialDef) assetManager.loadAsset(new AssetKey(extendedMat));
-            if (def == null)
-                throw new IOException("Extended material "+extendedMat+" cannot be found.");
+            if (def == null) {
+                throw new MatParseException("Extended material " + extendedMat + " cannot be found.", materialStat);
+            }
 
             material = new Material(def);
             material.setKey(key);
 //            material.setAssetName(fileName);
         }else if (split.length == 1){
             if (extending){
-                throw new IOException("Expected ':', got '{'");
+                throw new MatParseException("Expected ':', got '{'", materialStat);               
             }
             materialDef = new MaterialDef(assetManager, materialName);
             // NOTE: pass file name for defs so they can be loaded later
             materialDef.setAssetName(key.getName());
         }else{
-            throw new IOException("Cannot use colon in material name/path");
+            throw new MatParseException("Cannot use colon in material name/path", materialStat);   
         }
         
         for (Statement statement : materialStat.getContents()){
@@ -521,18 +534,18 @@ public class J3MLoader implements AssetLoader {
                 }else if (statType.equals("MaterialParameters")){
                     readMaterialParams(statement.getContents());
                 }else{
-                    throw new IOException("Expected material statement, got '"+statType+"'");
+                    throw new MatParseException("Expected material statement, got '"+statType+"'", statement);                       
                 }
             }
         }
     }
 
-    public Object load(AssetInfo info) throws IOException {
+    public Object load(AssetInfo info) throws IOException {       
         this.assetManager = info.getManager();
 
-        InputStream in = info.openStream();
+        InputStream in = info.openStream();        
         try {
-            key = info.getKey();
+            key = info.getKey();            
             loadFromRoot(BlockLanguageParser.parse(in));
         } finally {
             if (in != null){
@@ -551,5 +564,24 @@ public class J3MLoader implements AssetLoader {
             return materialDef;
         }
     }
+    
+    public MaterialDef loadMaterialDef(List<Statement> roots, AssetManager manager, AssetKey key) throws IOException {
+        this.key = key;
+        this.assetManager = manager;
+        loadFromRoot(roots);
+        return materialDef;
+    }
+
+    protected void initNodesLoader() {
+        if (!isUseNodes) {
+            isUseNodes = fragName == null && vertName == null;
+            if (isUseNodes) {                
+                nodesLoaderDelegate = new ShaderNodeLoaderDelegate();
+                nodesLoaderDelegate.setTechniqueDef(technique);
+                nodesLoaderDelegate.setMaterialDef(materialDef);
+                nodesLoaderDelegate.setAssetManager(assetManager);
+            }
+        }
+    }
 
 }

+ 90 - 0
engine/src/core-plugins/com/jme3/material/plugins/MatParseException.java

@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.material.plugins;
+
+import com.jme3.util.blockparser.Statement;
+import java.io.IOException;
+
+/**
+ * Custom Exception to report a j3md Material definition file parsing error.
+ * This exception reports the line number where the error occured.
+ *
+ * @author Nehon
+ */
+public class MatParseException extends IOException {
+
+    /**
+     * creates a MatParseException
+     *
+     * @param expected the expected value
+     * @param got the actual value
+     * @param statement the read statement
+     */
+    public MatParseException(String expected, String got, Statement statement) {
+        super("Error On line " + statement.getLineNumber() + " : " + statement.getLine() + "\n->Expected " + (expected == null ? "a statement" : expected) + ", got '" + got + "'!");
+
+    }
+
+    /**
+     * creates a MatParseException
+     *
+     * @param text the error message
+     * @param statement the statement where the error occur
+     */
+    public MatParseException(String text, Statement statement) {
+        super("Error On line " + statement.getLineNumber() + " : " + statement.getLine() + "\n->" + text);
+    }
+
+    /**
+     * creates a MatParseException
+     *
+     * @param expected the expected value
+     * @param got the actual value
+     * @param statement the read statement
+     * @param cause the embed exception that occured
+     */
+    public MatParseException(String expected, String got, Statement statement, Throwable cause) {
+        super("Error On line " + statement.getLineNumber() + " : " + statement.getLine() + "\n->Expected " + (expected == null ? "a statement" : expected) + ", got '" + got + "'!", cause);
+
+    }
+
+    /**
+     * creates a MatParseException
+     *
+     * @param text the error message
+     * @param statement the statement where the error occur
+     * @param cause the embed exception that occured
+     */
+    public MatParseException(String text, Statement statement, Throwable cause) {
+        super("Error On line " + statement.getLineNumber() + " : " + statement.getLine() + "\n->" + text, cause);
+    }
+}

+ 85 - 0
engine/src/core-plugins/com/jme3/material/plugins/ShaderNodeDefinitionLoader.java

@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.material.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetLoadException;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.ShaderNodeDefinitionKey;
+import com.jme3.util.blockparser.BlockLanguageParser;
+import com.jme3.util.blockparser.Statement;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * ShaderNodeDefnition file loader (.j3sn)
+ *
+ * a j3sn file is a block style file like j3md or j3m. It must contain one
+ * ShaderNodeDefinition{} block that contains several ShaderNodeDefinition{}
+ * blocks
+ *
+ * @author Nehon
+ */
+public class ShaderNodeDefinitionLoader implements AssetLoader {
+
+    private ShaderNodeLoaderDelegate loaderDelegate;
+
+    @Override
+    public Object load(AssetInfo assetInfo) throws IOException {
+        AssetKey k = assetInfo.getKey();
+        if (!(k instanceof ShaderNodeDefinitionKey)) {
+            throw new IOException("ShaderNodeDefinition file must be loaded via ShaderNodeDefinitionKey");
+        }
+        ShaderNodeDefinitionKey key = (ShaderNodeDefinitionKey) k;
+        loaderDelegate = new ShaderNodeLoaderDelegate();
+
+        InputStream in = assetInfo.openStream();
+        List<Statement> roots = BlockLanguageParser.parse(in);
+
+        if (roots.size() == 2) {
+            Statement exception = roots.get(0);
+            String line = exception.getLine();
+            if (line.startsWith("Exception")) {
+                throw new AssetLoadException(line.substring("Exception ".length()));
+            } else {
+                throw new MatParseException("In multiroot shader node definition, expected first statement to be 'Exception'", exception);
+            }
+        } else if (roots.size() != 1) {
+            throw new MatParseException("Too many roots in J3SN file", roots.get(0));
+        }
+
+        return loaderDelegate.readNodesDefinitions(roots.get(0).getContents(), key);
+
+    }
+}

+ 937 - 0
engine/src/core-plugins/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java

@@ -0,0 +1,937 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.material.plugins;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.AssetNotFoundException;
+import com.jme3.asset.ShaderNodeDefinitionKey;
+import com.jme3.material.MatParam;
+import com.jme3.material.MaterialDef;
+import com.jme3.material.ShaderGenerationInfo;
+import com.jme3.material.TechniqueDef;
+import com.jme3.shader.Shader;
+import com.jme3.shader.ShaderNode;
+import com.jme3.shader.ShaderNodeDefinition;
+import com.jme3.shader.ShaderNodeVariable;
+import com.jme3.shader.ShaderUtils;
+import com.jme3.shader.UniformBinding;
+import com.jme3.shader.VariableMapping;
+import com.jme3.util.blockparser.Statement;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class is here to be able to load shaderNodeDefinition from both the
+ * J3MLoader and ShaderNodeDefinitionLoader.
+ *
+ * Also it allows to load the ShaderNodes from a j3md file and build the
+ * ShaderNodes list of each technique and the ShaderGenerationInfo needed to
+ * generate the sahders
+ *
+ * @author Nehon
+ */
+public class ShaderNodeLoaderDelegate {
+
+    protected Map<String, ShaderNodeDefinition> nodeDefinitions;
+    protected Map<String, ShaderNode> nodes;
+    protected ShaderNodeDefinition shaderNodeDefinition;
+    protected ShaderNode shaderNode;
+    protected TechniqueDef techniqueDef;
+    protected Map<String, ShaderNodeVariable> attributes = new HashMap<String, ShaderNodeVariable>();
+    protected Map<String, ShaderNodeVariable> vertexDeclaredUniforms = new HashMap<String, ShaderNodeVariable>();
+    protected Map<String, ShaderNodeVariable> fragmentDeclaredUniforms = new HashMap<String, ShaderNodeVariable>();
+    protected Map<String, ShaderNodeVariable> varyings = new HashMap<String, ShaderNodeVariable>();
+    protected MaterialDef materialDef;
+    protected String shaderLanguage;
+    protected String shaderName;
+    protected String varNames = "";
+    protected AssetManager assetManager;
+    protected ConditionParser conditionParser = new ConditionParser();
+
+
+    /**
+     * Read the ShaderNodesDefinitions block and returns a list of
+     * ShaderNodesDefinition This method is used by the j3sn loader
+     *
+     * note that the order of the definitions in the list is not guaranteed.
+     *
+     * @param statements the list statements to parse
+     * @param key the ShaderNodeDefinitionKey
+     * @return a list of ShaderNodesDefinition
+     * @throws IOException
+     */
+    public List<ShaderNodeDefinition> readNodesDefinitions(List<Statement> statements, ShaderNodeDefinitionKey key) throws IOException {
+        
+        for (Statement statement : statements) {
+            String[] split = statement.getLine().split("[ \\{]");
+            if (statement.getLine().startsWith("ShaderNodeDefinition")) {
+                String name = statement.getLine().substring("ShaderNodeDefinition".length()).trim();
+
+
+                if (!getNodeDefinitions().containsKey(name)) {
+                    shaderNodeDefinition = new ShaderNodeDefinition();
+                    getNodeDefinitions().put(name, shaderNodeDefinition);
+                    shaderNodeDefinition.setName(name);
+                    readShaderNodeDefinition(statement.getContents(), key);
+
+                }
+            } else {
+                throw new MatParseException("ShaderNodeDefinition", split[0], statement);
+            }
+        }
+
+        return new ArrayList<ShaderNodeDefinition>(getNodeDefinitions().values());
+    }
+
+    /**
+     * Read the ShaderNodesDefinitions block and internally stores a map of
+     * ShaderNodesDefinition This method is used by the j3m loader.
+     *
+     * When loaded in a material, the definitions are not stored as a list, but
+     * they are stores in Shadernodes based onthis definition.
+     *
+     * The map is here to map the defintion to the nodes, and ovoid reloading
+     * already loaded definitions
+     *
+     * @param statements the list of statements to parse
+     * @throws IOException
+     */
+    public void readNodesDefinitions(List<Statement> statements) throws IOException {
+        readNodesDefinitions(statements, new ShaderNodeDefinitionKey());
+    }
+
+    /**
+     * effectiveliy reads the ShaderNodesDefinitions block
+     *
+     * @param statements the list of statements to parse
+     * @param key the ShaderNodeDefinitionKey
+     * @throws IOException
+     */
+    protected void readShaderNodeDefinition(List<Statement> statements, ShaderNodeDefinitionKey key) throws IOException {
+        boolean isLoadDoc = key instanceof ShaderNodeDefinitionKey && ((ShaderNodeDefinitionKey) key).isLoadDocumentation();
+        for (Statement statement : statements) {
+            String[] split = statement.getLine().split("[ \\{]");
+            String line = statement.getLine();
+
+            if (line.startsWith("Type")) {
+                String type = line.substring(line.lastIndexOf(':') + 1).trim();
+                shaderNodeDefinition.setType(Shader.ShaderType.valueOf(type));
+            } else if (line.startsWith("Shader ")) {
+                readShaderStatement(statement);
+                shaderNodeDefinition.getShadersLanguage().add(shaderLanguage);
+                shaderNodeDefinition.getShadersPath().add(shaderName);
+            } else if (line.startsWith("Documentation")) {
+                if (isLoadDoc) {
+                    String doc = "";
+                    for (Statement statement1 : statement.getContents()) {
+                        doc += "\n" + statement1.getLine();
+                    }
+                    shaderNodeDefinition.setDocumentation(doc);
+                }
+            } else if (line.startsWith("Input")) {
+                varNames = "";
+                for (Statement statement1 : statement.getContents()) {
+                    shaderNodeDefinition.getInputs().add(readVariable(statement1));
+                }
+            } else if (line.startsWith("Output")) {
+                varNames = "";
+                for (Statement statement1 : statement.getContents()) {
+                    shaderNodeDefinition.getOutputs().add(readVariable(statement1));
+                }
+            } else {
+                throw new MatParseException("one of Type, Shader, Documentation, Input, Output", split[0], statement);
+            }
+        }
+    }
+
+    /**
+     * reads a variable declaration statement <glslType> <varName>
+     *
+     * @param statement the statement to parse
+     * @return a ShaderNodeVariable axtracted from the statement
+     * @throws IOException
+     */
+    protected ShaderNodeVariable readVariable(Statement statement) throws IOException {
+        String[] splitVar = statement.getLine().trim().split("\\s");
+        if (varNames.contains(splitVar[1] + ";")) {
+            throw new MatParseException("Duplicate variable name " + splitVar[1], statement);
+        }
+        varNames += splitVar[1] + ";";
+        return new ShaderNodeVariable(splitVar[0], splitVar[1]);
+    }
+
+    /**
+     * reads the VertexShaderNodes{} block
+     *
+     * @param statements the list of statements to parse
+     * @throws IOException
+     */
+    public void readVertexShaderNodes(List<Statement> statements) throws IOException {
+        attributes.clear();
+        readNodes(statements);
+    }
+
+    /**
+     * reads a list of ShaderNode{} blocks
+     *
+     * @param statements the list of statements to parse
+     * @throws IOException
+     */
+    protected void readShaderNode(List<Statement> statements) throws IOException {
+        for (Statement statement : statements) {
+            String line = statement.getLine();
+            String[] split = statement.getLine().split("[ \\{]");
+            if (line.startsWith("Definition")) {
+                ShaderNodeDefinition def = findDefinition(statement);
+                shaderNode.setDefinition(def);
+            } else if (line.startsWith("Condition")) {
+                String condition = line.substring(line.lastIndexOf(":") + 1).trim();
+                extractCondition(condition, statement);
+                shaderNode.setCondition(conditionParser.getFormattedExpression());
+            } else if (line.startsWith("InputMapping")) {
+                for (Statement statement1 : statement.getContents()) {
+                    VariableMapping mapping = readInputMapping(statement1);
+                    techniqueDef.getShaderGenerationInfo().getUnusedNodes().remove(mapping.getRightVariable().getNameSpace());
+                    shaderNode.getInputMapping().add(mapping);
+                }
+            } else if (line.startsWith("OutputMapping")) {
+                for (Statement statement1 : statement.getContents()) {
+                    VariableMapping mapping = readOutputMapping(statement1);
+                    techniqueDef.getShaderGenerationInfo().getUnusedNodes().remove(shaderNode.getName());
+                    shaderNode.getOutputMapping().add(mapping);
+                }
+            } else {
+                throw new MatParseException("ShaderNodeDefinition", split[0], statement);
+            }
+        }
+
+    }
+
+    /**
+     * reads a mapping statement. Sets the nameSpace, name and swizzling of the
+     * left variable. Sets the name, nameSpace and swizzling of the right
+     * variable types will be determined later.
+     *
+     * Format : <nameSpace>.<varName>[.<swizzling>] =
+     * <nameSpace>.<varName>[.<swizzling>][:Condition]
+     *
+     * @param statement the statement to read
+     * @return the read mapping
+     */
+    protected VariableMapping parseMapping(Statement statement, boolean[] hasNameSpace) throws IOException {
+        VariableMapping mapping = new VariableMapping();
+        String[] cond = statement.getLine().split(":");
+
+        String[] vars = cond[0].split("=");
+        checkMappingFormat(vars, statement);
+        ShaderNodeVariable[] variables = new ShaderNodeVariable[2];
+        String[] swizzle = new String[2];
+        for (int i = 0; i < vars.length; i++) {
+            String[] expression = vars[i].trim().split("\\.");
+            if (hasNameSpace[i]) {
+                if (expression.length <= 3) {
+                    variables[i] = new ShaderNodeVariable("", expression[0].trim(), expression[1].trim());
+                }
+                if (expression.length == 3) {
+                    swizzle[i] = expression[2].trim();
+                }
+            } else {
+                if (expression.length <= 2) {
+                    variables[i] = new ShaderNodeVariable("", expression[0].trim());
+                }
+                if (expression.length == 2) {
+                    swizzle[i] = expression[1].trim();
+                }
+            }
+
+        }
+
+        mapping.setLeftVariable(variables[0]);
+        mapping.setLeftSwizzling(swizzle[0] != null ? swizzle[0] : "");
+        mapping.setRightVariable(variables[1]);
+        mapping.setRightSwizzling(swizzle[1] != null ? swizzle[1] : "");
+
+        if (cond.length > 1) {
+            extractCondition(cond[1], statement);
+            mapping.setCondition(conditionParser.getFormattedExpression());
+        }
+
+        return mapping;
+    }
+
+    /**
+     * reads the FragmentShaderNodes{} block
+     *
+     * @param statements the list of statements to parse
+     * @throws IOException
+     */
+    public void readFragmentShaderNodes(List<Statement> statements) throws IOException {
+        readNodes(statements);
+    }
+
+    /**
+     * Reads a Shader statement of this form <TYPE> <LANG> : <SOURCE>
+     *
+     * @param statement
+     * @throws IOException
+     */
+    protected void readShaderStatement(Statement statement) throws IOException {
+        String[] split = statement.getLine().split(":");
+        if (split.length != 2) {
+            throw new MatParseException("Shader statement syntax incorrect", statement);
+        }
+        String[] typeAndLang = split[0].split("\\p{javaWhitespace}+");
+        if (typeAndLang.length != 2) {
+            throw new MatParseException("Shader statement syntax incorrect", statement);
+        }
+        shaderName = split[1].trim();
+        shaderLanguage = typeAndLang[1];
+    }
+
+    /**
+     * Sets the technique definition currently being loaded
+     *
+     * @param techniqueDef the technique def
+     */
+    public void setTechniqueDef(TechniqueDef techniqueDef) {
+        this.techniqueDef = techniqueDef;
+    }
+
+    /**
+     * sets the material def currently being loaded
+     *
+     * @param materialDef
+     */
+    public void setMaterialDef(MaterialDef materialDef) {
+        this.materialDef = materialDef;
+    }
+
+    /**
+     * searcha variable in the given list and updates its type and namespace
+     *
+     * @param var the variable to update
+     * @param list the variables list
+     * @return true if the variable has been found and updated
+     */
+    protected boolean updateVariableFromList(ShaderNodeVariable var, List<ShaderNodeVariable> list) {
+        for (ShaderNodeVariable shaderNodeVariable : list) {
+            if (shaderNodeVariable.getName().equals(var.getName())) {
+                var.setType(shaderNodeVariable.getType());
+                var.setNameSpace(shaderNode.getName());
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * updates the type of the right variable of a mapping from the type of the
+     * left variable
+     *
+     * @param mapping the mapping to consider
+     */
+    protected void updateRightTypeFromLeftType(VariableMapping mapping) {
+        String type = mapping.getLeftVariable().getType();
+        int card = ShaderUtils.getCardinality(type, mapping.getRightSwizzling());
+        if (card > 0) {
+            if (card == 1) {
+                type = "float";
+            } else {
+                type = "vec" + card;
+            }
+        }
+        mapping.getRightVariable().setType(type);
+    }
+
+    /**
+     * check if once a mapping expression is split by "=" the resulting array
+     * have 2 elements
+     *
+     * @param vars the array
+     * @param statement the statement
+     * @throws IOException
+     */
+    protected void checkMappingFormat(String[] vars, Statement statement) throws IOException {
+        if (vars.length != 2) {
+            throw new MatParseException("Not a valid expression should be '<varName>[.<swizzling>] = <nameSpace>.<varName>[.<swizzling>][:Condition]'", statement);
+        }
+    }
+
+    /**
+     * finds a MatParam in the materialDef from the given name
+     *
+     * @param varName the matparam name
+     * @return the MatParam
+     */
+    protected MatParam findMatParam(String varName) {
+        for (MatParam matParam : materialDef.getMaterialParams()) {
+            if (varName.equals(matParam.getName())) {
+                return matParam;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * finds an UniformBinding representing a WorldParam from the techniqueDef
+     *
+     * @param varName the name of the WorldParam
+     * @return the corresponding UniformBinding to the WorldParam
+     */
+    protected UniformBinding findWorldParam(String varName) {
+        for (UniformBinding worldParam : techniqueDef.getWorldBindings()) {
+            if (varName.equals(worldParam.toString())) {
+                return worldParam;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * updates the right variable of the given mapping from a UniformBinding (a
+     * WorldParam) it checks if the unifrom hasn't already been loaded, add it
+     * to the maps if not.
+     *
+     * @param param the WorldParam UniformBinding
+     * @param mapping the mapping
+     * @param map the map of uniforms to search into
+     * @return true if the param was added to the map
+     */
+    protected boolean updateRightFromUniforms(UniformBinding param, VariableMapping mapping, Map<String, ShaderNodeVariable> map) {
+        ShaderNodeVariable right = mapping.getRightVariable();
+        String name = "g_" + param.toString();
+        ShaderNodeVariable var = map.get(name);
+        if (var == null) {
+            right.setType(param.getGlslType());
+            right.setName(name);
+            map.put(right.getName(), right);
+            mapping.setRightVariable(right);
+            return true;
+        }
+        mapping.setRightVariable(var);
+        return false;
+    }
+
+    /**
+     * updates the right variable of the given mapping from a MatParam (a
+     * WorldParam) it checks if the unifrom hasn't already been loaded, add it
+     * to the maps if not.
+     *
+     * @param param the MatParam
+     * @param mapping the mapping
+     * @param map the map of uniforms to search into
+     * @return true if the param was added to the map
+     */
+    public boolean updateRightFromUniforms(MatParam param, VariableMapping mapping, Map<String, ShaderNodeVariable> map) {
+        ShaderNodeVariable right = mapping.getRightVariable();
+        ShaderNodeVariable var = map.get(param.getPrefixedName());
+        if (var == null) {
+            right.setType(param.getVarType().getGlslType());
+            right.setName(param.getPrefixedName());
+            map.put(right.getName(), right);
+            mapping.setRightVariable(right);
+            return true;
+        }
+        mapping.setRightVariable(var);
+        return false;
+    }
+
+    /**
+     * updates a variable from the Attribute list
+     *
+     * @param right the variable
+     * @param mapping the mapping
+     */
+    public void updateVarFromAttributes(ShaderNodeVariable right, VariableMapping mapping) {
+        ShaderNodeVariable var = attributes.get(right.getName());
+        if (var == null) {
+            updateRightTypeFromLeftType(mapping);
+        } else {
+
+            mapping.setRightVariable(var);
+        }
+    }
+
+    /**
+     * Adds a define to the techniquedef
+     *
+     * @param paramName
+     */
+    public void addDefine(String paramName) {
+        if (techniqueDef.getShaderParamDefine(paramName) == null) {
+            techniqueDef.addShaderParamDefine(paramName, paramName.toUpperCase());
+        }
+    }
+
+    /**
+     * find a variable with the given name from the list of variable
+     *
+     * @param vars a list of shaderNodeVariables
+     * @param rightVarName the variable name to search for
+     * @return the found variable or null is not found
+     */
+    public ShaderNodeVariable findNodeOutput(List<ShaderNodeVariable> vars, String rightVarName) {
+        ShaderNodeVariable var = null;
+        for (ShaderNodeVariable variable : vars) {
+            if (variable.getName().equals(rightVarName)) {
+                var = variable;
+            }
+        }
+        return var;
+    }
+
+    /**
+     * extract and check a condition expression
+     *
+     * @param cond the condition expression
+     * @param statement the statement being read
+     * @throws IOException
+     */
+    public void extractCondition(String cond, Statement statement) throws IOException {
+        List<String> defines = conditionParser.extractDefines(cond);
+        for (String string : defines) {
+            MatParam param = findMatParam(string);
+            if (param != null) {
+                addDefine(param.getName());
+            } else {
+                throw new MatParseException("Invalid condition, condition must match a Material Parameter named " + cond, statement);
+            }
+        }
+    }
+
+    /**
+     * reads an input mapping
+     *
+     * @param statement1 the statement being read
+     * @return the mapping
+     * @throws IOException
+     */
+    public VariableMapping readInputMapping(Statement statement1) throws IOException {
+        VariableMapping mapping = null;
+        try {
+            mapping = parseMapping(statement1, new boolean[]{false, true});
+        } catch (Exception e) {
+            throw new MatParseException("Unexpected mapping format", statement1, e);
+        }
+        ShaderNodeVariable left = mapping.getLeftVariable();
+        ShaderNodeVariable right = mapping.getRightVariable();
+        if (!updateVariableFromList(left, shaderNode.getDefinition().getInputs())) {
+            throw new MatParseException(left.getName() + " is not an input variable of " + shaderNode.getDefinition().getName(), statement1);
+        }
+
+        if (left.getType().startsWith("sampler") && !right.getNameSpace().equals("MatParam")) {
+            throw new MatParseException("Samplers can only be assigned to MatParams", statement1);
+        }
+
+        if (right.getNameSpace().equals("Global")) {
+            right.setType("vec4");//Globals are all vec4 for now (maybe forever...)
+            updateCondition(right, mapping);
+            storeGlobal(right, statement1);
+
+        } else if (right.getNameSpace().equals("Attr")) {
+            if (shaderNode.getDefinition().getType() == Shader.ShaderType.Fragment) {
+                throw new MatParseException("Cannot have an attribute as input in a fragment shader" + right.getName(), statement1);
+            }
+            updateVarFromAttributes(mapping.getRightVariable(), mapping);
+            updateCondition(mapping.getRightVariable(), mapping);
+            storeAttribute(mapping.getRightVariable());
+        } else if (right.getNameSpace().equals("MatParam")) {
+            MatParam param = findMatParam(right.getName());
+            if (param == null) {
+                throw new MatParseException("Could not find a Material Parameter named " + right.getName(), statement1);
+            }
+            if (shaderNode.getDefinition().getType() == Shader.ShaderType.Vertex) {
+                if (updateRightFromUniforms(param, mapping, vertexDeclaredUniforms)) {
+                    updateCondition(mapping.getRightVariable(), mapping);
+                    storeVertexUniform(mapping.getRightVariable());
+                }
+            } else {
+                if (updateRightFromUniforms(param, mapping, fragmentDeclaredUniforms)) {
+                    if (mapping.getRightVariable().getType().contains("|")) {
+                        String type = fixSamplerType(left.getType(), mapping.getRightVariable().getType());
+                        if (type != null) {
+                            mapping.getRightVariable().setType(type);
+                        } else {
+                            throw new MatParseException(param.getVarType().toString() + " can only be matched to one of " + param.getVarType().getGlslType().replaceAll("\\|", ",") + " found " + left.getType(), statement1);
+                        }
+                    }
+                    updateCondition(mapping.getRightVariable(), mapping);
+                    storeFragmentUniform(mapping.getRightVariable());
+                }
+            }
+
+        } else if (right.getNameSpace().equals("WorldParam")) {
+            UniformBinding worldParam = findWorldParam(right.getName());
+            if (worldParam == null) {
+                throw new MatParseException("Could not find a World Parameter named " + right.getName(), statement1);
+            }
+            if (shaderNode.getDefinition().getType() == Shader.ShaderType.Vertex) {
+                if (updateRightFromUniforms(worldParam, mapping, vertexDeclaredUniforms)) {
+                    updateCondition(mapping.getRightVariable(), mapping);
+                    storeVertexUniform(mapping.getRightVariable());
+                }
+            } else {
+                if (updateRightFromUniforms(worldParam, mapping, fragmentDeclaredUniforms)) {
+                    updateCondition(mapping.getRightVariable(), mapping);
+                    storeFragmentUniform(mapping.getRightVariable());
+                }
+            }
+
+        } else {
+            ShaderNode node = nodes.get(right.getNameSpace());
+            if (node == null) {
+                throw new MatParseException("Undeclared node" + right.getNameSpace() + ". Make sure this node is declared before the current node", statement1);
+            }
+            ShaderNodeVariable var = findNodeOutput(node.getDefinition().getOutputs(), right.getName());
+            if (var == null) {
+                throw new MatParseException("Cannot find output variable" + right.getName() + " form ShaderNode " + node.getName(), statement1);
+            }
+            right.setNameSpace(node.getName());
+            right.setType(var.getType());
+            mapping.setRightVariable(right);
+            updateCondition(mapping.getRightVariable(), mapping);
+            storeVaryings(node, mapping.getRightVariable());
+
+        }
+
+        checkTypes(mapping, statement1);
+
+        return mapping;
+    }
+
+    /**
+     * reads an output mapping
+     *
+     * @param statement1 the staement being read
+     * @return the mapping
+     * @throws IOException
+     */
+    public VariableMapping readOutputMapping(Statement statement1) throws IOException {
+        VariableMapping mapping = null;
+        try {
+            mapping = parseMapping(statement1, new boolean[]{true, false});
+        } catch (Exception e) {
+            throw new MatParseException("Unexpected mapping format", statement1, e);
+        }
+        ShaderNodeVariable left = mapping.getLeftVariable();
+        ShaderNodeVariable right = mapping.getRightVariable();
+
+
+        if (left.getType().startsWith("sampler") || right.getType().startsWith("sampler")) {
+            throw new MatParseException("Samplers can only be inputs", statement1);
+        }
+
+        if (left.getNameSpace().equals("Global")) {
+            left.setType("vec4");//Globals are all vec4 for now (maybe forever...)
+            updateCondition(left, mapping);
+            storeGlobal(left, statement1);
+        } else {
+            throw new MatParseException("Only Global nameSpace is allowed for outputMapping, got" + left.getNameSpace(), statement1);
+        }
+
+        if (!updateVariableFromList(right, shaderNode.getDefinition().getOutputs())) {
+            throw new MatParseException(right.getName() + " is not an output variable of " + shaderNode.getDefinition().getName(), statement1);
+        }
+
+        checkTypes(mapping, statement1);
+
+        return mapping;
+    }
+
+    /**
+     * Reads alist of ShaderNodes
+     *
+     * @param statements the list of statements to read
+     * @throws IOException
+     */
+    public void readNodes(List<Statement> statements) throws IOException {
+        if (techniqueDef.getShaderNodes() == null) {
+            techniqueDef.setShaderNodes(new ArrayList<ShaderNode>());
+            techniqueDef.setShaderGenerationInfo(new ShaderGenerationInfo());
+        }
+
+        for (Statement statement : statements) {
+            String[] split = statement.getLine().split("[ \\{]");
+            if (statement.getLine().startsWith("ShaderNode ")) {
+                String name = statement.getLine().substring("ShaderNode".length()).trim();
+                if (nodes == null) {
+                    nodes = new HashMap<String, ShaderNode>();
+                }
+                if (!nodes.containsKey(name)) {
+                    shaderNode = new ShaderNode();
+                    shaderNode.setName(name);
+                    techniqueDef.getShaderGenerationInfo().getUnusedNodes().add(name);
+
+                    readShaderNode(statement.getContents());
+                    nodes.put(name, shaderNode);
+                    techniqueDef.getShaderNodes().add(shaderNode);
+                } else {
+                    throw new MatParseException("ShaderNode " + name + " is already defined", statement);
+                }
+
+            } else {
+                throw new MatParseException("ShaderNode", split[0], statement);
+            }
+        }
+    }
+
+    /**
+     * retrieve the leftType corresponding sampler type from the rightType
+     *
+     * @param leftType the left samplerType
+     * @param rightType the right sampler type (can be multiple types sparated
+     * by "|"
+     * @return the type or null if not found
+     */
+    public String fixSamplerType(String leftType, String rightType) {
+        String[] types = rightType.split("\\|");
+        for (String string : types) {
+            if (leftType.equals(string)) {
+                return string;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * stores a global output
+     *
+     * @param var the variable to store
+     * @param statement1 the statement being read
+     * @throws IOException
+     */
+    public void storeGlobal(ShaderNodeVariable var, Statement statement1) throws IOException {
+        var.setShaderOutput(true);
+        if (shaderNode.getDefinition().getType() == Shader.ShaderType.Vertex) {
+            ShaderNodeVariable global = techniqueDef.getShaderGenerationInfo().getVertexGlobal();
+            if (global != null) {
+                global.setCondition(mergeConditions(global.getCondition(), var.getCondition(), "||"));
+                var.setCondition(global.getCondition());
+                if (!global.getName().equals(var.getName())) {
+                    throw new MatParseException("A global output is already defined for the vertex shader: " + global.getName() + ". vertex shader can only have one global output", statement1);
+                }
+            } else {
+                techniqueDef.getShaderGenerationInfo().setVertexGlobal(var);
+            }
+        } else if (shaderNode.getDefinition().getType() == Shader.ShaderType.Fragment) {
+            mergeConditionsAndStoreVariable(var, techniqueDef.getShaderGenerationInfo().getFragmentGlobals());
+        }
+    }
+
+    /**
+     * store an attribute
+     *
+     * @param var the variable ot store
+     */
+    public void storeAttribute(ShaderNodeVariable var) {
+        mergeConditionsAndStoreVariable(var, techniqueDef.getShaderGenerationInfo().getAttributes());
+    }
+
+    /**
+     * store a vertex uniform
+     *
+     * @param var the variable ot store
+     */
+    public void storeVertexUniform(ShaderNodeVariable var) {
+        mergeConditionsAndStoreVariable(var, techniqueDef.getShaderGenerationInfo().getVertexUniforms());
+
+    }
+
+    /**
+     * store a fragment uniform
+     *
+     * @param var the variable ot store
+     */
+    public void storeFragmentUniform(ShaderNodeVariable var) {
+        mergeConditionsAndStoreVariable(var, techniqueDef.getShaderGenerationInfo().getFragmentUniforms());
+
+    }
+
+    /**
+     * sets the assetManager
+     *
+     * @param assetManager
+     */
+    public void setAssetManager(AssetManager assetManager) {
+        this.assetManager = assetManager;
+    }
+
+    /**
+     * find the definiton from this statement (loads it if necessary)
+     *
+     * @param statement the statement being read
+     * @return the definition
+     * @throws IOException
+     */
+    public ShaderNodeDefinition findDefinition(Statement statement) throws IOException {
+        String defLine[] = statement.getLine().split(":");
+        String defName = defLine[1].trim();
+
+        ShaderNodeDefinition def = getNodeDefinitions().get(defName);
+        if (def == null) {
+            if (defLine.length == 3) {
+                List<ShaderNodeDefinition> defs = null;
+                try {
+                    defs = assetManager.loadAsset(new ShaderNodeDefinitionKey(defLine[2].trim()));
+                } catch (AssetNotFoundException e) {
+                    throw new MatParseException("Couldn't find " + defLine[2].trim(), statement, e);
+                }
+
+                for (ShaderNodeDefinition definition : defs) {
+                    definition.setPath(defLine[2].trim());
+                    if (defName.equals(definition.getName())) {
+                        def = definition;
+                    }
+                    if (!(getNodeDefinitions().containsKey(definition.getName()))) {
+                        getNodeDefinitions().put(definition.getName(), definition);
+                    }
+                }
+            }
+            if (def == null) {
+                throw new MatParseException(defName + " is not a declared as Shader Node Definition", statement);
+            }
+        }
+        return def;
+    }
+
+    /**
+     * updates a variable condition form a mapping condition
+     *
+     * @param var the variable
+     * @param mapping the mapping
+     */
+    public void updateCondition(ShaderNodeVariable var, VariableMapping mapping) {
+
+        String condition = mergeConditions(shaderNode.getCondition(), mapping.getCondition(), "&&");
+
+        if (var.getCondition() == null) {
+            var.setCondition(condition);
+        } else {
+            var.setCondition(mergeConditions(var.getCondition(), condition, "||"));
+        }
+    }
+
+    /**
+     * store a varying
+     *
+     * @param node the shaderNode
+     * @param variable the variable to store
+     */
+    public void storeVaryings(ShaderNode node, ShaderNodeVariable variable) {
+        variable.setShaderOutput(true);
+        if (node.getDefinition().getType() == Shader.ShaderType.Vertex && shaderNode.getDefinition().getType() == Shader.ShaderType.Fragment) {
+            ShaderNodeVariable var = varyings.get(variable.getName());
+            if (var == null) {
+                techniqueDef.getShaderGenerationInfo().getVaryings().add(variable);
+                varyings.put(variable.getName(), variable);
+            } else {
+                var.setCondition(mergeConditions(var.getCondition(), variable.getCondition(), "||"));
+                variable.setCondition(var.getCondition());
+            }
+            //if a variable is declared with the same name as an input and an output and is a varying, set it as a shader output so it's declared as a varying only once.
+            for (VariableMapping variableMapping : node.getInputMapping()) {
+                if (variableMapping.getLeftVariable().getName().equals(variable.getName())) {
+                    variableMapping.getLeftVariable().setShaderOutput(true);
+                }
+            }
+        }
+
+    }
+
+    /**
+     * 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 (operator.equals("||") && (condition1 == null || condition2 == null)) {
+            return null;
+        }
+        if (condition1 != null) {
+            if (condition2 == null) {
+                return condition1;
+            } else {
+                String mergedCondition = "(" + condition1 + ") " + operator + " (" + condition2 + ")";
+                return mergedCondition;
+            }
+        } else {
+            return condition2;
+        }
+    }
+
+    /**
+     * search a variable in a list from its name and merge the conditions of the
+     * variables
+     *
+     * @param variable the variable
+     * @param varList the variable list
+     */
+    public void mergeConditionsAndStoreVariable(ShaderNodeVariable variable, List<ShaderNodeVariable> varList) {
+        for (ShaderNodeVariable var : varList) {
+            if (var.getName().equals(variable.getName())) {
+                var.setCondition(mergeConditions(var.getCondition(), variable.getCondition(), "||"));
+                variable.setCondition(var.getCondition());
+                return;
+            }
+        }
+        varList.add(variable);
+    }
+
+    /**
+     * check the types of a mapping, left type must match right type tkae the
+     * swizzle into account
+     *
+     * @param mapping the mapping
+     * @param statement1 the statement being read
+     * @throws MatParseException
+     */
+    protected void checkTypes(VariableMapping mapping, Statement statement1) throws MatParseException {
+        if (!ShaderUtils.typesMatch(mapping)) {
+            String ls = mapping.getLeftSwizzling().length() == 0 ? "" : "." + mapping.getLeftSwizzling();
+            String rs = mapping.getRightSwizzling().length() == 0 ? "" : "." + mapping.getRightSwizzling();
+            throw new MatParseException("Type mismatch, cannot convert" + mapping.getLeftVariable().getType() + ls + " to " + mapping.getRightVariable().getType() + rs, statement1);
+        }
+    }
+
+    private Map<String, ShaderNodeDefinition> getNodeDefinitions() {
+        if (nodeDefinitions == null) {
+            nodeDefinitions = new HashMap<String, ShaderNodeDefinition>();
+        }
+        return nodeDefinitions;
+    }
+}

+ 16 - 0
engine/src/core/com/jme3/asset/AssetManager.java

@@ -38,12 +38,15 @@ import com.jme3.audio.AudioKey;
 import com.jme3.font.BitmapFont;
 import com.jme3.material.Material;
 import com.jme3.post.FilterPostProcessor;
+import com.jme3.renderer.Caps;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.plugins.OBJLoader;
 import com.jme3.shader.Shader;
+import com.jme3.shader.ShaderGenerator;
 import com.jme3.shader.ShaderKey;
 import com.jme3.texture.Texture;
 import com.jme3.texture.plugins.TGALoader;
+import java.util.EnumSet;
 import java.util.List;
 
 /**
@@ -364,4 +367,17 @@ public interface AssetManager {
      * @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
      */
     public FilterPostProcessor loadFilter(String name);
+    
+    /**
+     * Sets the shaderGenerator to generate shaders based on shaderNodes.
+     * @param generator the shaderGenerator 
+     */    
+    public void setShaderGenerator(ShaderGenerator generator);
+    
+    /**
+     * Returns the shaderGenerator responsible for generating the shaders
+     * @return the shaderGenerator 
+     */
+    public ShaderGenerator getShaderGenerator(EnumSet<Caps> caps);
+    
 }

+ 1 - 0
engine/src/core/com/jme3/asset/Desktop.cfg

@@ -6,6 +6,7 @@ LOADER com.jme3.audio.plugins.OGGLoader : ogg
 LOADER com.jme3.cursors.plugins.CursorLoader : ani, cur, ico
 LOADER com.jme3.material.plugins.J3MLoader : j3m
 LOADER com.jme3.material.plugins.J3MLoader : j3md
+LOADER com.jme3.material.plugins.ShaderNodeDefinitionLoader : j3sn
 LOADER com.jme3.font.plugins.BitmapFontLoader : fnt
 LOADER com.jme3.texture.plugins.DDSLoader : dds
 LOADER com.jme3.texture.plugins.PFMLoader : pfm

+ 32 - 1
engine/src/core/com/jme3/asset/DesktopAssetManager.java

@@ -38,8 +38,12 @@ import com.jme3.audio.AudioKey;
 import com.jme3.font.BitmapFont;
 import com.jme3.material.Material;
 import com.jme3.post.FilterPostProcessor;
+import com.jme3.renderer.Caps;
 import com.jme3.scene.Spatial;
+import com.jme3.shader.Glsl100ShaderGenerator;
+import com.jme3.shader.Glsl150ShaderGenerator;
 import com.jme3.shader.Shader;
+import com.jme3.shader.ShaderGenerator;
 import com.jme3.shader.ShaderKey;
 import com.jme3.texture.Texture;
 import java.io.IOException;
@@ -48,6 +52,7 @@ import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.logging.Level;
@@ -62,6 +67,7 @@ import java.util.logging.Logger;
 public class DesktopAssetManager implements AssetManager {
 
     private static final Logger logger = Logger.getLogger(AssetManager.class.getName());
+    private ShaderGenerator shaderGenerator;
     
     private final ImplHandler handler = new ImplHandler(this);
 
@@ -83,7 +89,7 @@ public class DesktopAssetManager implements AssetManager {
     public DesktopAssetManager(URL configFile){
         if (configFile != null){
             loadConfigFile(configFile);
-        }
+        }        
         logger.fine("DesktopAssetManager created.");
     }
 
@@ -407,4 +413,29 @@ public class DesktopAssetManager implements AssetManager {
         }
         return shader;
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ShaderGenerator getShaderGenerator(EnumSet<Caps> caps) {
+        if (shaderGenerator == null) {
+            if(caps.contains(Caps.GLSL150)){
+                shaderGenerator = new Glsl150ShaderGenerator(this);
+            }else{
+                shaderGenerator = new Glsl100ShaderGenerator(this);
+            }
+        }
+        return shaderGenerator;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setShaderGenerator(ShaderGenerator shaderGenerator) {
+        this.shaderGenerator = shaderGenerator;
+    }
+
+    
 }

+ 89 - 0
engine/src/core/com/jme3/asset/ShaderNodeDefinitionKey.java

@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.asset;
+
+import com.jme3.asset.cache.AssetCache;
+import com.jme3.shader.ShaderNodeDefinition;
+import java.util.List;
+
+/**
+ * Used for loading {@link ShaderNodeDefinition shader nodes definition}
+ *
+ * Tells if the defintion has to be loaded with or without its documentation
+ *
+ * @author Kirill Vainer
+ */
+public class ShaderNodeDefinitionKey extends AssetKey<List<ShaderNodeDefinition>> {
+
+    private boolean loadDocumentation = false;
+
+    /**
+     * creates a ShaderNodeDefinitionKey
+     *
+     * @param name the name of the asset to load
+     */
+    public ShaderNodeDefinitionKey(String name) {
+        super(name);
+    }
+
+    /**
+     * creates a ShaderNodeDefinitionKey
+     */
+    public ShaderNodeDefinitionKey() {
+        super();
+    }
+
+    @Override
+    public Class<? extends AssetCache> getCacheType() {
+        return null;
+    }
+
+    /**
+     *
+     * @return true if the asset loaded with this key will contain its
+     * documentation
+     */
+    public boolean isLoadDocumentation() {
+        return loadDocumentation;
+    }
+
+    /**
+     * sets to true to load the documentation along with the
+     * ShaderNodeDefinition
+     *
+     * @param loadDocumentation true to load the documentation along with the
+     * ShaderNodeDefinition
+     */
+    public void setLoadDocumentation(boolean loadDocumentation) {
+        this.loadDocumentation = loadDocumentation;
+    }
+}

+ 5 - 5
engine/src/core/com/jme3/material/Material.java

@@ -873,10 +873,10 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
     public void selectTechnique(String name, RenderManager renderManager) {
         // check if already created
         Technique tech = techniques.get(name);
+        // When choosing technique, we choose one that
+        // supports all the caps.
+        EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps();
         if (tech == null) {
-            // When choosing technique, we choose one that
-            // supports all the caps.
-            EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps();
 
             if (name.equals("Default")) {
                 List<TechniqueDef> techDefs = def.getDefaultTechniques();
@@ -923,7 +923,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         }
 
         technique = tech;
-        tech.makeCurrent(def.getAssetManager(), true);
+        tech.makeCurrent(def.getAssetManager(), true, rendererCaps);
 
         // shader was changed
         sortingId = -1;
@@ -933,7 +933,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         if (technique == null) {
             selectTechnique("Default", rm);
         } else {
-            technique.makeCurrent(def.getAssetManager(), false);
+            technique.makeCurrent(def.getAssetManager(), false, rm.getRenderer().getCaps());
         }
     }
 

+ 190 - 0
engine/src/core/com/jme3/material/ShaderGenerationInfo.java

@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.material;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.shader.ShaderNodeVariable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * this class is basically a struct that contains the ShaderNodes informations
+ * in an appropriate way to ease the shader generation process and make it
+ * faster.
+ *
+ * @author Nehon
+ */
+public class ShaderGenerationInfo implements Savable {
+
+    /**
+     * the list of attributes of the vertex shader
+     */
+    protected List<ShaderNodeVariable> attributes = new ArrayList<ShaderNodeVariable>();
+    /**
+     * the list of all the uniforms to declare in the vertex shader
+     */
+    protected List<ShaderNodeVariable> vertexUniforms = new ArrayList<ShaderNodeVariable>();
+    /**
+     * the global output of the vertex shader (to assign ot gl_Position)
+     */
+    protected ShaderNodeVariable vertexGlobal = null;
+    /**
+     * the list of varyings
+     */
+    protected List<ShaderNodeVariable> varyings = new ArrayList<ShaderNodeVariable>();
+    /**
+     * the list of all the uniforms to declare in the fragment shader
+     */
+    protected List<ShaderNodeVariable> fragmentUniforms = new ArrayList<ShaderNodeVariable>();
+    /**
+     * the list of all the fragment shader global outputs (to assign ot gl_FragColor or gl_Fragdata[n])
+     */
+    protected List<ShaderNodeVariable> fragmentGlobals = new ArrayList<ShaderNodeVariable>();
+    /**
+     * the unused node names of this shader (node whose output are never used)
+     */
+    protected List<String> unusedNodes = new ArrayList<String>();
+
+    /**
+     *
+     * @return the attributes
+     */
+    public List<ShaderNodeVariable> getAttributes() {
+        return attributes;
+    }
+
+    /**
+     *
+     * @return the vertex shader uniforms
+     */
+    public List<ShaderNodeVariable> getVertexUniforms() {
+        return vertexUniforms;
+    }
+
+    /**
+     *
+     * @return the fragment shader uniforms
+     */
+    public List<ShaderNodeVariable> getFragmentUniforms() {
+        return fragmentUniforms;
+    }
+
+    /**
+     *
+     * @return the vertex shader global ouput
+     */
+    public ShaderNodeVariable getVertexGlobal() {
+        return vertexGlobal;
+    }
+
+    /**
+     *
+     * @return the fragment shader global outputs
+     */
+    public List<ShaderNodeVariable> getFragmentGlobals() {
+        return fragmentGlobals;
+    }
+
+    /**
+     *
+     * @return the varyings
+     */
+    public List<ShaderNodeVariable> getVaryings() {
+        return varyings;
+    }
+
+    /**
+     * sets the vertex shader global output
+     *
+     * @param vertexGlobal the global output
+     */
+    public void setVertexGlobal(ShaderNodeVariable vertexGlobal) {
+        this.vertexGlobal = vertexGlobal;
+    }
+
+    /**
+     * 
+     * @return the list on unused node names
+     */
+    public List<String> getUnusedNodes() {
+        return unusedNodes;
+    }
+
+    /**
+     * the list of unused node names
+     * @param unusedNodes 
+     */
+    public void setUnusedNodes(List<String> unusedNodes) {
+        this.unusedNodes = unusedNodes;
+    }
+    
+    /**
+     * convenient toString method
+     *
+     * @return the informations
+     */
+    @Override
+    public String toString() {
+        return "ShaderGenerationInfo{" + "attributes=" + attributes + ", vertexUniforms=" + vertexUniforms + ", vertexGlobal=" + vertexGlobal + ", varyings=" + varyings + ", fragmentUniforms=" + fragmentUniforms + ", fragmentGlobals=" + fragmentGlobals + '}';
+    }
+
+    
+    
+
+    @Override
+    public void write(JmeExporter ex) throws IOException {
+        OutputCapsule oc = ex.getCapsule(this);
+        oc.writeSavableArrayList((ArrayList) attributes, "attributes", new ArrayList<ShaderNodeVariable>());
+        oc.writeSavableArrayList((ArrayList) vertexUniforms, "vertexUniforms", new ArrayList<ShaderNodeVariable>());
+        oc.writeSavableArrayList((ArrayList) varyings, "varyings", new ArrayList<ShaderNodeVariable>());
+        oc.writeSavableArrayList((ArrayList) fragmentUniforms, "fragmentUniforms", new ArrayList<ShaderNodeVariable>());
+        oc.writeSavableArrayList((ArrayList) fragmentGlobals, "fragmentGlobals", new ArrayList<ShaderNodeVariable>());
+        oc.write(vertexGlobal, "vertexGlobal", null);
+    }
+
+    @Override
+    public void read(JmeImporter im) throws IOException {
+        InputCapsule ic = im.getCapsule(this);
+        attributes = ic.readSavableArrayList("attributes", new ArrayList<ShaderNodeVariable>());
+        vertexUniforms = ic.readSavableArrayList("vertexUniforms", new ArrayList<ShaderNodeVariable>());
+        varyings = ic.readSavableArrayList("varyings", new ArrayList<ShaderNodeVariable>());
+        fragmentUniforms = ic.readSavableArrayList("fragmentUniforms", new ArrayList<ShaderNodeVariable>());
+        fragmentGlobals = ic.readSavableArrayList("fragmentGlobals", new ArrayList<ShaderNodeVariable>());
+        vertexGlobal = (ShaderNodeVariable) ic.readSavable("vertexGlobal", null);
+
+    }
+}

+ 29 - 16
engine/src/core/com/jme3/material/Technique.java

@@ -32,9 +32,11 @@
 package com.jme3.material;
 
 import com.jme3.asset.AssetManager;
+import com.jme3.renderer.Caps;
 import com.jme3.shader.*;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.logging.Logger;
 
@@ -170,7 +172,7 @@ public class Technique /* implements Savable */ {
      * 
      * @param assetManager The asset manager to use for loading shaders.
      */
-    public void makeCurrent(AssetManager assetManager, boolean techniqueSwitched) {
+    public void makeCurrent(AssetManager assetManager, boolean techniqueSwitched, EnumSet<Caps> rendererCaps) {
         if (!def.isUsingShaders()) {
             // No shaders are used, no processing is neccessary. 
             return;
@@ -197,23 +199,23 @@ public class Technique /* implements Savable */ {
         }
 
         if (needReload) {
-            loadShader(assetManager);
+            loadShader(assetManager,rendererCaps);
         }
     }
 
-    private void loadShader(AssetManager manager) {
-        // recompute define list
-        DefineList allDefines = new DefineList();
-        allDefines.addFrom(def.getShaderPresetDefines());
-        allDefines.addFrom(defines);
-
-        ShaderKey key = new ShaderKey(def.getVertexShaderName(),
-                                      def.getFragmentShaderName(),
-                                      allDefines,
-                                      def.getVertexShaderLanguage(),
-                                      def.getFragmentShaderLanguage());
-        shader = manager.loadShader(key);
-
+    private void loadShader(AssetManager manager,EnumSet<Caps> rendererCaps) {
+        
+        if (getDef().isUsingShaderNodes()) {
+            shader = manager.getShaderGenerator(rendererCaps).generateShader(this);
+        } else {
+            ShaderKey key = new ShaderKey(def.getVertexShaderName(),
+                    def.getFragmentShaderName(),
+                    getAllDefines(),
+                    def.getVertexShaderLanguage(),
+                    def.getFragmentShaderLanguage());
+            shader = manager.loadShader(key);
+        
+        }
         // register the world bound uniforms
         worldBindUniforms.clear();
         if (def.getWorldBindings() != null) {
@@ -224,10 +226,21 @@ public class Technique /* implements Savable */ {
                    worldBindUniforms.add(uniform);
                }
            }
-        }
+        }        
         needReload = false;
     }
     
+    /**
+     * Computes the define list
+     * @return the complete define list
+     */
+    public DefineList getAllDefines() {
+        DefineList allDefines = new DefineList();
+        allDefines.addFrom(def.getShaderPresetDefines());
+        allDefines.addFrom(defines);
+        return allDefines;
+    } 
+    
     /*
     public void write(JmeExporter ex) throws IOException {
         OutputCapsule oc = ex.getCapsule(this);

+ 44 - 1
engine/src/core/com/jme3/material/TechniqueDef.java

@@ -35,6 +35,7 @@ import com.jme3.export.*;
 import com.jme3.renderer.Caps;
 import com.jme3.renderer.Renderer;
 import com.jme3.shader.DefineList;
+import com.jme3.shader.ShaderNode;
 import com.jme3.shader.UniformBinding;
 import com.jme3.shader.VarType;
 import java.io.IOException;
@@ -110,6 +111,9 @@ public class TechniqueDef implements Savable {
     
     private DefineList presetDefines;
     private boolean usesShaders;
+    private boolean usesNodes = false;
+    private List<ShaderNode> shaderNodes;
+    private ShaderGenerationInfo shaderGenerationInfo;
 
     private RenderState renderState;
     private RenderState forcedRenderState;
@@ -217,6 +221,16 @@ public class TechniqueDef implements Savable {
     public boolean isUsingShaders(){
         return usesShaders;
     }
+    
+    /**
+     * Returns true if this technique uses Shader Nodes, false otherwise.
+     * 
+     * @return true if this technique uses Shader Nodes, false otherwise.
+     * 
+     */
+    public boolean isUsingShaderNodes(){
+        return usesNodes;
+    }
 
     /**
      * Gets the {@link Caps renderer capabilities} that are required
@@ -408,6 +422,9 @@ public class TechniqueDef implements Savable {
         oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
         oc.write(renderState, "renderState", null);
         oc.write(usesShaders, "usesShaders", false);
+        oc.write(usesNodes, "usesNodes", false);
+        oc.writeSavableArrayList((ArrayList)shaderNodes,"shaderNodes", null);
+        oc.write(shaderGenerationInfo, "shaderGenerationInfo", null);
         
         // TODO: Finish this when Map<String, String> export is available
 //        oc.write(defineParams, "defineParams", null);
@@ -435,6 +452,32 @@ public class TechniqueDef implements Savable {
             vertLanguage = ic.readString("vertLanguage", null);
             fragLanguage = ic.readString("fragLanguage", null);;
         }
+        
+        usesNodes = ic.readBoolean("usesNodes", false);
+        shaderNodes = ic.readSavableArrayList("shaderNodes", null);
+        shaderGenerationInfo = (ShaderGenerationInfo) ic.readSavable("shaderGenerationInfo", null);
     }
-    
+
+    public List<ShaderNode> getShaderNodes() {
+        return shaderNodes;
+    }
+
+    public void setShaderNodes(List<ShaderNode> shaderNodes) {
+        this.shaderNodes = shaderNodes;
+        usesNodes = true;
+        usesShaders = true;
+    }
+
+    public ShaderGenerationInfo getShaderGenerationInfo() {
+        return shaderGenerationInfo;
+    }
+
+    public void setShaderGenerationInfo(ShaderGenerationInfo shaderGenerationInfo) {
+        this.shaderGenerationInfo = shaderGenerationInfo;
+    }
+
+    @Override
+    public String toString() {
+        return "TechniqueDef{" + "requiredCaps=" + requiredCaps + ", name=" + name + ", vertName=" + vertName + ", fragName=" + fragName + ", vertLanguage=" + vertLanguage + ", fragLanguage=" + fragLanguage + ", presetDefines=" + presetDefines + ", usesShaders=" + usesShaders + ", usesNodes=" + usesNodes + ", shaderNodes=" + shaderNodes + ", shaderGenerationInfo=" + shaderGenerationInfo + ", renderState=" + renderState + ", forcedRenderState=" + forcedRenderState + ", lightMode=" + lightMode + ", shadowMode=" + shadowMode + ", defineParams=" + defineParams + ", worldBinds=" + worldBinds + '}';
+    }    
 }

+ 38 - 10
engine/src/core/com/jme3/renderer/queue/GeometryList.java

@@ -33,6 +33,7 @@ package com.jme3.renderer.queue;
 
 import com.jme3.renderer.Camera;
 import com.jme3.scene.Geometry;
+import com.jme3.util.ListSort;
 import com.jme3.util.SortUtil;
 
 /**
@@ -48,9 +49,15 @@ public class GeometryList {
     private static final int DEFAULT_SIZE = 32;
 
     private Geometry[] geometries;
-    private Geometry[] geometries2;
+ //   private Geometry[] geometries2;
+    private ListSort listSort;
     private int size;
     private GeometryComparator comparator;
+    
+    /*static private int count =0;
+    static private int cpt =0;
+    static private long time = 0;
+    */
 
     /**
      * Initializes the GeometryList to use the given {@link GeometryComparator}
@@ -61,8 +68,9 @@ public class GeometryList {
     public GeometryList(GeometryComparator comparator) {
         size = 0;
         geometries = new Geometry[DEFAULT_SIZE];
-        geometries2 = new Geometry[DEFAULT_SIZE];
+   //     geometries2 = new Geometry[DEFAULT_SIZE];
         this.comparator = comparator;
+        listSort = new ListSort<Geometry>();
     }
 
     /**
@@ -115,9 +123,9 @@ public class GeometryList {
             System.arraycopy(geometries, 0, temp, 0, size);
             geometries = temp; // original list replaced by double-size list
             
-            geometries2 = new Geometry[size * 2];
+    //        geometries2 = new Geometry[size * 2];            
         }
-        geometries[size++] = g;
+        geometries[size++] = g;       
     }
 
     /**
@@ -128,7 +136,7 @@ public class GeometryList {
             geometries[i] = null;
         }
 
-        size = 0;
+        size = 0;        
     }
 
     /**
@@ -137,14 +145,34 @@ public class GeometryList {
     public void sort() {
         if (size > 1) {
             // sort the spatial list using the comparator
+//            count++;
+//            long t = System.nanoTime();
             
-//            SortUtil.qsort(geometries, 0, size, comparator);
-//            Arrays.sort(geometries, 0, size, comparator);            
+            if(listSort.getLength() != size){
+                listSort.allocateStack(size);
+            }                       
+            listSort.sort(geometries,comparator);
             
-            System.arraycopy(geometries, 0, geometries2, 0, size);
-            SortUtil.msort(geometries2, geometries, 0, size-1, comparator);
+//            time += System.nanoTime() - t;
             
-
+          
+            
+//            count++;            
+//             long t = System.nanoTime();
+//            System.arraycopy(geometries, 0, geometries2, 0, size);
+//            SortUtil.msort(geometries2, geometries, 0, size-1, comparator);
+//            time += System.nanoTime() - t;
+//            
+//            count++;
+//            long t = System.nanoTime();            
+//            Arrays.sort(geometries,0,size, comparator);
+//            time += System.nanoTime() - t;
         }
+        
+//        if( count>50){
+//            count = 0;
+//            cpt++;
+//            System.err.println(50*cpt+"\t"+time/1000000);
+//        }
     }
 }

+ 572 - 0
engine/src/core/com/jme3/shader/Glsl100ShaderGenerator.java

@@ -0,0 +1,572 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.shader;
+
+import com.jme3.asset.AssetManager;
+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;
+
+/**
+ * This shader Generator can generate Vertex and Fragment shaders from
+ * shadernodes for GLSL 1.0
+ *
+ * @author Nehon
+ */
+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";
+
+    /**
+     * creates a Glsl100ShaderGenerator
+     * @param assetManager the assetManager
+     */
+    public Glsl100ShaderGenerator(AssetManager assetManager) {
+        super(assetManager);
+    }
+
+    @Override
+    protected void generateUniforms(StringBuilder source, ShaderGenerationInfo info, ShaderType type) {
+        generateUniforms(source, type == ShaderType.Vertex ? info.getVertexUniforms() : info.getFragmentUniforms());
+    }
+
+    /**
+     * decalre a list of uniforms
+     *
+     * @param source the source to append to
+     * @param uniforms the list of uniforms
+     */
+    protected void generateUniforms(StringBuilder source, List<ShaderNodeVariable> uniforms) {
+        source.append("\n");
+        for (ShaderNodeVariable var : uniforms) {
+            declareVariable(source, var, false, "uniform");
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * attributes are all declared, inPositon is decalred even if it's not in
+     * the list and it's condition is nulled.
+     */
+    @Override
+    protected void generateAttributes(StringBuilder source, ShaderGenerationInfo info) {
+        source.append("\n");
+        boolean inPosition = false;
+        for (ShaderNodeVariable var : info.getAttributes()) {
+            if (var.getName().equals("inPosition")) {
+                inPosition = true;
+                var.setCondition(null);
+            }
+            declareAttribute(source, var);
+
+        }
+        if (!inPosition) {
+            declareAttribute(source, new ShaderNodeVariable("vec4", "inPosition"));
+        }
+
+    }
+
+    @Override
+    protected void generateVaryings(StringBuilder source, ShaderGenerationInfo info, ShaderType type) {
+        source.append("\n");
+        for (ShaderNodeVariable var : info.getVaryings()) {
+            declareVarying(source, var, type == ShaderType.Vertex ? false : true);
+        }
+    }
+
+    /**
+     * {@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);
+            source.append("\n");
+            unIndent();
+            startCondition(shaderNode.getCondition(), source);
+            source.append(nodeSource);
+            endCondition(shaderNode.getCondition(), source);
+            indent();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Shader outputs are declared and initialized inside the main section
+     */
+    @Override
+    protected void generateStartOfMainSection(StringBuilder source, ShaderGenerationInfo info, ShaderType type) {
+        source.append("\n");
+        source.append("void main(){\n");
+        indent();
+        appendIndent(source);
+        if (type == ShaderType.Vertex) {
+            declareVariable(source, info.getVertexGlobal(), "inPosition");
+        } else if (type == ShaderType.Fragment) {
+            for (ShaderNodeVariable global : info.getFragmentGlobals()) {
+                declareVariable(source, global, "vec4(1.0)");
+            }
+        }
+        source.append("\n");
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * outputs are assigned to built in glsl output. then the main section is
+     * closed
+     *
+     * This code accounts for multi render target and correctly output to
+     * gl_FragData if several output are declared for the fragment shader
+     */
+    @Override
+    protected void generateEndOfMainSection(StringBuilder source, ShaderGenerationInfo info, ShaderType type) {
+        source.append("\n");
+        if (type == ShaderType.Vertex) {
+            appendOutput(source, "gl_Position", info.getVertexGlobal());
+        } else if (type == ShaderType.Fragment) {
+            List<ShaderNodeVariable> globals = info.getFragmentGlobals();
+            if (globals.size() == 1) {
+                appendOutput(source, "gl_FragColor", globals.get(0));
+            } else {
+                int i = 0;
+                //Multi Render Target
+                for (ShaderNodeVariable global : globals) {
+                    appendOutput(source, "gl_FragData[" + i + "]", global);
+                    i++;
+                }
+            }
+        }
+        unIndent();
+        appendIndent(source);
+        source.append("}\n");
+    }
+
+    /**
+     * Appends an ouput assignment to a shader globalOutputName =
+     * nameSpace_varName;
+     *
+     * @param source the source StringBuilter to append the code.
+     * @param globalOutputName the name of the global output (can be gl_Position
+     * or gl_FragColor etc...).
+     * @param var the variable to assign to the output.
+     */
+    protected void appendOutput(StringBuilder source, String globalOutputName, ShaderNodeVariable var) {
+        appendIndent(source);
+        source.append(globalOutputName);
+        source.append(" = ");
+        source.append(var.getNameSpace());
+        source.append("_");
+        source.append(var.getName());
+        source.append(";\n");
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * this methods does things in this order : 
+     * 
+     * 1. declaring and mapping input<br>
+     * variables : variable replaced with MatParams or WorldParams are not
+     * declared and are replaced by the parma acual name in the code. For others
+     * variables, the name space is appended with a "_" before the variable name
+     * in the code to avoid names collision between shaderNodes. <br>
+     * 
+     * 2. declaring output variables : <br>
+     * variables are declared if they were not already
+     * declared as input (inputs can also be outputs) or if they are not
+     * declared as varyings. The variable name is also prefixed with the s=name
+     * space and "_" in the shaderNode code <br>
+     * 
+     * 3. append of the actual ShaderNode code <br>
+     * 
+     * 4. mapping outputs to global output if needed<br>
+     * 
+     *<br>
+     * All of this is embed in a #if coditional statement if needed
+     */
+    @Override
+    protected void generateNodeMainSection(StringBuilder source, ShaderNode shaderNode, String nodeSource, ShaderGenerationInfo info) {
+
+        nodeSource = updateDefinesName(nodeSource, shaderNode);
+        source.append("\n");
+        comment(source, shaderNode, "Begin");
+        startCondition(shaderNode.getCondition(), source);
+
+        List<String> declaredInputs = new ArrayList<String>();
+        for (VariableMapping mapping : shaderNode.getInputMapping()) {
+
+            //all variables fed with a matparam or world param are replaced but the matparam itself
+            //it avoids issue with samplers that have to be uniforms, and it optimize a but the shader code.
+            if (isWorldOrMaterialParam(mapping.getRightVariable())) {
+                nodeSource = replace(nodeSource, mapping.getLeftVariable(), mapping.getRightVariable().getName());
+            } else {
+                if (mapping.getLeftVariable().getType().startsWith("sampler")) {
+                    throw new IllegalArgumentException("a Sampler must be a uniform");
+                }
+                map(mapping, source);
+                String newName = shaderNode.getName() + "_" + mapping.getLeftVariable().getName();
+                if (!declaredInputs.contains(newName)) {
+                    nodeSource = replace(nodeSource, mapping.getLeftVariable(), newName);
+                    declaredInputs.add(newName);
+                }
+            }
+        }
+       
+        for (ShaderNodeVariable var : shaderNode.getDefinition().getOutputs()) {
+            ShaderNodeVariable v = new ShaderNodeVariable(var.getType(), shaderNode.getName(), var.getName());
+            if (!declaredInputs.contains(shaderNode.getName() + "_" + var.getName())) {
+                if (!isVarying(info, v)) {
+                    declareVariable(source, v);
+                }
+                nodeSource = replaceVariableName(nodeSource, v);
+            }
+        }
+        
+        source.append(nodeSource);
+   
+        for (VariableMapping mapping : shaderNode.getOutputMapping()) {
+            map(mapping, source);
+        }
+        endCondition(shaderNode.getCondition(), source);
+        comment(source, shaderNode, "End");
+    }
+
+    /**
+     * declares a variable, embed in a conditional block if needed
+     * @param source the StringBuilder to use
+     * @param var the variable to declare
+     * @param appendNameSpace true to append the nameSpace + "_"
+     */
+    protected void declareVariable(StringBuilder source, ShaderNodeVariable var, boolean appendNameSpace) {
+        declareVariable(source, var, appendNameSpace, null);
+    }
+
+    /**
+     * declares a variable, embed in a conditional block if needed. the namespace is appended with "_"
+     * @param source the StringBuilder to use
+     * @param var the variable to declare    
+     */
+    protected void declareVariable(StringBuilder source, ShaderNodeVariable var) {
+        declareVariable(source, var, true, null);
+    }
+
+     /**
+     * declares a variable, embed in a conditional block if needed. the namespace is appended with "_"
+     * @param source the StringBuilder to use
+     * @param var the variable to declare    
+     * @param value the initialization value to assign the the variable
+     */
+    protected void declareVariable(StringBuilder source, ShaderNodeVariable var, String value) {
+        declareVariable(source, var, value, true, null);
+    }
+
+    /**
+     * declares a variable, embed in a conditional block if needed.
+     * @param source the StringBuilder to use
+     * @param var the variable to declare    
+     * @param appendNameSpace true to append the nameSpace + "_"
+     * @param modifier the modifier of the variable (attribute, varying, in , out,...)
+     */
+    protected void declareVariable(StringBuilder source, ShaderNodeVariable var, boolean appendNameSpace, String modifier) {
+        declareVariable(source, var, null, appendNameSpace, modifier);
+    }
+
+    /**
+     * declares a variable, embed in a conditional block if needed.
+     * @param source the StringBuilder to use
+     * @param var the variable to declare    
+     * @param value the initialization value to assign the the variable
+     * @param appendNameSpace true to append the nameSpace + "_"
+     * @param modifier the modifier of the variable (attribute, varying, in , out,...)
+     */
+    protected void declareVariable(StringBuilder source, ShaderNodeVariable var, String value, boolean appendNameSpace, String modifier) {
+        startCondition(var.getCondition(), source);
+        appendIndent(source);
+        if (modifier != null) {
+            source.append(modifier);
+            source.append(" ");
+        }
+
+        source.append(var.getType());
+        source.append(" ");
+        if (appendNameSpace) {
+            source.append(var.getNameSpace());
+            source.append("_");
+        }
+        source.append(var.getName());
+        if (value != null) {
+            source.append(" = ");
+            source.append(value);
+        }
+        source.append(";\n");
+        endCondition(var.getCondition(), source);
+    }
+
+    /**
+     * Starts a conditional block
+     * @param condition the block condition
+     * @param source the StringBuilder to use
+     */
+    protected void startCondition(String condition, StringBuilder source) {
+        if (condition != null) {
+            appendIndent(source);
+            source.append("#if ");
+            source.append(condition);
+            source.append("\n");
+            indent();
+        }
+    }
+
+    /**
+     * Ends a conditional block
+     * @param condition the block condition
+     * @param source the StringBuilder to use
+     */
+    protected void endCondition(String condition, StringBuilder source) {
+        if (condition != null) {
+            unIndent();
+            appendIndent(source);
+            source.append("#endif\n");
+
+        }
+    }
+
+    /**
+     * Appends a mapping to the source, embed in a conditional block if needed, 
+     * with variables nameSpaces and swizzle.
+     * @param mapping the VariableMapping to append
+     * @param source the StringBuilder to use    
+     */
+    protected void map(VariableMapping mapping, StringBuilder source) {
+        startCondition(mapping.getCondition(), source);
+        appendIndent(source);
+        if (!mapping.getLeftVariable().isShaderOutput()) {
+            source.append(mapping.getLeftVariable().getType());
+            source.append(" ");
+        }
+        source.append(mapping.getLeftVariable().getNameSpace());
+        source.append("_");
+        source.append(mapping.getLeftVariable().getName());
+        if (mapping.getLeftSwizzling().length() > 0) {
+            source.append(".");
+            source.append(mapping.getLeftSwizzling());
+        }
+        source.append(" = ");
+        String namePrefix = getAppendableNameSpace(mapping.getRightVariable());
+        source.append(namePrefix);
+        source.append(mapping.getRightVariable().getName());
+        if (mapping.getRightSwizzling().length() > 0) {
+            source.append(".");
+            source.append(mapping.getRightSwizzling());
+        }
+        source.append(";\n");
+        endCondition(mapping.getCondition(), source);
+    }
+
+    /**
+     * replaces a variable name in a shaderNode source code by prefixing it 
+     * with its nameSpace and "_" if needed.
+     * @param nodeSource the source ot modify
+     * @param var the variable to replace
+     * @return the modified source
+     */
+    protected String replaceVariableName(String nodeSource, ShaderNodeVariable var) {
+        String namePrefix = getAppendableNameSpace(var);
+        String newName = namePrefix + var.getName();
+        nodeSource = replace(nodeSource, var, newName);
+        return nodeSource;
+    }
+
+    /**
+     * Finds if a variable is a varying
+     * @param info the ShaderGenerationInfo
+     * @param v the variable
+     * @return true is the given variable is a varying
+     */
+    protected boolean isVarying(ShaderGenerationInfo info, ShaderNodeVariable v) {
+        boolean isVarying = false;
+        for (ShaderNodeVariable shaderNodeVariable : info.getVaryings()) {
+            if (shaderNodeVariable.equals(v)) {
+                isVarying = true;
+            }
+        }
+        return isVarying;
+    }
+
+    /**
+     * Appends a comment to the generated code
+     * @param source the StringBuilder to use 
+     * @param shaderNode the shader node being processed (to append its name)
+     * @param comment the comment to append
+     */
+    protected void comment(StringBuilder source, ShaderNode shaderNode, String comment) {        
+        appendIndent(source);
+        source.append("//");
+        source.append(shaderNode.getName());
+        source.append(" : ");
+        source.append(comment);
+        source.append("\n");
+    }
+
+    /**
+     * returns the name space to append for a variable. 
+     * Attributes, WorldParam and MatParam names psace must not be appended
+     * @param var the variable
+     * @return the namespace to append for this variable
+     */
+    protected String getAppendableNameSpace(ShaderNodeVariable var) {
+        String namePrefix = var.getNameSpace() + "_";
+        if (namePrefix.equals("Attr_") || namePrefix.equals("WorldParam_") || namePrefix.equals("MatParam_")) {
+            namePrefix = "";
+        }
+        return namePrefix;
+    }
+
+    /**
+     * transforms defines name is the shader node code.
+     * One can use a #if defined(inputVariableName) in a shaderNode code.
+     * This method is responsible for changing the variable name with the 
+     * appropriate defined based on the mapping condition of this variable.
+     * Complex condition synthax are handled.     
+     * 
+     * @param nodeSource the sahderNode source code
+     * @param shaderNode the ShaderNode being processed
+     * @return the modified shaderNode source.
+     */
+    protected String updateDefinesName(String nodeSource, ShaderNode shaderNode) {
+        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();
+                boolean match = false;
+                for (String param : params) {
+                    for (VariableMapping map : shaderNode.getInputMapping()) {
+                        if ((map.getLeftVariable().getName()).equals(param)) {
+                            if (map.getCondition() != null) {
+                                l = l.replaceAll(param, map.getCondition());
+                                match = true;
+                            }
+                        }
+                    }
+                }
+                if (match) {
+                    nodeSource = nodeSource.replace(line.trim(), "#if " + l);
+                }
+            }
+        }
+        return nodeSource;
+    }
+
+    /**
+     * replaced a variable name in a source code with the given name
+     * @param nodeSource the source to use
+     * @param var the variable
+     * @param newName the new name of the variable
+     * @return the modified source code
+     */
+    protected String replace(String nodeSource, ShaderNodeVariable var, String newName) {
+        nodeSource = nodeSource.replaceAll("(\\W)" + var.getName() + "(\\W)", "$1" + newName + "$2");
+        return nodeSource;
+    }
+
+    /**
+     * Finds if a variable is a world or a material parameter
+     * @param var the variable
+     * @return true if the variable is a Word or material parameter
+     */
+    protected boolean isWorldOrMaterialParam(ShaderNodeVariable var) {
+        return var.getNameSpace().equals("MatParam") || var.getNameSpace().equals("WorldParam");
+    }
+
+    @Override
+    protected String getLanguageAndVersion(ShaderType type) {
+        return "GLSL100";
+    }
+
+    /**
+     * appends indentation.
+     * @param source 
+     */
+    protected void appendIndent(StringBuilder source) {
+        source.append(INDENTCHAR.substring(0, indent));
+    }
+
+    /**
+     * Declares an attribute
+     * @param source the StringBuilder to use
+     * @param var the variable to declare as an attribute
+     */    
+    protected void declareAttribute(StringBuilder source, ShaderNodeVariable var) {
+        declareVariable(source, var, false, "attribute");
+    }
+
+    /**
+     * Declares a varying
+     * @param source the StringBuilder to use
+     * @param var the variable to declare as an varying
+     * @param input a boolean set to true if the this varying is an input.
+     * this in not used in this implementaiton but can be used in overidings 
+     * implementation
+     */
+    protected void declareVarying(StringBuilder source, ShaderNodeVariable var, boolean input) {
+        declareVariable(source, var, true, "varying");
+    }
+
+    /**
+     * Decrease indentation with a check so the indent is never negative.
+     */
+    protected void unIndent() {
+        indent--;
+        indent = Math.max(0, indent);
+    }
+
+    /**
+     * increase indentation with a check so that indentation is never over 10
+     */
+    protected void indent() {
+        indent++;
+        indent = Math.min(10, indent);
+    }
+}

+ 146 - 0
engine/src/core/com/jme3/shader/Glsl150ShaderGenerator.java

@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.shader;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.ShaderGenerationInfo;
+import com.jme3.shader.Shader.ShaderType;
+
+/**
+ * This shader Generator can generate Vertex and Fragment shaders from
+ * ShaderNodes for GLSL 1.5
+ *
+ * @author Nehon
+ */
+public class Glsl150ShaderGenerator extends Glsl100ShaderGenerator {
+
+    /**
+     * Creates a Glsl150ShaderGenerator
+     *
+     * @param assetManager the assetmanager
+     */
+    public Glsl150ShaderGenerator(AssetManager assetManager) {
+        super(assetManager);
+    }
+
+    @Override
+    protected String getLanguageAndVersion(ShaderType type) {
+        return "GLSL150";
+    }
+
+    /**
+     * {@inheritDoc} in glsl 1.5 attributes are prefixed with the "in" keyword
+     * and not the "attribute" keyword
+     */
+    @Override
+    protected void declareAttribute(StringBuilder source, ShaderNodeVariable var) {
+        declareVariable(source, var, false, "in");
+    }
+
+    /**
+     * {@inheritDoc} in glsl 1.5 varying are prefixed with the "in" or "out"
+     * keyword and not the "varying" keyword.
+     *
+     * "in" is used for Fragment shader (maybe Geometry shader later) "out" is
+     * used for Vertex shader (maybe Geometry shader later)
+     */
+    @Override
+    protected void declareVarying(StringBuilder source, ShaderNodeVariable var, boolean input) {
+        declareVariable(source, var, true, input ? "in" : "out");
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Fragment shader outputs are declared before the "void main(){" with the
+     * "out" keyword.
+     *
+     * after the "void main(){", the vertex output are declared and initialized
+     * and the frgament outputs are declared
+     */
+    @Override
+    protected void generateStartOfMainSection(StringBuilder source, ShaderGenerationInfo info, Shader.ShaderType type) {
+        source.append("\n");
+
+        if (type == Shader.ShaderType.Fragment) {
+            for (ShaderNodeVariable global : info.getFragmentGlobals()) {
+                declareVariable(source, global, null, true, "out");
+            }
+        }
+        source.append("\n");
+
+        appendIndent(source);
+        source.append("void main(){\n");
+        indent();
+
+        if (type == Shader.ShaderType.Vertex) {
+            declareVariable(source, info.getVertexGlobal(), "inPosition");
+        } else if (type == Shader.ShaderType.Fragment) {
+            for (ShaderNodeVariable global : info.getFragmentGlobals()) {
+                initVariable(source, global, "vec4(1.0)");
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * only vertex shader output are mapped here, since fragment shader outputs
+     * must have been mapped in the main section.
+     */
+    @Override
+    protected void generateEndOfMainSection(StringBuilder source, ShaderGenerationInfo info, Shader.ShaderType type) {
+        if (type == Shader.ShaderType.Vertex) {
+            appendOutput(source, "gl_Position", info.getVertexGlobal());
+        }
+        unIndent();
+        appendIndent(source);
+        source.append("}\n");
+    }
+
+    /**
+     * Append a variable initialization to the code
+     *
+     * @param source the StringBuilder to use
+     * @param var the variable to initialize
+     * @param initValue the init value to assign to the variable
+     */
+    protected void initVariable(StringBuilder source, ShaderNodeVariable var, String initValue) {
+        appendIndent(source);
+        source.append(var.getNameSpace());
+        source.append("_");
+        source.append(var.getName());
+        source.append(" = ");
+        source.append(initValue);
+        source.append(";\n");
+    }
+}

+ 290 - 0
engine/src/core/com/jme3/shader/ShaderGenerator.java

@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.shader;
+
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetManager;
+import com.jme3.material.ShaderGenerationInfo;
+import com.jme3.material.Technique;
+import com.jme3.material.TechniqueDef;
+import com.jme3.shader.Shader.ShaderType;
+import java.util.List;
+
+/**
+ * This class is the base for a shader generator using the ShaderNodes system,
+ * it contains basis mechnaism of generation, but no actual generation code.
+ * This class is abstract, any Shader generator must extend it.
+ *
+ * @author Nehon
+ */
+public abstract class ShaderGenerator {
+
+    //the asset manager
+    protected AssetManager assetManager;
+    //indentation value for generation
+    protected int indent;
+
+    /**
+     * Build a shaderGenerator
+     *
+     * @param assetManager
+     */
+    protected ShaderGenerator(AssetManager assetManager) {
+        this.assetManager = assetManager;
+    }
+
+    /**
+     * Generate vertex and fragment shaders for the given technique
+     *
+     * @param technique the technique to use to generate the shaders
+     * @return a Shader program
+     */
+    public Shader generateShader(Technique technique) {
+
+        DefineList defines = technique.getAllDefines();
+        TechniqueDef def = technique.getDef();
+        ShaderGenerationInfo info = def.getShaderGenerationInfo();
+
+        String vertexSource = buildShader(def.getShaderNodes(), info, ShaderType.Vertex);
+        String fragmentSource = buildShader(def.getShaderNodes(), info, ShaderType.Fragment);
+
+        Shader shader = new Shader();
+        shader.initialize();
+        shader.addSource(Shader.ShaderType.Vertex, technique.getDef().getName() + ".vert", vertexSource, defines.getCompiled(), getLanguageAndVersion(ShaderType.Vertex));
+        shader.addSource(Shader.ShaderType.Fragment, technique.getDef().getName() + ".frag", fragmentSource, defines.getCompiled(), getLanguageAndVersion(ShaderType.Fragment));
+
+        return shader;
+    }
+
+    /**
+     * This method is responsible for the shader generation.
+     *
+     * @param shaderNodes the list of shader nodes
+     * @param info the ShaderGenerationInfo filled during the Technique loading
+     * @param type the type of shader to generate
+     * @return the code of the generated vertex shader
+     */
+    protected String buildShader(List<ShaderNode> shaderNodes, ShaderGenerationInfo info, ShaderType type) {
+        indent = 0;
+
+        StringBuilder sourceDeclaration = new StringBuilder();
+        StringBuilder source = new StringBuilder();
+
+        generateUniforms(sourceDeclaration, info, type);
+
+        if (type == ShaderType.Vertex) {
+            generateAttributes(sourceDeclaration, info);
+        }
+        generateVaryings(sourceDeclaration, info, type);
+
+        generateStartOfMainSection(source, info, type);
+
+        generateDeclarationAndMainBody(shaderNodes, sourceDeclaration, source, info, type);
+
+        generateEndOfMainSection(source, info, type);
+
+        sourceDeclaration.append(source);
+
+        return sourceDeclaration.toString();
+    }
+
+    /**
+     * iterates through shader nodes to load them and generate the shader
+     * declaration part and main body extracted from the shader nodes, for the
+     * given shader type
+     *
+     * @param shaderNodes the list of shader nodes
+     * @param sourceDeclaration the declaration part StringBuilder of the shader
+     * to generate
+     * @param source the main part StringBuilder of the shader to generate
+     * @param info the ShaderGenerationInfo
+     * @param type the Shader type
+     */
+    protected void generateDeclarationAndMainBody(List<ShaderNode> shaderNodes, StringBuilder sourceDeclaration, StringBuilder source, ShaderGenerationInfo info, Shader.ShaderType type) {
+        for (ShaderNode shaderNode : shaderNodes) {
+            if (info.getUnusedNodes().contains(shaderNode.getName())) {
+                continue;
+            }
+            if (shaderNode.getDefinition().getType() == type) {
+                int index = findShaderIndexFromVersion(shaderNode, type);
+                String loadedSource = (String) assetManager.loadAsset(new AssetKey(shaderNode.getDefinition().getShadersPath().get(index)));
+                appendNodeDeclarationAndMain(loadedSource, sourceDeclaration, source, shaderNode, info);
+            }
+        }
+    }
+
+    /**
+     * 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 sourceDeclaration the Shader declaration part string builder.
+     * @param source the Shader main part StringBuilder.
+     * @param shaderNode the shader node.
+     * @param info the ShaderGenerationInfo.
+     */
+    protected void appendNodeDeclarationAndMain(String loadedSource, StringBuilder sourceDeclaration, StringBuilder source, ShaderNode shaderNode, ShaderGenerationInfo info) {
+        if (loadedSource.length() > 1) {
+            loadedSource = loadedSource.substring(0, loadedSource.lastIndexOf("}"));
+            String[] sourceParts = loadedSource.split("void main\\(\\)\\{");
+            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);
+        }
+
+    }
+
+    /**
+     * returns the laguage + version of the shader should be somthing like
+     * "GLSL100" for glsl 1.0 "GLSL150" for glsl 1.5.
+     *
+     * @param type the shader type for wich the version should be returned.
+     *
+     * @return the shaderLanguage and version.
+     */
+    protected abstract String getLanguageAndVersion(Shader.ShaderType type);
+
+    /**
+     * generates the uniforms declaration for a shader of the given type.
+     *
+     * @param source the source StringBuilder to append generated code.
+     * @param info the ShaderGenerationInfo.
+     * @param type the shader type the uniforms have to be generated for.
+     */
+    protected abstract void generateUniforms(StringBuilder source, ShaderGenerationInfo info, ShaderType type);
+
+    /**
+     * generates the attributes declaration for the vertex shader. There is no
+     * Shader type passed here as attributes are only used in vertex shaders
+     *
+     * @param source the source StringBuilder to append generated code.
+     * @param info the ShaderGenerationInfo.
+     */
+    protected abstract void generateAttributes(StringBuilder source, ShaderGenerationInfo info);
+
+    /**
+     * generates the varyings for the given shader type shader. Note that
+     * varyings are deprecated in glsl 1.3, but this method will still be called
+     * to generate all non global inputs and output of the shaders.
+     *
+     * @param source the source StringBuilder to append generated code.
+     * @param info the ShaderGenerationInfo.
+     * @param type the shader type the varyings have to be generated for.
+     */
+    protected abstract void generateVaryings(StringBuilder source, ShaderGenerationInfo info, ShaderType type);
+
+    /**
+     * Appends the given shaderNode declarative part to the shader declarative
+     * part. If needed the sahder type can be determined by fetching the
+     * shaderNode's definition type.
+     *
+     * @see ShaderNode#getDefinition()
+     * @see ShaderNodeDefinition#getType()
+     *
+     * @param source the StringBuilder to append generated code.
+     * @param shaderNode the shaderNode.
+     * @param nodeSource the declaration part of the loaded shaderNode source.
+     * @param info the ShaderGenerationInfo.
+     */
+    protected abstract void generateDeclarativeSection(StringBuilder source, ShaderNode shaderNode, String nodeDecalarationSource, ShaderGenerationInfo info);
+
+    /**
+     * generates the start of the shader main section. this method is
+     * responsible of appending the "void main(){" in the shader and declaring
+     * all global outputs of the shader
+     *
+     * @param source the StringBuilder to append generated code.
+     * @param info the ShaderGenerationInfo.
+     * @param type the shader type the section has to be generated for.
+     */
+    protected abstract void generateStartOfMainSection(StringBuilder source, ShaderGenerationInfo info, ShaderType type);
+
+    /**
+     * generates the end of the shader main section. this method is responsible
+     * of appending the last "}" in the shader and mapping all global outputs of
+     * the shader
+     *
+     * @param source the StringBuilder to append generated code.
+     * @param info the ShaderGenerationInfo.
+     * @param type the shader type the section has to be generated for.
+     */
+    protected abstract void generateEndOfMainSection(StringBuilder source, ShaderGenerationInfo info, ShaderType type);
+
+    /**
+     * Appends the given shaderNode main part to the shader declarative part. If
+     * needed the sahder type can be determined by fetching the shaderNode's
+     * definition type.
+     *
+     * @see ShaderNode#getDefinition()
+     * @see ShaderNodeDefinition#getType()
+     *
+     * @param source the StringBuilder to append generated code.
+     * @param shaderNode the shaderNode.
+     * @param nodeSource the declaration part of the loaded shaderNode source.
+     * @param info the ShaderGenerationInfo.
+     */
+    protected abstract void generateNodeMainSection(StringBuilder source, ShaderNode shaderNode, String nodeSource, ShaderGenerationInfo info);
+
+    /**
+     * returns the shaderpath index according to the version of the generator.
+     * This allow to select the higher version of the shader that the generator
+     * can handle
+     *
+     * @param shaderNode the shaderNode being processed
+     * @param type the shaderType
+     * @return the index of the shader path in ShaderNodeDefinition shadersPath
+     * list
+     * @throws NumberFormatException
+     */
+    protected int findShaderIndexFromVersion(ShaderNode shaderNode, ShaderType type) throws NumberFormatException {
+        int index = 0;
+        List<String> lang = shaderNode.getDefinition().getShadersLanguage();
+        int genVersion = Integer.parseInt(getLanguageAndVersion(type).substring(4));
+        int curVersion = 0;
+        for (int i = 0; i < lang.size(); i++) {
+            int version = Integer.parseInt(lang.get(i).substring(4));
+            if (version > curVersion && version <= genVersion) {
+                curVersion = version;
+                index = i;
+            }
+        }
+        return index;
+    }
+}

+ 215 - 0
engine/src/core/com/jme3/shader/ShaderNode.java

@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.shader;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A ShaderNode is the unit brick part of a shader program. A shader can be
+ * describe with several shader nodes that are plugged together through inputs
+ * and outputs.
+ *
+ * A ShaderNode is based on a definition that has a shader code, inputs and
+ * output variables. This node can be activated based on a condition, and has
+ * input and ouput mapping.
+ *
+ * This class is not intended to be used by JME users directly. It's the
+ * stucture for loading shader nodes from a J3md ùaterial definition file
+ *
+ * @author Nehon
+ */
+public class ShaderNode implements Savable {
+
+    private String name;
+    private ShaderNodeDefinition definition;
+    private String condition;
+    private List<VariableMapping> inputMapping = new ArrayList<VariableMapping>();
+    private List<VariableMapping> outputMapping = new ArrayList<VariableMapping>();
+
+    /**
+     * creates a ShaderNode
+     *
+     * @param name the name
+     * @param definition the ShaderNodeDefinition
+     * @param condition the conditionto activate this node
+     */
+    public ShaderNode(String name, ShaderNodeDefinition definition, String condition) {
+        this.name = name;
+        this.definition = definition;
+        this.condition = condition;
+    }
+
+    /**
+     * creates a ShaderNode
+     */
+    public ShaderNode() {
+    }
+
+    /**
+     *
+     * @return the name of the node
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * sets the name of th node
+     *
+     * @param name the name
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * returns the definition
+     *
+     * @return the ShaderNodeDefinition
+     */
+    public ShaderNodeDefinition getDefinition() {
+        return definition;
+    }
+
+    /**
+     * sets the definition
+     *
+     * @param definition the ShaderNodeDefinition
+     */
+    public void setDefinition(ShaderNodeDefinition definition) {
+        this.definition = definition;
+    }
+
+    /**
+     *
+     * @return the condition
+     */
+    public String getCondition() {
+        return condition;
+    }
+
+    /**
+     * sets the ocndition
+     *
+     * @param condition the condition
+     */
+    public void setCondition(String condition) {
+        this.condition = condition;
+    }
+
+    /**
+     * return a list of VariableMapping representing the input mappings of this
+     * node
+     *
+     * @return the input mappings
+     */
+    public List<VariableMapping> getInputMapping() {
+        return inputMapping;
+    }
+
+    /**
+     * sets the input mappings
+     *
+     * @param inputMapping the input mappings
+     */
+    public void setInputMapping(List<VariableMapping> inputMapping) {
+        this.inputMapping = inputMapping;
+    }
+
+    /**
+     * return a list of VariableMapping representing the output mappings of this
+     * node
+     *
+     * @return the output mappings
+     */
+    public List<VariableMapping> getOutputMapping() {
+        return outputMapping;
+    }
+
+    /**
+     * sets the output mappings
+     *
+     * @param inputMapping the output mappings
+     */
+    public void setOutputMapping(List<VariableMapping> outputMapping) {
+        this.outputMapping = outputMapping;
+    }
+
+    /**
+     * jme seralization
+     *
+     * @param ex the exporter
+     * @throws IOException
+     */
+    @Override
+    public void write(JmeExporter ex) throws IOException {
+        OutputCapsule oc = (OutputCapsule) ex.getCapsule(this);
+        oc.write(name, "name", "");
+        oc.write(definition, "definition", null);
+        oc.write(condition, "condition", null);
+        oc.writeSavableArrayList((ArrayList) inputMapping, "inputMapping", new ArrayList<VariableMapping>());
+        oc.writeSavableArrayList((ArrayList) outputMapping, "outputMapping", new ArrayList<VariableMapping>());
+    }
+
+    /**
+     * jme seralization 
+     *
+     * @param im the importer
+     * @throws IOException
+     */
+    @Override
+    public void read(JmeImporter im) throws IOException {
+        InputCapsule ic = (InputCapsule) im.getCapsule(this);
+        name = ic.readString("name", "");
+        definition = (ShaderNodeDefinition) ic.readSavable("definition", null);
+        condition = ic.readString("condition", null);
+        inputMapping = (List<VariableMapping>) ic.readSavableArrayList("inputMapping", new ArrayList<VariableMapping>());
+        outputMapping = (List<VariableMapping>) ic.readSavableArrayList("outputMapping", new ArrayList<VariableMapping>());
+    }
+
+    /**
+     * convenience tostring
+     *
+     * @return a string
+     */
+    @Override
+    public String toString() {
+        return "\nShaderNode{" + "\nname=" + name + ", \ndefinition=" + definition.getName() + ", \ncondition=" + condition + ", \ninputMapping=" + inputMapping + ", \noutputMapping=" + outputMapping + '}';
+    }
+}

+ 253 - 0
engine/src/core/com/jme3/shader/ShaderNodeDefinition.java

@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.shader;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.shader.Shader.ShaderType;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Shader node definition structure meant for holding loaded datat from a
+ * material definition j3md file
+ *
+ * @author Nehon
+ */
+public class ShaderNodeDefinition implements Savable {
+
+    private String name;
+    private Shader.ShaderType type;
+    private List<String> shadersLanguage = new ArrayList<String>();
+    private List<String> shadersPath = new ArrayList<String>();
+    private String documentation;
+    private List<ShaderNodeVariable> inputs = new ArrayList<ShaderNodeVariable>();
+    private List<ShaderNodeVariable> outputs = new ArrayList<ShaderNodeVariable>();
+    private String path = null;
+
+    /**
+     * creates a ShaderNodeDefinition
+     *
+     * @param name the name of the definition
+     * @param type the type of the shader
+     * @param shaderPath the path of the shader
+     * @param shaderLanguage the shader language (minimum required for this
+     * definition)
+     */
+    public ShaderNodeDefinition(String name, ShaderType type, String shaderPath, String shaderLanguage) {
+        this.name = name;
+        this.type = type;
+        shadersLanguage.add(shaderLanguage);
+        shadersPath.add(shaderPath);
+    }
+
+    /**
+     * creates a ShaderNodeDefinition
+     */
+    public ShaderNodeDefinition() {
+    }
+
+    /**
+     * returns the name of the definition
+     *
+     * @return the name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * sets the name of the definition
+     *
+     * @param name the name
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     *
+     * @return the type of shader the definition applies to
+     */
+    public ShaderType getType() {
+        return type;
+    }
+
+    /**
+     * sets the type of shader this def applies to
+     *
+     * @param type the type
+     */
+    public void setType(ShaderType type) {
+        this.type = type;
+    }
+
+    /**
+     *
+     * @return the docuentation for tthis definition
+     */
+    public String getDocumentation() {
+        return documentation;
+    }
+
+    /**
+     * sets the dcumentation
+     *
+     * @param documentation the documentation
+     */
+    public void setDocumentation(String documentation) {
+        this.documentation = documentation;
+    }
+
+    /**
+     *
+     * @return the input variables of this definition
+     */
+    public List<ShaderNodeVariable> getInputs() {
+        return inputs;
+    }
+
+    /**
+     * sets the input variables of this definition
+     *
+     * @param inputs the inputs
+     */
+    public void setInputs(List<ShaderNodeVariable> inputs) {
+        this.inputs = inputs;
+    }
+
+    /**
+     *
+     * @return the output variables of this definition
+     */
+    public List<ShaderNodeVariable> getOutputs() {
+        return outputs;
+    }
+
+    /**
+     * sets the output variables of this definition
+     *
+     * @param inputs the output
+     */
+    public void setOutputs(List<ShaderNodeVariable> outputs) {
+        this.outputs = outputs;
+    }
+
+    /**
+     * retrun the path of this definition
+     * @return 
+     */
+    public String getPath() {
+        return path;
+    }
+
+    /**
+     * sets the path of this definition
+     * @param path 
+     */
+    public void setPath(String path) {
+        this.path = path;
+    }
+    
+    
+
+    /**
+     * jme seralization (not used)
+     *
+     * @param ex the exporter
+     * @throws IOException
+     */
+    public void write(JmeExporter ex) throws IOException {
+        OutputCapsule oc = (OutputCapsule) 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>());
+    }
+
+    public List<String> getShadersLanguage() {
+        return shadersLanguage;
+    }
+
+    public List<String> getShadersPath() {
+        return shadersPath;
+    }
+
+    
+    
+    /**
+     * jme seralization (not used)
+     *
+     * @param im the importer
+     * @throws IOException
+     */
+    public void read(JmeImporter im) throws IOException {
+        InputCapsule ic = (InputCapsule) im.getCapsule(this);
+        name = ic.readString("name", "");
+
+        String[] str = ic.readStringArray("shadersLanguage", null);
+        if (str != null) {
+            shadersLanguage = Arrays.asList(str);
+        } else {
+            shadersLanguage = new ArrayList<String>();
+        }
+
+        str = ic.readStringArray("shadersPath", null);
+        if (str != null) {
+            shadersPath = Arrays.asList(str);
+        } else {
+            shadersPath = new ArrayList<String>();
+        }
+
+        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>());
+    }
+
+    /**
+     * convenience tostring
+     *
+     * @return a string
+     */
+    @Override
+    public String toString() {
+        return "\nShaderNodeDefinition{\n" + "name=" + name + "\ntype=" + type + "\nshaderPath=" + shadersPath + "\nshaderLanguage=" + shadersLanguage + "\ndocumentation=" + documentation + "\ninputs=" + inputs + ",\noutputs=" + outputs + '}';
+    }
+}

+ 234 - 0
engine/src/core/com/jme3/shader/ShaderNodeVariable.java

@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.shader;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import java.io.IOException;
+
+/**
+ * A shader node variable
+ *
+ * @author Nehon
+ */
+public class ShaderNodeVariable implements Savable, Cloneable {
+
+    private String name;
+    private String type;
+    private String nameSpace;
+    private String condition;
+    private boolean shaderOutput = false;
+
+    /**
+     * creates a ShaderNodeVariable
+     *
+     * @param type the glsl type of the variable
+     * @param name the name of the variable
+     */
+    public ShaderNodeVariable(String type, String name) {
+        this.name = name;
+        this.type = type;
+    }
+
+    /**
+     * creates a ShaderNodeVariable
+     *
+     * @param type the glsl type of the variable
+     * @param nameSpace the nameSpace (can be the name of the shaderNode or
+     * Globel,Attr,MatParam,WorldParam)
+     * @param name the name of the variable
+     */
+    public ShaderNodeVariable(String type, String nameSpace, String name) {
+        this.name = name;
+        this.nameSpace = nameSpace;
+        this.type = type;
+    }
+
+    /**
+     * returns the name
+     *
+     * @return the name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * sets the name
+     *
+     * @param name the name
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     *
+     * @return the glsl type
+     */
+    public String getType() {
+        return type;
+    }
+
+    /**
+     * sets the glsl type
+     *
+     * @param type the type
+     */
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    /**
+     *
+     * @return the name space (can be the name of the shaderNode or
+     * Globel,Attr,MatParam,WorldParam)
+     */
+    public String getNameSpace() {
+        return nameSpace;
+    }
+
+    /**
+     * sets the nameSpace (can be the name of the shaderNode or
+     * Globel,Attr,MatParam,WorldParam)
+     *
+     * @param nameSpace
+     */
+    public void setNameSpace(String nameSpace) {
+        this.nameSpace = nameSpace;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 5;
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final ShaderNodeVariable other = (ShaderNodeVariable) obj;
+        if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
+            return false;
+        }
+        if ((this.type == null) ? (other.type != null) : !this.type.equals(other.type)) {
+            return false;
+        }
+        if ((this.nameSpace == null) ? (other.nameSpace != null) : !this.nameSpace.equals(other.nameSpace)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * jme seralization (not used)
+     *
+     * @param ex the exporter
+     * @throws IOException
+     */
+    public void write(JmeExporter ex) throws IOException {
+        OutputCapsule oc = (OutputCapsule) ex.getCapsule(this);
+        oc.write(name, "name", "");
+        oc.write(type, "type", "");
+        oc.write(nameSpace, "nameSpace", "");
+        oc.write(condition, "condition", null);
+        oc.write(shaderOutput, "shaderOutput", false);
+
+    }
+
+    /**
+     * jme seralization (not used)
+     *
+     * @param im the importer
+     * @throws IOException
+     */
+    public void read(JmeImporter im) throws IOException {
+        InputCapsule ic = (InputCapsule) im.getCapsule(this);
+        name = ic.readString("name", "");
+        type = ic.readString("type", "");
+        nameSpace = ic.readString("nameSpace", "");
+        condition = ic.readString("condition", null);
+        shaderOutput = ic.readBoolean("shaderOutput", false);
+    }
+
+    /**
+     *
+     * @return the condition for this variable to be declared
+     */
+    public String getCondition() {
+        return condition;
+    }
+
+    /**
+     * sets the condition
+     *
+     * @param condition the condition
+     */
+    public void setCondition(String condition) {
+        this.condition = condition;
+    }
+
+    @Override
+    public String toString() {
+        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
+     */
+    public boolean isShaderOutput() {
+        return shaderOutput;
+    }
+
+    /**
+     * sets to true if this variable is a shader output
+     *
+     * @param shaderOutput true if this variable is a shader output
+     */
+    public void setShaderOutput(boolean shaderOutput) {
+        this.shaderOutput = shaderOutput;
+    }
+}

+ 70 - 3
engine/src/core/com/jme3/shader/ShaderUtils.java

@@ -33,12 +33,12 @@ package com.jme3.shader;
 
 public class ShaderUtils {
 
-    public static String convertToGLSL130(String input, boolean isFrag){
+    public static String convertToGLSL130(String input, boolean isFrag) {
         StringBuilder sb = new StringBuilder();
         sb.append("#version 130\n");
-        if (isFrag){
+        if (isFrag) {
             input = input.replaceAll("varying", "in");
-        }else{
+        } else {
             input = input.replaceAll("attribute", "in");
             input = input.replaceAll("varying", "out");
         }
@@ -46,4 +46,71 @@ public class ShaderUtils {
         return sb.toString();
     }
 
+    /**
+     * Check if a mapping is valid by checking the types and swizzle of both of
+     * the variables
+     *
+     * @param mapping the mapping
+     * @return true if this mapping is valid
+     */
+    public static boolean typesMatch(VariableMapping mapping) {
+        String leftType = mapping.getLeftVariable().getType();
+        String rightType = mapping.getRightVariable().getType();
+        String leftSwizzling = mapping.getLeftSwizzling();
+        String rightSwizzling = mapping.getRightSwizzling();
+
+        //types match : no error
+        if (leftType.equals(rightType) && leftSwizzling.length() == rightSwizzling.length()) {
+            return true;
+        }
+        if (isSwizzlable(leftType) && isSwizzlable(rightType)) {
+            if (getCardinality(leftType, leftSwizzling) == getCardinality(rightType, rightSwizzling)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * return the cardinality of a type and a swizzle example : vec4 cardinality
+     * is 4 float cardinality is 1 vec4.xyz cardinality is 3. sampler2D
+     * cardinality is 0
+     *
+     * @param type the glsl type
+     * @param swizzling the swizzling of a variable
+     * @return the cardinality
+     */
+    public static int getCardinality(String type, String swizzling) {
+        int card = 0;
+        if (isSwizzlable(type)) {
+            if (type.equals("float")) {
+                card = 1;
+                if (swizzling.length() != 0) {
+                    card = 0;
+                }
+            } else {
+                card = Integer.parseInt(type.replaceAll("vec", ""));
+
+                if (swizzling.length() > 0) {
+                    if (card >= swizzling.length()) {
+                        card = swizzling.length();
+                    } else {
+                        card = 0;
+                    }
+                }
+            }
+        }
+        return card;
+    }
+
+    /**
+     * returns true if a variable of the given type can have a swizzle
+     *
+     * @param type the glsl type
+     * @return true if a variable of the given type can have a swizzle
+     */
+    public static boolean isSwizzlable(String type) {
+        return type.equals("vec4") || type.equals("vec3") || type.equals("vec2") || type.equals("float");
+    }
 }

+ 42 - 27
engine/src/core/com/jme3/shader/UniformBinding.java

@@ -37,63 +37,63 @@ public enum UniformBinding {
      * The world matrix. Converts Model space to World space.
      * Type: mat4
      */
-    WorldMatrix,
+    WorldMatrix("mat4"),
 
     /**
      * The view matrix. Converts World space to View space.
      * Type: mat4
      */
-    ViewMatrix,
+    ViewMatrix("mat4"),
 
     /**
      * The projection matrix. Converts View space to Clip/Projection space.
      * Type: mat4
      */
-    ProjectionMatrix,
+    ProjectionMatrix("mat4"),
 
     /**
      * The world view matrix. Converts Model space to View space.
      * Type: mat4
      */
-    WorldViewMatrix,
+    WorldViewMatrix("mat4"),
 
     /**
      * The normal matrix. The inverse transpose of the worldview matrix.
      * Converts normals from model space to view space.
      * Type: mat3
      */
-    NormalMatrix,
+    NormalMatrix("mat3"),
 
     /**
      * The world view projection matrix. Converts Model space to Clip/Projection
      * space.
      * Type: mat4
      */
-    WorldViewProjectionMatrix,
+    WorldViewProjectionMatrix("mat4"),
 
     /**
      * The view projection matrix. Converts World space to Clip/Projection
      * space.
      * Type: mat4
      */
-    ViewProjectionMatrix,
+    ViewProjectionMatrix("mat4"),
 
     /**
      * The world matrix inverse transpose. Converts a normals from Model space
      * to world space.
      * Type: mat3
      */
-    WorldMatrixInverseTranspose,      
+    WorldMatrixInverseTranspose("mat3"),      
 
 
 
-    WorldMatrixInverse,
-    ViewMatrixInverse,
-    ProjectionMatrixInverse,
-    ViewProjectionMatrixInverse,
-    WorldViewMatrixInverse,
-    NormalMatrixInverse,
-    WorldViewProjectionMatrixInverse,
+    WorldMatrixInverse("mat4"),
+    ViewMatrixInverse("mat4"),
+    ProjectionMatrixInverse("mat4"),
+    ViewProjectionMatrixInverse("mat4"),
+    WorldViewMatrixInverse("mat4"),
+    NormalMatrixInverse("mat3"),
+    WorldViewProjectionMatrixInverse("mat4"),
 
     /**
      * Contains the four viewport parameters in this order:
@@ -103,7 +103,7 @@ public enum UniformBinding {
      * W = Bottom.
      * Type: vec4
      */
-    ViewPort,
+    ViewPort("vec4"),
 
     /**
      * The near and far values for the camera frustum.
@@ -111,65 +111,80 @@ public enum UniformBinding {
      * Y = Far.
      * Type: vec2
      */
-    FrustumNearFar,
+    FrustumNearFar("vec2"),
     
     /**
      * The width and height of the camera.
      * Type: vec2
      */
-    Resolution,
+    Resolution("vec2"),
     
     /**
      * The inverse of the resolution, 1/width and 1/height. 
      * Type: vec2
      */
-    ResolutionInverse,
+    ResolutionInverse("vec2"),
 
     /**
      * Aspect ratio of the resolution currently set. Width/Height.
      * Type: float
      */
-    Aspect,
+    Aspect("float"),
 
     /**
      * Camera position in world space.
      * Type: vec3
      */
-    CameraPosition,
+    CameraPosition("vec3"),
 
     /**
      * Direction of the camera.
      * Type: vec3
      */
-    CameraDirection,
+    CameraDirection("vec3"),
 
     /**
      * Left vector of the camera.
      * Type: vec3
      */
-    CameraLeft,
+    CameraLeft("vec3"),
 
     /**
      * Up vector of the camera.
      * Type: vec3
      */
-    CameraUp,
+    CameraUp("vec3"),
 
     /**
      * Time in seconds since the application was started.
      * Type: float
      */
-    Time,
+    Time("float"),
 
     /**
      * Time in seconds that the last frame took.
      * Type: float
      */
-    Tpf,
+    Tpf("float"),
 
     /**
      * Frames per second.
      * Type: float
      */
-    FrameRate,
+    FrameRate("float");
+    
+    String glslType;
+
+    private UniformBinding() {
+    }
+
+    private UniformBinding(String glslType) {
+        this.glslType = glslType;
+    }
+
+    public String getGlslType() {
+        return glslType;
+    }
+    
+    
 }

+ 30 - 22
engine/src/core/com/jme3/shader/VarType.java

@@ -33,40 +33,44 @@ package com.jme3.shader;
 
 public enum VarType {
 
-    Float,
-    Vector2,
-    Vector3,
-    Vector4,
+    Float("float"),
+    Vector2("vec2"),
+    Vector3("vec3"),
+    Vector4("vec4"),
 
-    FloatArray(true,false),
-    Vector2Array(true,false),
-    Vector3Array(true,false),
-    Vector4Array(true,false),
+    FloatArray(true,false,"float[]"),
+    Vector2Array(true,false,"vec2[]"),
+    Vector3Array(true,false,"vec3[]"),
+    Vector4Array(true,false,"vec4[]"),
 
-    Boolean,
+    Boolean("bool"),
 
-    Matrix3(true,false),
-    Matrix4(true,false),
+    Matrix3(true,false,"mat3"),
+    Matrix4(true,false,"mat4"),
 
-    Matrix3Array(true,false),
-    Matrix4Array(true,false),
-
-    TextureBuffer(false,true),
-    Texture2D(false,true),
-    Texture3D(false,true),
-    TextureArray(false,true),
-    TextureCubeMap(false,true),
-    Int;
+    Matrix3Array(true,false,"mat3[]"),
+    Matrix4Array(true,false,"mat4[]"),
+    
+    TextureBuffer(false,true,"sampler1D|sampler1DShadow"),
+    Texture2D(false,true,"sampler2D|sampler2DShadow"),
+    Texture3D(false,true,"sampler3D"),
+    TextureArray(false,true,"sampler2DArray"),
+    TextureCubeMap(false,true,"samplerCube"),
+    Int("int");
 
     private boolean usesMultiData = false;
     private boolean textureType = false;
+    private String glslType;
 
-    VarType(){
+    
+    VarType(String glslType){
+        this.glslType = glslType;
     }
 
-    VarType(boolean multiData, boolean textureType){
+    VarType(boolean multiData, boolean textureType,String glslType){
         usesMultiData = multiData;
         this.textureType = textureType;
+        this.glslType = glslType;
     }
 
     public boolean isTextureType() {
@@ -77,4 +81,8 @@ public enum VarType {
         return usesMultiData;
     }
 
+    public String getGlslType() {
+        return glslType;
+    }    
+
 }

+ 196 - 0
engine/src/core/com/jme3/shader/VariableMapping.java

@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.shader;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import java.io.IOException;
+
+/**
+ * represents a mapping between 2 ShaderNodeVariables
+ *
+ * @author Nehon
+ */
+public class VariableMapping implements Savable {
+
+    private ShaderNodeVariable leftVariable;
+    private ShaderNodeVariable rightVariable;
+    private String condition;
+    private String leftSwizzling = "";
+    private String rightSwizzling = "";
+
+    /**
+     * creates a VariableMapping
+     */
+    public VariableMapping() {
+    }
+
+    /**
+     * creates a VariableMapping
+     *
+     * @param leftVariable the left hand side variable of the expression
+     * @param leftSwizzling the swizzling of the left variable
+     * @param rightVariable the right hand side variable of the expression
+     * @param rightSwizzling the swizzling of the right variable
+     * @param condition the condition for this mapping
+     */
+    public VariableMapping(ShaderNodeVariable leftVariable, String leftSwizzling, ShaderNodeVariable rightVariable, String rightSwizzling, String condition) {
+        this.leftVariable = leftVariable;
+        this.rightVariable = rightVariable;
+        this.condition = condition;
+        this.leftSwizzling = leftSwizzling;
+        this.rightSwizzling = rightSwizzling;
+    }
+
+    /**
+     *
+     * @return the left variable
+     */
+    public ShaderNodeVariable getLeftVariable() {
+        return leftVariable;
+    }
+
+    /**
+     * sets the left variable
+     *
+     * @param leftVariable the left variable
+     */
+    public void setLeftVariable(ShaderNodeVariable leftVariable) {
+        this.leftVariable = leftVariable;
+    }
+
+    /**
+     *
+     * @return the right variable
+     */
+    public ShaderNodeVariable getRightVariable() {
+        return rightVariable;
+    }
+
+    /**
+     * sets the right variable
+     *
+     * @param leftVariable the right variable
+     */
+    public void setRightVariable(ShaderNodeVariable rightVariable) {
+        this.rightVariable = rightVariable;
+    }
+
+    /**
+     *
+     * @return the condition
+     */
+    public String getCondition() {
+        return condition;
+    }
+
+    /**
+     * sets the condition
+     *
+     * @param condition the condition
+     */
+    public void setCondition(String condition) {
+        this.condition = condition;
+    }
+
+    /**
+     *
+     * @return the left swizzle
+     */
+    public String getLeftSwizzling() {
+        return leftSwizzling;
+    }
+
+    /**
+     * sets the left swizzle
+     *
+     * @param leftSwizzling the left swizzle
+     */
+    public void setLeftSwizzling(String leftSwizzling) {
+        this.leftSwizzling = leftSwizzling;
+    }
+
+    /**
+     *
+     * @return the right swizzle
+     */
+    public String getRightSwizzling() {
+        return rightSwizzling;
+    }
+
+    /**
+     * sets the right swizzle
+     *
+     * @param leftSwizzling the right swizzle
+     */
+    public void setRightSwizzling(String rightSwizzling) {
+        this.rightSwizzling = rightSwizzling;
+    }
+
+    /**
+     * jme seralization (not used)
+     *
+     * @param ex the exporter
+     * @throws IOException
+     */
+    public void write(JmeExporter ex) throws IOException {
+        OutputCapsule oc = (OutputCapsule) ex.getCapsule(this);
+        oc.write(leftVariable, "leftVariable", null);
+        oc.write(rightVariable, "rightVariable", null);
+        oc.write(condition, "condition", "");
+        oc.write(leftSwizzling, "leftSwizzling", "");
+        oc.write(rightSwizzling, "rightSwizzling", "");
+    }
+
+    /**
+     * jme seralization (not used)
+     *
+     * @param im the importer
+     * @throws IOException
+     */
+    public void read(JmeImporter im) throws IOException {
+        InputCapsule ic = (InputCapsule) im.getCapsule(this);
+        leftVariable = (ShaderNodeVariable) ic.readSavable("leftVariable", null);
+        rightVariable = (ShaderNodeVariable) ic.readSavable("rightVariable", null);
+        condition = ic.readString("condition", "");
+        leftSwizzling = ic.readString("leftSwizzling", "");
+        rightSwizzling = ic.readString("rightSwizzling", "");
+    }
+
+    @Override
+    public String toString() {
+        return "\n{" + leftVariable.toString() + (leftSwizzling.length() > 0 ? ("." + leftSwizzling) : "") + " = " + rightVariable.getType() + " " + rightVariable.getNameSpace() + "." + rightVariable.getName() + (rightSwizzling.length() > 0 ? ("." + rightSwizzling) : "") + " : " + condition + "}";
+    }
+}

+ 22 - 22
engine/src/core/com/jme3/util/blockparser/Statement.java

@@ -35,27 +35,28 @@ import java.util.ArrayList;
 import java.util.List;
 
 public class Statement {
-    
-    private int lineNumber;
-    private String line;
-    private List<Statement> contents = new ArrayList<Statement>();
 
-    Statement(int lineNumber, String line) {
+    protected int lineNumber;
+    protected String line;
+    protected List<Statement> contents = new ArrayList<Statement>();
+
+    protected Statement(int lineNumber, String line) {
         this.lineNumber = lineNumber;
         this.line = line;
     }
-    
-    void addStatement(Statement statement){
-//        if (contents == null){
-//            contents = new ArrayList<Statement>();
-//        }
+
+    protected void addStatement(Statement statement) {
         contents.add(statement);
     }
 
-    public int getLineNumber(){
+    protected void addStatement(int index, Statement statement) {
+        contents.add(index, statement);
+    }
+
+    public int getLineNumber() {
         return lineNumber;
     }
-    
+
     public String getLine() {
         return line;
     }
@@ -63,19 +64,19 @@ public class Statement {
     public List<Statement> getContents() {
         return contents;
     }
-    
-    private String getIndent(int indent){
+
+    protected String getIndent(int indent) {
         return "                               ".substring(0, indent);
     }
-    
-    private String toString(int indent){
+
+    protected String toString(int indent) {
         StringBuilder sb = new StringBuilder();
         sb.append(getIndent(indent));
         sb.append(line);
-        if (contents != null){
+        if (contents != null) {
             sb.append(" {\n");
-            for (Statement statement : contents){
-                sb.append(statement.toString(indent+4));
+            for (Statement statement : contents) {
+                sb.append(statement.toString(indent + 4));
                 sb.append("\n");
             }
             sb.append(getIndent(indent));
@@ -83,10 +84,9 @@ public class Statement {
         }
         return sb.toString();
     }
-    
+
     @Override
-    public String toString(){
+    public String toString() {
         return toString(0);
     }
-    
 }