Compare commits
4 Commits
master
...
shader-nod
Author | SHA1 | Date | |
---|---|---|---|
|
523087a82f | ||
|
b57ecf35ea | ||
|
9f9edee332 | ||
|
7055de4531 |
@ -38,6 +38,8 @@ import com.jme3.shader.Shader.ShaderType;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This shader Generator can generate Vertex and Fragment shaders from
|
* This shader Generator can generate Vertex and Fragment shaders from
|
||||||
@ -51,6 +53,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
|
|||||||
* the indentation characters 1à tabulation characters
|
* the indentation characters 1à tabulation characters
|
||||||
*/
|
*/
|
||||||
private final static String INDENTCHAR = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
|
private final static String INDENTCHAR = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
|
||||||
|
private final static Logger log = Logger.getLogger(Glsl100ShaderGenerator.class.getName());
|
||||||
|
|
||||||
protected ShaderNodeVariable inPosTmp;
|
protected ShaderNodeVariable inPosTmp;
|
||||||
|
|
||||||
@ -116,21 +119,16 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
protected void generateDeclarationSection(StringBuilder source) {
|
||||||
*
|
for (String defName : declaredNodes.keySet()) {
|
||||||
* if the declaration contains no code nothing is done, else it's appended
|
NodeDeclaration nd = declaredNodes.get(defName);
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void generateDeclarativeSection(StringBuilder source, ShaderNode shaderNode, String nodeSource, ShaderGenerationInfo info) {
|
|
||||||
if (nodeSource.replaceAll("\\n", "").trim().length() > 0) {
|
|
||||||
nodeSource = updateDefinesName(nodeSource, shaderNode);
|
|
||||||
source.append("\n");
|
source.append("\n");
|
||||||
unIndent();
|
unIndent();
|
||||||
startCondition(shaderNode.getCondition(), source);
|
startCondition(nd.condition, source);
|
||||||
source.append(nodeSource);
|
source.append(nd.source);
|
||||||
source.append("\n");
|
source.append("\n");
|
||||||
endCondition(shaderNode.getCondition(), source);
|
endCondition(nd.condition, source);
|
||||||
indent();
|
indent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,82 +232,130 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void generateNodeMainSection(StringBuilder source, ShaderNode shaderNode, String nodeSource, ShaderGenerationInfo info) {
|
protected void generateNodeMainSection(StringBuilder source, ShaderNode shaderNode, String nodeSource, ShaderGenerationInfo info) {
|
||||||
|
|
||||||
nodeSource = updateDefinesName(nodeSource, shaderNode);
|
|
||||||
source.append("\n");
|
source.append("\n");
|
||||||
comment(source, shaderNode, "Begin");
|
comment(source, shaderNode, "");
|
||||||
startCondition(shaderNode.getCondition(), source);
|
startCondition(shaderNode.getCondition(), source);
|
||||||
|
|
||||||
final List<String> declaredInputs = new ArrayList<>();
|
|
||||||
|
|
||||||
// Decalring variables with default values first
|
|
||||||
final ShaderNodeDefinition definition = shaderNode.getDefinition();
|
final ShaderNodeDefinition definition = shaderNode.getDefinition();
|
||||||
|
|
||||||
for (final ShaderNodeVariable var : definition.getInputs()) {
|
StringBuilder b = new StringBuilder();
|
||||||
|
appendIndent(b);
|
||||||
if (var.getType().startsWith("sampler")) {
|
b.append(definition.getName()).append("(");
|
||||||
continue;
|
boolean isFirst = true;
|
||||||
|
List<VariableMapping> maps = new ArrayList<>();
|
||||||
|
for (ShaderNodeVariable v : definition.getParams()) {
|
||||||
|
if (!isFirst) {
|
||||||
|
b.append(", ");
|
||||||
}
|
}
|
||||||
|
if (definition.getInputs().contains(v)) {
|
||||||
|
|
||||||
final String fullName = shaderNode.getName() + "_" + var.getName();
|
shaderNode.getInputMapping(v.getName(), maps);
|
||||||
|
|
||||||
final ShaderNodeVariable variable = new ShaderNodeVariable(var.getType(), shaderNode.getName(),
|
boolean declared = false;
|
||||||
var.getName(), var.getMultiplicity());
|
for (VariableMapping m : maps) {
|
||||||
|
// map varyings to their inputs, as the code may not do the mapping.
|
||||||
|
if (isVarying(info, m.getLeftVariable())) {
|
||||||
|
map(m, source, false);
|
||||||
|
declared = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!isVarying(info, variable)) {
|
if (maps.isEmpty()) {
|
||||||
declareVariable(source, variable, var.getDefaultValue(), true, null);
|
//no mapping found
|
||||||
}
|
if (v.getDefaultValue() != null) {
|
||||||
|
// if there is a default value append it to the function call
|
||||||
nodeSource = replaceVariableName(nodeSource, variable);
|
b.append(v.getDefaultValue());
|
||||||
declaredInputs.add(fullName);
|
} 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("*/ ");
|
||||||
for (VariableMapping mapping : shaderNode.getInputMapping()) {
|
b.append(getConstructor(v.getType()));
|
||||||
|
log.log(Level.WARNING, "No input defined for variable " + v.getName() + " on shader node " + shaderNode.getName());
|
||||||
final ShaderNodeVariable rightVariable = mapping.getRightVariable();
|
}
|
||||||
final ShaderNodeVariable leftVariable = mapping.getLeftVariable();
|
} else if (maps.size() == 1 && !declared) {
|
||||||
|
// one mapping for this variable, directly append the
|
||||||
String newName = shaderNode.getName() + "_" + leftVariable.getName();
|
// other variable from the mapping to the function call
|
||||||
boolean isDeclared = declaredInputs.contains(newName);
|
VariableMapping m = maps.get(0);
|
||||||
//Variables fed with a sampler matparam or world param are replaced by the matparam itself
|
if(m.getRightExpression()!=null) {
|
||||||
//It avoids issue with samplers that have to be uniforms.
|
b.append(m.getRightExpression());
|
||||||
if (rightVariable != null && isWorldOrMaterialParam(rightVariable) && rightVariable.getType().startsWith("sampler")) {
|
} else {
|
||||||
nodeSource = replace(nodeSource, leftVariable, rightVariable.getPrefix() + rightVariable.getName());
|
ShaderNodeVariable v2 = m.getRightVariable();
|
||||||
|
b.append(getAppendableNameSpace(v2))
|
||||||
|
.append(v2.getPrefix())
|
||||||
|
.append(v2.getName());
|
||||||
|
if (m.getRightSwizzling().length() > 0) {
|
||||||
|
b.append(".");
|
||||||
|
b.append(m.getRightSwizzling());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 2 possible cases here
|
||||||
|
// the variable is a varrying: we can append it directly
|
||||||
|
// or
|
||||||
|
// several mappings with different conditions: we have to declare the variable and
|
||||||
|
// map it properly before appending the variable in the function call
|
||||||
|
for (VariableMapping mapping : maps) {
|
||||||
|
map(mapping, source, true);
|
||||||
|
}
|
||||||
|
appendVariable(shaderNode.getName(), b, v);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// outputs
|
||||||
if (leftVariable.getType().startsWith("sampler")) {
|
declareOutput(source, shaderNode.getName(), info, v);
|
||||||
throw new IllegalArgumentException("a Sampler must be a uniform");
|
// append the variable to the function call
|
||||||
}
|
appendVariable(shaderNode.getName(), b, v);
|
||||||
map(mapping, source, !isDeclared);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isDeclared) {
|
|
||||||
nodeSource = replace(nodeSource, leftVariable, newName);
|
|
||||||
declaredInputs.add(newName);
|
|
||||||
}
|
}
|
||||||
|
isFirst = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.append(");\n");
|
||||||
|
|
||||||
|
if(!definition.getReturnType().equals("void")){
|
||||||
for (ShaderNodeVariable var : definition.getOutputs()) {
|
// non void return type, the first output is the result
|
||||||
ShaderNodeVariable v = new ShaderNodeVariable(var.getType(), shaderNode.getName(), var.getName(), var.getMultiplicity());
|
ShaderNodeVariable v = definition.getOutputs().get(0);
|
||||||
if (!declaredInputs.contains(shaderNode.getName() + "_" + var.getName())) {
|
declareOutput(source, shaderNode.getName(), info, v);
|
||||||
if (!isVarying(info, v)) {
|
appendIndent(source);
|
||||||
declareVariable(source, v);
|
appendVariable(shaderNode.getName(), source, v);
|
||||||
}
|
source.append(" =");
|
||||||
nodeSource = replaceVariableName(nodeSource, v);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
source.append(nodeSource);
|
// Map any output to global output.
|
||||||
|
|
||||||
for (VariableMapping mapping : shaderNode.getOutputMapping()) {
|
for (VariableMapping mapping : shaderNode.getOutputMapping()) {
|
||||||
map(mapping, source, true);
|
map(mapping, b, false);
|
||||||
}
|
}
|
||||||
|
source.append(b);
|
||||||
|
|
||||||
endCondition(shaderNode.getCondition(), source);
|
endCondition(shaderNode.getCondition(), source);
|
||||||
comment(source, shaderNode, "End");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String getConstructor(String type) {
|
||||||
|
if (type.startsWith("i") || type.startsWith("u")) {
|
||||||
|
return type + "(0)";
|
||||||
|
}
|
||||||
|
if (type.equals("boolean") || type.startsWith("u")) {
|
||||||
|
return "false";
|
||||||
|
}
|
||||||
|
return type + "(0.0)";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* declares a variable, embed in a conditional block if needed
|
* declares a variable, embed in a conditional block if needed
|
||||||
* @param source the StringBuilder to use
|
* @param source the StringBuilder to use
|
||||||
@ -510,6 +556,17 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
|
|||||||
return isVarying;
|
return isVarying;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean isVarying(ShaderGenerationInfo info, String variableName) {
|
||||||
|
for (ShaderNodeVariable shaderNodeVariable : info.getVaryings()) {
|
||||||
|
String name = shaderNodeVariable.getNameSpace() + "_" + shaderNodeVariable.getName();
|
||||||
|
if (name.equals(variableName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends a comment to the generated code
|
* Appends a comment to the generated code
|
||||||
* @param source the StringBuilder to use
|
* @param source the StringBuilder to use
|
||||||
@ -554,10 +611,10 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
|
|||||||
String[] lines = nodeSource.split("\\n");
|
String[] lines = nodeSource.split("\\n");
|
||||||
ConditionParser parser = new ConditionParser();
|
ConditionParser parser = new ConditionParser();
|
||||||
for (String line : lines) {
|
for (String line : lines) {
|
||||||
|
line = line.trim();
|
||||||
if (line.trim().startsWith("#if")) {
|
if (line.startsWith("#if")) {
|
||||||
List<String> params = parser.extractDefines(line.trim());
|
List<String> params = parser.extractDefines(line);
|
||||||
String l = line.trim().replaceAll("defined", "").replaceAll("#if ", "").replaceAll("#ifdef", "");//parser.getFormattedExpression();
|
String l = line.replaceAll("defined", "").replaceAll("#if ", "").replaceAll("#ifdef", "");
|
||||||
boolean match = false;
|
boolean match = false;
|
||||||
for (String param : params) {
|
for (String param : params) {
|
||||||
for (VariableMapping map : shaderNode.getInputMapping()) {
|
for (VariableMapping map : shaderNode.getInputMapping()) {
|
||||||
@ -599,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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,6 +103,15 @@ public final class Shader extends NativeObject {
|
|||||||
public String getExtension() {
|
public String getExtension() {
|
||||||
return extension;
|
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) {
|
private ShaderType(String extension) {
|
||||||
this.extension = extension;
|
this.extension = extension;
|
||||||
|
@ -34,6 +34,7 @@ package com.jme3.shader;
|
|||||||
import com.jme3.asset.AssetManager;
|
import com.jme3.asset.AssetManager;
|
||||||
import com.jme3.material.ShaderGenerationInfo;
|
import com.jme3.material.ShaderGenerationInfo;
|
||||||
import com.jme3.material.TechniqueDef;
|
import com.jme3.material.TechniqueDef;
|
||||||
|
import com.jme3.material.plugins.ConditionParser;
|
||||||
import com.jme3.shader.Shader.ShaderType;
|
import com.jme3.shader.Shader.ShaderType;
|
||||||
import com.jme3.shader.plugins.ShaderAssetKey;
|
import com.jme3.shader.plugins.ShaderAssetKey;
|
||||||
|
|
||||||
@ -72,7 +73,15 @@ public abstract class ShaderGenerator {
|
|||||||
*/
|
*/
|
||||||
Pattern extensions = Pattern.compile("(#extension.*\\s+)");
|
Pattern extensions = Pattern.compile("(#extension.*\\s+)");
|
||||||
|
|
||||||
private Map<String, String> imports = new LinkedHashMap<>();
|
/**
|
||||||
|
* a set of imports to append to the shader source
|
||||||
|
*/
|
||||||
|
private Set<String> imports = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The nodes function and their condition to be declared in the shader
|
||||||
|
*/
|
||||||
|
protected Map<String, NodeDeclaration> declaredNodes = new LinkedHashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a shaderGenerator
|
* Build a shaderGenerator
|
||||||
@ -105,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) {
|
||||||
@ -155,12 +164,6 @@ public abstract class ShaderGenerator {
|
|||||||
|
|
||||||
generateEndOfMainSection(source, info, type);
|
generateEndOfMainSection(source, info, type);
|
||||||
|
|
||||||
//insert imports backward
|
|
||||||
int insertIndex = sourceDeclaration.length();
|
|
||||||
for (String importSource : imports.values()) {
|
|
||||||
sourceDeclaration.insert(insertIndex, importSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceDeclaration.append(source);
|
sourceDeclaration.append(source);
|
||||||
|
|
||||||
return moveExtensionsUp(sourceDeclaration);
|
return moveExtensionsUp(sourceDeclaration);
|
||||||
@ -195,67 +198,109 @@ public abstract class ShaderGenerator {
|
|||||||
* @param type the Shader type
|
* @param type the Shader type
|
||||||
*/
|
*/
|
||||||
protected void generateDeclarationAndMainBody(List<ShaderNode> shaderNodes, StringBuilder sourceDeclaration, StringBuilder source, ShaderGenerationInfo info, Shader.ShaderType type) {
|
protected void generateDeclarationAndMainBody(List<ShaderNode> shaderNodes, StringBuilder sourceDeclaration, StringBuilder source, ShaderGenerationInfo info, Shader.ShaderType type) {
|
||||||
|
declaredNodes.clear();
|
||||||
for (ShaderNode shaderNode : shaderNodes) {
|
for (ShaderNode shaderNode : shaderNodes) {
|
||||||
if (info.getUnusedNodes().contains(shaderNode.getName())) {
|
if (info.getUnusedNodes().contains(shaderNode.getName())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (shaderNode.getDefinition().getType() == type) {
|
ShaderNodeDefinition def = shaderNode.getDefinition();
|
||||||
int index = findShaderIndexFromVersion(shaderNode, type);
|
if (def.getType() == type) {
|
||||||
String shaderPath = shaderNode.getDefinition().getShadersPath().get(index);
|
String loadedSource = def.getInlinedCode();
|
||||||
Map<String, String> sources = (Map<String, String>) assetManager.loadAsset(new ShaderAssetKey(shaderPath, false));
|
String shaderPath = def.getName() + "_Inlined";
|
||||||
String loadedSource = sources.get("[main]");
|
if(loadedSource == null) {
|
||||||
for (String name : sources.keySet()) {
|
int index = findShaderIndexFromVersion(shaderNode);
|
||||||
if (!name.equals("[main]")) {
|
shaderPath = def.getShadersPath().get(index);
|
||||||
imports.put(name, sources.get(name));
|
Map<String, String> sources = (Map<String, String>) assetManager.loadAsset(new ShaderAssetKey(shaderPath, false));
|
||||||
|
loadedSource = sources.get("[main]");
|
||||||
|
for (String name : sources.keySet()) {
|
||||||
|
if (!name.equals("[main]") && !imports.contains(name)) {
|
||||||
|
imports.add(name);
|
||||||
|
// append the imported file in place if it hasn't been imported already.
|
||||||
|
sourceDeclaration.append(sources.get(name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
appendNodeDeclarationAndMain(loadedSource, sourceDeclaration, source, shaderNode, info, shaderPath);
|
// 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(shaderPath);
|
||||||
|
if (nd == null) {
|
||||||
|
if (def.getInlinedCode() == null && !def.getPath().equals(shaderPath)) {
|
||||||
|
// old style shader node definition
|
||||||
|
loadedSource = functionize(loadedSource, shaderNode.getDefinition());
|
||||||
|
}
|
||||||
|
nd = new NodeDeclaration(shaderNode.getCondition(), loadedSource);
|
||||||
|
declaredNodes.put(shaderPath, nd);
|
||||||
|
} else {
|
||||||
|
nd.condition = ShaderUtils.mergeConditions(nd.condition, shaderNode.getCondition(), "||");
|
||||||
|
}
|
||||||
|
|
||||||
|
generateNodeMainSection(source, shaderNode, loadedSource, info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateDeclarationSection(sourceDeclaration);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends declaration and main part of a node to the shader declaration and
|
* Turns old style shader node code into a proper function so that it can be appended to the declarative section.
|
||||||
* main part. the loadedSource is split by "void main(){" to split
|
* Note that this only needed for nodes coming from a j3sn file.
|
||||||
* declaration from main part of the node source code.The trailing "}" is
|
* @param source
|
||||||
* removed from the main part. Each part is then respectively passed to
|
* @param def
|
||||||
* generateDeclarativeSection and generateNodeMainSection.
|
* @return
|
||||||
*
|
|
||||||
* @see ShaderGenerator#generateDeclarativeSection
|
|
||||||
* @see ShaderGenerator#generateNodeMainSection
|
|
||||||
*
|
|
||||||
* @param loadedSource the actual source code loaded for this node.
|
|
||||||
* @param shaderPath path to the shader file
|
|
||||||
* @param sourceDeclaration the Shader declaration part string builder.
|
|
||||||
* @param source the Shader main part StringBuilder.
|
|
||||||
* @param shaderNode the shader node.
|
|
||||||
* @param info the ShaderGenerationInfo.
|
|
||||||
*/
|
*/
|
||||||
protected void appendNodeDeclarationAndMain(String loadedSource, StringBuilder sourceDeclaration, StringBuilder source, ShaderNode shaderNode, ShaderGenerationInfo info, String shaderPath) {
|
public static String functionize(String source, ShaderNodeDefinition def){
|
||||||
if (loadedSource.length() > 1) {
|
StringBuffer signature = new StringBuffer();
|
||||||
loadedSource = loadedSource.substring(0, loadedSource.lastIndexOf("}"));
|
def.setReturnType("void");
|
||||||
String[] sourceParts = loadedSource.split("\\s*void\\s*main\\s*\\(\\s*\\)\\s*\\{");
|
signature.append("void ").append(def.getName()).append("(");
|
||||||
if(sourceParts.length<2){
|
boolean addParam = false;
|
||||||
throw new IllegalArgumentException("Syntax error in "+ shaderPath +". Cannot find 'void main(){' in \n"+ loadedSource);
|
if(def.getParams().isEmpty()){
|
||||||
}
|
addParam = true;
|
||||||
generateDeclarativeSection(sourceDeclaration, shaderNode, sourceParts[0], info);
|
|
||||||
generateNodeMainSection(source, shaderNode, sourceParts[1], info);
|
|
||||||
} else {
|
|
||||||
//if source is empty, we still call generateNodeMainSection so that mappings can be done.
|
|
||||||
generateNodeMainSection(source, shaderNode, loadedSource, info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isFirst = true;
|
||||||
|
for (ShaderNodeVariable v : def.getInputs()) {
|
||||||
|
if(!isFirst){
|
||||||
|
signature.append(", ");
|
||||||
|
}
|
||||||
|
String qualifier;
|
||||||
|
qualifier = "const in";
|
||||||
|
signature.append(qualifier).append(" ").append(v.getType()).append(" ").append(v.getName());
|
||||||
|
isFirst = false;
|
||||||
|
if(addParam) {
|
||||||
|
def.getParams().add(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ShaderNodeVariable v : def.getOutputs()) {
|
||||||
|
if(def.getInputs().contains(v)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(!isFirst){
|
||||||
|
signature.append(", ");
|
||||||
|
}
|
||||||
|
signature.append("out ").append(v.getType()).append(" ").append(v.getName());
|
||||||
|
isFirst = false;
|
||||||
|
if(addParam) {
|
||||||
|
def.getParams().add(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signature.append("){");
|
||||||
|
|
||||||
|
source = source.replaceAll("\\s*void\\s*main\\s*\\(\\s*\\)\\s*\\{", signature.toString());
|
||||||
|
|
||||||
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
@ -287,19 +332,12 @@ public abstract class ShaderGenerator {
|
|||||||
protected abstract void generateVaryings(StringBuilder source, ShaderGenerationInfo info, ShaderType type);
|
protected abstract void generateVaryings(StringBuilder source, ShaderGenerationInfo info, ShaderType type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends the given shaderNode declarative part to the shader declarative
|
* Appends the shaderNodes function to the shader declarative
|
||||||
* part. If needed the shader type can be determined by fetching the
|
* part.
|
||||||
* shaderNode's definition type.
|
|
||||||
*
|
|
||||||
* @see ShaderNode#getDefinition()
|
|
||||||
* @see ShaderNodeDefinition#getType()
|
|
||||||
*
|
*
|
||||||
* @param nodeDecalarationSource the declaration part of the node
|
|
||||||
* @param source the StringBuilder to append generated code.
|
* @param source the StringBuilder to append generated code.
|
||||||
* @param shaderNode the shaderNode.
|
|
||||||
* @param info the ShaderGenerationInfo.
|
|
||||||
*/
|
*/
|
||||||
protected abstract void generateDeclarativeSection(StringBuilder source, ShaderNode shaderNode, String nodeDecalarationSource, ShaderGenerationInfo info);
|
protected abstract void generateDeclarationSection(StringBuilder source);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* generates the start of the shader main section. this method is
|
* generates the start of the shader main section. this method is
|
||||||
@ -344,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));
|
||||||
@ -363,4 +400,15 @@ public abstract class ShaderGenerator {
|
|||||||
}
|
}
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected class NodeDeclaration{
|
||||||
|
String condition;
|
||||||
|
String source;
|
||||||
|
|
||||||
|
public NodeDeclaration(String condition, String source) {
|
||||||
|
this.condition = condition;
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,20 @@ public class ShaderNode implements Savable, Cloneable {
|
|||||||
return inputMapping;
|
return inputMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of variable mapping for the given node input.
|
||||||
|
*
|
||||||
|
* @return the input mappings.
|
||||||
|
*/
|
||||||
|
public void getInputMapping(String varName, List<VariableMapping> list) {
|
||||||
|
list.clear();
|
||||||
|
for (VariableMapping v : inputMapping) {
|
||||||
|
if (v.getLeftVariable().getName().equals(varName)){
|
||||||
|
list.add(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the input mappings.
|
* Sets the input mappings.
|
||||||
*
|
*
|
||||||
|
@ -57,8 +57,11 @@ public class ShaderNodeDefinition implements Savable {
|
|||||||
private String documentation;
|
private String documentation;
|
||||||
private List<ShaderNodeVariable> inputs = new ArrayList<ShaderNodeVariable>();
|
private List<ShaderNodeVariable> inputs = new ArrayList<ShaderNodeVariable>();
|
||||||
private List<ShaderNodeVariable> outputs = new ArrayList<ShaderNodeVariable>();
|
private List<ShaderNodeVariable> outputs = new ArrayList<ShaderNodeVariable>();
|
||||||
|
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creates a ShaderNodeDefinition
|
* creates a ShaderNodeDefinition
|
||||||
@ -183,8 +186,22 @@ public class ShaderNodeDefinition implements Savable {
|
|||||||
public void setPath(String path) {
|
public void setPath(String path) {
|
||||||
this.path = path;
|
this.path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getReturnType() {
|
||||||
|
return returnType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReturnType(String returnType) {
|
||||||
|
this.returnType = returnType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ShaderNodeVariable> getParams() {
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParams(List<ShaderNodeVariable> params) {
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* jme serialization (not used)
|
* jme serialization (not used)
|
||||||
@ -194,14 +211,15 @@ public class ShaderNodeDefinition implements Savable {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
OutputCapsule oc = (OutputCapsule) ex.getCapsule(this);
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
oc.write(name, "name", "");
|
oc.write(name, "name", "");
|
||||||
String[] str = new String[shadersLanguage.size()];
|
String[] str = new String[shadersLanguage.size()];
|
||||||
oc.write(shadersLanguage.toArray(str), "shadersLanguage", null);
|
oc.write(shadersLanguage.toArray(str), "shadersLanguage", null);
|
||||||
oc.write(shadersPath.toArray(str), "shadersPath", null);
|
oc.write(shadersPath.toArray(str), "shadersPath", null);
|
||||||
oc.write(type, "type", null);
|
oc.write(type, "type", null);
|
||||||
oc.writeSavableArrayList((ArrayList) inputs, "inputs", new ArrayList<ShaderNodeVariable>());
|
oc.writeSavableArrayList((ArrayList) inputs, "inputs", new ArrayList<ShaderNodeVariable>());
|
||||||
oc.writeSavableArrayList((ArrayList) outputs, "inputs", new ArrayList<ShaderNodeVariable>());
|
oc.writeSavableArrayList((ArrayList) outputs, "outputs", new ArrayList<ShaderNodeVariable>());
|
||||||
|
oc.writeSavableArrayList((ArrayList) params, "params", new ArrayList<ShaderNodeVariable>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getShadersLanguage() {
|
public List<String> getShadersLanguage() {
|
||||||
@ -220,8 +238,14 @@ 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)
|
||||||
*
|
*
|
||||||
@ -250,6 +274,8 @@ public class ShaderNodeDefinition implements Savable {
|
|||||||
type = ic.readEnum("type", Shader.ShaderType.class, null);
|
type = ic.readEnum("type", Shader.ShaderType.class, null);
|
||||||
inputs = (List<ShaderNodeVariable>) ic.readSavableArrayList("inputs", new ArrayList<ShaderNodeVariable>());
|
inputs = (List<ShaderNodeVariable>) ic.readSavableArrayList("inputs", new ArrayList<ShaderNodeVariable>());
|
||||||
outputs = (List<ShaderNodeVariable>) ic.readSavableArrayList("outputs", new ArrayList<ShaderNodeVariable>());
|
outputs = (List<ShaderNodeVariable>) ic.readSavableArrayList("outputs", new ArrayList<ShaderNodeVariable>());
|
||||||
|
params = (List<ShaderNodeVariable>) ic.readSavableArrayList("params", new ArrayList<ShaderNodeVariable>());
|
||||||
|
im.getAssetManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -337,7 +337,11 @@ public class ShaderNodeVariable implements Savable, Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ShaderNodeVariable clone() throws CloneNotSupportedException {
|
public ShaderNodeVariable clone() {
|
||||||
return (ShaderNodeVariable) super.clone();
|
try {
|
||||||
|
return (ShaderNodeVariable) super.clone();
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,20 +31,27 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.shader;
|
package com.jme3.shader;
|
||||||
|
|
||||||
|
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 java.text.ParseException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
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
|
||||||
@ -71,8 +78,8 @@ public class ShaderUtils {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a mapping is valid by checking the multiplicity of both of
|
* Check if a mapping is valid by checking the multiplicity of both of
|
||||||
* the variables if they are arrays
|
* the variables if they are arrays
|
||||||
*
|
*
|
||||||
@ -82,16 +89,16 @@ public class ShaderUtils {
|
|||||||
public static boolean multiplicityMatch(VariableMapping mapping) {
|
public static boolean multiplicityMatch(VariableMapping mapping) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +111,7 @@ public class ShaderUtils {
|
|||||||
* is 4 float cardinality is 1 vec4.xyz cardinality is 3. sampler2D
|
* is 4 float cardinality is 1 vec4.xyz cardinality is 3. sampler2D
|
||||||
* cardinality is 0
|
* cardinality is 0
|
||||||
*
|
*
|
||||||
* @param type the glsl type
|
* @param type the glsl type
|
||||||
* @param swizzling the swizzling of a variable
|
* @param swizzling the swizzling of a variable
|
||||||
* @return the cardinality
|
* @return the cardinality
|
||||||
*/
|
*/
|
||||||
@ -134,6 +141,405 @@ 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
|
#import "Common/ShaderLib/Skinning.glsllib"
|
||||||
|
|
||||||
void main(){
|
void main(){
|
||||||
modModelPosition = (mat4(0.0) +
|
#ifdef NUM_BONES
|
||||||
boneMatrices[int(boneIndex.x)] * boneWeight.x +
|
modModelPosition = modelPosition;
|
||||||
boneMatrices[int(boneIndex.y)] * boneWeight.y +
|
Skinning_Compute(modModelPosition);
|
||||||
boneMatrices[int(boneIndex.z)] * boneWeight.z +
|
#endif
|
||||||
boneMatrices[int(boneIndex.w)] * boneWeight.w) * vec4(modelPosition.xyz,1.0);
|
|
||||||
}
|
}
|
@ -1,12 +1,10 @@
|
|||||||
|
#import "Common/ShaderLib/Skinning.glsllib"
|
||||||
|
|
||||||
void main(){
|
void main(){
|
||||||
modModelPosition = (mat4(0.0) +
|
#ifdef NUM_BONES
|
||||||
boneMatrices[int(boneIndex.x)] * boneWeight.x +
|
modModelPosition = modelPosition;
|
||||||
boneMatrices[int(boneIndex.y)] * boneWeight.y +
|
modModelNormal = modelNormal;
|
||||||
boneMatrices[int(boneIndex.z)] * boneWeight.z +
|
modModelTangents = modelTangents;
|
||||||
boneMatrices[int(boneIndex.w)] * boneWeight.w) * modelPosition;
|
Skinning_Compute(modModelPosition, modModelNormal, modModelTangents);
|
||||||
|
#endif
|
||||||
mat3 rotMat = mat3(mat[0].xyz, mat[1].xyz, mat[2].xyz);
|
|
||||||
modModelTangent = rotMat * modelTangent;
|
|
||||||
modModelNormal = rotMat * modelNormal;
|
|
||||||
}
|
}
|
@ -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)) {
|
||||||
|
@ -40,9 +40,11 @@ import com.jme3.material.ShaderGenerationInfo;
|
|||||||
import com.jme3.material.TechniqueDef;
|
import com.jme3.material.TechniqueDef;
|
||||||
import com.jme3.shader.Shader.ShaderType;
|
import com.jme3.shader.Shader.ShaderType;
|
||||||
import com.jme3.shader.*;
|
import com.jme3.shader.*;
|
||||||
|
import com.jme3.shader.plugins.ShaderAssetKey;
|
||||||
import com.jme3.util.blockparser.Statement;
|
import com.jme3.util.blockparser.Statement;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.text.ParseException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -580,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(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);
|
||||||
@ -996,9 +998,15 @@ public class ShaderNodeLoaderDelegate {
|
|||||||
|
|
||||||
List<ShaderNodeDefinition> defs;
|
List<ShaderNodeDefinition> defs;
|
||||||
try {
|
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) {
|
} catch (final AssetNotFoundException e) {
|
||||||
throw new MatParseException("Couldn't find " + definitionPath, statement, 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) {
|
for (final ShaderNodeDefinition definition : defs) {
|
||||||
@ -1057,26 +1065,6 @@ public class ShaderNodeLoaderDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* merges 2 condition with the given operator
|
|
||||||
*
|
|
||||||
* @param condition1 the first condition
|
|
||||||
* @param condition2 the second condition
|
|
||||||
* @param operator the operator ("&&" or "||&)
|
|
||||||
* @return the merged condition
|
|
||||||
*/
|
|
||||||
public String mergeConditions(String condition1, String condition2, String operator) {
|
|
||||||
if (condition1 != null) {
|
|
||||||
if (condition2 == null) {
|
|
||||||
return condition1;
|
|
||||||
} else {
|
|
||||||
String mergedCondition = "(" + condition1 + ") " + operator + " (" + condition2 + ")";
|
|
||||||
return mergedCondition;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return condition2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches a variable in a list from its name and merges the conditions of the
|
* Searches a variable in a list from its name and merges the conditions of the
|
||||||
|
@ -4,7 +4,7 @@ import com.jme3.app.SimpleApplication;
|
|||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
import com.jme3.material.Technique;
|
import com.jme3.material.Technique;
|
||||||
import com.jme3.material.TechniqueDef;
|
import com.jme3.material.TechniqueDef;
|
||||||
import com.jme3.math.ColorRGBA;
|
import com.jme3.math.*;
|
||||||
import com.jme3.scene.Geometry;
|
import com.jme3.scene.Geometry;
|
||||||
import com.jme3.scene.shape.Box;
|
import com.jme3.scene.shape.Box;
|
||||||
import com.jme3.shader.Shader;
|
import com.jme3.shader.Shader;
|
||||||
@ -24,7 +24,7 @@ public class TestShaderNodes extends SimpleApplication {
|
|||||||
flyCam.setMoveSpeed(20);
|
flyCam.setMoveSpeed(20);
|
||||||
Logger.getLogger("com.jme3").setLevel(Level.WARNING);
|
Logger.getLogger("com.jme3").setLevel(Level.WARNING);
|
||||||
Box boxshape1 = new Box(1f, 1f, 1f);
|
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");
|
Texture tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg");
|
||||||
|
|
||||||
Material mat = new Material(assetManager, "Common/MatDefs/Misc/UnshadedNodes.j3md");
|
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.setColor("Color", ColorRGBA.Yellow);
|
||||||
mat.setTexture("ColorMap", tex);
|
mat.setTexture("ColorMap", tex);
|
||||||
cube_tex.setMaterial(mat);
|
cube.setMaterial(mat);
|
||||||
rootNode.attachChild(cube_tex);
|
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,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);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package jme3test.material;
|
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication;
|
||||||
|
import com.jme3.material.*;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
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 TestShaderNodesModifApi extends SimpleApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
TestShaderNodesModifApi app = new TestShaderNodesModifApi();
|
||||||
|
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,"jme3test/matdefs/test.j3md");
|
||||||
|
mb.addMatParam(VarType.Vector4, "Color2");
|
||||||
|
mb.addMatParam(VarType.Texture2D, "Texture");
|
||||||
|
|
||||||
|
mb.technique().inlineVertexNode("vec2","TexCoord", "%texIn")
|
||||||
|
.inputs(
|
||||||
|
mb.map("texIn", VertexBuffer.Type.TexCoord)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
mb.technique().inlineFragmentNode("vec4","TextureFetch","texture2D(%tex, %texCoord)")
|
||||||
|
.inputs(
|
||||||
|
mb.map("tex", "MatParam.Texture"),
|
||||||
|
mb.map("texCoord", "TexCoord.result")
|
||||||
|
).outputs(
|
||||||
|
mb.map("result", "Global.color")
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
mb.technique().addNode("ColorMult2", "ColorMult", "jme3test/matdefs/ColorMult.frag")
|
||||||
|
.inputs(
|
||||||
|
mb.map("color1", "ColorMult.result"),
|
||||||
|
mb.map("color2", "MatParam.Color2"),
|
||||||
|
mb.map("color3", "TextureFetch.result"))
|
||||||
|
.outputs(
|
||||||
|
mb.map("result", "Global.color")
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO we need a way to order the nodes. They could be sorted by scanning the inputs and outputs and building a node tree, then sort it with a topological sort,
|
||||||
|
// but that won't work in some cases when node output is the Global color or Global position. So we'd need a fallback to manually order the nodes.
|
||||||
|
// So as long as we implement the fallback... The sort is maybe not worth it.
|
||||||
|
// API could be mb.technique().moveNode("Node1").before("Node2") or after("Node2")...
|
||||||
|
// or something like mb.technique().setOrder("Node1", "Node2", etc...) but this would require the user to know all the existing nodes.
|
||||||
|
// or maybe both...
|
||||||
|
|
||||||
|
Material mat = mb.build();
|
||||||
|
|
||||||
|
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.setColor("Color2", ColorRGBA.Red);
|
||||||
|
mat.setTexture("Texture", tex);
|
||||||
|
cube.setMaterial(mat);
|
||||||
|
cube.move(0, 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