Adds an alternative way to declare shader nodes definitions

shader-nodes-enhancement
Nehon 7 years ago
parent 7055de4531
commit 9f9edee332
  1. 44
      jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java
  2. 9
      jme3-core/src/main/java/com/jme3/shader/Shader.java
  3. 11
      jme3-core/src/main/java/com/jme3/shader/ShaderGenerator.java
  4. 5
      jme3-core/src/main/java/com/jme3/shader/ShaderNode.java
  5. 121
      jme3-core/src/main/java/com/jme3/shader/ShaderUtils.java
  6. 10
      jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java
  7. 24
      jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java
  8. 5
      jme3-examples/src/main/resources/jme3test/matdefs/ColorMult.frag
  9. 9
      jme3-examples/src/main/resources/jme3test/matdefs/CommonVert.vert
  10. 45
      jme3-examples/src/main/resources/jme3test/matdefs/test.j3md

@ -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

@ -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;

@ -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

@ -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;
}
/**

@ -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;
}
}

@ -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) {

@ -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);
}
}

@ -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;
}

@ -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;
}

@ -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
}
}
}
}
}
Loading…
Cancel
Save