Adds an alternative way to declare shader nodes definitions
This commit is contained in:
parent
7055de4531
commit
9f9edee332
@ -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;
|
||||
}
|
45
jme3-examples/src/main/resources/jme3test/matdefs/test.j3md
Normal file
45
jme3-examples/src/main/resources/jme3test/matdefs/test.j3md
Normal file
@ -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…
x
Reference in New Issue
Block a user