Jelajahi Sumber

Adds an alternative way to declare shader nodes definitions

Nehon 7 tahun lalu
induk
melakukan
9f9edee332

+ 29 - 15
jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java

@@ -36,6 +36,7 @@ import com.jme3.material.ShaderGenerationInfo;
 import com.jme3.material.plugins.ConditionParser;
 import com.jme3.shader.Shader.ShaderType;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -231,8 +232,6 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
      */
     @Override
     protected void generateNodeMainSection(StringBuilder source, ShaderNode shaderNode, String nodeSource, ShaderGenerationInfo info) {
-
-
         source.append("\n");
         comment(source, shaderNode, "");
         startCondition(shaderNode.getCondition(), source);
@@ -243,13 +242,14 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
         appendIndent(b);
         b.append(definition.getName()).append("(");
         boolean isFirst = true;
+        List<VariableMapping> maps = new ArrayList<>();
         for (ShaderNodeVariable v : definition.getParams()) {
             if (!isFirst) {
                 b.append(", ");
             }
             if (definition.getInputs().contains(v)) {
 
-                List<VariableMapping> maps = shaderNode.getInputMapping(v.getName());
+                shaderNode.getInputMapping(v.getName(), maps);
 
                 boolean declared = false;
                 for (VariableMapping m : maps) {
@@ -267,6 +267,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
                         b.append(v.getDefaultValue());
                     } else {
                         // no default value, construct a variable with the proper type and dummy value and raise a warning
+                        b.append("/*UNMAPPED_").append(v.getName()).append("*/ ");
                         b.append(getConstructor(v.getType()));
                         log.log(Level.WARNING, "No input defined for variable " + v.getName() + " on shader node " + shaderNode.getName());
                     }
@@ -291,28 +292,28 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
                     for (VariableMapping mapping : maps) {
                         map(mapping, source, true);
                     }
-                    b.append(shaderNode.getName())
-                            .append("_")
-                            .append(v.getName());
+                    appendVariable(shaderNode.getName(), b, v);
                 }
             } else {
                 // outputs
-                String name = shaderNode.getName() + "_" + v.getName();
-                // if the output is not a varying (already declared) we declare it)
-                if (!isVarying(info, name)) {
-                    appendIndent(source);
-                    source.append(v.getType()).append(" ").append(name).append(";\n");
-                }
+                declareOutput(source, shaderNode.getName(), info, v);
                 // append the variable to the function call
-                b.append(shaderNode.getName())
-                        .append("_")
-                        .append(v.getName());
+                appendVariable(shaderNode.getName(), b, v);
             }
             isFirst = false;
         }
 
         b.append(");\n");
 
+        if(!definition.getReturnType().equals("void")){
+            // non void return type, the first output is the result
+            ShaderNodeVariable v = definition.getOutputs().get(0);
+            declareOutput(source, shaderNode.getName(), info, v);
+            appendIndent(source);
+            appendVariable(shaderNode.getName(), source, v);
+            source.append(" =");
+        }
+
         // Map any output to global output.
         for (VariableMapping mapping : shaderNode.getOutputMapping()) {
             map(mapping, b, false);
@@ -322,6 +323,19 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
         endCondition(shaderNode.getCondition(), source);
     }
 
+    private void declareOutput(StringBuilder source, String nameSpace, ShaderGenerationInfo info, ShaderNodeVariable v) {
+        String name = nameSpace + "_" + v.getName();
+        // if the output is not a varying (already declared) we declare it)
+        if (!isVarying(info, name)) {
+            appendIndent(source);
+            source.append(v.getType()).append(" ").append(name).append(";\n");
+        }
+    }
+
+    private void appendVariable(String nameSpace, StringBuilder b, ShaderNodeVariable v) {
+        b.append(nameSpace).append("_").append(v.getName());
+    }
+
     /**
      * Returns a proper constructor call for a given type
      * @param type

+ 9 - 0
jme3-core/src/main/java/com/jme3/shader/Shader.java

@@ -103,6 +103,15 @@ public final class Shader extends NativeObject {
         public String getExtension() {
             return extension;
         }
+
+        public static ShaderType fromExtention(String ext){
+            for (ShaderType shaderType : values()) {
+                if(shaderType.extension.equals(ext)){
+                    return  shaderType;
+                }
+            }
+            return null;
+        }
         
         private ShaderType(String extension) {
             this.extension = extension;

+ 7 - 4
jme3-core/src/main/java/com/jme3/shader/ShaderGenerator.java

@@ -219,11 +219,14 @@ public abstract class ShaderGenerator {
                 // Nodes are functions added to the declaration part of the shader
                 // Multiple nodes may use the same definition and we don't want to declare it several times.
                 // Also nodes can have #ifdef conditions so we need to properly merge this conditions to declare the Node function.
-                NodeDeclaration nd = declaredNodes.get(shaderNode.getDefinition().getName());
-                loadedSource =  functionize(loadedSource, shaderNode.getDefinition());
+                NodeDeclaration nd = declaredNodes.get(shaderPath);
                 if(nd == null){
+                    if(!shaderNode.getDefinition().getPath().equals(shaderPath)) {
+                        // old style shader node definition
+                        loadedSource = functionize(loadedSource, shaderNode.getDefinition());
+                    }
                     nd = new NodeDeclaration(shaderNode.getCondition(),  loadedSource);
-                    declaredNodes.put(shaderNode.getDefinition().getName(), nd);
+                    declaredNodes.put(shaderPath, nd);
                 } else {
                     nd.condition = ConditionParser.mergeConditions(nd.condition, shaderNode.getCondition(), "||");
                 }
@@ -237,7 +240,7 @@ public abstract class ShaderGenerator {
     }
 
     /**
-     * Tuns old style shader node code into a proper function so that it can be appended to the declarative sectio.
+     * Turns old style shader node code into a proper function so that it can be appended to the declarative section.
      * Note that this only needed for nodes coming from a j3sn file.
      * @param source
      * @param def

+ 2 - 3
jme3-core/src/main/java/com/jme3/shader/ShaderNode.java

@@ -148,14 +148,13 @@ public class ShaderNode implements Savable, Cloneable {
      *
      * @return the input mappings.
      */
-    public List<VariableMapping> getInputMapping(String varName) {
-        List<VariableMapping> list = new ArrayList<>();
+    public void getInputMapping(String varName, List<VariableMapping> list) {
+        list.clear();
         for (VariableMapping v : inputMapping) {
             if (v.getLeftVariable().getName().equals(varName)){
                 list.add(v);
             }
         }
-        return list;
     }
 
     /**

+ 121 - 0
jme3-core/src/main/java/com/jme3/shader/ShaderUtils.java

@@ -31,6 +31,15 @@
  */
 package com.jme3.shader;
 
+import com.jme3.asset.AssetManager;
+import com.jme3.shader.plugins.ShaderAssetKey;
+
+import java.io.StringReader;
+import java.text.ParseException;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 public class ShaderUtils {
 
     public static String convertToGLSL130(String input, boolean isFrag) {
@@ -136,4 +145,116 @@ public class ShaderUtils {
     public static boolean isSwizzlable(String type) {
         return type.indexOf("vec4")>-1 || type.indexOf("vec3")>-1 || type.indexOf("vec2")>-1 || type.equals("float");
     }
+
+    private final static Pattern defaultsPattern = Pattern.compile("defaults\\s*\\(\\s*(.*)\\s*\\)");
+    // matches "<type> <functionName>("
+    private final static Pattern typeNamePattern = Pattern.compile("(\\w+)\\s+(\\w+)\\s*\\(");
+    // matches "const? <in/out> <type> <parmaName>,"
+    private final static Pattern paramsPattern = Pattern.compile("((const)?\\s*(\\w+)\\s+(\\w+)\\s+(\\w+)\\s*[,\\)])");
+
+    public static List<ShaderNodeDefinition> parseDefinitions(String glsl) throws ParseException {
+        List<ShaderNodeDefinition> defs = new ArrayList<>();
+        String nodesCode[] = glsl.split("#pragma ShaderNode");
+        for (String code : nodesCode) {
+            if (code.trim().length() == 0) {
+                continue;
+            }
+            int firstCr = code.indexOf("\n");
+            int firstBracket = code.indexOf("{");
+            String pragma = code.substring(0, firstCr);
+            Matcher m1 = defaultsPattern.matcher(pragma);
+            String[] defaults = null;
+            if (m1.find()) {
+                defaults = m1.group(1).split(",");
+            }
+
+            code = code.substring(firstCr + 1, firstBracket);
+
+            Matcher m = typeNamePattern.matcher(code);
+
+            String returnType = null;
+            String functionName = null;
+            while (m.find()) {
+                returnType = m.group(1);
+                functionName = m.group(2);
+            }
+            if (returnType == null || functionName == null) {
+                throw new ParseException("Unmatched return type or function name in \n" + code, firstCr + 1);
+            }
+
+            ShaderNodeDefinition def = new ShaderNodeDefinition();
+            def.setName(functionName);
+            def.setReturnType(returnType);
+
+            m.reset();
+            m.usePattern(paramsPattern);
+
+            List<ShaderNodeVariable> inputs = new ArrayList<>();
+            List<ShaderNodeVariable> outputs = new ArrayList<>();
+            List<ShaderNodeVariable> params = new ArrayList<>();
+
+            if (!returnType.equals("void")) {
+                ShaderNodeVariable result = new ShaderNodeVariable(returnType, "result");
+                outputs.add(result);
+            }
+
+            int cpt = 0;
+            while (m.find()) {
+                String dir = m.group(3);
+                String type = m.group(4);
+                String varName = m.group(5);
+                ShaderNodeVariable v = new ShaderNodeVariable(type, varName);
+                params.add(v);
+                String defVal = null;
+                if (defaults != null && defaults.length > cpt) {
+                    defVal = defaults[cpt].trim();
+                    defVal = defVal.isEmpty() ? null : defVal;
+                }
+                v.setDefaultValue(defVal);
+                switch (dir) {
+                    case "in":
+                        inputs.add(v);
+                        break;
+                    case "out":
+                        outputs.add(v);
+                        break;
+                    default:
+                        throw new ParseException("Missing in or out keyword for variable " + varName + " in function " + functionName, m.start());
+                }
+                cpt++;
+            }
+
+            def.setParams(params);
+            def.setInputs(inputs);
+            if (outputs.isEmpty()) {
+                def.setNoOutput(true);
+            } else {
+                def.setOutputs(outputs);
+            }
+
+            defs.add(def);
+        }
+
+        return defs;
+    }
+
+    public static Shader.ShaderType getShaderType(String shaderPath) {
+        String ext = shaderPath.substring(shaderPath.lastIndexOf(".") + 1);
+        return Shader.ShaderType.fromExtention(ext);
+    }
+
+    public static List<ShaderNodeDefinition> loadSahderNodeDefinition(AssetManager assetManager, String definitionPath) throws ParseException {
+        Map<String, String> sources =  (Map<String, String>)  assetManager.loadAsset(new ShaderAssetKey(definitionPath, false));
+        String glsl = sources.get("[main]");
+        List<ShaderNodeDefinition> defs = ShaderUtils.parseDefinitions(glsl);
+        Shader.ShaderType type = ShaderUtils.getShaderType(definitionPath);
+        for (ShaderNodeDefinition d : defs) {
+            d.setType(type);
+            d.getShadersLanguage().add("GLSL100");
+            d.setPath(definitionPath);
+            d.getShadersPath().add(definitionPath);
+        }
+
+        return defs;
+    }
 }

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

@@ -40,9 +40,11 @@ import com.jme3.material.ShaderGenerationInfo;
 import com.jme3.material.TechniqueDef;
 import com.jme3.shader.Shader.ShaderType;
 import com.jme3.shader.*;
+import com.jme3.shader.plugins.ShaderAssetKey;
 import com.jme3.util.blockparser.Statement;
 
 import java.io.IOException;
+import java.text.ParseException;
 import java.util.*;
 
 /**
@@ -996,9 +998,15 @@ public class ShaderNodeLoaderDelegate {
 
         List<ShaderNodeDefinition> defs;
         try {
-            defs = assetManager.loadAsset(new ShaderNodeDefinitionKey(definitionPath));
+            if(definitionPath.endsWith(".j3sn")) {
+                defs = assetManager.loadAsset(new ShaderNodeDefinitionKey(definitionPath));
+            } else {
+                defs = ShaderUtils.loadSahderNodeDefinition(assetManager, definitionPath);
+            }
         } catch (final AssetNotFoundException e) {
             throw new MatParseException("Couldn't find " + definitionPath, statement, e);
+        } catch (ParseException e) {
+            throw new MatParseException("Error while loading shader node definition" + definitionPath, statement, e);
         }
 
         for (final ShaderNodeDefinition definition : defs) {

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

@@ -24,7 +24,7 @@ public class TestShaderNodes extends SimpleApplication {
         flyCam.setMoveSpeed(20);
         Logger.getLogger("com.jme3").setLevel(Level.WARNING);
         Box boxshape1 = new Box(1f, 1f, 1f);
-        Geometry cube_tex = new Geometry("A Textured Box", boxshape1);
+        Geometry cube = new Geometry("A Box", boxshape1);
         Texture tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg");
 
         Material mat = new Material(assetManager, "Common/MatDefs/Misc/UnshadedNodes.j3md");
@@ -37,7 +37,25 @@ public class TestShaderNodes extends SimpleApplication {
         
         mat.setColor("Color", ColorRGBA.Yellow);
         mat.setTexture("ColorMap", tex);
-        cube_tex.setMaterial(mat);
-        rootNode.attachChild(cube_tex);
+        cube.setMaterial(mat);
+        cube.move(-2.5f,0,0);
+        rootNode.attachChild(cube);
+
+
+        cube = cube.clone();
+        mat = new Material(assetManager, "jme3test/matdefs/test.j3md");
+        mat.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
+        t = mat.getActiveTechnique();
+
+        for (Shader.ShaderSource shaderSource : t.getDef().getShader(assetManager, renderer.getCaps(), t.getDynamicDefines()).getSources()) {
+            System.out.println(shaderSource.getSource());
+        }
+
+        mat.setColor("Color", ColorRGBA.Yellow);
+        //mat.setTexture("ColorMap", tex);
+        cube.setMaterial(mat);
+        cube.move(2.5f,0,0);
+        rootNode.attachChild(cube);
+
     }
 }

+ 5 - 0
jme3-examples/src/main/resources/jme3test/matdefs/ColorMult.frag

@@ -0,0 +1,5 @@
+#pragma ShaderNode defaults(vec4(1.0), ,vec4(1.0))
+vec4 ColorMult(const in vec4 color1, const in vec4 color2, const in vec4 color3){
+    return color1 * color2 * color3;
+}
+

+ 9 - 0
jme3-examples/src/main/resources/jme3test/matdefs/CommonVert.vert

@@ -0,0 +1,9 @@
+#pragma ShaderNode
+vec4 CommonVert(const in mat4 worldViewProjectionMatrix, const in vec3 modelPosition){
+    return worldViewProjectionMatrix * vec4(modelPosition, 1.0);
+}
+
+#pragma ShaderNode
+vec4 DoThing( in vec4 color ){
+    return color * 0.1;
+}

+ 45 - 0
jme3-examples/src/main/resources/jme3test/matdefs/test.j3md

@@ -0,0 +1,45 @@
+MaterialDef Simple {
+    MaterialParameters {
+        Vector4 Color
+    }
+
+    Technique {
+        WorldParameters {
+            WorldViewProjectionMatrix
+        }
+
+        VertexShaderNodes {
+            ShaderNode CommonVert {
+                Definition: CommonVert: jme3test/matdefs/CommonVert.vert
+                InputMappings{
+                    worldViewProjectionMatrix = WorldParam.WorldViewProjectionMatrix
+                    modelPosition = Attr.inPosition
+                }
+                OutputMappings{
+                    Global.position = result
+                }
+            }
+            ShaderNode ColorStuff {
+                Definition: DoThing: jme3test/matdefs/CommonVert.vert
+                InputMappings{
+                    color = MatParam.Color
+                }
+                OutputMappings{
+                }
+            }
+        }
+
+        FragmentShaderNodes {
+            ShaderNode ColorMult {
+                Definition: ColorMult: jme3test/matdefs/ColorMult.frag
+                InputMappings{
+                    color2 = ColorStuff.result
+                }
+                OutputMappings{
+                    Global.color = result
+                }
+            }
+        }
+
+    }
+}