Implementing value mappings for shader nodes. (#786)

* implemented value mappings for shader nodes.
empirephoenix-patch-1
Alexandr Brui 7 years ago committed by Rémy Bouquet
parent 7e66911901
commit a8c7a85fc1
  1. 65
      jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java
  2. 5
      jme3-core/src/main/java/com/jme3/shader/ShaderGenerator.java
  3. 100
      jme3-core/src/main/java/com/jme3/shader/ShaderNode.java
  4. 2
      jme3-core/src/main/java/com/jme3/shader/ShaderNodeVariable.java
  5. 128
      jme3-core/src/main/java/com/jme3/shader/VariableMapping.java
  6. 392
      jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java
  7. 93
      jme3-plugins/src/main/java/com/jme3/material/plugin/export/materialdef/J3mdTechniqueDefWriter.java

@ -35,6 +35,7 @@ import com.jme3.asset.AssetManager;
import com.jme3.material.ShaderGenerationInfo; import com.jme3.material.ShaderGenerationInfo;
import com.jme3.material.plugins.ConditionParser; import com.jme3.material.plugins.ConditionParser;
import com.jme3.shader.Shader.ShaderType; import com.jme3.shader.Shader.ShaderType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -50,6 +51,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
* the indentation characters tabulation characters * the indentation characters 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";
protected ShaderNodeVariable inPosTmp; protected ShaderNodeVariable inPosTmp;
/** /**
@ -99,7 +101,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
declareAttribute(source, var); declareAttribute(source, var);
} }
if (!inPosition) { if (!inPosition) {
inPosTmp = new ShaderNodeVariable("vec3", "inPosition"); inPosTmp = new ShaderNodeVariable("vec3", "inPosition");
declareAttribute(source, inPosTmp); declareAttribute(source, inPosTmp);
} }
@ -246,7 +248,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
//Variables fed with a sampler matparam or world param are replaced by the matparam itself //Variables fed with a sampler matparam or world param are replaced by the matparam itself
//It avoids issue with samplers that have to be uniforms. //It avoids issue with samplers that have to be uniforms.
if (isWorldOrMaterialParam(rightVariable) && rightVariable.getType().startsWith("sampler")) { if (rightVariable != null && isWorldOrMaterialParam(rightVariable) && rightVariable.getType().startsWith("sampler")) {
nodeSource = replace(nodeSource, leftVariable, rightVariable.getPrefix() + rightVariable.getName()); nodeSource = replace(nodeSource, leftVariable, rightVariable.getPrefix() + rightVariable.getName());
} else { } else {
@ -415,56 +417,71 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
} }
/** /**
* Appends a mapping to the source, embed in a conditional block if needed, * Appends a mapping to the source, embed in a conditional block if needed,
* with variables nameSpaces and swizzle. * with variables nameSpaces and swizzle.
*
* @param mapping the VariableMapping to append * @param mapping the VariableMapping to append
* @param source the StringBuilder to use * @param source the StringBuilder to use
*/ */
protected void map(VariableMapping mapping, StringBuilder source) { protected void map(VariableMapping mapping, StringBuilder source) {
final ShaderNodeVariable leftVariable = mapping.getLeftVariable();
final ShaderNodeVariable rightVariable = mapping.getRightVariable();
final String rightExpression = mapping.getRightExpression();
startCondition(mapping.getCondition(), source); startCondition(mapping.getCondition(), source);
appendIndent(source); appendIndent(source);
if (!mapping.getLeftVariable().isShaderOutput()) { if (!leftVariable.isShaderOutput()) {
source.append(mapping.getLeftVariable().getType()); source.append(leftVariable.getType());
source.append(" "); source.append(" ");
} }
source.append(mapping.getLeftVariable().getNameSpace()); source.append(leftVariable.getNameSpace());
source.append("_"); source.append("_");
source.append(mapping.getLeftVariable().getName()); source.append(leftVariable.getName());
if (mapping.getLeftVariable().getMultiplicity() != null){ if (leftVariable.getMultiplicity() != null){
source.append("["); source.append("[");
source.append(mapping.getLeftVariable().getMultiplicity()); source.append(leftVariable.getMultiplicity());
source.append("]"); source.append("]");
} }
//left swizzle, the variable can't be declared and assigned on the same line. // left swizzle, the variable can't be declared and assigned on the same line.
if (mapping.getLeftSwizzling().length() > 0) { if (mapping.getLeftSwizzling().length() > 0) {
//initialize the declared variable to 0.0 //initialize the declared variable to 0.0
source.append(" = "); source.append(" = ");
source.append(mapping.getLeftVariable().getType()); source.append(leftVariable.getType());
source.append("(0.0);\n"); source.append("(0.0);\n");
appendIndent(source); appendIndent(source);
//assign the value on a new line // assign the value on a new line
source.append(mapping.getLeftVariable().getNameSpace()); source.append(leftVariable.getNameSpace());
source.append("_"); source.append("_");
source.append(mapping.getLeftVariable().getName()); source.append(leftVariable.getName());
source.append("."); source.append(".");
source.append(mapping.getLeftSwizzling()); source.append(mapping.getLeftSwizzling());
} }
source.append(" = "); source.append(" = ");
String namePrefix = getAppendableNameSpace(mapping.getRightVariable());
source.append(namePrefix); if (rightVariable != null) {
source.append(mapping.getRightVariable().getPrefix());
source.append(mapping.getRightVariable().getName()); String namePrefix = getAppendableNameSpace(rightVariable);
if (mapping.getRightSwizzling().length() > 0) { source.append(namePrefix);
source.append("."); source.append(rightVariable.getPrefix());
source.append(mapping.getRightSwizzling()); source.append(rightVariable.getName());
if (mapping.getRightSwizzling().length() > 0) {
source.append(".");
source.append(mapping.getRightSwizzling());
}
} else {
source.append(rightExpression);
} }
source.append(";\n"); source.append(";\n");
endCondition(mapping.getCondition(), source); endCondition(mapping.getCondition(), source);
} }
/** /**
* replaces a variable name in a shaderNode source code by prefixing it * replaces a variable name in a shaderNode source code by prefixing it
* with its nameSpace and "_" if needed. * with its nameSpace and "_" if needed.
* @param nodeSource the source to modify * @param nodeSource the source to modify
* @param var the variable to replace * @param var the variable to replace

@ -50,6 +50,11 @@ import java.util.regex.Pattern;
*/ */
public abstract class ShaderGenerator { public abstract class ShaderGenerator {
public static final String NAME_SPACE_GLOBAL = "Global";
public static final String NAME_SPACE_VERTEX_ATTRIBUTE = "Attr";
public static final String NAME_SPACE_MAT_PARAM = "MatParam";
public static final String NAME_SPACE_WORLD_PARAM = "WorldParam";
/** /**
* the asset manager * the asset manager
*/ */

@ -31,11 +31,8 @@
*/ */
package com.jme3.shader; package com.jme3.shader;
import com.jme3.export.InputCapsule; import com.jme3.export.*;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -59,15 +56,16 @@ public class ShaderNode implements Savable, Cloneable {
private String name; private String name;
private ShaderNodeDefinition definition; private ShaderNodeDefinition definition;
private String condition; private String condition;
private List<VariableMapping> inputMapping = new ArrayList<VariableMapping>();
private List<VariableMapping> outputMapping = new ArrayList<VariableMapping>(); private List<VariableMapping> inputMapping = new ArrayList<>();
private List<VariableMapping> outputMapping = new ArrayList<>();
/** /**
* creates a ShaderNode * Creates a shader node.
* *
* @param name the name * @param name the name.
* @param definition the ShaderNodeDefinition * @param definition the shader node definition.
* @param condition the condition to activate this node * @param condition the condition to activate this node.
*/ */
public ShaderNode(String name, ShaderNodeDefinition definition, String condition) { public ShaderNode(String name, ShaderNodeDefinition definition, String condition) {
this.name = name; this.name = name;
@ -76,12 +74,13 @@ public class ShaderNode implements Savable, Cloneable {
} }
/** /**
* creates a ShaderNode * Creates a shader node.
*/ */
public ShaderNode() { public ShaderNode() {
} }
/** /**
* Gets the name of the node.
* *
* @return the name of the node * @return the name of the node
*/ */
@ -90,82 +89,83 @@ public class ShaderNode implements Savable, Cloneable {
} }
/** /**
* sets the name of th node * Sets the name of the node.
* *
* @param name the name * @param name the name of the node.
*/ */
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
/** /**
* returns the definition * Returns the shader node definition.
* *
* @return the ShaderNodeDefinition * @return the shader node definition.
*/ */
public ShaderNodeDefinition getDefinition() { public ShaderNodeDefinition getDefinition() {
return definition; return definition;
} }
/** /**
* sets the definition * Sets the shader node definition.
* *
* @param definition the ShaderNodeDefinition * @param definition the shader node definition.
*/ */
public void setDefinition(ShaderNodeDefinition definition) { public void setDefinition(ShaderNodeDefinition definition) {
this.definition = definition; this.definition = definition;
} }
/** /**
* Gets the condition.
* *
* @return the condition * @return the condition.
*/ */
public String getCondition() { public String getCondition() {
return condition; return condition;
} }
/** /**
* sets the condition * Sets the condition.
* *
* @param condition the condition * @param condition the condition.
*/ */
public void setCondition(String condition) { public void setCondition(String condition) {
this.condition = condition; this.condition = condition;
} }
/** /**
* return a list of VariableMapping representing the input mappings of this * Returns a list of variable mapping representing the input mappings of this
* node * node.
* *
* @return the input mappings * @return the input mappings.
*/ */
public List<VariableMapping> getInputMapping() { public List<VariableMapping> getInputMapping() {
return inputMapping; return inputMapping;
} }
/** /**
* sets the input mappings * Sets the input mappings.
* *
* @param inputMapping the input mappings * @param inputMapping the input mappings.
*/ */
public void setInputMapping(List<VariableMapping> inputMapping) { public void setInputMapping(List<VariableMapping> inputMapping) {
this.inputMapping = inputMapping; this.inputMapping = inputMapping;
} }
/** /**
* return a list of VariableMapping representing the output mappings of this * Returns a list of variable mapping representing the output mappings of this
* node * node.
* *
* @return the output mappings * @return the output mappings.
*/ */
public List<VariableMapping> getOutputMapping() { public List<VariableMapping> getOutputMapping() {
return outputMapping; return outputMapping;
} }
/** /**
* sets the output mappings * Sets the output mappings.
* *
* @param outputMapping the output mappings * @param outputMapping the output mappings.
*/ */
public void setOutputMapping(List<VariableMapping> outputMapping) { public void setOutputMapping(List<VariableMapping> outputMapping) {
this.outputMapping = outputMapping; this.outputMapping = outputMapping;
@ -179,7 +179,7 @@ public class ShaderNode implements Savable, Cloneable {
*/ */
@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", "");
oc.write(definition, "definition", null); oc.write(definition, "definition", null);
oc.write(condition, "condition", null); oc.write(condition, "condition", null);
@ -188,14 +188,14 @@ public class ShaderNode implements Savable, Cloneable {
} }
/** /**
* jme serialization * jme serialization
* *
* @param im the importer * @param im the importer
* @throws IOException * @throws IOException
*/ */
@Override @Override
public void read(JmeImporter im) throws IOException { public void read(JmeImporter im) throws IOException {
InputCapsule ic = (InputCapsule) im.getCapsule(this); InputCapsule ic = im.getCapsule(this);
name = ic.readString("name", ""); name = ic.readString("name", "");
definition = (ShaderNodeDefinition) ic.readSavable("definition", null); definition = (ShaderNodeDefinition) ic.readSavable("definition", null);
condition = ic.readString("condition", null); condition = ic.readString("condition", null);
@ -210,24 +210,48 @@ public class ShaderNode implements Savable, Cloneable {
*/ */
@Override @Override
public String toString() { public String toString() {
return "\nShaderNode{" + "\nname=" + name + ", \ndefinition=" + definition.getName() + ", \ncondition=" + condition + ", \ninputMapping=" + inputMapping + ", \noutputMapping=" + outputMapping + '}';
final StringBuilder builder = new StringBuilder("ShaderNode:");
builder.append("\n\tname=").append(name)
.append("\n\tdefinition=").append(definition.getName())
.append("\n\tcondition=").append(condition);
if (!inputMapping.isEmpty()) {
builder.append("\n\tinputMapping:\n");
for (final VariableMapping mapping : inputMapping) {
builder.append("\t\t").append(mapping).append('\n');
}
}
if (!outputMapping.isEmpty()) {
builder.append("\n\toutputMapping:\n");
for (final VariableMapping mapping : outputMapping) {
builder.append("\t\t").append(mapping).append('\n');
}
}
if (builder.charAt(builder.length() - 1) == '\n') {
builder.delete(builder.length() - 1, builder.length());
}
return builder.toString();
} }
@Override @Override
public ShaderNode clone() throws CloneNotSupportedException { public ShaderNode clone() throws CloneNotSupportedException {
ShaderNode clone = (ShaderNode) super.clone(); ShaderNode clone = (ShaderNode) super.clone();
//No need to clone the definition. // No need to clone the definition.
clone.definition = definition; clone.definition = definition;
clone.inputMapping = new ArrayList<>(); clone.inputMapping = new ArrayList<>();
for (VariableMapping variableMapping : inputMapping) { for (VariableMapping variableMapping : inputMapping) {
clone.inputMapping.add((VariableMapping) variableMapping.clone()); clone.inputMapping.add(variableMapping.clone());
} }
clone.outputMapping = new ArrayList<>(); clone.outputMapping = new ArrayList<>();
for (VariableMapping variableMapping : outputMapping) { for (VariableMapping variableMapping : outputMapping) {
clone.outputMapping.add((VariableMapping) variableMapping.clone()); clone.outputMapping.add(variableMapping.clone());
} }
return clone; return clone;

@ -299,7 +299,7 @@ public class ShaderNodeVariable implements Savable, Cloneable {
@Override @Override
public String toString() { public String toString() {
return "\n" + type + ' ' + (nameSpace != null ? (nameSpace + '.') : "") + name; return type + ' ' + (nameSpace != null ? (nameSpace + '.') : "") + name;
} }
/** /**

@ -31,15 +31,13 @@
*/ */
package com.jme3.shader; package com.jme3.shader;
import com.jme3.export.InputCapsule; import com.jme3.export.*;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
/** /**
* represents a mapping between 2 ShaderNodeVariables * Represents a mapping between 2 shader node variables or a left shader node variable and a value expression.
* *
* @author Nehon * @author Nehon
*/ */
@ -47,68 +45,91 @@ public class VariableMapping implements Savable, Cloneable {
private ShaderNodeVariable leftVariable; private ShaderNodeVariable leftVariable;
private ShaderNodeVariable rightVariable; private ShaderNodeVariable rightVariable;
private String rightExpression;
private String condition; private String condition;
private String leftSwizzling = ""; private String leftSwizzling = "";
private String rightSwizzling = ""; private String rightSwizzling = "";
/** /**
* creates a VariableMapping * Creates a VariableMapping.
*/ */
public VariableMapping() { public VariableMapping() {
} }
/** /**
* creates a VariableMapping * Creates a VariableMapping.
* *
* @param leftVariable the left hand side variable of the expression * @param leftVariable the left hand side variable of the expression
* @param leftSwizzling the swizzling of the left variable * @param leftSwizzling the swizzling of the left variable
* @param rightVariable the right hand side variable of the expression * @param rightVariable the right hand side variable of the expression
* @param rightSwizzling the swizzling of the right variable * @param rightSwizzling the swizzling of the right variable
* @param condition the condition for this mapping * @param condition the condition for this mapping
*/ */
public VariableMapping(ShaderNodeVariable leftVariable, String leftSwizzling, ShaderNodeVariable rightVariable, String rightSwizzling, String condition) { public VariableMapping(ShaderNodeVariable leftVariable, String leftSwizzling, ShaderNodeVariable rightVariable,
String rightSwizzling, String condition) {
this.leftVariable = leftVariable; this.leftVariable = leftVariable;
this.rightVariable = rightVariable; this.rightVariable = rightVariable;
this.condition = condition; this.condition = condition;
this.leftSwizzling = leftSwizzling; this.leftSwizzling = Objects.requireNonNull(leftSwizzling);
this.rightSwizzling = rightSwizzling; this.rightSwizzling = Objects.requireNonNull(rightSwizzling);
} }
/** /**
* Gets the left variable.
* *
* @return the left variable * @return the left variable.
*/ */
public ShaderNodeVariable getLeftVariable() { public ShaderNodeVariable getLeftVariable() {
return leftVariable; return leftVariable;
} }
/** /**
* sets the left variable * Sets the left variable.
* *
* @param leftVariable the left variable * @param leftVariable the left variable.
*/ */
public void setLeftVariable(ShaderNodeVariable leftVariable) { public void setLeftVariable(ShaderNodeVariable leftVariable) {
this.leftVariable = leftVariable; this.leftVariable = leftVariable;
} }
/** /**
* Gets the right variable.
* *
* @return the right variable * @return the right variable or null.
*/ */
public ShaderNodeVariable getRightVariable() { public ShaderNodeVariable getRightVariable() {
return rightVariable; return rightVariable;
} }
/** /**
* sets the right variable * Sets the right variable.
* *
* @param rightVariable the right variable * @param rightVariable the right variable.
*/ */
public void setRightVariable(ShaderNodeVariable rightVariable) { public void setRightVariable(ShaderNodeVariable rightVariable) {
this.rightVariable = rightVariable; this.rightVariable = rightVariable;
} }
/** /**
* Gets the right expression.
*
* @return the right expression or null.
*/
public String getRightExpression() {
return rightExpression;
}
/**
* Sets the right expression.
*
* @param rightExpression the right expression.
*/
public void setRightExpression(final String rightExpression) {
this.rightExpression = rightExpression;
}
/**
* Gets the condition.
* *
* @return the condition * @return the condition
*/ */
@ -117,46 +138,48 @@ public class VariableMapping implements Savable, Cloneable {
} }
/** /**
* sets the condition * Sets the condition.
* *
* @param condition the condition * @param condition the condition or null.
*/ */
public void setCondition(String condition) { public void setCondition(String condition) {
this.condition = condition; this.condition = condition;
} }
/** /**
* Gets the left swizzle.
* *
* @return the left swizzle * @return the left swizzle or empty string.
*/ */
public String getLeftSwizzling() { public String getLeftSwizzling() {
return leftSwizzling; return leftSwizzling;
} }
/** /**
* sets the left swizzle * Sets the left swizzle.
* *
* @param leftSwizzling the left swizzle * @param leftSwizzling the left swizzle.
*/ */
public void setLeftSwizzling(String leftSwizzling) { public void setLeftSwizzling(String leftSwizzling) {
this.leftSwizzling = leftSwizzling; this.leftSwizzling = Objects.requireNonNull(leftSwizzling);
} }
/** /**
* Gets the right swizzle.
* *
* @return the right swizzle * @return the right swizzle or empty string.
*/ */
public String getRightSwizzling() { public String getRightSwizzling() {
return rightSwizzling; return rightSwizzling;
} }
/** /**
* sets the right swizzle * Sets the right swizzle.
* *
* @param rightSwizzling the right swizzle * @param rightSwizzling the right swizzle.
*/ */
public void setRightSwizzling(String rightSwizzling) { public void setRightSwizzling(String rightSwizzling) {
this.rightSwizzling = rightSwizzling; this.rightSwizzling = Objects.requireNonNull(rightSwizzling);
} }
/** /**
@ -167,9 +190,10 @@ public class VariableMapping implements Savable, Cloneable {
*/ */
@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(leftVariable, "leftVariable", null); oc.write(leftVariable, "leftVariable", null);
oc.write(rightVariable, "rightVariable", null); oc.write(rightVariable, "rightVariable", null);
oc.write(rightExpression, "rightExpression", null);
oc.write(condition, "condition", ""); oc.write(condition, "condition", "");
oc.write(leftSwizzling, "leftSwizzling", ""); oc.write(leftSwizzling, "leftSwizzling", "");
oc.write(rightSwizzling, "rightSwizzling", ""); oc.write(rightSwizzling, "rightSwizzling", "");
@ -183,9 +207,10 @@ public class VariableMapping implements Savable, Cloneable {
*/ */
@Override @Override
public void read(JmeImporter im) throws IOException { public void read(JmeImporter im) throws IOException {
InputCapsule ic = (InputCapsule) im.getCapsule(this); InputCapsule ic = im.getCapsule(this);
leftVariable = (ShaderNodeVariable) ic.readSavable("leftVariable", null); leftVariable = (ShaderNodeVariable) ic.readSavable("leftVariable", null);
rightVariable = (ShaderNodeVariable) ic.readSavable("rightVariable", null); rightVariable = (ShaderNodeVariable) ic.readSavable("rightVariable", null);
rightExpression = ic.readString("rightExpression", null);
condition = ic.readString("condition", ""); condition = ic.readString("condition", "");
leftSwizzling = ic.readString("leftSwizzling", ""); leftSwizzling = ic.readString("leftSwizzling", "");
rightSwizzling = ic.readString("rightSwizzling", ""); rightSwizzling = ic.readString("rightSwizzling", "");
@ -193,16 +218,45 @@ public class VariableMapping implements Savable, Cloneable {
@Override @Override
public String toString() { public String toString() {
return "\n{" + leftVariable.toString() + (leftSwizzling.length() > 0 ? ("." + leftSwizzling) : "") + " = " + rightVariable.getType() + " " + rightVariable.getNameSpace() + "." + rightVariable.getName() + (rightSwizzling.length() > 0 ? ("." + rightSwizzling) : "") + " : " + condition + "}";
final StringBuilder builder = new StringBuilder(leftVariable.toString());
if (!leftSwizzling.isEmpty()) {
builder.append('.').append(leftSwizzling);
}
builder.append(" = ");
if (rightVariable != null) {
builder.append(rightVariable.getType())
.append(' ')
.append(rightVariable.getNameSpace())
.append('.')
.append(rightVariable.getName());
if (!rightSwizzling.isEmpty()) {
builder.append('.').append(rightSwizzling);
}
} else if (rightExpression != null) {
builder.append(rightExpression);
}
if (condition != null && !condition.isEmpty()) {
builder.append(" : ").append(condition);
}
return builder.toString();
} }
@Override @Override
protected VariableMapping clone() throws CloneNotSupportedException { protected VariableMapping clone() throws CloneNotSupportedException {
VariableMapping clone = (VariableMapping) super.clone(); VariableMapping clone = (VariableMapping) super.clone();
clone.leftVariable = leftVariable.clone(); clone.leftVariable = leftVariable.clone();
clone.rightVariable = rightVariable.clone(); if (rightVariable != null) {
clone.rightVariable = rightVariable.clone();
}
return clone; return clone;
} }
} }

@ -38,15 +38,10 @@ import com.jme3.material.MatParam;
import com.jme3.material.MaterialDef; import com.jme3.material.MaterialDef;
import com.jme3.material.ShaderGenerationInfo; import com.jme3.material.ShaderGenerationInfo;
import com.jme3.material.TechniqueDef; import com.jme3.material.TechniqueDef;
import com.jme3.shader.Shader; import com.jme3.shader.Shader.ShaderType;
import com.jme3.shader.ShaderNode; import com.jme3.shader.*;
import com.jme3.shader.ShaderNodeDefinition;
import com.jme3.shader.ShaderNodeVariable;
import com.jme3.shader.ShaderUtils;
import com.jme3.shader.UniformBinding;
import com.jme3.shader.VarType;
import com.jme3.shader.VariableMapping;
import com.jme3.util.blockparser.Statement; import com.jme3.util.blockparser.Statement;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -65,6 +60,9 @@ import java.util.Map;
*/ */
public class ShaderNodeLoaderDelegate { public class ShaderNodeLoaderDelegate {
private static final boolean[] IM_HAS_NAME_SPACE = {false, true};
private static final boolean[] OM_HAS_NAME_SPACE = {true, false};
protected Map<String, ShaderNodeDefinition> nodeDefinitions; protected Map<String, ShaderNodeDefinition> nodeDefinitions;
protected Map<String, ShaderNode> nodes; protected Map<String, ShaderNode> nodes;
protected ShaderNodeDefinition shaderNodeDefinition; protected ShaderNodeDefinition shaderNodeDefinition;
@ -165,7 +163,7 @@ public class ShaderNodeLoaderDelegate {
if (line.startsWith("Type")) { if (line.startsWith("Type")) {
String type = line.substring(line.lastIndexOf(':') + 1).trim(); String type = line.substring(line.lastIndexOf(':') + 1).trim();
shaderNodeDefinition.setType(Shader.ShaderType.valueOf(type)); shaderNodeDefinition.setType(ShaderType.valueOf(type));
} else if (line.startsWith("Shader ")) { } else if (line.startsWith("Shader ")) {
readShaderStatement(statement); readShaderStatement(statement);
shaderNodeDefinition.getShadersLanguage().add(shaderLanguage); shaderNodeDefinition.getShadersLanguage().add(shaderLanguage);
@ -267,61 +265,86 @@ public class ShaderNodeLoaderDelegate {
* @throws IOException * @throws IOException
*/ */
protected void readShaderNode(List<Statement> statements) throws IOException { protected void readShaderNode(List<Statement> statements) throws IOException {
final ShaderGenerationInfo generationInfo = techniqueDef.getShaderGenerationInfo();
final List<String> unusedNodes = generationInfo.getUnusedNodes();
for (Statement statement : statements) { for (Statement statement : statements) {
String line = statement.getLine(); String line = statement.getLine();
String[] split = statement.getLine().split("[ \\{]"); String[] split = statement.getLine().split("[ \\{]");
if (line.startsWith("Definition")) { if (line.startsWith("Definition")) {
ShaderNodeDefinition def = findDefinition(statement); ShaderNodeDefinition def = findDefinition(statement);
shaderNode.setDefinition(def); shaderNode.setDefinition(def);
if(def.isNoOutput()){ if(def.isNoOutput()){
techniqueDef.getShaderGenerationInfo().getUnusedNodes().remove(shaderNode.getName()); unusedNodes.remove(shaderNode.getName());
} }
} else if (line.startsWith("Condition")) { } else if (line.startsWith("Condition")) {
String condition = line.substring(line.lastIndexOf(":") + 1).trim(); String condition = line.substring(line.lastIndexOf(":") + 1).trim();
extractCondition(condition, statement); extractCondition(condition, statement);
shaderNode.setCondition(conditionParser.getFormattedExpression()); shaderNode.setCondition(conditionParser.getFormattedExpression());
} else if (line.startsWith("InputMapping")) { } else if (line.startsWith("InputMappings")) {
for (Statement statement1 : statement.getContents()) { for (final Statement subStatement : statement.getContents()) {
VariableMapping mapping = readInputMapping(statement1);
techniqueDef.getShaderGenerationInfo().getUnusedNodes().remove(mapping.getRightVariable().getNameSpace()); VariableMapping mapping = readInputMapping(subStatement);
final ShaderNodeVariable rightVariable = mapping.getRightVariable();
if (rightVariable != null) {
unusedNodes.remove(rightVariable.getNameSpace());
}
shaderNode.getInputMapping().add(mapping); shaderNode.getInputMapping().add(mapping);
} }
} else if (line.startsWith("OutputMapping")) { } else if (line.startsWith("OutputMappings")) {
for (Statement statement1 : statement.getContents()) { for (Statement statement1 : statement.getContents()) {
VariableMapping mapping = readOutputMapping(statement1); VariableMapping mapping = readOutputMapping(statement1);
techniqueDef.getShaderGenerationInfo().getUnusedNodes().remove(shaderNode.getName()); unusedNodes.remove(shaderNode.getName());
shaderNode.getOutputMapping().add(mapping); shaderNode.getOutputMapping().add(mapping);
} }
} else { } else {
throw new MatParseException("ShaderNodeDefinition", split[0], statement); throw new MatParseException("ShaderNodeDefinition", split[0], statement);
} }
} }
} }
/** /**
* reads a mapping statement. Sets the nameSpace, name and swizzling of the * Reads a mapping statement. Sets the nameSpace, name and swizzling of the
* left variable. Sets the name, nameSpace and swizzling of the right * left variable. Sets the name, nameSpace and swizzling of the right
* variable types will be determined later. * variable types will be determined later. Also, we can have the right part as expression.
* * <pre>
* <code> * Format variable to variable: &lt;nameSpace&gt;.&lt;varName&gt;[.&lt;swizzling&gt;] = &lt;nameSpace&gt;.&lt;varName&gt;[.&lt;swizzling&gt;][:Condition]
* Format : <nameSpace>.<varName>[.<swizzling>] = * Format expression to variable: &lt;nameSpace&gt;.&lt;varName&gt;[.&lt;swizzling&gt;] = %% expression %% [:Condition]
* <nameSpace>.<varName>[.<swizzling>][:Condition] * </pre>
* </code>
* *
* @param statement the statement to read * @param statement the statement to read.
* @return the read mapping * @return the read mapping.
* @throws MatParseException if the statement isn't valid.
*/ */
protected VariableMapping parseMapping(Statement statement, boolean[] hasNameSpace) throws IOException { protected VariableMapping parseMapping(Statement statement, boolean[] hasNameSpace) throws MatParseException {
VariableMapping mapping = new VariableMapping(); VariableMapping mapping = new VariableMapping();
String[] cond = statement.getLine().split(":"); String[] cond = statement.getLine().split(":");
String[] vars = cond[0].split("="); String[] vars = cond[0].split("=");
checkMappingFormat(vars, statement); checkMappingFormat(vars, statement);
ShaderNodeVariable[] variables = new ShaderNodeVariable[2]; ShaderNodeVariable[] variables = new ShaderNodeVariable[2];
String[] swizzle = new String[2]; String[] swizzle = new String[2];
String rightExpression = null;
for (int i = 0; i < vars.length; i++) { for (int i = 0; i < vars.length; i++) {
String[] expression = vars[i].trim().split("\\.");
final String var = vars[i].trim();
// it seems that is expression, not variable
if (var.contains("%%")) {
rightExpression = var.substring(2, var.length() - 2);
continue;
}
String[] expression = var.split("\\.");
if (hasNameSpace[i]) { if (hasNameSpace[i]) {
if (expression.length <= 3) { if (expression.length <= 3) {
variables[i] = new ShaderNodeVariable("", expression[0].trim(), expression[1].trim()); variables[i] = new ShaderNodeVariable("", expression[0].trim(), expression[1].trim());
@ -337,13 +360,17 @@ public class ShaderNodeLoaderDelegate {
swizzle[i] = expression[1].trim(); swizzle[i] = expression[1].trim();
} }
} }
} }
mapping.setLeftVariable(variables[0]); mapping.setLeftVariable(variables[0]);
mapping.setLeftSwizzling(swizzle[0] != null ? swizzle[0] : ""); mapping.setLeftSwizzling(swizzle[0] != null ? swizzle[0] : "");
mapping.setRightVariable(variables[1]);
mapping.setRightSwizzling(swizzle[1] != null ? swizzle[1] : ""); if (rightExpression != null) {
mapping.setRightExpression(rightExpression);
} else {
mapping.setRightVariable(variables[1]);
mapping.setRightSwizzling(swizzle[1] != null ? swizzle[1] : "");
}
if (cond.length > 1) { if (cond.length > 1) {
extractCondition(cond[1], statement); extractCondition(cond[1], statement);
@ -401,11 +428,11 @@ public class ShaderNodeLoaderDelegate {
} }
/** /**
* search a variable in the given list and updates its type and namespace * Searches a variable in the given list and updates its type and namespace.
* *
* @param var the variable to update * @param var the variable to update.
* @param list the variables list * @param list the variables list.
* @return true if the variable has been found and updated * @return true if the variable has been found and updated.
*/ */
protected boolean updateVariableFromList(ShaderNodeVariable var, List<ShaderNodeVariable> list) { protected boolean updateVariableFromList(ShaderNodeVariable var, List<ShaderNodeVariable> list) {
for (ShaderNodeVariable shaderNodeVariable : list) { for (ShaderNodeVariable shaderNodeVariable : list) {
@ -420,10 +447,10 @@ public class ShaderNodeLoaderDelegate {
} }
/** /**
* updates the type of the right variable of a mapping from the type of the * Updates the type of the right variable of a mapping from the type of the
* left variable * left variable.
* *
* @param mapping the mapping to consider * @param mapping the mapping to consider.
*/ */
protected void updateRightTypeFromLeftType(VariableMapping mapping) { protected void updateRightTypeFromLeftType(VariableMapping mapping) {
String type = mapping.getLeftVariable().getType(); String type = mapping.getLeftVariable().getType();
@ -439,24 +466,25 @@ public class ShaderNodeLoaderDelegate {
} }
/** /**
* check if once a mapping expression is split by "=" the resulting array * Checks if once a mapping expression is split by "=" the resulting array
* have 2 elements * have 2 elements.
* *
* @param vars the array * @param vars the array.
* @param statement the statement * @param statement the statement.
* @throws IOException * @throws MatParseException if the array isn't correct.
*/ */
protected void checkMappingFormat(String[] vars, Statement statement) throws IOException { protected void checkMappingFormat(String[] vars, Statement statement) throws MatParseException {
if (vars.length != 2) { if (vars.length != 2) {
throw new MatParseException("Not a valid expression should be '<varName>[.<swizzling>] = <nameSpace>.<varName>[.<swizzling>][:Condition]'", statement); throw new MatParseException("Not a valid expression should be '<varName>[.<swizzling>] = " +
"<nameSpace>.<varName>[.<swizzling>][:Condition]'", statement);
} }
} }
/** /**
* finds a MatParam in the materialDef from the given name * Finds a {@link MatParam} in the {@link MaterialDef} from the given name.
* *
* @param varName the matparam name * @param varName the material param name.
* @return the MatParam * @return the found {@link MatParam} or null.
*/ */
protected MatParam findMatParam(String varName) { protected MatParam findMatParam(String varName) {
for (MatParam matParam : materialDef.getMaterialParams()) { for (MatParam matParam : materialDef.getMaterialParams()) {
@ -493,6 +521,7 @@ public class ShaderNodeLoaderDelegate {
* @return true if the param was added to the map * @return true if the param was added to the map
*/ */
protected boolean updateRightFromUniforms(UniformBinding param, VariableMapping mapping, Map<String, DeclaredVariable> map) { protected boolean updateRightFromUniforms(UniformBinding param, VariableMapping mapping, Map<String, DeclaredVariable> map) {
ShaderNodeVariable right = mapping.getRightVariable(); ShaderNodeVariable right = mapping.getRightVariable();
String name = param.toString(); String name = param.toString();
@ -513,60 +542,74 @@ public class ShaderNodeLoaderDelegate {
} }
/** /**
* updates the right variable of the given mapping from a MatParam (a * Updates the right variable of the given mapping from a {@link MatParam} (a
* WorldParam) it checks if the uniform hasn't already been loaded, add it * WorldParam) it checks if the uniform hasn't already been loaded, add it
* to the maps if not. * to the maps if not.
* *
* @param param the MatParam * @param param the mat param.
* @param mapping the mapping * @param mapping the mapping.
* @param map the map of uniforms to search into * @param map the map of uniforms to search into.
* @return true if the param was added to the map * @return true if the param was added to the map.
*/ */
public boolean updateRightFromUniforms(MatParam param, VariableMapping mapping, Map<String, DeclaredVariable> map, Statement statement) throws MatParseException { public boolean updateRightFromUniforms(MatParam param, VariableMapping mapping, Map<String, DeclaredVariable> map,
ShaderNodeVariable right = mapping.getRightVariable(); Statement statement) throws MatParseException {
final ShaderNodeVariable left = mapping.getLeftVariable();
final ShaderNodeVariable right = mapping.getRightVariable();
DeclaredVariable dv = map.get(param.getName()); DeclaredVariable dv = map.get(param.getName());
if (dv == null) { if (dv == null) {
right.setType(param.getVarType().getGlslType()); right.setType(param.getVarType().getGlslType());
right.setName(param.getName()); right.setName(param.getName());
right.setPrefix("m_"); right.setPrefix("m_");
if(mapping.getLeftVariable().getMultiplicity() != null){
if(!param.getVarType().name().endsWith("Array")){ if (left.getMultiplicity() != null) {
if (!param.getVarType().name().endsWith("Array")) {
throw new MatParseException(param.getName() + " is not of Array type", statement); throw new MatParseException(param.getName() + " is not of Array type", statement);
} }
String multiplicity = mapping.getLeftVariable().getMultiplicity();
String multiplicity = left.getMultiplicity();
try { try {
Integer.parseInt(multiplicity); Integer.parseInt(multiplicity);
} catch (NumberFormatException nfe) { } catch (final NumberFormatException nfe) {
//multiplicity is not an int attempting to find for a material parameter. // multiplicity is not an int attempting to find for a material parameter.
MatParam mp = findMatParam(multiplicity); MatParam mp = findMatParam(multiplicity);
if (mp != null) { if (mp != null) {
//It's tied to a material param, let's create a define and use this as the multiplicity // It's tied to a material param, let's create a define and use this as the multiplicity
addDefine(multiplicity, VarType.Int); addDefine(multiplicity, VarType.Int);
multiplicity = multiplicity.toUpperCase(); multiplicity = multiplicity.toUpperCase();
mapping.getLeftVariable().setMultiplicity(multiplicity); left.setMultiplicity(multiplicity);
//only declare the variable if the define is defined. // only declare the variable if the define is defined.
mapping.getLeftVariable().setCondition(mergeConditions(mapping.getLeftVariable().getCondition(), "defined(" + multiplicity + ")", "||")); left.setCondition(mergeConditions(left.getCondition(), "defined(" + multiplicity + ")", "||"));
} else { } else {
throw new MatParseException("Wrong multiplicity for variable" + mapping.getLeftVariable().getName() + ". " + multiplicity + " should be an int or a declared material parameter.", statement); throw new MatParseException("Wrong multiplicity for variable" + left.getName() + ". " +
multiplicity + " should be an int or a declared material parameter.", statement);
} }
} }
//the right variable must have the same multiplicity and the same condition.
// the right variable must have the same multiplicity and the same condition.
right.setMultiplicity(multiplicity); right.setMultiplicity(multiplicity);
right.setCondition(mapping.getLeftVariable().getCondition()); right.setCondition(left.getCondition());
} }
dv = new DeclaredVariable(right); dv = new DeclaredVariable(right);
map.put(right.getName(), dv); map.put(right.getName(), dv);
dv.addNode(shaderNode); dv.addNode(shaderNode);
mapping.setRightVariable(right); mapping.setRightVariable(right);
return true; return true;
} }
dv.addNode(shaderNode); dv.addNode(shaderNode);
mapping.setRightVariable(dv.var); mapping.setRightVariable(dv.var);
return false; return false;
} }
/** /**
* updates a variable from the Attribute list * Updates a variable from the attribute list.
* *
* @param right the variable * @param right the variable
* @param mapping the mapping * @param mapping the mapping
@ -595,11 +638,11 @@ public class ShaderNodeLoaderDelegate {
} }
/** /**
* find a variable with the given name from the list of variable * Finds a variable with the given name from the list of variable.
* *
* @param vars a list of shaderNodeVariables * @param vars the list of shader node variables.
* @param rightVarName the variable name to search for * @param rightVarName the variable name to search for.
* @return the found variable or null is not found * @return the found variable or null is not found.
*/ */
public ShaderNodeVariable findNodeOutput(List<ShaderNodeVariable> vars, String rightVarName) { public ShaderNodeVariable findNodeOutput(List<ShaderNodeVariable> vars, String rightVarName) {
ShaderNodeVariable var = null; ShaderNodeVariable var = null;
@ -612,81 +655,95 @@ public class ShaderNodeLoaderDelegate {
} }
/** /**
* extract and check a condition expression * Extracts and checks a condition expression.
* *
* @param cond the condition expression * @param condition the condition expression.
* @param statement the statement being read * @param statement the statement being read.
* @throws IOException * @throws MatParseException if the condition isn't valid.
*/ */
public void extractCondition(String cond, Statement statement) throws IOException { public void extractCondition(String condition, Statement statement) throws MatParseException {
List<String> defines = conditionParser.extractDefines(cond); List<String> defines = conditionParser.extractDefines(condition);
for (String string : defines) { for (String string : defines) {
MatParam param = findMatParam(string); MatParam param = findMatParam(string);
if (param != null) { if (param != null) {
addDefine(param.getName(), param.getVarType()); addDefine(param.getName(), param.getVarType());
} else { } else {
throw new MatParseException("Invalid condition, condition must match a Material Parameter named " + cond, statement); throw new MatParseException("Invalid condition, condition must match a Material Parameter named " + condition, statement);
} }
} }
} }
/** /**
* reads an input mapping * Reads an input mapping.
* *
* @param statement1 the statement being read * @param statement the statement being read.
* @return the mapping * @return the variable mapping.
* @throws IOException * @throws MatParseException if we have a problem with parsing input mapping statement.
*/ */
public VariableMapping readInputMapping(Statement statement1) throws IOException { public VariableMapping readInputMapping(Statement statement) throws MatParseException {
VariableMapping mapping = null;
VariableMapping mapping;
try { try {
mapping = parseMapping(statement1, new boolean[]{false, true}); mapping = parseMapping(statement, IM_HAS_NAME_SPACE);
} catch (Exception e) { } catch (final Exception e) {
throw new MatParseException("Unexpected mapping format", statement1, e); throw new MatParseException("Unexpected mapping format", statement, e);
} }
ShaderNodeVariable left = mapping.getLeftVariable();
ShaderNodeVariable right = mapping.getRightVariable(); final ShaderNodeDefinition definition = shaderNode.getDefinition();
if (!updateVariableFromList(left, shaderNode.getDefinition().getInputs())) { final ShaderNodeVariable left = mapping.getLeftVariable();
throw new MatParseException(left.getName() + " is not an input variable of " + shaderNode.getDefinition().getName(), statement1); final ShaderNodeVariable right = mapping.getRightVariable();
final String expression = mapping.getRightExpression();
if (!updateVariableFromList(left, definition.getInputs())) {
throw new MatParseException(left.getName() + " is not an input variable of " + definition.getName(), statement);
} else if (left.getType().startsWith("sampler") && (right == null || !right.getNameSpace().equals(ShaderGenerator.NAME_SPACE_MAT_PARAM))) {
throw new MatParseException("Samplers can only be assigned to MatParams", statement);
} }
if (left.getType().startsWith("sampler") && !right.getNameSpace().equals("MatParam")) { if (right == null && expression == null) {
throw new MatParseException("Samplers can only be assigned to MatParams", statement1); throw new MatParseException("The mapping doesn't have a right variable or a right expression.", statement);
} }
if (right.getNameSpace().equals("Global")) { if (right == null) {
right.setType("vec4");//Globals are all vec4 for now (maybe forever...) return mapping;
storeGlobal(right, statement1); }
} else if (right.getNameSpace().equals("Attr")) { if (right.getNameSpace().equals(ShaderGenerator.NAME_SPACE_GLOBAL)) {
if (shaderNode.getDefinition().getType() == Shader.ShaderType.Fragment) { right.setType("vec4"); // Globals are all vec4 for now (maybe forever...)
throw new MatParseException("Cannot have an attribute as input in a fragment shader" + right.getName(), statement1); storeGlobal(right, statement);
} else if (right.getNameSpace().equals(ShaderGenerator.NAME_SPACE_VERTEX_ATTRIBUTE)) {
if (definition.getType() == ShaderType.Fragment) {
throw new MatParseException("Cannot have an attribute as input in a fragment shader" + right.getName(), statement);
} }
updateVarFromAttributes(mapping.getRightVariable(), mapping); updateVarFromAttributes(mapping.getRightVariable(), mapping);
storeAttribute(mapping.getRightVariable()); storeAttribute(mapping.getRightVariable());
} else if (right.getNameSpace().equals("MatParam")) { } else if (right.getNameSpace().equals(ShaderGenerator.NAME_SPACE_MAT_PARAM)) {
MatParam param = findMatParam(right.getName()); MatParam param = findMatParam(right.getName());
if (param == null) { if (param == null) {
throw new MatParseException("Could not find a Material Parameter named " + right.getName(), statement1); throw new MatParseException("Could not find a Material Parameter named " + right.getName(), statement);
} }
if (shaderNode.getDefinition().getType() == Shader.ShaderType.Vertex) {
if (updateRightFromUniforms(param, mapping, vertexDeclaredUniforms, statement1)) { if (definition.getType() == ShaderType.Vertex) {
updateMaterialTextureType(statement1, mapping, left, param); if (updateRightFromUniforms(param, mapping, vertexDeclaredUniforms, statement)) {
updateMaterialTextureType(statement, mapping, left, param);
storeVertexUniform(mapping.getRightVariable()); storeVertexUniform(mapping.getRightVariable());
} }
} else { } else {
if (updateRightFromUniforms(param, mapping, fragmentDeclaredUniforms, statement1)) { if (updateRightFromUniforms(param, mapping, fragmentDeclaredUniforms, statement)) {
updateMaterialTextureType(statement1, mapping, left, param); updateMaterialTextureType(statement, mapping, left, param);
storeFragmentUniform(mapping.getRightVariable()); storeFragmentUniform(mapping.getRightVariable());
} }
} }
} else if (right.getNameSpace().equals("WorldParam")) { } else if (right.getNameSpace().equals(ShaderGenerator.NAME_SPACE_WORLD_PARAM)) {
UniformBinding worldParam = findWorldParam(right.getName()); UniformBinding worldParam = findWorldParam(right.getName());
if (worldParam == null) { if (worldParam == null) {
throw new MatParseException("Could not find a World Parameter named " + right.getName(), statement1); throw new MatParseException("Could not find a World Parameter named " + right.getName(), statement);
} }
if (shaderNode.getDefinition().getType() == Shader.ShaderType.Vertex) {
if (definition.getType() == ShaderType.Vertex) {
if (updateRightFromUniforms(worldParam, mapping, vertexDeclaredUniforms)) { if (updateRightFromUniforms(worldParam, mapping, vertexDeclaredUniforms)) {
storeVertexUniform(mapping.getRightVariable()); storeVertexUniform(mapping.getRightVariable());
} }
@ -697,35 +754,43 @@ public class ShaderNodeLoaderDelegate {
} }
} else { } else {
ShaderNode node = nodes.get(right.getNameSpace()); ShaderNode node = nodes.get(right.getNameSpace());
if (node == null) { if (node == null) {
throw new MatParseException("Undeclared node" + right.getNameSpace() + ". Make sure this node is declared before the current node", statement1); throw new MatParseException("Undeclared node" + right.getNameSpace() +
". Make sure this node is declared before the current node", statement);
} }
ShaderNodeVariable var = findNodeOutput(node.getDefinition().getOutputs(), right.getName()); ShaderNodeVariable var = findNodeOutput(node.getDefinition().getOutputs(), right.getName());
if (var == null) { if (var == null) {
throw new MatParseException("Cannot find output variable" + right.getName() + " form ShaderNode " + node.getName(), statement1); throw new MatParseException("Cannot find output variable" + right.getName() +
" form ShaderNode " + node.getName(), statement);
} }
right.setNameSpace(node.getName()); right.setNameSpace(node.getName());
right.setType(var.getType()); right.setType(var.getType());
right.setMultiplicity(var.getMultiplicity()); right.setMultiplicity(var.getMultiplicity());
mapping.setRightVariable(right); mapping.setRightVariable(right);
storeVaryings(node, mapping.getRightVariable());
storeVaryings(node, mapping.getRightVariable());
} }
checkTypes(mapping, statement1); checkTypes(mapping, statement);
return mapping; return mapping;
} }
/** /**
* Updated the material texture type of the variable mapping. * Updates the material texture type of the variable mapping.
* *
* @param statement the statement. * @param statement the statement.
* @param mapping the variable mapping. * @param mapping the variable mapping.
* @param left the left variable. * @param left the left variable.
* @param param the material parameter. * @param param the material parameter.
* @throws MatParseException * @throws MatParseException if the texture type isn't valid.
*/ */
private void updateMaterialTextureType(final Statement statement, final VariableMapping mapping, private void updateMaterialTextureType(final Statement statement, final VariableMapping mapping,
final ShaderNodeVariable left, final MatParam param) throws MatParseException { final ShaderNodeVariable left, final MatParam param) throws MatParseException {
@ -745,39 +810,41 @@ public class ShaderNodeLoaderDelegate {
} }
/** /**
* reads an output mapping * Reads an output mapping.
* *
* @param statement1 the statement being read * @param statement the statement being read.
* @return the mapping * @return the mapping
* @throws IOException * @throws MatParseException if we have a problem with parsing the statement.
*/ */
public VariableMapping readOutputMapping(Statement statement1) throws IOException { public VariableMapping readOutputMapping(Statement statement) throws MatParseException {
VariableMapping mapping = null;
VariableMapping mapping;
try { try {
mapping = parseMapping(statement1, new boolean[]{true, false}); mapping = parseMapping(statement, OM_HAS_NAME_SPACE);
} catch (Exception e) { } catch (final Exception e) {
throw new MatParseException("Unexpected mapping format", statement1, e); throw new MatParseException("Unexpected mapping format", statement, e);
} }
ShaderNodeVariable left = mapping.getLeftVariable();
ShaderNodeVariable right = mapping.getRightVariable();
final ShaderNodeDefinition definition = shaderNode.getDefinition();
final ShaderNodeVariable left = mapping.getLeftVariable();
final ShaderNodeVariable right = mapping.getRightVariable();
if (left.getType().startsWith("sampler") || right.getType().startsWith("sampler")) { if (left.getType().startsWith("sampler") || right.getType().startsWith("sampler")) {
throw new MatParseException("Samplers can only be inputs", statement1); throw new MatParseException("Samplers can only be inputs", statement);
} }
if (left.getNameSpace().equals("Global")) { if (left.getNameSpace().equals(ShaderGenerator.NAME_SPACE_GLOBAL)) {
left.setType("vec4");//Globals are all vec4 for now (maybe forever...) left.setType("vec4"); // Globals are all vec4 for now (maybe forever...)
storeGlobal(left, statement1); storeGlobal(left, statement);
} else { } else {
throw new MatParseException("Only Global nameSpace is allowed for outputMapping, got" + left.getNameSpace(), statement1); throw new MatParseException("Only Global nameSpace is allowed for outputMapping, got" + left.getNameSpace(), statement);
} }
if (!updateVariableFromList(right, shaderNode.getDefinition().getOutputs())) { if (!updateVariableFromList(right, definition.getOutputs())) {
throw new MatParseException(right.getName() + " is not an output variable of " + shaderNode.getDefinition().getName(), statement1); throw new MatParseException(right.getName() + " is not an output variable of " + definition.getName(), statement);
} }
checkTypes(mapping, statement1); checkTypes(mapping, statement);
return mapping; return mapping;
} }
@ -805,7 +872,6 @@ public class ShaderNodeLoaderDelegate {
shaderNode = new ShaderNode(); shaderNode = new ShaderNode();
shaderNode.setName(name); shaderNode.setName(name);
techniqueDef.getShaderGenerationInfo().getUnusedNodes().add(name); techniqueDef.getShaderGenerationInfo().getUnusedNodes().add(name);
readShaderNode(statement.getContents()); readShaderNode(statement.getContents());
nodes.put(name, shaderNode); nodes.put(name, shaderNode);
techniqueDef.getShaderNodes().add(shaderNode); techniqueDef.getShaderNodes().add(shaderNode);
@ -838,45 +904,54 @@ public class ShaderNodeLoaderDelegate {
} }
/** /**
* stores a global output * Stores a global output.
* *
* @param var the variable to store * @param var the variable to store.
* @param statement1 the statement being read * @param varStatement the statement being read.
* @throws IOException * @throws MatParseException if we have duplicates of a global vertex output variable.
*/ */
public void storeGlobal(ShaderNodeVariable var, Statement statement1) throws IOException { public void storeGlobal(ShaderNodeVariable var, Statement varStatement) throws MatParseException {
var.setShaderOutput(true); var.setShaderOutput(true);
if (shaderNode.getDefinition().getType() == Shader.ShaderType.Vertex) {
ShaderNodeVariable global = techniqueDef.getShaderGenerationInfo().getVertexGlobal(); final ShaderGenerationInfo generationInfo = techniqueDef.getShaderGenerationInfo();
final ShaderNodeDefinition definition = shaderNode.getDefinition();
if (definition.getType() == ShaderType.Vertex) {
ShaderNodeVariable global = generationInfo.getVertexGlobal();
if (global != null) { if (global != null) {
if (!global.getName().equals(var.getName())) { if (!global.getName().equals(var.getName())) {
throw new MatParseException("A global output is already defined for the vertex shader: " + global.getName() + ". vertex shader can only have one global output", statement1); throw new MatParseException("A global output is already defined for the vertex shader: " +
global.getName() + ". vertex shader can only have one global output", varStatement);
} }
} else { } else {
techniqueDef.getShaderGenerationInfo().setVertexGlobal(var); generationInfo.setVertexGlobal(var);
} }
} else if (shaderNode.getDefinition().getType() == Shader.ShaderType.Fragment) {
storeVariable(var, techniqueDef.getShaderGenerationInfo().getFragmentGlobals()); } else if (definition.getType() == ShaderType.Fragment) {
storeVariable(var, generationInfo.getFragmentGlobals());
} }
} }
/** /**
* store an attribute * Stores an attribute.
* *
* @param var the variable to store * @param var the variable to store.
*/ */
public void storeAttribute(ShaderNodeVariable var) { public void storeAttribute(ShaderNodeVariable var) {
storeVariable(var, techniqueDef.getShaderGenerationInfo().getAttributes()); storeVariable(var, techniqueDef.getShaderGenerationInfo().getAttributes());
} }
/** /**
* store a vertex uniform * Stores a vertex uniform.
* *
* @param var the variable to store * @param var the variable to store.
*/ */
public void storeVertexUniform(ShaderNodeVariable var) { public void storeVertexUniform(ShaderNodeVariable var) {
storeVariable(var, techniqueDef.getShaderGenerationInfo().getVertexUniforms()); storeVariable(var, techniqueDef.getShaderGenerationInfo().getVertexUniforms());
} }
/** /**
@ -886,7 +961,6 @@ public class ShaderNodeLoaderDelegate {
*/ */
public void storeFragmentUniform(ShaderNodeVariable var) { public void storeFragmentUniform(ShaderNodeVariable var) {
storeVariable(var, techniqueDef.getShaderGenerationInfo().getFragmentUniforms()); storeVariable(var, techniqueDef.getShaderGenerationInfo().getFragmentUniforms());
} }
/** /**
@ -959,8 +1033,8 @@ public class ShaderNodeLoaderDelegate {
final ShaderNodeDefinition nodeDefinition = node.getDefinition(); final ShaderNodeDefinition nodeDefinition = node.getDefinition();
final ShaderNodeDefinition currentDefinition = shaderNode.getDefinition(); final ShaderNodeDefinition currentDefinition = shaderNode.getDefinition();
if (nodeDefinition.getType() != Shader.ShaderType.Vertex || if (nodeDefinition.getType() != ShaderType.Vertex ||
currentDefinition.getType() != Shader.ShaderType.Fragment) { currentDefinition.getType() != ShaderType.Fragment) {
return; return;
} }
@ -1008,11 +1082,11 @@ public class ShaderNodeLoaderDelegate {
} }
/** /**
* search a variable in a list from its name and merge the conditions of the * Searches a variable in a list from its name and merges the conditions of the
* variables * variables.
* *
* @param variable the variable * @param variable the variable.
* @param varList the variable list * @param varList the variable list.
*/ */
public void storeVariable(ShaderNodeVariable variable, List<ShaderNodeVariable> varList) { public void storeVariable(ShaderNodeVariable variable, List<ShaderNodeVariable> varList) {
for (ShaderNodeVariable var : varList) { for (ShaderNodeVariable var : varList) {

@ -5,14 +5,17 @@
*/ */
package com.jme3.material.plugin.export.materialdef; package com.jme3.material.plugin.export.materialdef;
import com.jme3.material.*; import com.jme3.material.MatParam;
import com.jme3.material.RenderState;
import com.jme3.material.TechniqueDef;
import com.jme3.shader.*; import com.jme3.shader.*;
import java.io.*; import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Collection; import java.util.Collection;
import java.util.regex.*; import java.util.List;
import java.util.regex.Matcher;
import static java.util.regex.Pattern.compile; import java.util.regex.Pattern;
/** /**
* @author nehon * @author nehon
@ -168,47 +171,79 @@ public class J3mdTechniqueDefWriter {
out.write(shaderNode.getDefinition().getPath()); out.write(shaderNode.getDefinition().getPath());
out.write("\n"); out.write("\n");
out.write(" InputMappings {\n"); final List<VariableMapping> inputMapping = shaderNode.getInputMapping();
for (VariableMapping mapping : shaderNode.getInputMapping()) { final List<VariableMapping> outputMapping = shaderNode.getOutputMapping();
writeVariableMapping(out, shaderNode, mapping, matParams);
}
out.write(" }\n");
out.write(" OutputMappings {\n"); if (!inputMapping.isEmpty()) {
for (VariableMapping mapping : shaderNode.getOutputMapping()) { out.write(" InputMappings {\n");
writeVariableMapping(out, shaderNode, mapping, matParams); for (VariableMapping mapping : inputMapping) {
writeVariableMapping(out, shaderNode, mapping, matParams);
}
out.write(" }\n");
} }
out.write(" }\n");
if (!outputMapping.isEmpty()) {
out.write(" OutputMappings {\n");
for (VariableMapping mapping : outputMapping) {
writeVariableMapping(out, shaderNode, mapping, matParams);
}
out.write(" }\n");
}
out.write(" }\n"); out.write(" }\n");
} }
private void writeVariableMapping(OutputStreamWriter out, ShaderNode shaderNode, VariableMapping mapping, Collection<MatParam> matParams) throws IOException { private void writeVariableMapping(final OutputStreamWriter out, final ShaderNode shaderNode,
final VariableMapping mapping, final Collection<MatParam> matParams)
throws IOException {
final ShaderNodeVariable leftVar = mapping.getLeftVariable();
final ShaderNodeVariable rightVar = mapping.getRightVariable();
final String rightExpression = mapping.getRightExpression();
out.write(" "); out.write(" ");
if(!mapping.getLeftVariable().getNameSpace().equals(shaderNode.getName())) {
out.write(mapping.getLeftVariable().getNameSpace()); if (!leftVar.getNameSpace().equals(shaderNode.getName())) {
out.write(leftVar.getNameSpace());
out.write("."); out.write(".");
} }
out.write(mapping.getLeftVariable().getName());
if(!mapping.getLeftSwizzling().equals("")){ out.write(leftVar.getName());
if (!mapping.getLeftSwizzling().equals("")) {
out.write("."); out.write(".");
out.write(mapping.getLeftSwizzling()); out.write(mapping.getLeftSwizzling());
} }
out.write(" = "); out.write(" = ");
if(!mapping.getRightVariable().getNameSpace().equals(shaderNode.getName())) {
out.write(mapping.getRightVariable().getNameSpace()); if (rightVar != null) {
out.write(".");
} if (!rightVar.getNameSpace().equals(shaderNode.getName())) {
out.write(mapping.getRightVariable().getName().replaceFirst("g_","").replaceFirst("m_","")); out.write(rightVar.getNameSpace());
if(!mapping.getRightSwizzling().equals("")){ out.write(".");
out.write("."); }
out.write(mapping.getRightSwizzling());
String rightVarName = rightVar.getName();
if (rightVarName.startsWith("g_") || rightVarName.startsWith("m_")) {
rightVarName = rightVarName.substring(2, rightVarName.length());
}
out.write(rightVarName);
if (!mapping.getRightSwizzling().equals("")) {
out.write(".");
out.write(mapping.getRightSwizzling());
}
} else {
out.write("%%");
out.write(rightExpression);
out.write("%%");
} }
if (mapping.getCondition() != null){ if (mapping.getCondition() != null) {
out.write(" : "); out.write(" : ");
out.write(formatCondition(mapping.getCondition(),matParams)); out.write(formatCondition(mapping.getCondition(), matParams));
} }
out.write("\n"); out.write("\n");

Loading…
Cancel
Save