Adds a new java API for shader nodes
This commit is contained in:
parent
9f9edee332
commit
b57ecf35ea
@ -275,6 +275,9 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
|
|||||||
// one mapping for this variable, directly append the
|
// one mapping for this variable, directly append the
|
||||||
// other variable from the mapping to the function call
|
// other variable from the mapping to the function call
|
||||||
VariableMapping m = maps.get(0);
|
VariableMapping m = maps.get(0);
|
||||||
|
if(m.getRightExpression()!=null) {
|
||||||
|
b.append(m.getRightExpression());
|
||||||
|
} else {
|
||||||
ShaderNodeVariable v2 = m.getRightVariable();
|
ShaderNodeVariable v2 = m.getRightVariable();
|
||||||
b.append(getAppendableNameSpace(v2))
|
b.append(getAppendableNameSpace(v2))
|
||||||
.append(v2.getPrefix())
|
.append(v2.getPrefix())
|
||||||
@ -283,6 +286,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
|
|||||||
b.append(".");
|
b.append(".");
|
||||||
b.append(m.getRightSwizzling());
|
b.append(m.getRightSwizzling());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 2 possible cases here
|
// 2 possible cases here
|
||||||
// the variable is a varrying: we can append it directly
|
// the variable is a varrying: we can append it directly
|
||||||
@ -652,7 +656,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getLanguageAndVersion(ShaderType type) {
|
protected String getLanguageAndVersion() {
|
||||||
return "GLSL100";
|
return "GLSL100";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ public class Glsl150ShaderGenerator extends Glsl100ShaderGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getLanguageAndVersion(ShaderType type) {
|
protected String getLanguageAndVersion() {
|
||||||
return "GLSL150";
|
return "GLSL150";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ public abstract class ShaderGenerator {
|
|||||||
Shader shader = new Shader();
|
Shader shader = new Shader();
|
||||||
for (ShaderType type : ShaderType.values()) {
|
for (ShaderType type : ShaderType.values()) {
|
||||||
String extension = type.getExtension();
|
String extension = type.getExtension();
|
||||||
String language = getLanguageAndVersion(type);
|
String language = getLanguageAndVersion();
|
||||||
String shaderSourceCode = buildShader(techniqueDef.getShaderNodes(), info, type);
|
String shaderSourceCode = buildShader(techniqueDef.getShaderNodes(), info, type);
|
||||||
|
|
||||||
if (shaderSourceCode != null) {
|
if (shaderSourceCode != null) {
|
||||||
@ -203,12 +203,15 @@ public abstract class ShaderGenerator {
|
|||||||
if (info.getUnusedNodes().contains(shaderNode.getName())) {
|
if (info.getUnusedNodes().contains(shaderNode.getName())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
ShaderNodeDefinition def = shaderNode.getDefinition();
|
||||||
if (shaderNode.getDefinition().getType() == type) {
|
if (def.getType() == type) {
|
||||||
int index = findShaderIndexFromVersion(shaderNode, type);
|
String loadedSource = def.getInlinedCode();
|
||||||
String shaderPath = shaderNode.getDefinition().getShadersPath().get(index);
|
String shaderPath = def.getName() + "_Inlined";
|
||||||
|
if(loadedSource == null) {
|
||||||
|
int index = findShaderIndexFromVersion(shaderNode);
|
||||||
|
shaderPath = def.getShadersPath().get(index);
|
||||||
Map<String, String> sources = (Map<String, String>) assetManager.loadAsset(new ShaderAssetKey(shaderPath, false));
|
Map<String, String> sources = (Map<String, String>) assetManager.loadAsset(new ShaderAssetKey(shaderPath, false));
|
||||||
String loadedSource = sources.get("[main]");
|
loadedSource = sources.get("[main]");
|
||||||
for (String name : sources.keySet()) {
|
for (String name : sources.keySet()) {
|
||||||
if (!name.equals("[main]") && !imports.contains(name)) {
|
if (!name.equals("[main]") && !imports.contains(name)) {
|
||||||
imports.add(name);
|
imports.add(name);
|
||||||
@ -216,19 +219,20 @@ public abstract class ShaderGenerator {
|
|||||||
sourceDeclaration.append(sources.get(name));
|
sourceDeclaration.append(sources.get(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Nodes are functions added to the declaration part of the shader
|
// 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.
|
// 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.
|
// Also nodes can have #ifdef conditions so we need to properly merge this conditions to declare the Node function.
|
||||||
NodeDeclaration nd = declaredNodes.get(shaderPath);
|
NodeDeclaration nd = declaredNodes.get(shaderPath);
|
||||||
if(nd == null){
|
if (nd == null) {
|
||||||
if(!shaderNode.getDefinition().getPath().equals(shaderPath)) {
|
if (def.getInlinedCode() == null && !def.getPath().equals(shaderPath)) {
|
||||||
// old style shader node definition
|
// old style shader node definition
|
||||||
loadedSource = functionize(loadedSource, shaderNode.getDefinition());
|
loadedSource = functionize(loadedSource, shaderNode.getDefinition());
|
||||||
}
|
}
|
||||||
nd = new NodeDeclaration(shaderNode.getCondition(), loadedSource);
|
nd = new NodeDeclaration(shaderNode.getCondition(), loadedSource);
|
||||||
declaredNodes.put(shaderPath, nd);
|
declaredNodes.put(shaderPath, nd);
|
||||||
} else {
|
} else {
|
||||||
nd.condition = ConditionParser.mergeConditions(nd.condition, shaderNode.getCondition(), "||");
|
nd.condition = ShaderUtils.mergeConditions(nd.condition, shaderNode.getCondition(), "||");
|
||||||
}
|
}
|
||||||
|
|
||||||
generateNodeMainSection(source, shaderNode, loadedSource, info);
|
generateNodeMainSection(source, shaderNode, loadedSource, info);
|
||||||
@ -293,11 +297,10 @@ public abstract class ShaderGenerator {
|
|||||||
* returns the language + version of the shader should be something like
|
* returns the language + version of the shader should be something like
|
||||||
* "GLSL100" for glsl 1.0 "GLSL150" for glsl 1.5.
|
* "GLSL100" for glsl 1.0 "GLSL150" for glsl 1.5.
|
||||||
*
|
*
|
||||||
* @param type the shader type for which the version should be returned.
|
|
||||||
*
|
*
|
||||||
* @return the shaderLanguage and version.
|
* @return the shaderLanguage and version.
|
||||||
*/
|
*/
|
||||||
protected abstract String getLanguageAndVersion(Shader.ShaderType type);
|
protected abstract String getLanguageAndVersion();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* generates the uniforms declaration for a shader of the given type.
|
* generates the uniforms declaration for a shader of the given type.
|
||||||
@ -379,15 +382,14 @@ public abstract class ShaderGenerator {
|
|||||||
* can handle
|
* can handle
|
||||||
*
|
*
|
||||||
* @param shaderNode the shaderNode being processed
|
* @param shaderNode the shaderNode being processed
|
||||||
* @param type the shaderType
|
|
||||||
* @return the index of the shader path in ShaderNodeDefinition shadersPath
|
* @return the index of the shader path in ShaderNodeDefinition shadersPath
|
||||||
* list
|
* list
|
||||||
* @throws NumberFormatException
|
* @throws NumberFormatException
|
||||||
*/
|
*/
|
||||||
protected int findShaderIndexFromVersion(ShaderNode shaderNode, ShaderType type) throws NumberFormatException {
|
protected int findShaderIndexFromVersion(ShaderNode shaderNode) throws NumberFormatException {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
List<String> lang = shaderNode.getDefinition().getShadersLanguage();
|
List<String> lang = shaderNode.getDefinition().getShadersLanguage();
|
||||||
int genVersion = Integer.parseInt(getLanguageAndVersion(type).substring(4));
|
int genVersion = Integer.parseInt(getLanguageAndVersion().substring(4));
|
||||||
int curVersion = 0;
|
int curVersion = 0;
|
||||||
for (int i = 0; i < lang.size(); i++) {
|
for (int i = 0; i < lang.size(); i++) {
|
||||||
int version = Integer.parseInt(lang.get(i).substring(4));
|
int version = Integer.parseInt(lang.get(i).substring(4));
|
||||||
|
@ -59,6 +59,7 @@ public class ShaderNodeDefinition implements Savable {
|
|||||||
private List<ShaderNodeVariable> outputs = new ArrayList<ShaderNodeVariable>();
|
private List<ShaderNodeVariable> outputs = new ArrayList<ShaderNodeVariable>();
|
||||||
private List<ShaderNodeVariable> params = new ArrayList<>();
|
private List<ShaderNodeVariable> params = new ArrayList<>();
|
||||||
private String path = null;
|
private String path = null;
|
||||||
|
private String inlinedCode;
|
||||||
private boolean noOutput = false;
|
private boolean noOutput = false;
|
||||||
private String returnType;
|
private String returnType;
|
||||||
|
|
||||||
@ -237,7 +238,13 @@ public class ShaderNodeDefinition implements Savable {
|
|||||||
this.noOutput = noOutput;
|
this.noOutput = noOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getInlinedCode() {
|
||||||
|
return inlinedCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInlinedCode(String inlinedCode) {
|
||||||
|
this.inlinedCode = inlinedCode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* jme serialization (not used)
|
* jme serialization (not used)
|
||||||
|
@ -337,7 +337,11 @@ public class ShaderNodeVariable implements Savable, Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ShaderNodeVariable clone() throws CloneNotSupportedException {
|
public ShaderNodeVariable clone() {
|
||||||
|
try {
|
||||||
return (ShaderNodeVariable) super.clone();
|
return (ShaderNodeVariable) super.clone();
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,9 +32,11 @@
|
|||||||
package com.jme3.shader;
|
package com.jme3.shader;
|
||||||
|
|
||||||
import com.jme3.asset.AssetManager;
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.material.*;
|
||||||
|
import com.jme3.material.plugins.ConditionParser;
|
||||||
|
import com.jme3.scene.VertexBuffer;
|
||||||
import com.jme3.shader.plugins.ShaderAssetKey;
|
import com.jme3.shader.plugins.ShaderAssetKey;
|
||||||
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
@ -42,18 +44,14 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
public class ShaderUtils {
|
public class ShaderUtils {
|
||||||
|
|
||||||
public static String convertToGLSL130(String input, boolean isFrag) {
|
private static ConditionParser parser = new ConditionParser();
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("#version 130\n");
|
// matches "defaults (<defaultParam1>, <defaultParam2>, ...)"
|
||||||
if (isFrag) {
|
private final static Pattern defaultsPattern = Pattern.compile("defaults\\s*\\(\\s*(.*)\\s*\\)");
|
||||||
input = input.replaceAll("varying", "in");
|
// matches "<type> <functionName>("
|
||||||
} else {
|
private final static Pattern typeNamePattern = Pattern.compile("(\\w+)\\s+(\\w+)\\s*\\(");
|
||||||
input = input.replaceAll("attribute", "in");
|
// matches "const? <in/out> <type> <parmaName>,"
|
||||||
input = input.replaceAll("varying", "out");
|
private final static Pattern paramsPattern = Pattern.compile("((const)?\\s*(\\w+)\\s+(\\w+)\\s+(\\w+)\\s*[,\\)])");
|
||||||
}
|
|
||||||
sb.append(input);
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a mapping is valid by checking the types and swizzle of both of
|
* Check if a mapping is valid by checking the types and swizzle of both of
|
||||||
@ -92,15 +90,15 @@ public class ShaderUtils {
|
|||||||
String leftMult = mapping.getLeftVariable().getMultiplicity();
|
String leftMult = mapping.getLeftVariable().getMultiplicity();
|
||||||
String rightMult = mapping.getRightVariable().getMultiplicity();
|
String rightMult = mapping.getRightVariable().getMultiplicity();
|
||||||
|
|
||||||
if(leftMult == null){
|
if (leftMult == null) {
|
||||||
if(rightMult != null){
|
if (rightMult != null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
if(rightMult == null){
|
if (rightMult == null) {
|
||||||
return false;
|
return false;
|
||||||
}else{
|
} else {
|
||||||
if(!leftMult.equalsIgnoreCase(rightMult)){
|
if (!leftMult.equalsIgnoreCase(rightMult)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,15 +141,9 @@ public class ShaderUtils {
|
|||||||
* @return true if a variable of the given type can have a swizzle
|
* @return true if a variable of the given type can have a swizzle
|
||||||
*/
|
*/
|
||||||
public static boolean isSwizzlable(String type) {
|
public static boolean isSwizzlable(String type) {
|
||||||
return type.indexOf("vec4")>-1 || type.indexOf("vec3")>-1 || type.indexOf("vec2")>-1 || type.equals("float");
|
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 {
|
public static List<ShaderNodeDefinition> parseDefinitions(String glsl) throws ParseException {
|
||||||
List<ShaderNodeDefinition> defs = new ArrayList<>();
|
List<ShaderNodeDefinition> defs = new ArrayList<>();
|
||||||
String nodesCode[] = glsl.split("#pragma ShaderNode");
|
String nodesCode[] = glsl.split("#pragma ShaderNode");
|
||||||
@ -257,4 +249,297 @@ public class ShaderUtils {
|
|||||||
|
|
||||||
return defs;
|
return defs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getDefaultAttributeType(VertexBuffer.Type type) {
|
||||||
|
switch (type) {
|
||||||
|
|
||||||
|
case BoneWeight:
|
||||||
|
case BindPoseNormal:
|
||||||
|
case Binormal:
|
||||||
|
case Normal:
|
||||||
|
return "vec3";
|
||||||
|
case Size:
|
||||||
|
return "float";
|
||||||
|
case Position:
|
||||||
|
case BindPosePosition:
|
||||||
|
case BindPoseTangent:
|
||||||
|
case Tangent:
|
||||||
|
case Color:
|
||||||
|
return "vec4";
|
||||||
|
case InterleavedData:
|
||||||
|
return "int";
|
||||||
|
case Index:
|
||||||
|
return "uint";
|
||||||
|
case BoneIndex:
|
||||||
|
return "uvec4";
|
||||||
|
case TexCoord:
|
||||||
|
case TexCoord2:
|
||||||
|
case TexCoord3:
|
||||||
|
case TexCoord4:
|
||||||
|
case TexCoord5:
|
||||||
|
case TexCoord6:
|
||||||
|
case TexCoord7:
|
||||||
|
case TexCoord8:
|
||||||
|
return "vec2";
|
||||||
|
default:
|
||||||
|
return "float";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO put this in core but this should be changed to handle all kinds of shader not just Vertex and Fragments.
|
||||||
|
* TODO Also getShaderGenerationInfo ArrayLists should be sets really, to avoid duplicated and not have to avoid them ourselves.
|
||||||
|
* This method could be in core actually and be used after loading a techniqueDef.
|
||||||
|
* It computes all the information needed to generate the shader faster, from the ShaderNodes.
|
||||||
|
*
|
||||||
|
* @param technique
|
||||||
|
*/
|
||||||
|
public static void computeShaderNodeGenerationInfo(TechniqueDef technique, MaterialDef matDef) {
|
||||||
|
|
||||||
|
|
||||||
|
List<ShaderNodeVariable> attributes = technique.getShaderGenerationInfo().getAttributes();
|
||||||
|
List<ShaderNodeVariable> fragmentGlobals = technique.getShaderGenerationInfo().getFragmentGlobals();
|
||||||
|
List<ShaderNodeVariable> fragmentUniforms = technique.getShaderGenerationInfo().getFragmentUniforms();
|
||||||
|
List<ShaderNodeVariable> vertexUniforms = technique.getShaderGenerationInfo().getVertexUniforms();
|
||||||
|
List<ShaderNodeVariable> varyings = technique.getShaderGenerationInfo().getVaryings();
|
||||||
|
List<String> unusedNodes = technique.getShaderGenerationInfo().getUnusedNodes();
|
||||||
|
attributes.clear();
|
||||||
|
fragmentGlobals.clear();
|
||||||
|
fragmentUniforms.clear();
|
||||||
|
vertexUniforms.clear();
|
||||||
|
varyings.clear();
|
||||||
|
unusedNodes.clear();
|
||||||
|
|
||||||
|
//considering that none of the nodes are used, we'll remove them from the list when we have proof they are actually used.
|
||||||
|
for (ShaderNode shaderNode : technique.getShaderNodes()) {
|
||||||
|
unusedNodes.add(shaderNode.getName());
|
||||||
|
}
|
||||||
|
for (ShaderNode sn : technique.getShaderNodes()) {
|
||||||
|
checkDefineFromCondition(sn.getCondition(), matDef, technique);
|
||||||
|
ShaderNodeDefinition def = sn.getDefinition();
|
||||||
|
List<VariableMapping> in = sn.getInputMapping();
|
||||||
|
if (in != null) {
|
||||||
|
for (VariableMapping map : in) {
|
||||||
|
checkDefineFromCondition(map.getCondition(), matDef, technique);
|
||||||
|
if (map.getRightExpression() != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ShaderNodeVariable var = map.getRightVariable();
|
||||||
|
if (var.getNameSpace().equals("Global")) {
|
||||||
|
computeGlobals(technique, fragmentGlobals, def, var);
|
||||||
|
} else if (var.getNameSpace().equals("Attr")) {
|
||||||
|
addUnique(attributes, var);
|
||||||
|
} else if (var.getNameSpace().equals("MatParam") || var.getNameSpace().equals("WorldParam")) {
|
||||||
|
checkMultiplicity(technique, matDef, map, var);
|
||||||
|
if (def.getType() == Shader.ShaderType.Fragment) {
|
||||||
|
addUnique(fragmentUniforms, var);
|
||||||
|
} else {
|
||||||
|
addUnique(vertexUniforms, var);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//the nameSpace is the name of another node, if it comes from a different type of node the var is a varying
|
||||||
|
ShaderNode otherNode = null;
|
||||||
|
otherNode = findShaderNodeByName(technique, var.getNameSpace());
|
||||||
|
if (otherNode == null) {
|
||||||
|
//we have a problem this should not happen...but let's not crash...
|
||||||
|
//TODO Maybe we could have an error list and report in it, then present the errors to the user.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (otherNode.getDefinition().getType() != def.getType()) {
|
||||||
|
addUnique(varyings, var);
|
||||||
|
var.setShaderOutput(true);
|
||||||
|
for (VariableMapping variableMapping : otherNode.getInputMapping()) {
|
||||||
|
if (variableMapping.getLeftVariable().getName().equals(var.getName())) {
|
||||||
|
variableMapping.getLeftVariable().setShaderOutput(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//and this other node is apparently used so we remove it from the unusedNodes list
|
||||||
|
unusedNodes.remove(otherNode.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
List<VariableMapping> out = sn.getOutputMapping();
|
||||||
|
if (out != null && !out.isEmpty()) {
|
||||||
|
for (VariableMapping map : out) {
|
||||||
|
checkDefineFromCondition(map.getCondition(), matDef, technique);
|
||||||
|
ShaderNodeVariable var = map.getLeftVariable();
|
||||||
|
if (var.getNameSpace().equals("Global")) {
|
||||||
|
computeGlobals(technique, fragmentGlobals, def, var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//shader has an output it's used in the shader code.
|
||||||
|
unusedNodes.remove(sn.getName());
|
||||||
|
} else {
|
||||||
|
//some nodes has no output by design ans their def specifies so.
|
||||||
|
if (sn.getDefinition().isNoOutput()) {
|
||||||
|
unusedNodes.remove(sn.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkDefineFromCondition(String condition, MaterialDef matDef, TechniqueDef techniqueDef) {
|
||||||
|
if (condition == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<String> defines = parser.extractDefines(condition);
|
||||||
|
for (String define : defines) {
|
||||||
|
MatParam param = findMatParam(define, matDef);
|
||||||
|
if (param != null) {
|
||||||
|
addDefine(techniqueDef, param.getName(), param.getVarType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkMultiplicity(TechniqueDef technique, MaterialDef matDef, VariableMapping map, ShaderNodeVariable var) {
|
||||||
|
if (map.getLeftVariable().getMultiplicity() != null) {
|
||||||
|
MatParam param = findMatParam(map.getRightVariable().getName(), matDef);
|
||||||
|
if (!param.getVarType().name().endsWith("Array")) {
|
||||||
|
throw new IllegalArgumentException(param.getName() + " is not of Array type");
|
||||||
|
}
|
||||||
|
String multiplicity = map.getLeftVariable().getMultiplicity();
|
||||||
|
try {
|
||||||
|
Integer.parseInt(multiplicity);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
//multiplicity is not an int attempting to find for a material parameter.
|
||||||
|
MatParam mp = findMatParam(multiplicity, matDef);
|
||||||
|
if (mp != null) {
|
||||||
|
//It's tied to a material param, let's create a define and use this as the multiplicity
|
||||||
|
addDefine(technique, multiplicity, VarType.Int);
|
||||||
|
multiplicity = multiplicity.toUpperCase();
|
||||||
|
map.getLeftVariable().setMultiplicity(multiplicity);
|
||||||
|
//only declare the variable if the define is defined.
|
||||||
|
map.getLeftVariable().setCondition(mergeConditions(map.getLeftVariable().getCondition(), "defined(" + multiplicity + ")", "||"));
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Wrong multiplicity for variable" + map.getLeftVariable().getName() + ". " + multiplicity + " should be an int or a declared material parameter.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//the right variable must have the same multiplicity and the same condition.
|
||||||
|
var.setMultiplicity(multiplicity);
|
||||||
|
var.setCondition(map.getLeftVariable().getCondition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* merges 2 condition with the given operator
|
||||||
|
*
|
||||||
|
* @param condition1 the first condition
|
||||||
|
* @param condition2 the second condition
|
||||||
|
* @param operator the operator ("&&" or "||&)
|
||||||
|
* @return the merged condition
|
||||||
|
*/
|
||||||
|
public static String mergeConditions(String condition1, String condition2, String operator) {
|
||||||
|
if (condition1 != null) {
|
||||||
|
if (condition1.equals(condition2)) {
|
||||||
|
return condition1;
|
||||||
|
}
|
||||||
|
if (condition2 == null) {
|
||||||
|
return condition1;
|
||||||
|
} else {
|
||||||
|
String mergedCondition = "(" + condition1 + ") " + operator + " (" + condition2 + ")";
|
||||||
|
return mergedCondition;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return condition2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addDefine(TechniqueDef techniqueDef, String paramName, VarType paramType) {
|
||||||
|
if (techniqueDef.getShaderParamDefine(paramName) == null) {
|
||||||
|
techniqueDef.addShaderParamDefine(paramName, paramType, paramName.toUpperCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static MatParam findMatParam(String varName, MaterialDef matDef) {
|
||||||
|
for (MatParam matParam : matDef.getMaterialParams()) {
|
||||||
|
if (varName.toLowerCase().equals(matParam.getName().toLowerCase())) {
|
||||||
|
return matParam;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addUnique(List<ShaderNodeVariable> variables, ShaderNodeVariable var) {
|
||||||
|
for (ShaderNodeVariable variable : variables) {
|
||||||
|
if (var.equals(variable)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
variables.add(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a shader node by name
|
||||||
|
*
|
||||||
|
* @param technique
|
||||||
|
* @param name
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static ShaderNode findShaderNodeByName(TechniqueDef technique, String name) {
|
||||||
|
for (ShaderNode shaderNode : technique.getShaderNodes()) {
|
||||||
|
if (shaderNode.getName().equals(name)) {
|
||||||
|
return shaderNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some parameters may not be used, or not used as an input, but as a flag to command a define.
|
||||||
|
* We didn't get them when looking into shader nodes mappings so let's do that now.
|
||||||
|
*
|
||||||
|
* @param uniforms
|
||||||
|
*/
|
||||||
|
public static void getAllUniforms(TechniqueDef technique, MaterialDef matDef, List<ShaderNodeVariable> uniforms) {
|
||||||
|
uniforms.clear();
|
||||||
|
uniforms.addAll(technique.getShaderGenerationInfo().getFragmentUniforms());
|
||||||
|
uniforms.addAll(technique.getShaderGenerationInfo().getVertexUniforms());
|
||||||
|
|
||||||
|
for (UniformBinding worldParam : technique.getWorldBindings()) {
|
||||||
|
ShaderNodeVariable var = new ShaderNodeVariable(worldParam.getGlslType(), "WorldParam", worldParam.name());
|
||||||
|
if (!contains(uniforms, var)) {
|
||||||
|
uniforms.add(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MatParam matParam : matDef.getMaterialParams()) {
|
||||||
|
ShaderNodeVariable var = new ShaderNodeVariable(matParam.getVarType().getGlslType(), "MatParam", matParam.getName());
|
||||||
|
if (!contains(uniforms, var)) {
|
||||||
|
uniforms.add(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void computeGlobals(TechniqueDef technique, List<ShaderNodeVariable> fragmentGlobals, ShaderNodeDefinition def, ShaderNodeVariable var) {
|
||||||
|
var.setShaderOutput(true);
|
||||||
|
if (def.getType() == Shader.ShaderType.Vertex) {
|
||||||
|
if (technique.getShaderGenerationInfo().getVertexGlobal() == null) {
|
||||||
|
technique.getShaderGenerationInfo().setVertexGlobal(var);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!contains(fragmentGlobals, var)) {
|
||||||
|
fragmentGlobals.add(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns true if a ShaderNode variable is already contained in a list of variables.
|
||||||
|
* TODO This could be handled with a Collection.contains, if ShaderNodeVariable had a proper equals and hashcode
|
||||||
|
*
|
||||||
|
* @param vars
|
||||||
|
* @param var
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean contains(List<ShaderNodeVariable> vars, ShaderNodeVariable var) {
|
||||||
|
for (ShaderNodeVariable shaderNodeVariable : vars) {
|
||||||
|
if (shaderNodeVariable.getName().equals(var.getName()) && shaderNodeVariable.getNameSpace().equals(var.getNameSpace())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,105 @@
|
|||||||
|
package com.jme3.shader.builder;
|
||||||
|
|
||||||
|
import com.jme3.material.TechniqueDef;
|
||||||
|
import com.jme3.shader.*;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class InlineShaderNodeBuilder extends ShaderNodeBuilder {
|
||||||
|
|
||||||
|
|
||||||
|
private TechniqueDef technique;
|
||||||
|
private Pattern varPattern = Pattern.compile("%(\\w+)");
|
||||||
|
private String[] outputTypes;
|
||||||
|
|
||||||
|
public InlineShaderNodeBuilder(String name, ShaderNodeDefinition def, String code, TechniqueDef technique) {
|
||||||
|
super(name, def);
|
||||||
|
this.technique = technique;
|
||||||
|
Matcher m = varPattern.matcher(code);
|
||||||
|
while (m.find()) {
|
||||||
|
// type will be inferred with mapping
|
||||||
|
ShaderNodeVariable v = new ShaderNodeVariable(null, m.group(1));
|
||||||
|
def.getParams().add(v);
|
||||||
|
def.getInputs().add(v);
|
||||||
|
}
|
||||||
|
def.setInlinedCode(code.replaceAll("%", ""));
|
||||||
|
def.getOutputs().add(new ShaderNodeVariable(def.getReturnType(), "result"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineShaderNodeBuilder inputs(VariableMappingBuilder... inputs) {
|
||||||
|
ShaderNodeDefinition def = getNode().getDefinition();
|
||||||
|
for (VariableMappingBuilder map : inputs) {
|
||||||
|
ShaderNodeVariable v = findVariable(map.getName(), def.getInputs());
|
||||||
|
|
||||||
|
def.getInputs().add(v);
|
||||||
|
ShaderNodeVariable right = map.getVariable();
|
||||||
|
if (right.getDefaultValue() != null) {
|
||||||
|
throw new IllegalArgumentException("Inlined expression for input " + v.getName()
|
||||||
|
+ " is not supported with inline node " + getNode().getName()
|
||||||
|
+ ". Please inline the expression in the node code.");
|
||||||
|
}
|
||||||
|
// infer type
|
||||||
|
int idx = right.getType().indexOf("|");
|
||||||
|
if (idx > 0) {
|
||||||
|
// texture type, taking the first available type
|
||||||
|
String type = right.getType().substring(0, right.getType().indexOf("|"));
|
||||||
|
right.setType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
v.setType(right.getType());
|
||||||
|
|
||||||
|
}
|
||||||
|
super.inputs(inputs);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineShaderNodeBuilder outputs(VariableMappingBuilder... outputs) {
|
||||||
|
if (outputs.length > 1 || !outputs[0].getName().equals("result")) {
|
||||||
|
throw new IllegalArgumentException("Only the 'result' output can be mapped for an inlined node");
|
||||||
|
}
|
||||||
|
super.outputs(outputs);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void build() {
|
||||||
|
ShaderNodeDefinition def = getNode().getDefinition();
|
||||||
|
//generate the code
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(def.getReturnType()).append(" ").append(def.getName()).append("(");
|
||||||
|
boolean isFirst = true;
|
||||||
|
int outTypeIndex = 0;
|
||||||
|
for (ShaderNodeVariable v : def.getParams()) {
|
||||||
|
if (!isFirst) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
sb.append("const in ");
|
||||||
|
if (def.getInputs().contains(v)) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
sb.append("out ");
|
||||||
|
if (!def.getOutputs().contains(v)) {
|
||||||
|
// the variable is not in the output list
|
||||||
|
def.getOutputs().add(v);
|
||||||
|
}
|
||||||
|
if (v.getType() == null && outTypeIndex < outputTypes.length) {
|
||||||
|
v.setType(outputTypes[outTypeIndex]);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Output variable " + v.getName() + " has no type in node " + getNode().getName() + ". Make sure you properly declare it");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (v.getType() == null) {
|
||||||
|
throw new IllegalArgumentException("Unable to infer type for input variable " + v.getName() + " in node " + getNode().getName());
|
||||||
|
}
|
||||||
|
sb.append(v.getType()).append(" ");
|
||||||
|
sb.append(v.getName());
|
||||||
|
isFirst = false;
|
||||||
|
}
|
||||||
|
sb.append("){\n\treturn ").append(def.getInlinedCode()).append(";\n}");
|
||||||
|
def.setInlinedCode(sb.toString());
|
||||||
|
super.build();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
package com.jme3.shader.builder;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.material.*;
|
||||||
|
import com.jme3.scene.VertexBuffer;
|
||||||
|
import com.jme3.shader.*;
|
||||||
|
import com.jme3.texture.image.ColorSpace;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class MaterialBuilder {
|
||||||
|
|
||||||
|
private MaterialDef matDef;
|
||||||
|
private Map<String, TechniqueBuilder> techBuilders = new HashMap<>();
|
||||||
|
private AssetManager assetManager;
|
||||||
|
private TechniqueBuilder currentTechnique;
|
||||||
|
|
||||||
|
public MaterialBuilder(AssetManager assetManager) {
|
||||||
|
matDef = new MaterialDef(assetManager, "MatDef");
|
||||||
|
this.assetManager = assetManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MaterialBuilder(AssetManager assetManager, MaterialDef matDef) {
|
||||||
|
this.matDef = matDef;
|
||||||
|
this.assetManager = assetManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MaterialBuilder(AssetManager assetManager, String matDefName) {
|
||||||
|
this.matDef = (MaterialDef) assetManager.loadAsset(matDefName);
|
||||||
|
this.assetManager = assetManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TechniqueBuilder technique(String name) {
|
||||||
|
TechniqueBuilder tb = techBuilders.get(name);
|
||||||
|
if (tb == null) {
|
||||||
|
List<TechniqueDef> defs = matDef.getTechniqueDefs(name);
|
||||||
|
if (defs == null || defs.isEmpty()) {
|
||||||
|
String techniqueUniqueName = matDef.getAssetName() + "@" + name;
|
||||||
|
tb = new TechniqueBuilder(assetManager, name, techniqueUniqueName.hashCode());
|
||||||
|
matDef.addTechniqueDef(tb.getTechniqueDef());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
tb = new TechniqueBuilder(assetManager, defs.get(0));
|
||||||
|
}
|
||||||
|
techBuilders.put(name, tb);
|
||||||
|
}
|
||||||
|
currentTechnique = tb;
|
||||||
|
return tb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TechniqueBuilder technique() {
|
||||||
|
return this.technique("Default");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Material build() {
|
||||||
|
|
||||||
|
for (Map.Entry<String, TechniqueBuilder> entry : techBuilders.entrySet()) {
|
||||||
|
TechniqueBuilder tb = entry.getValue();
|
||||||
|
tb.build();
|
||||||
|
ShaderUtils.computeShaderNodeGenerationInfo(tb.getTechniqueDef(), matDef);
|
||||||
|
}
|
||||||
|
return new Material(matDef);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShaderNodeVariable var(String expression) {
|
||||||
|
String[] names = expression.split("\\.");
|
||||||
|
if (names.length != 2) {
|
||||||
|
// we might have an inlined expression
|
||||||
|
ShaderNodeVariable tmp = new ShaderNodeVariable(null, null);
|
||||||
|
tmp.setDefaultValue(expression);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
if (names[0].equals("MatParam")) {
|
||||||
|
MatParam param = matDef.getMaterialParam(names[1]);
|
||||||
|
if (param == null) {
|
||||||
|
throw new IllegalArgumentException("Couldn't find material parameter named " + names[1]);
|
||||||
|
}
|
||||||
|
return new ShaderNodeVariable(param.getVarType().getGlslType(), names[0], names[1], null, "m_");
|
||||||
|
}
|
||||||
|
if (names[0].equals("WorldParam")) {
|
||||||
|
UniformBinding worldParam = UniformBinding.valueOf(names[1]);
|
||||||
|
currentTechnique.addWorldParam(worldParam.name());
|
||||||
|
return new ShaderNodeVariable(worldParam.getGlslType(), "WorldParam", worldParam.name(), null, "g_");
|
||||||
|
}
|
||||||
|
if (names[0].equals("Attr")) {
|
||||||
|
String n = names[1].substring(2);
|
||||||
|
VertexBuffer.Type attribute = VertexBuffer.Type.valueOf(n);
|
||||||
|
return new ShaderNodeVariable(ShaderUtils.getDefaultAttributeType(attribute), names[0], names[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (names[0].equals("Global")) {
|
||||||
|
if(!names[1].equals("position") && !names[1].startsWith("color")){
|
||||||
|
throw new IllegalArgumentException("Global output must be outPosition or outColor, got " + names[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ShaderNodeVariable("vec4", names[0], names[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderNodeBuilder nb = currentTechnique.node(names[0]);
|
||||||
|
if (nb == null) {
|
||||||
|
throw new IllegalArgumentException("Couldn't find node named " + names[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderNodeVariable v = nb.variable(names[1]);
|
||||||
|
if (v == null) {
|
||||||
|
throw new IllegalArgumentException("Couldn't find variable named " + names[1] + " in node " + names[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableMappingBuilder map(String param, String expression) {
|
||||||
|
return new VariableMappingBuilder(param, var(expression));
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableMappingBuilder map(String param, ShaderNodeVariable variable) {
|
||||||
|
return new VariableMappingBuilder(param, variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableMappingBuilder map(String param, VertexBuffer.Type attribute) {
|
||||||
|
ShaderNodeVariable variable = new ShaderNodeVariable(ShaderUtils.getDefaultAttributeType(attribute), "Attr", "in" + attribute.name());
|
||||||
|
return new VariableMappingBuilder(param, variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableMappingBuilder map(String param, UniformBinding worldParam) {
|
||||||
|
ShaderNodeVariable variable = new ShaderNodeVariable(worldParam.getGlslType(), "WorldParam", worldParam.name(), null, "g_");
|
||||||
|
currentTechnique.addWorldParam(worldParam.name());
|
||||||
|
return new VariableMappingBuilder(param, variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addMatParam(VarType type, String name){
|
||||||
|
if(type.isTextureType()){
|
||||||
|
matDef.addMaterialParamTexture(type, name, ColorSpace.sRGB);
|
||||||
|
} else {
|
||||||
|
matDef.addMaterialParam(type, name, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addMatParamTexture(VarType type, String name, ColorSpace colorSpace){
|
||||||
|
if(!type.isTextureType()){
|
||||||
|
throw new IllegalArgumentException(type + "is not a texture type ");
|
||||||
|
}
|
||||||
|
matDef.addMaterialParamTexture(type, name, colorSpace);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,113 @@
|
|||||||
|
package com.jme3.shader.builder;
|
||||||
|
|
||||||
|
import com.jme3.shader.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ShaderNodeBuilder {
|
||||||
|
|
||||||
|
private ShaderNode node;
|
||||||
|
|
||||||
|
protected ShaderNodeBuilder(String name, ShaderNodeDefinition def) {
|
||||||
|
node = new ShaderNode(name, def, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ShaderNodeBuilder(ShaderNode node) {
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ShaderNode getNode() {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ShaderNodeVariable variable(String name){
|
||||||
|
ShaderNodeDefinition def = node.getDefinition();
|
||||||
|
for (ShaderNodeVariable variable : def.getParams()) {
|
||||||
|
if(variable.getName().equals(name)){
|
||||||
|
ShaderNodeVariable var = variable.clone();
|
||||||
|
var.setNameSpace(node.getName());
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ShaderNodeVariable variable : def.getOutputs()) {
|
||||||
|
if(variable.getName().equals(name)){
|
||||||
|
ShaderNodeVariable var = variable.clone();
|
||||||
|
var.setNameSpace(node.getName());
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShaderNodeBuilder inputs(VariableMappingBuilder... inputs){
|
||||||
|
List<VariableMapping> mappings = node.getInputMapping();
|
||||||
|
mappings.clear();
|
||||||
|
for (VariableMappingBuilder mb : inputs) {
|
||||||
|
ShaderNodeVariable left = findVariable(mb.getName(), node.getDefinition().getInputs() );
|
||||||
|
if(left == null){
|
||||||
|
throw new IllegalArgumentException("Couldn't find input " + mb.getName() + " in node definition " + node.getDefinition().getName());
|
||||||
|
}
|
||||||
|
left = left.clone();
|
||||||
|
left.setNameSpace(node.getName());
|
||||||
|
ShaderNodeVariable right = mb.getVariable();
|
||||||
|
VariableMapping m = map(left, right);
|
||||||
|
mappings.add(m);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ShaderNodeBuilder outputs(VariableMappingBuilder... outputs){
|
||||||
|
List<VariableMapping> mappings = node.getOutputMapping();
|
||||||
|
mappings.clear();
|
||||||
|
for (VariableMappingBuilder mb : outputs) {
|
||||||
|
ShaderNodeVariable right = findVariable(mb.getName(), node.getDefinition().getOutputs() );
|
||||||
|
if(right == null){
|
||||||
|
throw new IllegalArgumentException("Couldn't find input " + mb.getName() + " in node definition " + node.getDefinition().getName());
|
||||||
|
}
|
||||||
|
right = right.clone();
|
||||||
|
right.setNameSpace(node.getName());
|
||||||
|
ShaderNodeVariable left = mb.getVariable();
|
||||||
|
VariableMapping m = map(left, right);
|
||||||
|
mappings.add(m);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private VariableMapping map(ShaderNodeVariable left, ShaderNodeVariable right) {
|
||||||
|
if(right.getType() == null){
|
||||||
|
// tmp variable, with default value
|
||||||
|
VariableMapping m = new VariableMapping();
|
||||||
|
m.setLeftVariable(left);
|
||||||
|
m.setRightExpression(right.getDefaultValue());
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
int leftCard = ShaderUtils.getCardinality(left.getType(), "");
|
||||||
|
int rightCard = ShaderUtils.getCardinality(right.getType(), "");
|
||||||
|
String swizzle = "xyzw";
|
||||||
|
String rightVarSwizzle = "";
|
||||||
|
String leftVarSwizzle ="";
|
||||||
|
if (rightCard > leftCard) {
|
||||||
|
rightVarSwizzle = swizzle.substring(0, leftCard);
|
||||||
|
} else if (rightCard > rightCard) {
|
||||||
|
leftVarSwizzle = swizzle.substring(0, rightCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new VariableMapping(left, leftVarSwizzle, right, rightVarSwizzle, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ShaderNodeVariable findVariable(String name, List<ShaderNodeVariable> list){
|
||||||
|
for (ShaderNodeVariable variable : list) {
|
||||||
|
if(variable.getName().equals(name)){
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void build(){
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
package com.jme3.shader.builder;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.asset.ShaderNodeDefinitionKey;
|
||||||
|
import com.jme3.material.ShaderGenerationInfo;
|
||||||
|
import com.jme3.material.TechniqueDef;
|
||||||
|
import com.jme3.material.logic.*;
|
||||||
|
import com.jme3.shader.*;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class TechniqueBuilder {
|
||||||
|
|
||||||
|
private TechniqueDef techniqueDef;
|
||||||
|
private AssetManager assetManager;
|
||||||
|
private Map<String, ShaderNodeBuilder> nodeBuilders = new HashMap<>();
|
||||||
|
|
||||||
|
protected TechniqueBuilder(AssetManager assetManager, String name, int sortId) {
|
||||||
|
techniqueDef = new TechniqueDef(name, sortId);
|
||||||
|
this.assetManager = assetManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TechniqueBuilder(AssetManager assetManager, TechniqueDef techniqueDef) {
|
||||||
|
this.techniqueDef = techniqueDef;
|
||||||
|
this.assetManager = assetManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TechniqueDef getTechniqueDef() {
|
||||||
|
return techniqueDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShaderNodeBuilder node(String name) {
|
||||||
|
ShaderNodeBuilder b = nodeBuilders.get(name);
|
||||||
|
if (b == null){
|
||||||
|
ShaderNode n = findShaderNode(name);
|
||||||
|
if(n == null){
|
||||||
|
throw new IllegalArgumentException("Can't find node with name " + name + " in technique definition " + techniqueDef.getName());
|
||||||
|
}
|
||||||
|
b = new ShaderNodeBuilder(n);
|
||||||
|
nodeBuilders.put(name, b);
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShaderNodeBuilder addNode(String name, String defName, String shaderNodeDefPath) {
|
||||||
|
List<ShaderNodeDefinition> defs;
|
||||||
|
if(shaderNodeDefPath.endsWith(".j3sn")){
|
||||||
|
defs = assetManager.loadAsset(new ShaderNodeDefinitionKey(shaderNodeDefPath));
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
defs = ShaderUtils.loadSahderNodeDefinition(assetManager, shaderNodeDefPath);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new IllegalArgumentException("Couldn't parse definition " + shaderNodeDefPath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ShaderNodeDefinition definition = findDefinition(defName, defs);
|
||||||
|
if(definition == null){
|
||||||
|
throw new IllegalArgumentException("Couldn't find definition " + defName + " in " + shaderNodeDefPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderNodeBuilder b = new ShaderNodeBuilder(name,definition);
|
||||||
|
if(techniqueDef.getShaderNodes() == null){
|
||||||
|
techniqueDef.setShaderNodes(new ArrayList<ShaderNode>());
|
||||||
|
}
|
||||||
|
techniqueDef.setShaderFile(techniqueDef.hashCode() + "", techniqueDef.hashCode() + "", "GLSL100", "GLSL100");
|
||||||
|
techniqueDef.getShaderNodes().add(b.getNode());
|
||||||
|
techniqueDef.setShaderGenerationInfo(new ShaderGenerationInfo());
|
||||||
|
techniqueDef.setLogic(new DefaultTechniqueDefLogic(techniqueDef));
|
||||||
|
techniqueDef.setShaderPrologue("");
|
||||||
|
nodeBuilders.put(name, b);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public InlineShaderNodeBuilder inlineVertexNode(String type, String name, String code){
|
||||||
|
return inlineNode(type, name, code, Shader.ShaderType.Vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InlineShaderNodeBuilder inlineFragmentNode(String type, String name, String code){
|
||||||
|
return inlineNode(type, name, code, Shader.ShaderType.Fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InlineShaderNodeBuilder inlineNode(String returnType, String name, String code, Shader.ShaderType type){
|
||||||
|
ShaderNodeDefinition def = new ShaderNodeDefinition();
|
||||||
|
def.setName(name);
|
||||||
|
def.setType(type);
|
||||||
|
def.setReturnType(returnType);
|
||||||
|
InlineShaderNodeBuilder sb = new InlineShaderNodeBuilder(name, def, code, techniqueDef);
|
||||||
|
nodeBuilders.put(name, sb);
|
||||||
|
techniqueDef.getShaderNodes().add(sb.getNode());
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addWorldParam(String name){
|
||||||
|
return techniqueDef.addWorldParam(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setLightMode(TechniqueDef.LightMode mode){
|
||||||
|
switch (techniqueDef.getLightMode()) {
|
||||||
|
case Disable:
|
||||||
|
techniqueDef.setLogic(new DefaultTechniqueDefLogic(techniqueDef));
|
||||||
|
break;
|
||||||
|
case MultiPass:
|
||||||
|
techniqueDef.setLogic(new MultiPassLightingLogic(techniqueDef));
|
||||||
|
break;
|
||||||
|
case SinglePass:
|
||||||
|
techniqueDef.setLogic(new SinglePassLightingLogic(techniqueDef));
|
||||||
|
break;
|
||||||
|
case StaticPass:
|
||||||
|
techniqueDef.setLogic(new StaticPassLightingLogic(techniqueDef));
|
||||||
|
break;
|
||||||
|
case SinglePassAndImageBased:
|
||||||
|
techniqueDef.setLogic(new SinglePassAndImageBasedLightingLogic(techniqueDef));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShaderNodeDefinition findDefinition(String defName, List<ShaderNodeDefinition> defs) {
|
||||||
|
for (ShaderNodeDefinition def : defs) {
|
||||||
|
if(def.getName().equals(defName)){
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShaderNode findShaderNode(String name){
|
||||||
|
for (ShaderNode shaderNode : techniqueDef.getShaderNodes()) {
|
||||||
|
if(shaderNode.getName().equals(name)){
|
||||||
|
return shaderNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void build(){
|
||||||
|
for (Map.Entry<String, ShaderNodeBuilder> entry : nodeBuilders.entrySet()) {
|
||||||
|
ShaderNodeBuilder nb = entry.getValue();
|
||||||
|
nb.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.jme3.shader.builder;
|
||||||
|
|
||||||
|
import com.jme3.shader.ShaderNodeVariable;
|
||||||
|
|
||||||
|
public class VariableMappingBuilder {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private ShaderNodeVariable variable;
|
||||||
|
|
||||||
|
protected VariableMappingBuilder(String name, ShaderNodeVariable variable) {
|
||||||
|
this.name = name;
|
||||||
|
this.variable = variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShaderNodeVariable getVariable() {
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -104,28 +104,6 @@ public class ConditionParser {
|
|||||||
return defines;
|
return defines;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* merges 2 condition with the given operator
|
|
||||||
*
|
|
||||||
* @param condition1 the first condition
|
|
||||||
* @param condition2 the second condition
|
|
||||||
* @param operator the operator ("&&" or "||&)
|
|
||||||
* @return the merged condition
|
|
||||||
*/
|
|
||||||
public static String mergeConditions(String condition1, String condition2, String operator) {
|
|
||||||
if (condition1 != null) {
|
|
||||||
if (condition2 == null) {
|
|
||||||
return condition1;
|
|
||||||
} else {
|
|
||||||
String mergedCondition = "(" + condition1 + ") " + operator + " (" + condition2 + ")";
|
|
||||||
return mergedCondition;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return condition2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @return the formatted expression previously updated by extractDefines
|
* @return the formatted expression previously updated by extractDefines
|
||||||
|
@ -660,9 +660,8 @@ public class J3MLoader implements AssetLoader {
|
|||||||
if(isUseNodes){
|
if(isUseNodes){
|
||||||
//used for caching later, the shader here is not a file.
|
//used for caching later, the shader here is not a file.
|
||||||
|
|
||||||
// KIRILL 9/19/2015
|
// REMY 16/06/2018
|
||||||
// Not sure if this is needed anymore, since shader caching
|
// this is still needed in order for the weight of the technique to be computed.
|
||||||
// is now done by TechniqueDef.
|
|
||||||
technique.setShaderFile(technique.hashCode() + "", technique.hashCode() + "", "GLSL100", "GLSL100");
|
technique.setShaderFile(technique.hashCode() + "", technique.hashCode() + "", "GLSL100", "GLSL100");
|
||||||
techniqueDefs.add(technique);
|
techniqueDefs.add(technique);
|
||||||
}else if (shaderNames.containsKey(Shader.ShaderType.Vertex) && shaderNames.containsKey(Shader.ShaderType.Fragment)) {
|
}else if (shaderNames.containsKey(Shader.ShaderType.Vertex) && shaderNames.containsKey(Shader.ShaderType.Fragment)) {
|
||||||
|
@ -582,7 +582,7 @@ public class ShaderNodeLoaderDelegate {
|
|||||||
multiplicity = multiplicity.toUpperCase();
|
multiplicity = multiplicity.toUpperCase();
|
||||||
left.setMultiplicity(multiplicity);
|
left.setMultiplicity(multiplicity);
|
||||||
// only declare the variable if the define is defined.
|
// only declare the variable if the define is defined.
|
||||||
left.setCondition(ConditionParser.mergeConditions(left.getCondition(), "defined(" + multiplicity + ")", "||"));
|
left.setCondition(ShaderUtils.mergeConditions(left.getCondition(), "defined(" + multiplicity + ")", "||"));
|
||||||
} else {
|
} else {
|
||||||
throw new MatParseException("Wrong multiplicity for variable" + left.getName() + ". " +
|
throw new MatParseException("Wrong multiplicity for variable" + left.getName() + ". " +
|
||||||
multiplicity + " should be an int or a declared material parameter.", statement);
|
multiplicity + " should be an int or a declared material parameter.", statement);
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
package jme3test.material;
|
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication;
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.material.Technique;
|
||||||
|
import com.jme3.material.TechniqueDef;
|
||||||
|
import com.jme3.math.*;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.VertexBuffer;
|
||||||
|
import com.jme3.scene.shape.Box;
|
||||||
|
import com.jme3.shader.*;
|
||||||
|
import com.jme3.shader.builder.MaterialBuilder;
|
||||||
|
import com.jme3.texture.Texture;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class TestShaderNodesApi extends SimpleApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
TestShaderNodesApi app = new TestShaderNodesApi();
|
||||||
|
app.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void simpleInitApp() {
|
||||||
|
flyCam.setMoveSpeed(20);
|
||||||
|
Logger.getLogger("com.jme3").setLevel(Level.WARNING);
|
||||||
|
Box boxshape1 = new Box(1f, 1f, 1f);
|
||||||
|
Geometry cube = new Geometry("A Box", boxshape1);
|
||||||
|
Texture tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg");
|
||||||
|
|
||||||
|
MaterialBuilder mb = new MaterialBuilder(assetManager);
|
||||||
|
mb.addMatParam(VarType.Vector4, "Color");
|
||||||
|
mb.addMatParam(VarType.Texture2D, "Texture");
|
||||||
|
|
||||||
|
mb.technique().addNode("CommonVert", "CommonVert", "jme3test/matdefs/CommonVert.vert")
|
||||||
|
.inputs(
|
||||||
|
mb.map("worldViewProjectionMatrix", UniformBinding.WorldViewProjectionMatrix),
|
||||||
|
mb.map("modelPosition", VertexBuffer.Type.Position))
|
||||||
|
.outputs(
|
||||||
|
mb.map("result", "Global.position")
|
||||||
|
);
|
||||||
|
|
||||||
|
mb.technique().inlineVertexNode("vec2","TexCoord", "%texIn")
|
||||||
|
.inputs(
|
||||||
|
mb.map("texIn", VertexBuffer.Type.TexCoord)
|
||||||
|
);
|
||||||
|
|
||||||
|
mb.technique().addNode("ColorMult", "ColorMult", "jme3test/matdefs/ColorMult.frag")
|
||||||
|
.inputs(
|
||||||
|
mb.map("color1", "vec4(0.1, 0.1, 0.1, 1.0)"),
|
||||||
|
mb.map("color2", "MatParam.Color"))
|
||||||
|
.outputs(
|
||||||
|
mb.map("result", "Global.color")
|
||||||
|
);
|
||||||
|
|
||||||
|
mb.technique().inlineFragmentNode("vec4","InlineNode","%color1 * texture2D(%tex, %texCoord)")
|
||||||
|
.inputs(
|
||||||
|
mb.map("color1", "ColorMult.result"),
|
||||||
|
mb.map("tex", "MatParam.Texture"),
|
||||||
|
mb.map("texCoord", "TexCoord.result")
|
||||||
|
).outputs(
|
||||||
|
mb.map("result", "Global.color")
|
||||||
|
);
|
||||||
|
|
||||||
|
Material mat = mb.build();
|
||||||
|
|
||||||
|
//Material mat = new Material(assetManager, "jme3test/matdefs/test2.j3md");
|
||||||
|
|
||||||
|
mat.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
|
||||||
|
Technique 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("Texture", tex);
|
||||||
|
cube.setMaterial(mat);
|
||||||
|
cube.move(0, 0, 0);
|
||||||
|
rootNode.attachChild(cube);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user