Core system git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10432 75d07b2b-3a1a-0410-a2c5-0572b91ccdca3.0
parent
9e3a98931a
commit
55d065ab78
@ -0,0 +1,112 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.material.plugins; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.regex.Matcher; |
||||
import java.util.regex.Pattern; |
||||
|
||||
/** |
||||
* An utility class that allows to parse a define condition in a glsl language |
||||
* style. |
||||
* |
||||
* extractDefines is able to get a list of defines in an expression and update |
||||
* the formatter expression with upercased defines |
||||
* |
||||
* @author Nehon |
||||
*/ |
||||
public class ConditionParser { |
||||
|
||||
private String formattedExpression = ""; |
||||
|
||||
public static void main(String argv[]) { |
||||
ConditionParser parser = new ConditionParser(); |
||||
List<String> defines = parser.extractDefines("(LightMap && SeparateTexCoord) || !ColorMap"); |
||||
|
||||
for (String string : defines) { |
||||
System.err.println(string); |
||||
} |
||||
System.err.println(parser.formattedExpression); |
||||
|
||||
defines = parser.extractDefines("#if (defined(LightMap) && defined(SeparateTexCoord)) || !defined(ColorMap)"); |
||||
|
||||
for (String string : defines) { |
||||
System.err.println(string); |
||||
} |
||||
System.err.println(parser.formattedExpression); |
||||
|
||||
|
||||
// System.err.println(parser.getFormattedExpression());
|
||||
//
|
||||
// parser.parse("ShaderNode.var.xyz");
|
||||
// parser.parse("var.xyz");
|
||||
// parser.parse("ShaderNode.var");
|
||||
// parser.parse("var");
|
||||
} |
||||
|
||||
/** |
||||
* parse a condition and returns the list of defines of this condition. |
||||
* additionally this methods updates the formattedExpression with uppercased |
||||
* defines names |
||||
* |
||||
* supported expression syntax example: |
||||
* "(LightMap && SeparateTexCoord) || !ColorMap" |
||||
* "#if (defined(LightMap) && defined(SeparateTexCoord)) || !defined(ColorMap)" |
||||
* "#ifdef LightMap" |
||||
* "#ifdef (LightMap && SeparateTexCoord) || !ColorMap" |
||||
* |
||||
* @param expression the expression to parse |
||||
* @return the list of defines |
||||
*/ |
||||
public List<String> extractDefines(String expression) { |
||||
List<String> defines = new ArrayList<String>(); |
||||
expression = expression.replaceAll("#ifdef", "").replaceAll("#if", "").replaceAll("defined", ""); |
||||
Pattern pattern = Pattern.compile("(\\w+)"); |
||||
formattedExpression = expression; |
||||
Matcher m = pattern.matcher(expression); |
||||
while (m.find()) { |
||||
String match = m.group(); |
||||
defines.add(match); |
||||
formattedExpression = formattedExpression.replaceAll(match, "defined(" + match.toUpperCase() + ")"); |
||||
} |
||||
return defines; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the formatted expression previously updated by extractDefines |
||||
*/ |
||||
public String getFormattedExpression() { |
||||
return formattedExpression; |
||||
} |
||||
} |
@ -0,0 +1,90 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.material.plugins; |
||||
|
||||
import com.jme3.util.blockparser.Statement; |
||||
import java.io.IOException; |
||||
|
||||
/** |
||||
* Custom Exception to report a j3md Material definition file parsing error. |
||||
* This exception reports the line number where the error occured. |
||||
* |
||||
* @author Nehon |
||||
*/ |
||||
public class MatParseException extends IOException { |
||||
|
||||
/** |
||||
* creates a MatParseException |
||||
* |
||||
* @param expected the expected value |
||||
* @param got the actual value |
||||
* @param statement the read statement |
||||
*/ |
||||
public MatParseException(String expected, String got, Statement statement) { |
||||
super("Error On line " + statement.getLineNumber() + " : " + statement.getLine() + "\n->Expected " + (expected == null ? "a statement" : expected) + ", got '" + got + "'!"); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* creates a MatParseException |
||||
* |
||||
* @param text the error message |
||||
* @param statement the statement where the error occur |
||||
*/ |
||||
public MatParseException(String text, Statement statement) { |
||||
super("Error On line " + statement.getLineNumber() + " : " + statement.getLine() + "\n->" + text); |
||||
} |
||||
|
||||
/** |
||||
* creates a MatParseException |
||||
* |
||||
* @param expected the expected value |
||||
* @param got the actual value |
||||
* @param statement the read statement |
||||
* @param cause the embed exception that occured |
||||
*/ |
||||
public MatParseException(String expected, String got, Statement statement, Throwable cause) { |
||||
super("Error On line " + statement.getLineNumber() + " : " + statement.getLine() + "\n->Expected " + (expected == null ? "a statement" : expected) + ", got '" + got + "'!", cause); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* creates a MatParseException |
||||
* |
||||
* @param text the error message |
||||
* @param statement the statement where the error occur |
||||
* @param cause the embed exception that occured |
||||
*/ |
||||
public MatParseException(String text, Statement statement, Throwable cause) { |
||||
super("Error On line " + statement.getLineNumber() + " : " + statement.getLine() + "\n->" + text, cause); |
||||
} |
||||
} |
@ -0,0 +1,85 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.material.plugins; |
||||
|
||||
import com.jme3.asset.AssetInfo; |
||||
import com.jme3.asset.AssetKey; |
||||
import com.jme3.asset.AssetLoadException; |
||||
import com.jme3.asset.AssetLoader; |
||||
import com.jme3.asset.ShaderNodeDefinitionKey; |
||||
import com.jme3.util.blockparser.BlockLanguageParser; |
||||
import com.jme3.util.blockparser.Statement; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* ShaderNodeDefnition file loader (.j3sn) |
||||
* |
||||
* a j3sn file is a block style file like j3md or j3m. It must contain one |
||||
* ShaderNodeDefinition{} block that contains several ShaderNodeDefinition{} |
||||
* blocks |
||||
* |
||||
* @author Nehon |
||||
*/ |
||||
public class ShaderNodeDefinitionLoader implements AssetLoader { |
||||
|
||||
private ShaderNodeLoaderDelegate loaderDelegate; |
||||
|
||||
@Override |
||||
public Object load(AssetInfo assetInfo) throws IOException { |
||||
AssetKey k = assetInfo.getKey(); |
||||
if (!(k instanceof ShaderNodeDefinitionKey)) { |
||||
throw new IOException("ShaderNodeDefinition file must be loaded via ShaderNodeDefinitionKey"); |
||||
} |
||||
ShaderNodeDefinitionKey key = (ShaderNodeDefinitionKey) k; |
||||
loaderDelegate = new ShaderNodeLoaderDelegate(); |
||||
|
||||
InputStream in = assetInfo.openStream(); |
||||
List<Statement> roots = BlockLanguageParser.parse(in); |
||||
|
||||
if (roots.size() == 2) { |
||||
Statement exception = roots.get(0); |
||||
String line = exception.getLine(); |
||||
if (line.startsWith("Exception")) { |
||||
throw new AssetLoadException(line.substring("Exception ".length())); |
||||
} else { |
||||
throw new MatParseException("In multiroot shader node definition, expected first statement to be 'Exception'", exception); |
||||
} |
||||
} else if (roots.size() != 1) { |
||||
throw new MatParseException("Too many roots in J3SN file", roots.get(0)); |
||||
} |
||||
|
||||
return loaderDelegate.readNodesDefinitions(roots.get(0).getContents(), key); |
||||
|
||||
} |
||||
} |
@ -0,0 +1,937 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.material.plugins; |
||||
|
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.asset.AssetNotFoundException; |
||||
import com.jme3.asset.ShaderNodeDefinitionKey; |
||||
import com.jme3.material.MatParam; |
||||
import com.jme3.material.MaterialDef; |
||||
import com.jme3.material.ShaderGenerationInfo; |
||||
import com.jme3.material.TechniqueDef; |
||||
import com.jme3.shader.Shader; |
||||
import com.jme3.shader.ShaderNode; |
||||
import com.jme3.shader.ShaderNodeDefinition; |
||||
import com.jme3.shader.ShaderNodeVariable; |
||||
import com.jme3.shader.ShaderUtils; |
||||
import com.jme3.shader.UniformBinding; |
||||
import com.jme3.shader.VariableMapping; |
||||
import com.jme3.util.blockparser.Statement; |
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* This class is here to be able to load shaderNodeDefinition from both the |
||||
* J3MLoader and ShaderNodeDefinitionLoader. |
||||
* |
||||
* Also it allows to load the ShaderNodes from a j3md file and build the |
||||
* ShaderNodes list of each technique and the ShaderGenerationInfo needed to |
||||
* generate the sahders |
||||
* |
||||
* @author Nehon |
||||
*/ |
||||
public class ShaderNodeLoaderDelegate { |
||||
|
||||
protected Map<String, ShaderNodeDefinition> nodeDefinitions; |
||||
protected Map<String, ShaderNode> nodes; |
||||
protected ShaderNodeDefinition shaderNodeDefinition; |
||||
protected ShaderNode shaderNode; |
||||
protected TechniqueDef techniqueDef; |
||||
protected Map<String, ShaderNodeVariable> attributes = new HashMap<String, ShaderNodeVariable>(); |
||||
protected Map<String, ShaderNodeVariable> vertexDeclaredUniforms = new HashMap<String, ShaderNodeVariable>(); |
||||
protected Map<String, ShaderNodeVariable> fragmentDeclaredUniforms = new HashMap<String, ShaderNodeVariable>(); |
||||
protected Map<String, ShaderNodeVariable> varyings = new HashMap<String, ShaderNodeVariable>(); |
||||
protected MaterialDef materialDef; |
||||
protected String shaderLanguage; |
||||
protected String shaderName; |
||||
protected String varNames = ""; |
||||
protected AssetManager assetManager; |
||||
protected ConditionParser conditionParser = new ConditionParser(); |
||||
|
||||
|
||||
/** |
||||
* Read the ShaderNodesDefinitions block and returns a list of |
||||
* ShaderNodesDefinition This method is used by the j3sn loader |
||||
* |
||||
* note that the order of the definitions in the list is not guaranteed. |
||||
* |
||||
* @param statements the list statements to parse |
||||
* @param key the ShaderNodeDefinitionKey |
||||
* @return a list of ShaderNodesDefinition |
||||
* @throws IOException |
||||
*/ |
||||
public List<ShaderNodeDefinition> readNodesDefinitions(List<Statement> statements, ShaderNodeDefinitionKey key) throws IOException { |
||||
|
||||
for (Statement statement : statements) { |
||||
String[] split = statement.getLine().split("[ \\{]"); |
||||
if (statement.getLine().startsWith("ShaderNodeDefinition")) { |
||||
String name = statement.getLine().substring("ShaderNodeDefinition".length()).trim(); |
||||
|
||||
|
||||
if (!getNodeDefinitions().containsKey(name)) { |
||||
shaderNodeDefinition = new ShaderNodeDefinition(); |
||||
getNodeDefinitions().put(name, shaderNodeDefinition); |
||||
shaderNodeDefinition.setName(name); |
||||
readShaderNodeDefinition(statement.getContents(), key); |
||||
|
||||
} |
||||
} else { |
||||
throw new MatParseException("ShaderNodeDefinition", split[0], statement); |
||||
} |
||||
} |
||||
|
||||
return new ArrayList<ShaderNodeDefinition>(getNodeDefinitions().values()); |
||||
} |
||||
|
||||
/** |
||||
* Read the ShaderNodesDefinitions block and internally stores a map of |
||||
* ShaderNodesDefinition This method is used by the j3m loader. |
||||
* |
||||
* When loaded in a material, the definitions are not stored as a list, but |
||||
* they are stores in Shadernodes based onthis definition. |
||||
* |
||||
* The map is here to map the defintion to the nodes, and ovoid reloading |
||||
* already loaded definitions |
||||
* |
||||
* @param statements the list of statements to parse |
||||
* @throws IOException |
||||
*/ |
||||
public void readNodesDefinitions(List<Statement> statements) throws IOException { |
||||
readNodesDefinitions(statements, new ShaderNodeDefinitionKey()); |
||||
} |
||||
|
||||
/** |
||||
* effectiveliy reads the ShaderNodesDefinitions block |
||||
* |
||||
* @param statements the list of statements to parse |
||||
* @param key the ShaderNodeDefinitionKey |
||||
* @throws IOException |
||||
*/ |
||||
protected void readShaderNodeDefinition(List<Statement> statements, ShaderNodeDefinitionKey key) throws IOException { |
||||
boolean isLoadDoc = key instanceof ShaderNodeDefinitionKey && ((ShaderNodeDefinitionKey) key).isLoadDocumentation(); |
||||
for (Statement statement : statements) { |
||||
String[] split = statement.getLine().split("[ \\{]"); |
||||
String line = statement.getLine(); |
||||
|
||||
if (line.startsWith("Type")) { |
||||
String type = line.substring(line.lastIndexOf(':') + 1).trim(); |
||||
shaderNodeDefinition.setType(Shader.ShaderType.valueOf(type)); |
||||
} else if (line.startsWith("Shader ")) { |
||||
readShaderStatement(statement); |
||||
shaderNodeDefinition.getShadersLanguage().add(shaderLanguage); |
||||
shaderNodeDefinition.getShadersPath().add(shaderName); |
||||
} else if (line.startsWith("Documentation")) { |
||||
if (isLoadDoc) { |
||||
String doc = ""; |
||||
for (Statement statement1 : statement.getContents()) { |
||||
doc += "\n" + statement1.getLine(); |
||||
} |
||||
shaderNodeDefinition.setDocumentation(doc); |
||||
} |
||||
} else if (line.startsWith("Input")) { |
||||
varNames = ""; |
||||
for (Statement statement1 : statement.getContents()) { |
||||
shaderNodeDefinition.getInputs().add(readVariable(statement1)); |
||||
} |
||||
} else if (line.startsWith("Output")) { |
||||
varNames = ""; |
||||
for (Statement statement1 : statement.getContents()) { |
||||
shaderNodeDefinition.getOutputs().add(readVariable(statement1)); |
||||
} |
||||
} else { |
||||
throw new MatParseException("one of Type, Shader, Documentation, Input, Output", split[0], statement); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* reads a variable declaration statement <glslType> <varName> |
||||
* |
||||
* @param statement the statement to parse |
||||
* @return a ShaderNodeVariable axtracted from the statement |
||||
* @throws IOException |
||||
*/ |
||||
protected ShaderNodeVariable readVariable(Statement statement) throws IOException { |
||||
String[] splitVar = statement.getLine().trim().split("\\s"); |
||||
if (varNames.contains(splitVar[1] + ";")) { |
||||
throw new MatParseException("Duplicate variable name " + splitVar[1], statement); |
||||
} |
||||
varNames += splitVar[1] + ";"; |
||||
return new ShaderNodeVariable(splitVar[0], splitVar[1]); |
||||
} |
||||
|
||||
/** |
||||
* reads the VertexShaderNodes{} block |
||||
* |
||||
* @param statements the list of statements to parse |
||||
* @throws IOException |
||||
*/ |
||||
public void readVertexShaderNodes(List<Statement> statements) throws IOException { |
||||
attributes.clear(); |
||||
readNodes(statements); |
||||
} |
||||
|
||||
/** |
||||
* reads a list of ShaderNode{} blocks |
||||
* |
||||
* @param statements the list of statements to parse |
||||
* @throws IOException |
||||
*/ |
||||
protected void readShaderNode(List<Statement> statements) throws IOException { |
||||
for (Statement statement : statements) { |
||||
String line = statement.getLine(); |
||||
String[] split = statement.getLine().split("[ \\{]"); |
||||
if (line.startsWith("Definition")) { |
||||
ShaderNodeDefinition def = findDefinition(statement); |
||||
shaderNode.setDefinition(def); |
||||
} else if (line.startsWith("Condition")) { |
||||
String condition = line.substring(line.lastIndexOf(":") + 1).trim(); |
||||
extractCondition(condition, statement); |
||||
shaderNode.setCondition(conditionParser.getFormattedExpression()); |
||||
} else if (line.startsWith("InputMapping")) { |
||||
for (Statement statement1 : statement.getContents()) { |
||||
VariableMapping mapping = readInputMapping(statement1); |
||||
techniqueDef.getShaderGenerationInfo().getUnusedNodes().remove(mapping.getRightVariable().getNameSpace()); |
||||
shaderNode.getInputMapping().add(mapping); |
||||
} |
||||
} else if (line.startsWith("OutputMapping")) { |
||||
for (Statement statement1 : statement.getContents()) { |
||||
VariableMapping mapping = readOutputMapping(statement1); |
||||
techniqueDef.getShaderGenerationInfo().getUnusedNodes().remove(shaderNode.getName()); |
||||
shaderNode.getOutputMapping().add(mapping); |
||||
} |
||||
} else { |
||||
throw new MatParseException("ShaderNodeDefinition", split[0], statement); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* reads a mapping statement. Sets the nameSpace, name and swizzling of the |
||||
* left variable. Sets the name, nameSpace and swizzling of the right |
||||
* variable types will be determined later. |
||||
* |
||||
* Format : <nameSpace>.<varName>[.<swizzling>] = |
||||
* <nameSpace>.<varName>[.<swizzling>][:Condition] |
||||
* |
||||
* @param statement the statement to read |
||||
* @return the read mapping |
||||
*/ |
||||
protected VariableMapping parseMapping(Statement statement, boolean[] hasNameSpace) throws IOException { |
||||
VariableMapping mapping = new VariableMapping(); |
||||
String[] cond = statement.getLine().split(":"); |
||||
|
||||
String[] vars = cond[0].split("="); |
||||
checkMappingFormat(vars, statement); |
||||
ShaderNodeVariable[] variables = new ShaderNodeVariable[2]; |
||||
String[] swizzle = new String[2]; |
||||
for (int i = 0; i < vars.length; i++) { |
||||
String[] expression = vars[i].trim().split("\\."); |
||||
if (hasNameSpace[i]) { |
||||
if (expression.length <= 3) { |
||||
variables[i] = new ShaderNodeVariable("", expression[0].trim(), expression[1].trim()); |
||||
} |
||||
if (expression.length == 3) { |
||||
swizzle[i] = expression[2].trim(); |
||||
} |
||||
} else { |
||||
if (expression.length <= 2) { |
||||
variables[i] = new ShaderNodeVariable("", expression[0].trim()); |
||||
} |
||||
if (expression.length == 2) { |
||||
swizzle[i] = expression[1].trim(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
mapping.setLeftVariable(variables[0]); |
||||
mapping.setLeftSwizzling(swizzle[0] != null ? swizzle[0] : ""); |
||||
mapping.setRightVariable(variables[1]); |
||||
mapping.setRightSwizzling(swizzle[1] != null ? swizzle[1] : ""); |
||||
|
||||
if (cond.length > 1) { |
||||
extractCondition(cond[1], statement); |
||||
mapping.setCondition(conditionParser.getFormattedExpression()); |
||||
} |
||||
|
||||
return mapping; |
||||
} |
||||
|
||||
/** |
||||
* reads the FragmentShaderNodes{} block |
||||
* |
||||
* @param statements the list of statements to parse |
||||
* @throws IOException |
||||
*/ |
||||
public void readFragmentShaderNodes(List<Statement> statements) throws IOException { |
||||
readNodes(statements); |
||||
} |
||||
|
||||
/** |
||||
* Reads a Shader statement of this form <TYPE> <LANG> : <SOURCE> |
||||
* |
||||
* @param statement |
||||
* @throws IOException |
||||
*/ |
||||
protected void readShaderStatement(Statement statement) throws IOException { |
||||
String[] split = statement.getLine().split(":"); |
||||
if (split.length != 2) { |
||||
throw new MatParseException("Shader statement syntax incorrect", statement); |
||||
} |
||||
String[] typeAndLang = split[0].split("\\p{javaWhitespace}+"); |
||||
if (typeAndLang.length != 2) { |
||||
throw new MatParseException("Shader statement syntax incorrect", statement); |
||||
} |
||||
shaderName = split[1].trim(); |
||||
shaderLanguage = typeAndLang[1]; |
||||
} |
||||
|
||||
/** |
||||
* Sets the technique definition currently being loaded |
||||
* |
||||
* @param techniqueDef the technique def |
||||
*/ |
||||
public void setTechniqueDef(TechniqueDef techniqueDef) { |
||||
this.techniqueDef = techniqueDef; |
||||
} |
||||
|
||||
/** |
||||
* sets the material def currently being loaded |
||||
* |
||||
* @param materialDef |
||||
*/ |
||||
public void setMaterialDef(MaterialDef materialDef) { |
||||
this.materialDef = materialDef; |
||||
} |
||||
|
||||
/** |
||||
* searcha variable in the given list and updates its type and namespace |
||||
* |
||||
* @param var the variable to update |
||||
* @param list the variables list |
||||
* @return true if the variable has been found and updated |
||||
*/ |
||||
protected boolean updateVariableFromList(ShaderNodeVariable var, List<ShaderNodeVariable> list) { |
||||
for (ShaderNodeVariable shaderNodeVariable : list) { |
||||
if (shaderNodeVariable.getName().equals(var.getName())) { |
||||
var.setType(shaderNodeVariable.getType()); |
||||
var.setNameSpace(shaderNode.getName()); |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* updates the type of the right variable of a mapping from the type of the |
||||
* left variable |
||||
* |
||||
* @param mapping the mapping to consider |
||||
*/ |
||||
protected void updateRightTypeFromLeftType(VariableMapping mapping) { |
||||
String type = mapping.getLeftVariable().getType(); |
||||
int card = ShaderUtils.getCardinality(type, mapping.getRightSwizzling()); |
||||
if (card > 0) { |
||||
if (card == 1) { |
||||
type = "float"; |
||||
} else { |
||||
type = "vec" + card; |
||||
} |
||||
} |
||||
mapping.getRightVariable().setType(type); |
||||
} |
||||
|
||||
/** |
||||
* check if once a mapping expression is split by "=" the resulting array |
||||
* have 2 elements |
||||
* |
||||
* @param vars the array |
||||
* @param statement the statement |
||||
* @throws IOException |
||||
*/ |
||||
protected void checkMappingFormat(String[] vars, Statement statement) throws IOException { |
||||
if (vars.length != 2) { |
||||
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 |
||||
* |
||||
* @param varName the matparam name |
||||
* @return the MatParam |
||||
*/ |
||||
protected MatParam findMatParam(String varName) { |
||||
for (MatParam matParam : materialDef.getMaterialParams()) { |
||||
if (varName.equals(matParam.getName())) { |
||||
return matParam; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* finds an UniformBinding representing a WorldParam from the techniqueDef |
||||
* |
||||
* @param varName the name of the WorldParam |
||||
* @return the corresponding UniformBinding to the WorldParam |
||||
*/ |
||||
protected UniformBinding findWorldParam(String varName) { |
||||
for (UniformBinding worldParam : techniqueDef.getWorldBindings()) { |
||||
if (varName.equals(worldParam.toString())) { |
||||
return worldParam; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* updates the right variable of the given mapping from a UniformBinding (a |
||||
* WorldParam) it checks if the unifrom hasn't already been loaded, add it |
||||
* to the maps if not. |
||||
* |
||||
* @param param the WorldParam UniformBinding |
||||
* @param mapping the mapping |
||||
* @param map the map of uniforms to search into |
||||
* @return true if the param was added to the map |
||||
*/ |
||||
protected boolean updateRightFromUniforms(UniformBinding param, VariableMapping mapping, Map<String, ShaderNodeVariable> map) { |
||||
ShaderNodeVariable right = mapping.getRightVariable(); |
||||
String name = "g_" + param.toString(); |
||||
ShaderNodeVariable var = map.get(name); |
||||
if (var == null) { |
||||
right.setType(param.getGlslType()); |
||||
right.setName(name); |
||||
map.put(right.getName(), right); |
||||
mapping.setRightVariable(right); |
||||
return true; |
||||
} |
||||
mapping.setRightVariable(var); |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* updates the right variable of the given mapping from a MatParam (a |
||||
* WorldParam) it checks if the unifrom hasn't already been loaded, add it |
||||
* to the maps if not. |
||||
* |
||||
* @param param the MatParam |
||||
* @param mapping the mapping |
||||
* @param map the map of uniforms to search into |
||||
* @return true if the param was added to the map |
||||
*/ |
||||
public boolean updateRightFromUniforms(MatParam param, VariableMapping mapping, Map<String, ShaderNodeVariable> map) { |
||||
ShaderNodeVariable right = mapping.getRightVariable(); |
||||
ShaderNodeVariable var = map.get(param.getPrefixedName()); |
||||
if (var == null) { |
||||
right.setType(param.getVarType().getGlslType()); |
||||
right.setName(param.getPrefixedName()); |
||||
map.put(right.getName(), right); |
||||
mapping.setRightVariable(right); |
||||
return true; |
||||
} |
||||
mapping.setRightVariable(var); |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* updates a variable from the Attribute list |
||||
* |
||||
* @param right the variable |
||||
* @param mapping the mapping |
||||
*/ |
||||
public void updateVarFromAttributes(ShaderNodeVariable right, VariableMapping mapping) { |
||||
ShaderNodeVariable var = attributes.get(right.getName()); |
||||
if (var == null) { |
||||
updateRightTypeFromLeftType(mapping); |
||||
} else { |
||||
|
||||
mapping.setRightVariable(var); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Adds a define to the techniquedef |
||||
* |
||||
* @param paramName |
||||
*/ |
||||
public void addDefine(String paramName) { |
||||
if (techniqueDef.getShaderParamDefine(paramName) == null) { |
||||
techniqueDef.addShaderParamDefine(paramName, paramName.toUpperCase()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* find a variable with the given name from the list of variable |
||||
* |
||||
* @param vars a list of shaderNodeVariables |
||||
* @param rightVarName the variable name to search for |
||||
* @return the found variable or null is not found |
||||
*/ |
||||
public ShaderNodeVariable findNodeOutput(List<ShaderNodeVariable> vars, String rightVarName) { |
||||
ShaderNodeVariable var = null; |
||||
for (ShaderNodeVariable variable : vars) { |
||||
if (variable.getName().equals(rightVarName)) { |
||||
var = variable; |
||||
} |
||||
} |
||||
return var; |
||||
} |
||||
|
||||
/** |
||||
* extract and check a condition expression |
||||
* |
||||
* @param cond the condition expression |
||||
* @param statement the statement being read |
||||
* @throws IOException |
||||
*/ |
||||
public void extractCondition(String cond, Statement statement) throws IOException { |
||||
List<String> defines = conditionParser.extractDefines(cond); |
||||
for (String string : defines) { |
||||
MatParam param = findMatParam(string); |
||||
if (param != null) { |
||||
addDefine(param.getName()); |
||||
} else { |
||||
throw new MatParseException("Invalid condition, condition must match a Material Parameter named " + cond, statement); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* reads an input mapping |
||||
* |
||||
* @param statement1 the statement being read |
||||
* @return the mapping |
||||
* @throws IOException |
||||
*/ |
||||
public VariableMapping readInputMapping(Statement statement1) throws IOException { |
||||
VariableMapping mapping = null; |
||||
try { |
||||
mapping = parseMapping(statement1, new boolean[]{false, true}); |
||||
} catch (Exception e) { |
||||
throw new MatParseException("Unexpected mapping format", statement1, e); |
||||
} |
||||
ShaderNodeVariable left = mapping.getLeftVariable(); |
||||
ShaderNodeVariable right = mapping.getRightVariable(); |
||||
if (!updateVariableFromList(left, shaderNode.getDefinition().getInputs())) { |
||||
throw new MatParseException(left.getName() + " is not an input variable of " + shaderNode.getDefinition().getName(), statement1); |
||||
} |
||||
|
||||
if (left.getType().startsWith("sampler") && !right.getNameSpace().equals("MatParam")) { |
||||
throw new MatParseException("Samplers can only be assigned to MatParams", statement1); |
||||
} |
||||
|
||||
if (right.getNameSpace().equals("Global")) { |
||||
right.setType("vec4");//Globals are all vec4 for now (maybe forever...)
|
||||
updateCondition(right, mapping); |
||||
storeGlobal(right, statement1); |
||||
|
||||
} else if (right.getNameSpace().equals("Attr")) { |
||||
if (shaderNode.getDefinition().getType() == Shader.ShaderType.Fragment) { |
||||
throw new MatParseException("Cannot have an attribute as input in a fragment shader" + right.getName(), statement1); |
||||
} |
||||
updateVarFromAttributes(mapping.getRightVariable(), mapping); |
||||
updateCondition(mapping.getRightVariable(), mapping); |
||||
storeAttribute(mapping.getRightVariable()); |
||||
} else if (right.getNameSpace().equals("MatParam")) { |
||||
MatParam param = findMatParam(right.getName()); |
||||
if (param == null) { |
||||
throw new MatParseException("Could not find a Material Parameter named " + right.getName(), statement1); |
||||
} |
||||
if (shaderNode.getDefinition().getType() == Shader.ShaderType.Vertex) { |
||||
if (updateRightFromUniforms(param, mapping, vertexDeclaredUniforms)) { |
||||
updateCondition(mapping.getRightVariable(), mapping); |
||||
storeVertexUniform(mapping.getRightVariable()); |
||||
} |
||||
} else { |
||||
if (updateRightFromUniforms(param, mapping, fragmentDeclaredUniforms)) { |
||||
if (mapping.getRightVariable().getType().contains("|")) { |
||||
String type = fixSamplerType(left.getType(), mapping.getRightVariable().getType()); |
||||
if (type != null) { |
||||
mapping.getRightVariable().setType(type); |
||||
} else { |
||||
throw new MatParseException(param.getVarType().toString() + " can only be matched to one of " + param.getVarType().getGlslType().replaceAll("\\|", ",") + " found " + left.getType(), statement1); |
||||
} |
||||
} |
||||
updateCondition(mapping.getRightVariable(), mapping); |
||||
storeFragmentUniform(mapping.getRightVariable()); |
||||
} |
||||
} |
||||
|
||||
} else if (right.getNameSpace().equals("WorldParam")) { |
||||
UniformBinding worldParam = findWorldParam(right.getName()); |
||||
if (worldParam == null) { |
||||
throw new MatParseException("Could not find a World Parameter named " + right.getName(), statement1); |
||||
} |
||||
if (shaderNode.getDefinition().getType() == Shader.ShaderType.Vertex) { |
||||
if (updateRightFromUniforms(worldParam, mapping, vertexDeclaredUniforms)) { |
||||
updateCondition(mapping.getRightVariable(), mapping); |
||||
storeVertexUniform(mapping.getRightVariable()); |
||||
} |
||||
} else { |
||||
if (updateRightFromUniforms(worldParam, mapping, fragmentDeclaredUniforms)) { |
||||
updateCondition(mapping.getRightVariable(), mapping); |
||||
storeFragmentUniform(mapping.getRightVariable()); |
||||
} |
||||
} |
||||
|
||||
} else { |
||||
ShaderNode node = nodes.get(right.getNameSpace()); |
||||
if (node == null) { |
||||
throw new MatParseException("Undeclared node" + right.getNameSpace() + ". Make sure this node is declared before the current node", statement1); |
||||
} |
||||
ShaderNodeVariable var = findNodeOutput(node.getDefinition().getOutputs(), right.getName()); |
||||
if (var == null) { |
||||
throw new MatParseException("Cannot find output variable" + right.getName() + " form ShaderNode " + node.getName(), statement1); |
||||
} |
||||
right.setNameSpace(node.getName()); |
||||
right.setType(var.getType()); |
||||
mapping.setRightVariable(right); |
||||
updateCondition(mapping.getRightVariable(), mapping); |
||||
storeVaryings(node, mapping.getRightVariable()); |
||||
|
||||
} |
||||
|
||||
checkTypes(mapping, statement1); |
||||
|
||||
return mapping; |
||||
} |
||||
|
||||
/** |
||||
* reads an output mapping |
||||
* |
||||
* @param statement1 the staement being read |
||||
* @return the mapping |
||||
* @throws IOException |
||||
*/ |
||||
public VariableMapping readOutputMapping(Statement statement1) throws IOException { |
||||
VariableMapping mapping = null; |
||||
try { |
||||
mapping = parseMapping(statement1, new boolean[]{true, false}); |
||||
} catch (Exception e) { |
||||
throw new MatParseException("Unexpected mapping format", statement1, e); |
||||
} |
||||
ShaderNodeVariable left = mapping.getLeftVariable(); |
||||
ShaderNodeVariable right = mapping.getRightVariable(); |
||||
|
||||
|
||||
if (left.getType().startsWith("sampler") || right.getType().startsWith("sampler")) { |
||||
throw new MatParseException("Samplers can only be inputs", statement1); |
||||
} |
||||
|
||||
if (left.getNameSpace().equals("Global")) { |
||||
left.setType("vec4");//Globals are all vec4 for now (maybe forever...)
|
||||
updateCondition(left, mapping); |
||||
storeGlobal(left, statement1); |
||||
} else { |
||||
throw new MatParseException("Only Global nameSpace is allowed for outputMapping, got" + left.getNameSpace(), statement1); |
||||
} |
||||
|
||||
if (!updateVariableFromList(right, shaderNode.getDefinition().getOutputs())) { |
||||
throw new MatParseException(right.getName() + " is not an output variable of " + shaderNode.getDefinition().getName(), statement1); |
||||
} |
||||
|
||||
checkTypes(mapping, statement1); |
||||
|
||||
return mapping; |
||||
} |
||||
|
||||
/** |
||||
* Reads alist of ShaderNodes |
||||
* |
||||
* @param statements the list of statements to read |
||||
* @throws IOException |
||||
*/ |
||||
public void readNodes(List<Statement> statements) throws IOException { |
||||
if (techniqueDef.getShaderNodes() == null) { |
||||
techniqueDef.setShaderNodes(new ArrayList<ShaderNode>()); |
||||
techniqueDef.setShaderGenerationInfo(new ShaderGenerationInfo()); |
||||
} |
||||
|
||||
for (Statement statement : statements) { |
||||
String[] split = statement.getLine().split("[ \\{]"); |
||||
if (statement.getLine().startsWith("ShaderNode ")) { |
||||
String name = statement.getLine().substring("ShaderNode".length()).trim(); |
||||
if (nodes == null) { |
||||
nodes = new HashMap<String, ShaderNode>(); |
||||
} |
||||
if (!nodes.containsKey(name)) { |
||||
shaderNode = new ShaderNode(); |
||||
shaderNode.setName(name); |
||||
techniqueDef.getShaderGenerationInfo().getUnusedNodes().add(name); |
||||
|
||||
readShaderNode(statement.getContents()); |
||||
nodes.put(name, shaderNode); |
||||
techniqueDef.getShaderNodes().add(shaderNode); |
||||
} else { |
||||
throw new MatParseException("ShaderNode " + name + " is already defined", statement); |
||||
} |
||||
|
||||
} else { |
||||
throw new MatParseException("ShaderNode", split[0], statement); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* retrieve the leftType corresponding sampler type from the rightType |
||||
* |
||||
* @param leftType the left samplerType |
||||
* @param rightType the right sampler type (can be multiple types sparated |
||||
* by "|" |
||||
* @return the type or null if not found |
||||
*/ |
||||
public String fixSamplerType(String leftType, String rightType) { |
||||
String[] types = rightType.split("\\|"); |
||||
for (String string : types) { |
||||
if (leftType.equals(string)) { |
||||
return string; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* stores a global output |
||||
* |
||||
* @param var the variable to store |
||||
* @param statement1 the statement being read |
||||
* @throws IOException |
||||
*/ |
||||
public void storeGlobal(ShaderNodeVariable var, Statement statement1) throws IOException { |
||||
var.setShaderOutput(true); |
||||
if (shaderNode.getDefinition().getType() == Shader.ShaderType.Vertex) { |
||||
ShaderNodeVariable global = techniqueDef.getShaderGenerationInfo().getVertexGlobal(); |
||||
if (global != null) { |
||||
global.setCondition(mergeConditions(global.getCondition(), var.getCondition(), "||")); |
||||
var.setCondition(global.getCondition()); |
||||
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); |
||||
} |
||||
} else { |
||||
techniqueDef.getShaderGenerationInfo().setVertexGlobal(var); |
||||
} |
||||
} else if (shaderNode.getDefinition().getType() == Shader.ShaderType.Fragment) { |
||||
mergeConditionsAndStoreVariable(var, techniqueDef.getShaderGenerationInfo().getFragmentGlobals()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* store an attribute |
||||
* |
||||
* @param var the variable ot store |
||||
*/ |
||||
public void storeAttribute(ShaderNodeVariable var) { |
||||
mergeConditionsAndStoreVariable(var, techniqueDef.getShaderGenerationInfo().getAttributes()); |
||||
} |
||||
|
||||
/** |
||||
* store a vertex uniform |
||||
* |
||||
* @param var the variable ot store |
||||
*/ |
||||
public void storeVertexUniform(ShaderNodeVariable var) { |
||||
mergeConditionsAndStoreVariable(var, techniqueDef.getShaderGenerationInfo().getVertexUniforms()); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* store a fragment uniform |
||||
* |
||||
* @param var the variable ot store |
||||
*/ |
||||
public void storeFragmentUniform(ShaderNodeVariable var) { |
||||
mergeConditionsAndStoreVariable(var, techniqueDef.getShaderGenerationInfo().getFragmentUniforms()); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* sets the assetManager |
||||
* |
||||
* @param assetManager |
||||
*/ |
||||
public void setAssetManager(AssetManager assetManager) { |
||||
this.assetManager = assetManager; |
||||
} |
||||
|
||||
/** |
||||
* find the definiton from this statement (loads it if necessary) |
||||
* |
||||
* @param statement the statement being read |
||||
* @return the definition |
||||
* @throws IOException |
||||
*/ |
||||
public ShaderNodeDefinition findDefinition(Statement statement) throws IOException { |
||||
String defLine[] = statement.getLine().split(":"); |
||||
String defName = defLine[1].trim(); |
||||
|
||||
ShaderNodeDefinition def = getNodeDefinitions().get(defName); |
||||
if (def == null) { |
||||
if (defLine.length == 3) { |
||||
List<ShaderNodeDefinition> defs = null; |
||||
try { |
||||
defs = assetManager.loadAsset(new ShaderNodeDefinitionKey(defLine[2].trim())); |
||||
} catch (AssetNotFoundException e) { |
||||
throw new MatParseException("Couldn't find " + defLine[2].trim(), statement, e); |
||||
} |
||||
|
||||
for (ShaderNodeDefinition definition : defs) { |
||||
definition.setPath(defLine[2].trim()); |
||||
if (defName.equals(definition.getName())) { |
||||
def = definition; |
||||
} |
||||
if (!(getNodeDefinitions().containsKey(definition.getName()))) { |
||||
getNodeDefinitions().put(definition.getName(), definition); |
||||
} |
||||
} |
||||
} |
||||
if (def == null) { |
||||
throw new MatParseException(defName + " is not a declared as Shader Node Definition", statement); |
||||
} |
||||
} |
||||
return def; |
||||
} |
||||
|
||||
/** |
||||
* updates a variable condition form a mapping condition |
||||
* |
||||
* @param var the variable |
||||
* @param mapping the mapping |
||||
*/ |
||||
public void updateCondition(ShaderNodeVariable var, VariableMapping mapping) { |
||||
|
||||
String condition = mergeConditions(shaderNode.getCondition(), mapping.getCondition(), "&&"); |
||||
|
||||
if (var.getCondition() == null) { |
||||
var.setCondition(condition); |
||||
} else { |
||||
var.setCondition(mergeConditions(var.getCondition(), condition, "||")); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* store a varying |
||||
* |
||||
* @param node the shaderNode |
||||
* @param variable the variable to store |
||||
*/ |
||||
public void storeVaryings(ShaderNode node, ShaderNodeVariable variable) { |
||||
variable.setShaderOutput(true); |
||||
if (node.getDefinition().getType() == Shader.ShaderType.Vertex && shaderNode.getDefinition().getType() == Shader.ShaderType.Fragment) { |
||||
ShaderNodeVariable var = varyings.get(variable.getName()); |
||||
if (var == null) { |
||||
techniqueDef.getShaderGenerationInfo().getVaryings().add(variable); |
||||
varyings.put(variable.getName(), variable); |
||||
} else { |
||||
var.setCondition(mergeConditions(var.getCondition(), variable.getCondition(), "||")); |
||||
variable.setCondition(var.getCondition()); |
||||
} |
||||
//if a variable is declared with the same name as an input and an output and is a varying, set it as a shader output so it's declared as a varying only once.
|
||||
for (VariableMapping variableMapping : node.getInputMapping()) { |
||||
if (variableMapping.getLeftVariable().getName().equals(variable.getName())) { |
||||
variableMapping.getLeftVariable().setShaderOutput(true); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 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 (operator.equals("||") && (condition1 == null || condition2 == null)) { |
||||
return null; |
||||
} |
||||
if (condition1 != null) { |
||||
if (condition2 == null) { |
||||
return condition1; |
||||
} else { |
||||
String mergedCondition = "(" + condition1 + ") " + operator + " (" + condition2 + ")"; |
||||
return mergedCondition; |
||||
} |
||||
} else { |
||||
return condition2; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* search a variable in a list from its name and merge the conditions of the |
||||
* variables |
||||
* |
||||
* @param variable the variable |
||||
* @param varList the variable list |
||||
*/ |
||||
public void mergeConditionsAndStoreVariable(ShaderNodeVariable variable, List<ShaderNodeVariable> varList) { |
||||
for (ShaderNodeVariable var : varList) { |
||||
if (var.getName().equals(variable.getName())) { |
||||
var.setCondition(mergeConditions(var.getCondition(), variable.getCondition(), "||")); |
||||
variable.setCondition(var.getCondition()); |
||||
return; |
||||
} |
||||
} |
||||
varList.add(variable); |
||||
} |
||||
|
||||
/** |
||||
* check the types of a mapping, left type must match right type tkae the |
||||
* swizzle into account |
||||
* |
||||
* @param mapping the mapping |
||||
* @param statement1 the statement being read |
||||
* @throws MatParseException |
||||
*/ |
||||
protected void checkTypes(VariableMapping mapping, Statement statement1) throws MatParseException { |
||||
if (!ShaderUtils.typesMatch(mapping)) { |
||||
String ls = mapping.getLeftSwizzling().length() == 0 ? "" : "." + mapping.getLeftSwizzling(); |
||||
String rs = mapping.getRightSwizzling().length() == 0 ? "" : "." + mapping.getRightSwizzling(); |
||||
throw new MatParseException("Type mismatch, cannot convert" + mapping.getLeftVariable().getType() + ls + " to " + mapping.getRightVariable().getType() + rs, statement1); |
||||
} |
||||
} |
||||
|
||||
private Map<String, ShaderNodeDefinition> getNodeDefinitions() { |
||||
if (nodeDefinitions == null) { |
||||
nodeDefinitions = new HashMap<String, ShaderNodeDefinition>(); |
||||
} |
||||
return nodeDefinitions; |
||||
} |
||||
} |
@ -0,0 +1,89 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.asset; |
||||
|
||||
import com.jme3.asset.cache.AssetCache; |
||||
import com.jme3.shader.ShaderNodeDefinition; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Used for loading {@link ShaderNodeDefinition shader nodes definition} |
||||
* |
||||
* Tells if the defintion has to be loaded with or without its documentation |
||||
* |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public class ShaderNodeDefinitionKey extends AssetKey<List<ShaderNodeDefinition>> { |
||||
|
||||
private boolean loadDocumentation = false; |
||||
|
||||
/** |
||||
* creates a ShaderNodeDefinitionKey |
||||
* |
||||
* @param name the name of the asset to load |
||||
*/ |
||||
public ShaderNodeDefinitionKey(String name) { |
||||
super(name); |
||||
} |
||||
|
||||
/** |
||||
* creates a ShaderNodeDefinitionKey |
||||
*/ |
||||
public ShaderNodeDefinitionKey() { |
||||
super(); |
||||
} |
||||
|
||||
@Override |
||||
public Class<? extends AssetCache> getCacheType() { |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return true if the asset loaded with this key will contain its |
||||
* documentation |
||||
*/ |
||||
public boolean isLoadDocumentation() { |
||||
return loadDocumentation; |
||||
} |
||||
|
||||
/** |
||||
* sets to true to load the documentation along with the |
||||
* ShaderNodeDefinition |
||||
* |
||||
* @param loadDocumentation true to load the documentation along with the |
||||
* ShaderNodeDefinition |
||||
*/ |
||||
public void setLoadDocumentation(boolean loadDocumentation) { |
||||
this.loadDocumentation = loadDocumentation; |
||||
} |
||||
} |
@ -0,0 +1,190 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.material; |
||||
|
||||
import com.jme3.export.InputCapsule; |
||||
import com.jme3.export.JmeExporter; |
||||
import com.jme3.export.JmeImporter; |
||||
import com.jme3.export.OutputCapsule; |
||||
import com.jme3.export.Savable; |
||||
import com.jme3.shader.ShaderNodeVariable; |
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* this class is basically a struct that contains the ShaderNodes informations |
||||
* in an appropriate way to ease the shader generation process and make it |
||||
* faster. |
||||
* |
||||
* @author Nehon |
||||
*/ |
||||
public class ShaderGenerationInfo implements Savable { |
||||
|
||||
/** |
||||
* the list of attributes of the vertex shader |
||||
*/ |
||||
protected List<ShaderNodeVariable> attributes = new ArrayList<ShaderNodeVariable>(); |
||||
/** |
||||
* the list of all the uniforms to declare in the vertex shader |
||||
*/ |
||||
protected List<ShaderNodeVariable> vertexUniforms = new ArrayList<ShaderNodeVariable>(); |
||||
/** |
||||
* the global output of the vertex shader (to assign ot gl_Position) |
||||
*/ |
||||
protected ShaderNodeVariable vertexGlobal = null; |
||||
/** |
||||
* the list of varyings |
||||
*/ |
||||
protected List<ShaderNodeVariable> varyings = new ArrayList<ShaderNodeVariable>(); |
||||
/** |
||||
* the list of all the uniforms to declare in the fragment shader |
||||
*/ |
||||
protected List<ShaderNodeVariable> fragmentUniforms = new ArrayList<ShaderNodeVariable>(); |
||||
/** |
||||
* the list of all the fragment shader global outputs (to assign ot gl_FragColor or gl_Fragdata[n]) |
||||
*/ |
||||
protected List<ShaderNodeVariable> fragmentGlobals = new ArrayList<ShaderNodeVariable>(); |
||||
/** |
||||
* the unused node names of this shader (node whose output are never used) |
||||
*/ |
||||
protected List<String> unusedNodes = new ArrayList<String>(); |
||||
|
||||
/** |
||||
* |
||||
* @return the attributes |
||||
*/ |
||||
public List<ShaderNodeVariable> getAttributes() { |
||||
return attributes; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the vertex shader uniforms |
||||
*/ |
||||
public List<ShaderNodeVariable> getVertexUniforms() { |
||||
return vertexUniforms; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the fragment shader uniforms |
||||
*/ |
||||
public List<ShaderNodeVariable> getFragmentUniforms() { |
||||
return fragmentUniforms; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the vertex shader global ouput |
||||
*/ |
||||
public ShaderNodeVariable getVertexGlobal() { |
||||
return vertexGlobal; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the fragment shader global outputs |
||||
*/ |
||||
public List<ShaderNodeVariable> getFragmentGlobals() { |
||||
return fragmentGlobals; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the varyings |
||||
*/ |
||||
public List<ShaderNodeVariable> getVaryings() { |
||||
return varyings; |
||||
} |
||||
|
||||
/** |
||||
* sets the vertex shader global output |
||||
* |
||||
* @param vertexGlobal the global output |
||||
*/ |
||||
public void setVertexGlobal(ShaderNodeVariable vertexGlobal) { |
||||
this.vertexGlobal = vertexGlobal; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the list on unused node names |
||||
*/ |
||||
public List<String> getUnusedNodes() { |
||||
return unusedNodes; |
||||
} |
||||
|
||||
/** |
||||
* the list of unused node names |
||||
* @param unusedNodes |
||||
*/ |
||||
public void setUnusedNodes(List<String> unusedNodes) { |
||||
this.unusedNodes = unusedNodes; |
||||
} |
||||
|
||||
/** |
||||
* convenient toString method |
||||
* |
||||
* @return the informations |
||||
*/ |
||||
@Override |
||||
public String toString() { |
||||
return "ShaderGenerationInfo{" + "attributes=" + attributes + ", vertexUniforms=" + vertexUniforms + ", vertexGlobal=" + vertexGlobal + ", varyings=" + varyings + ", fragmentUniforms=" + fragmentUniforms + ", fragmentGlobals=" + fragmentGlobals + '}'; |
||||
} |
||||
|
||||
|
||||
|
||||
|
||||
@Override |
||||
public void write(JmeExporter ex) throws IOException { |
||||
OutputCapsule oc = ex.getCapsule(this); |
||||
oc.writeSavableArrayList((ArrayList) attributes, "attributes", new ArrayList<ShaderNodeVariable>()); |
||||
oc.writeSavableArrayList((ArrayList) vertexUniforms, "vertexUniforms", new ArrayList<ShaderNodeVariable>()); |
||||
oc.writeSavableArrayList((ArrayList) varyings, "varyings", new ArrayList<ShaderNodeVariable>()); |
||||
oc.writeSavableArrayList((ArrayList) fragmentUniforms, "fragmentUniforms", new ArrayList<ShaderNodeVariable>()); |
||||
oc.writeSavableArrayList((ArrayList) fragmentGlobals, "fragmentGlobals", new ArrayList<ShaderNodeVariable>()); |
||||
oc.write(vertexGlobal, "vertexGlobal", null); |
||||
} |
||||
|
||||
@Override |
||||
public void read(JmeImporter im) throws IOException { |
||||
InputCapsule ic = im.getCapsule(this); |
||||
attributes = ic.readSavableArrayList("attributes", new ArrayList<ShaderNodeVariable>()); |
||||
vertexUniforms = ic.readSavableArrayList("vertexUniforms", new ArrayList<ShaderNodeVariable>()); |
||||
varyings = ic.readSavableArrayList("varyings", new ArrayList<ShaderNodeVariable>()); |
||||
fragmentUniforms = ic.readSavableArrayList("fragmentUniforms", new ArrayList<ShaderNodeVariable>()); |
||||
fragmentGlobals = ic.readSavableArrayList("fragmentGlobals", new ArrayList<ShaderNodeVariable>()); |
||||
vertexGlobal = (ShaderNodeVariable) ic.readSavable("vertexGlobal", null); |
||||
|
||||
} |
||||
} |
@ -0,0 +1,572 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shader; |
||||
|
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.material.ShaderGenerationInfo; |
||||
import com.jme3.material.plugins.ConditionParser; |
||||
import com.jme3.shader.Shader.ShaderType; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* This shader Generator can generate Vertex and Fragment shaders from |
||||
* shadernodes for GLSL 1.0 |
||||
* |
||||
* @author Nehon |
||||
*/ |
||||
public class Glsl100ShaderGenerator extends ShaderGenerator { |
||||
|
||||
/** |
||||
* the indentation characters 1à tabulation characters |
||||
*/ |
||||
private final static String INDENTCHAR = "\t\t\t\t\t\t\t\t\t\t"; |
||||
|
||||
/** |
||||
* creates a Glsl100ShaderGenerator |
||||
* @param assetManager the assetManager |
||||
*/ |
||||
public Glsl100ShaderGenerator(AssetManager assetManager) { |
||||
super(assetManager); |
||||
} |
||||
|
||||
@Override |
||||
protected void generateUniforms(StringBuilder source, ShaderGenerationInfo info, ShaderType type) { |
||||
generateUniforms(source, type == ShaderType.Vertex ? info.getVertexUniforms() : info.getFragmentUniforms()); |
||||
} |
||||
|
||||
/** |
||||
* decalre a list of uniforms |
||||
* |
||||
* @param source the source to append to |
||||
* @param uniforms the list of uniforms |
||||
*/ |
||||
protected void generateUniforms(StringBuilder source, List<ShaderNodeVariable> uniforms) { |
||||
source.append("\n"); |
||||
for (ShaderNodeVariable var : uniforms) { |
||||
declareVariable(source, var, false, "uniform"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* {@inheritDoc} |
||||
* |
||||
* attributes are all declared, inPositon is decalred even if it's not in |
||||
* the list and it's condition is nulled. |
||||
*/ |
||||
@Override |
||||
protected void generateAttributes(StringBuilder source, ShaderGenerationInfo info) { |
||||
source.append("\n"); |
||||
boolean inPosition = false; |
||||
for (ShaderNodeVariable var : info.getAttributes()) { |
||||
if (var.getName().equals("inPosition")) { |
||||
inPosition = true; |
||||
var.setCondition(null); |
||||
} |
||||
declareAttribute(source, var); |
||||
|
||||
} |
||||
if (!inPosition) { |
||||
declareAttribute(source, new ShaderNodeVariable("vec4", "inPosition")); |
||||
} |
||||
|
||||
} |
||||
|
||||
@Override |
||||
protected void generateVaryings(StringBuilder source, ShaderGenerationInfo info, ShaderType type) { |
||||
source.append("\n"); |
||||
for (ShaderNodeVariable var : info.getVaryings()) { |
||||
declareVarying(source, var, type == ShaderType.Vertex ? false : true); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* {@inheritDoc} |
||||
* |
||||
* if the declaration contains no code nothing is done, else it's appended |
||||
*/ |
||||
@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"); |
||||
unIndent(); |
||||
startCondition(shaderNode.getCondition(), source); |
||||
source.append(nodeSource); |
||||
endCondition(shaderNode.getCondition(), source); |
||||
indent(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* {@inheritDoc} |
||||
* |
||||
* Shader outputs are declared and initialized inside the main section |
||||
*/ |
||||
@Override |
||||
protected void generateStartOfMainSection(StringBuilder source, ShaderGenerationInfo info, ShaderType type) { |
||||
source.append("\n"); |
||||
source.append("void main(){\n"); |
||||
indent(); |
||||
appendIndent(source); |
||||
if (type == ShaderType.Vertex) { |
||||
declareVariable(source, info.getVertexGlobal(), "inPosition"); |
||||
} else if (type == ShaderType.Fragment) { |
||||
for (ShaderNodeVariable global : info.getFragmentGlobals()) { |
||||
declareVariable(source, global, "vec4(1.0)"); |
||||
} |
||||
} |
||||
source.append("\n"); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritDoc} |
||||
* |
||||
* outputs are assigned to built in glsl output. then the main section is |
||||
* closed |
||||
* |
||||
* This code accounts for multi render target and correctly output to |
||||
* gl_FragData if several output are declared for the fragment shader |
||||
*/ |
||||
@Override |
||||
protected void generateEndOfMainSection(StringBuilder source, ShaderGenerationInfo info, ShaderType type) { |
||||
source.append("\n"); |
||||
if (type == ShaderType.Vertex) { |
||||
appendOutput(source, "gl_Position", info.getVertexGlobal()); |
||||
} else if (type == ShaderType.Fragment) { |
||||
List<ShaderNodeVariable> globals = info.getFragmentGlobals(); |
||||
if (globals.size() == 1) { |
||||
appendOutput(source, "gl_FragColor", globals.get(0)); |
||||
} else { |
||||
int i = 0; |
||||
//Multi Render Target
|
||||
for (ShaderNodeVariable global : globals) { |
||||
appendOutput(source, "gl_FragData[" + i + "]", global); |
||||
i++; |
||||
} |
||||
} |
||||
} |
||||
unIndent(); |
||||
appendIndent(source); |
||||
source.append("}\n"); |
||||
} |
||||
|
||||
/** |
||||
* Appends an ouput assignment to a shader globalOutputName = |
||||
* nameSpace_varName; |
||||
* |
||||
* @param source the source StringBuilter to append the code. |
||||
* @param globalOutputName the name of the global output (can be gl_Position |
||||
* or gl_FragColor etc...). |
||||
* @param var the variable to assign to the output. |
||||
*/ |
||||
protected void appendOutput(StringBuilder source, String globalOutputName, ShaderNodeVariable var) { |
||||
appendIndent(source); |
||||
source.append(globalOutputName); |
||||
source.append(" = "); |
||||
source.append(var.getNameSpace()); |
||||
source.append("_"); |
||||
source.append(var.getName()); |
||||
source.append(";\n"); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritDoc} |
||||
* |
||||
* this methods does things in this order : |
||||
* |
||||
* 1. declaring and mapping input<br> |
||||
* variables : variable replaced with MatParams or WorldParams are not |
||||
* declared and are replaced by the parma acual name in the code. For others |
||||
* variables, the name space is appended with a "_" before the variable name |
||||
* in the code to avoid names collision between shaderNodes. <br> |
||||
* |
||||
* 2. declaring output variables : <br> |
||||
* variables are declared if they were not already |
||||
* declared as input (inputs can also be outputs) or if they are not |
||||
* declared as varyings. The variable name is also prefixed with the s=name |
||||
* space and "_" in the shaderNode code <br> |
||||
* |
||||
* 3. append of the actual ShaderNode code <br> |
||||
* |
||||
* 4. mapping outputs to global output if needed<br> |
||||
* |
||||
*<br> |
||||
* All of this is embed in a #if coditional statement if needed |
||||
*/ |
||||
@Override |
||||
protected void generateNodeMainSection(StringBuilder source, ShaderNode shaderNode, String nodeSource, ShaderGenerationInfo info) { |
||||
|
||||
nodeSource = updateDefinesName(nodeSource, shaderNode); |
||||
source.append("\n"); |
||||
comment(source, shaderNode, "Begin"); |
||||
startCondition(shaderNode.getCondition(), source); |
||||
|
||||
List<String> declaredInputs = new ArrayList<String>(); |
||||
for (VariableMapping mapping : shaderNode.getInputMapping()) { |
||||
|
||||
//all variables fed with a matparam or world param are replaced but the matparam itself
|
||||
//it avoids issue with samplers that have to be uniforms, and it optimize a but the shader code.
|
||||
if (isWorldOrMaterialParam(mapping.getRightVariable())) { |
||||
nodeSource = replace(nodeSource, mapping.getLeftVariable(), mapping.getRightVariable().getName()); |
||||
} else { |
||||
if (mapping.getLeftVariable().getType().startsWith("sampler")) { |
||||
throw new IllegalArgumentException("a Sampler must be a uniform"); |
||||
} |
||||
map(mapping, source); |
||||
String newName = shaderNode.getName() + "_" + mapping.getLeftVariable().getName(); |
||||
if (!declaredInputs.contains(newName)) { |
||||
nodeSource = replace(nodeSource, mapping.getLeftVariable(), newName); |
||||
declaredInputs.add(newName); |
||||
} |
||||
} |
||||
} |
||||
|
||||
for (ShaderNodeVariable var : shaderNode.getDefinition().getOutputs()) { |
||||
ShaderNodeVariable v = new ShaderNodeVariable(var.getType(), shaderNode.getName(), var.getName()); |
||||
if (!declaredInputs.contains(shaderNode.getName() + "_" + var.getName())) { |
||||
if (!isVarying(info, v)) { |
||||
declareVariable(source, v); |
||||
} |
||||
nodeSource = replaceVariableName(nodeSource, v); |
||||
} |
||||
} |
||||
|
||||
source.append(nodeSource); |
||||
|
||||
for (VariableMapping mapping : shaderNode.getOutputMapping()) { |
||||
map(mapping, source); |
||||
} |
||||
endCondition(shaderNode.getCondition(), source); |
||||
comment(source, shaderNode, "End"); |
||||
} |
||||
|
||||
/** |
||||
* declares a variable, embed in a conditional block if needed |
||||
* @param source the StringBuilder to use |
||||
* @param var the variable to declare |
||||
* @param appendNameSpace true to append the nameSpace + "_" |
||||
*/ |
||||
protected void declareVariable(StringBuilder source, ShaderNodeVariable var, boolean appendNameSpace) { |
||||
declareVariable(source, var, appendNameSpace, null); |
||||
} |
||||
|
||||
/** |
||||
* declares a variable, embed in a conditional block if needed. the namespace is appended with "_" |
||||
* @param source the StringBuilder to use |
||||
* @param var the variable to declare |
||||
*/ |
||||
protected void declareVariable(StringBuilder source, ShaderNodeVariable var) { |
||||
declareVariable(source, var, true, null); |
||||
} |
||||
|
||||
/** |
||||
* declares a variable, embed in a conditional block if needed. the namespace is appended with "_" |
||||
* @param source the StringBuilder to use |
||||
* @param var the variable to declare |
||||
* @param value the initialization value to assign the the variable |
||||
*/ |
||||
protected void declareVariable(StringBuilder source, ShaderNodeVariable var, String value) { |
||||
declareVariable(source, var, value, true, null); |
||||
} |
||||
|
||||
/** |
||||
* declares a variable, embed in a conditional block if needed. |
||||
* @param source the StringBuilder to use |
||||
* @param var the variable to declare |
||||
* @param appendNameSpace true to append the nameSpace + "_" |
||||
* @param modifier the modifier of the variable (attribute, varying, in , out,...) |
||||
*/ |
||||
protected void declareVariable(StringBuilder source, ShaderNodeVariable var, boolean appendNameSpace, String modifier) { |
||||
declareVariable(source, var, null, appendNameSpace, modifier); |
||||
} |
||||
|
||||
/** |
||||
* declares a variable, embed in a conditional block if needed. |
||||
* @param source the StringBuilder to use |
||||
* @param var the variable to declare |
||||
* @param value the initialization value to assign the the variable |
||||
* @param appendNameSpace true to append the nameSpace + "_" |
||||
* @param modifier the modifier of the variable (attribute, varying, in , out,...) |
||||
*/ |
||||
protected void declareVariable(StringBuilder source, ShaderNodeVariable var, String value, boolean appendNameSpace, String modifier) { |
||||
startCondition(var.getCondition(), source); |
||||
appendIndent(source); |
||||
if (modifier != null) { |
||||
source.append(modifier); |
||||
source.append(" "); |
||||
} |
||||
|
||||
source.append(var.getType()); |
||||
source.append(" "); |
||||
if (appendNameSpace) { |
||||
source.append(var.getNameSpace()); |
||||
source.append("_"); |
||||
} |
||||
source.append(var.getName()); |
||||
if (value != null) { |
||||
source.append(" = "); |
||||
source.append(value); |
||||
} |
||||
source.append(";\n"); |
||||
endCondition(var.getCondition(), source); |
||||
} |
||||
|
||||
/** |
||||
* Starts a conditional block |
||||
* @param condition the block condition |
||||
* @param source the StringBuilder to use |
||||
*/ |
||||
protected void startCondition(String condition, StringBuilder source) { |
||||
if (condition != null) { |
||||
appendIndent(source); |
||||
source.append("#if "); |
||||
source.append(condition); |
||||
source.append("\n"); |
||||
indent(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Ends a conditional block |
||||
* @param condition the block condition |
||||
* @param source the StringBuilder to use |
||||
*/ |
||||
protected void endCondition(String condition, StringBuilder source) { |
||||
if (condition != null) { |
||||
unIndent(); |
||||
appendIndent(source); |
||||
source.append("#endif\n"); |
||||
|
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Appends a mapping to the source, embed in a conditional block if needed, |
||||
* with variables nameSpaces and swizzle. |
||||
* @param mapping the VariableMapping to append |
||||
* @param source the StringBuilder to use |
||||
*/ |
||||
protected void map(VariableMapping mapping, StringBuilder source) { |
||||
startCondition(mapping.getCondition(), source); |
||||
appendIndent(source); |
||||
if (!mapping.getLeftVariable().isShaderOutput()) { |
||||
source.append(mapping.getLeftVariable().getType()); |
||||
source.append(" "); |
||||
} |
||||
source.append(mapping.getLeftVariable().getNameSpace()); |
||||
source.append("_"); |
||||
source.append(mapping.getLeftVariable().getName()); |
||||
if (mapping.getLeftSwizzling().length() > 0) { |
||||
source.append("."); |
||||
source.append(mapping.getLeftSwizzling()); |
||||
} |
||||
source.append(" = "); |
||||
String namePrefix = getAppendableNameSpace(mapping.getRightVariable()); |
||||
source.append(namePrefix); |
||||
source.append(mapping.getRightVariable().getName()); |
||||
if (mapping.getRightSwizzling().length() > 0) { |
||||
source.append("."); |
||||
source.append(mapping.getRightSwizzling()); |
||||
} |
||||
source.append(";\n"); |
||||
endCondition(mapping.getCondition(), source); |
||||
} |
||||
|
||||
/** |
||||
* replaces a variable name in a shaderNode source code by prefixing it |
||||
* with its nameSpace and "_" if needed. |
||||
* @param nodeSource the source ot modify |
||||
* @param var the variable to replace |
||||
* @return the modified source |
||||
*/ |
||||
protected String replaceVariableName(String nodeSource, ShaderNodeVariable var) { |
||||
String namePrefix = getAppendableNameSpace(var); |
||||
String newName = namePrefix + var.getName(); |
||||
nodeSource = replace(nodeSource, var, newName); |
||||
return nodeSource; |
||||
} |
||||
|
||||
/** |
||||
* Finds if a variable is a varying |
||||
* @param info the ShaderGenerationInfo |
||||
* @param v the variable |
||||
* @return true is the given variable is a varying |
||||
*/ |
||||
protected boolean isVarying(ShaderGenerationInfo info, ShaderNodeVariable v) { |
||||
boolean isVarying = false; |
||||
for (ShaderNodeVariable shaderNodeVariable : info.getVaryings()) { |
||||
if (shaderNodeVariable.equals(v)) { |
||||
isVarying = true; |
||||
} |
||||
} |
||||
return isVarying; |
||||
} |
||||
|
||||
/** |
||||
* Appends a comment to the generated code |
||||
* @param source the StringBuilder to use |
||||
* @param shaderNode the shader node being processed (to append its name) |
||||
* @param comment the comment to append |
||||
*/ |
||||
protected void comment(StringBuilder source, ShaderNode shaderNode, String comment) { |
||||
appendIndent(source); |
||||
source.append("//"); |
||||
source.append(shaderNode.getName()); |
||||
source.append(" : "); |
||||
source.append(comment); |
||||
source.append("\n"); |
||||
} |
||||
|
||||
/** |
||||
* returns the name space to append for a variable. |
||||
* Attributes, WorldParam and MatParam names psace must not be appended |
||||
* @param var the variable |
||||
* @return the namespace to append for this variable |
||||
*/ |
||||
protected String getAppendableNameSpace(ShaderNodeVariable var) { |
||||
String namePrefix = var.getNameSpace() + "_"; |
||||
if (namePrefix.equals("Attr_") || namePrefix.equals("WorldParam_") || namePrefix.equals("MatParam_")) { |
||||
namePrefix = ""; |
||||
} |
||||
return namePrefix; |
||||
} |
||||
|
||||
/** |
||||
* transforms defines name is the shader node code. |
||||
* One can use a #if defined(inputVariableName) in a shaderNode code. |
||||
* This method is responsible for changing the variable name with the |
||||
* appropriate defined based on the mapping condition of this variable. |
||||
* Complex condition synthax are handled. |
||||
* |
||||
* @param nodeSource the sahderNode source code |
||||
* @param shaderNode the ShaderNode being processed |
||||
* @return the modified shaderNode source. |
||||
*/ |
||||
protected String updateDefinesName(String nodeSource, ShaderNode shaderNode) { |
||||
String[] lines = nodeSource.split("\\n"); |
||||
ConditionParser parser = new ConditionParser(); |
||||
for (String line : lines) { |
||||
|
||||
if (line.trim().startsWith("#if")) { |
||||
List<String> params = parser.extractDefines(line.trim()); |
||||
String l = line.trim().replaceAll("defined", "").replaceAll("#if ", "").replaceAll("#ifdef", "");//parser.getFormattedExpression();
|
||||
boolean match = false; |
||||
for (String param : params) { |
||||
for (VariableMapping map : shaderNode.getInputMapping()) { |
||||
if ((map.getLeftVariable().getName()).equals(param)) { |
||||
if (map.getCondition() != null) { |
||||
l = l.replaceAll(param, map.getCondition()); |
||||
match = true; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
if (match) { |
||||
nodeSource = nodeSource.replace(line.trim(), "#if " + l); |
||||
} |
||||
} |
||||
} |
||||
return nodeSource; |
||||
} |
||||
|
||||
/** |
||||
* replaced a variable name in a source code with the given name |
||||
* @param nodeSource the source to use |
||||
* @param var the variable |
||||
* @param newName the new name of the variable |
||||
* @return the modified source code |
||||
*/ |
||||
protected String replace(String nodeSource, ShaderNodeVariable var, String newName) { |
||||
nodeSource = nodeSource.replaceAll("(\\W)" + var.getName() + "(\\W)", "$1" + newName + "$2"); |
||||
return nodeSource; |
||||
} |
||||
|
||||
/** |
||||
* Finds if a variable is a world or a material parameter |
||||
* @param var the variable |
||||
* @return true if the variable is a Word or material parameter |
||||
*/ |
||||
protected boolean isWorldOrMaterialParam(ShaderNodeVariable var) { |
||||
return var.getNameSpace().equals("MatParam") || var.getNameSpace().equals("WorldParam"); |
||||
} |
||||
|
||||
@Override |
||||
protected String getLanguageAndVersion(ShaderType type) { |
||||
return "GLSL100"; |
||||
} |
||||
|
||||
/** |
||||
* appends indentation. |
||||
* @param source |
||||
*/ |
||||
protected void appendIndent(StringBuilder source) { |
||||
source.append(INDENTCHAR.substring(0, indent)); |
||||
} |
||||
|
||||
/** |
||||
* Declares an attribute |
||||
* @param source the StringBuilder to use |
||||
* @param var the variable to declare as an attribute |
||||
*/ |
||||
protected void declareAttribute(StringBuilder source, ShaderNodeVariable var) { |
||||
declareVariable(source, var, false, "attribute"); |
||||
} |
||||
|
||||
/** |
||||
* Declares a varying |
||||
* @param source the StringBuilder to use |
||||
* @param var the variable to declare as an varying |
||||
* @param input a boolean set to true if the this varying is an input. |
||||
* this in not used in this implementaiton but can be used in overidings |
||||
* implementation |
||||
*/ |
||||
protected void declareVarying(StringBuilder source, ShaderNodeVariable var, boolean input) { |
||||
declareVariable(source, var, true, "varying"); |
||||
} |
||||
|
||||
/** |
||||
* Decrease indentation with a check so the indent is never negative. |
||||
*/ |
||||
protected void unIndent() { |
||||
indent--; |
||||
indent = Math.max(0, indent); |
||||
} |
||||
|
||||
/** |
||||
* increase indentation with a check so that indentation is never over 10 |
||||
*/ |
||||
protected void indent() { |
||||
indent++; |
||||
indent = Math.min(10, indent); |
||||
} |
||||
} |
@ -0,0 +1,146 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shader; |
||||
|
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.material.ShaderGenerationInfo; |
||||
import com.jme3.shader.Shader.ShaderType; |
||||
|
||||
/** |
||||
* This shader Generator can generate Vertex and Fragment shaders from |
||||
* ShaderNodes for GLSL 1.5 |
||||
* |
||||
* @author Nehon |
||||
*/ |
||||
public class Glsl150ShaderGenerator extends Glsl100ShaderGenerator { |
||||
|
||||
/** |
||||
* Creates a Glsl150ShaderGenerator |
||||
* |
||||
* @param assetManager the assetmanager |
||||
*/ |
||||
public Glsl150ShaderGenerator(AssetManager assetManager) { |
||||
super(assetManager); |
||||
} |
||||
|
||||
@Override |
||||
protected String getLanguageAndVersion(ShaderType type) { |
||||
return "GLSL150"; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritDoc} in glsl 1.5 attributes are prefixed with the "in" keyword |
||||
* and not the "attribute" keyword |
||||
*/ |
||||
@Override |
||||
protected void declareAttribute(StringBuilder source, ShaderNodeVariable var) { |
||||
declareVariable(source, var, false, "in"); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritDoc} in glsl 1.5 varying are prefixed with the "in" or "out" |
||||
* keyword and not the "varying" keyword. |
||||
* |
||||
* "in" is used for Fragment shader (maybe Geometry shader later) "out" is |
||||
* used for Vertex shader (maybe Geometry shader later) |
||||
*/ |
||||
@Override |
||||
protected void declareVarying(StringBuilder source, ShaderNodeVariable var, boolean input) { |
||||
declareVariable(source, var, true, input ? "in" : "out"); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritDoc} |
||||
* |
||||
* Fragment shader outputs are declared before the "void main(){" with the |
||||
* "out" keyword. |
||||
* |
||||
* after the "void main(){", the vertex output are declared and initialized |
||||
* and the frgament outputs are declared |
||||
*/ |
||||
@Override |
||||
protected void generateStartOfMainSection(StringBuilder source, ShaderGenerationInfo info, Shader.ShaderType type) { |
||||
source.append("\n"); |
||||
|
||||
if (type == Shader.ShaderType.Fragment) { |
||||
for (ShaderNodeVariable global : info.getFragmentGlobals()) { |
||||
declareVariable(source, global, null, true, "out"); |
||||
} |
||||
} |
||||
source.append("\n"); |
||||
|
||||
appendIndent(source); |
||||
source.append("void main(){\n"); |
||||
indent(); |
||||
|
||||
if (type == Shader.ShaderType.Vertex) { |
||||
declareVariable(source, info.getVertexGlobal(), "inPosition"); |
||||
} else if (type == Shader.ShaderType.Fragment) { |
||||
for (ShaderNodeVariable global : info.getFragmentGlobals()) { |
||||
initVariable(source, global, "vec4(1.0)"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* {@inheritDoc} |
||||
* |
||||
* only vertex shader output are mapped here, since fragment shader outputs |
||||
* must have been mapped in the main section. |
||||
*/ |
||||
@Override |
||||
protected void generateEndOfMainSection(StringBuilder source, ShaderGenerationInfo info, Shader.ShaderType type) { |
||||
if (type == Shader.ShaderType.Vertex) { |
||||
appendOutput(source, "gl_Position", info.getVertexGlobal()); |
||||
} |
||||
unIndent(); |
||||
appendIndent(source); |
||||
source.append("}\n"); |
||||
} |
||||
|
||||
/** |
||||
* Append a variable initialization to the code |
||||
* |
||||
* @param source the StringBuilder to use |
||||
* @param var the variable to initialize |
||||
* @param initValue the init value to assign to the variable |
||||
*/ |
||||
protected void initVariable(StringBuilder source, ShaderNodeVariable var, String initValue) { |
||||
appendIndent(source); |
||||
source.append(var.getNameSpace()); |
||||
source.append("_"); |
||||
source.append(var.getName()); |
||||
source.append(" = "); |
||||
source.append(initValue); |
||||
source.append(";\n"); |
||||
} |
||||
} |
@ -0,0 +1,290 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shader; |
||||
|
||||
import com.jme3.asset.AssetKey; |
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.material.ShaderGenerationInfo; |
||||
import com.jme3.material.Technique; |
||||
import com.jme3.material.TechniqueDef; |
||||
import com.jme3.shader.Shader.ShaderType; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* This class is the base for a shader generator using the ShaderNodes system, |
||||
* it contains basis mechnaism of generation, but no actual generation code. |
||||
* This class is abstract, any Shader generator must extend it. |
||||
* |
||||
* @author Nehon |
||||
*/ |
||||
public abstract class ShaderGenerator { |
||||
|
||||
//the asset manager
|
||||
protected AssetManager assetManager; |
||||
//indentation value for generation
|
||||
protected int indent; |
||||
|
||||
/** |
||||
* Build a shaderGenerator |
||||
* |
||||
* @param assetManager |
||||
*/ |
||||
protected ShaderGenerator(AssetManager assetManager) { |
||||
this.assetManager = assetManager; |
||||
} |
||||
|
||||
/** |
||||
* Generate vertex and fragment shaders for the given technique |
||||
* |
||||
* @param technique the technique to use to generate the shaders |
||||
* @return a Shader program |
||||
*/ |
||||
public Shader generateShader(Technique technique) { |
||||
|
||||
DefineList defines = technique.getAllDefines(); |
||||
TechniqueDef def = technique.getDef(); |
||||
ShaderGenerationInfo info = def.getShaderGenerationInfo(); |
||||
|
||||
String vertexSource = buildShader(def.getShaderNodes(), info, ShaderType.Vertex); |
||||
String fragmentSource = buildShader(def.getShaderNodes(), info, ShaderType.Fragment); |
||||
|
||||
Shader shader = new Shader(); |
||||
shader.initialize(); |
||||
shader.addSource(Shader.ShaderType.Vertex, technique.getDef().getName() + ".vert", vertexSource, defines.getCompiled(), getLanguageAndVersion(ShaderType.Vertex)); |
||||
shader.addSource(Shader.ShaderType.Fragment, technique.getDef().getName() + ".frag", fragmentSource, defines.getCompiled(), getLanguageAndVersion(ShaderType.Fragment)); |
||||
|
||||
return shader; |
||||
} |
||||
|
||||
/** |
||||
* This method is responsible for the shader generation. |
||||
* |
||||
* @param shaderNodes the list of shader nodes |
||||
* @param info the ShaderGenerationInfo filled during the Technique loading |
||||
* @param type the type of shader to generate |
||||
* @return the code of the generated vertex shader |
||||
*/ |
||||
protected String buildShader(List<ShaderNode> shaderNodes, ShaderGenerationInfo info, ShaderType type) { |
||||
indent = 0; |
||||
|
||||
StringBuilder sourceDeclaration = new StringBuilder(); |
||||
StringBuilder source = new StringBuilder(); |
||||
|
||||
generateUniforms(sourceDeclaration, info, type); |
||||
|
||||
if (type == ShaderType.Vertex) { |
||||
generateAttributes(sourceDeclaration, info); |
||||
} |
||||
generateVaryings(sourceDeclaration, info, type); |
||||
|
||||
generateStartOfMainSection(source, info, type); |
||||
|
||||
generateDeclarationAndMainBody(shaderNodes, sourceDeclaration, source, info, type); |
||||
|
||||
generateEndOfMainSection(source, info, type); |
||||
|
||||
sourceDeclaration.append(source); |
||||
|
||||
return sourceDeclaration.toString(); |
||||
} |
||||
|
||||
/** |
||||
* iterates through shader nodes to load them and generate the shader |
||||
* declaration part and main body extracted from the shader nodes, for the |
||||
* given shader type |
||||
* |
||||
* @param shaderNodes the list of shader nodes |
||||
* @param sourceDeclaration the declaration part StringBuilder of the shader |
||||
* to generate |
||||
* @param source the main part StringBuilder of the shader to generate |
||||
* @param info the ShaderGenerationInfo |
||||
* @param type the Shader type |
||||
*/ |
||||
protected void generateDeclarationAndMainBody(List<ShaderNode> shaderNodes, StringBuilder sourceDeclaration, StringBuilder source, ShaderGenerationInfo info, Shader.ShaderType type) { |
||||
for (ShaderNode shaderNode : shaderNodes) { |
||||
if (info.getUnusedNodes().contains(shaderNode.getName())) { |
||||
continue; |
||||
} |
||||
if (shaderNode.getDefinition().getType() == type) { |
||||
int index = findShaderIndexFromVersion(shaderNode, type); |
||||
String loadedSource = (String) assetManager.loadAsset(new AssetKey(shaderNode.getDefinition().getShadersPath().get(index))); |
||||
appendNodeDeclarationAndMain(loadedSource, sourceDeclaration, source, shaderNode, info); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Appends declaration and main part of a node to the shader declaration and |
||||
* main part. the loadedSource is split by "void main(){" to split |
||||
* declaration from main part of the node source code.The trailing "}" is |
||||
* removed from the main part. Each part is then respectively passed to |
||||
* generateDeclarativeSection and generateNodeMainSection. |
||||
* |
||||
* @see ShaderGenerator#generateDeclarativeSection |
||||
* @see ShaderGenerator#generateNodeMainSection |
||||
* |
||||
* @param loadedSource the actual source code loaded for this node. |
||||
* @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) { |
||||
if (loadedSource.length() > 1) { |
||||
loadedSource = loadedSource.substring(0, loadedSource.lastIndexOf("}")); |
||||
String[] sourceParts = loadedSource.split("void main\\(\\)\\{"); |
||||
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); |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* returns the laguage + version of the shader should be somthing like |
||||
* "GLSL100" for glsl 1.0 "GLSL150" for glsl 1.5. |
||||
* |
||||
* @param type the shader type for wich the version should be returned. |
||||
* |
||||
* @return the shaderLanguage and version. |
||||
*/ |
||||
protected abstract String getLanguageAndVersion(Shader.ShaderType type); |
||||
|
||||
/** |
||||
* generates the uniforms declaration for a shader of the given type. |
||||
* |
||||
* @param source the source StringBuilder to append generated code. |
||||
* @param info the ShaderGenerationInfo. |
||||
* @param type the shader type the uniforms have to be generated for. |
||||
*/ |
||||
protected abstract void generateUniforms(StringBuilder source, ShaderGenerationInfo info, ShaderType type); |
||||
|
||||
/** |
||||
* generates the attributes declaration for the vertex shader. There is no |
||||
* Shader type passed here as attributes are only used in vertex shaders |
||||
* |
||||
* @param source the source StringBuilder to append generated code. |
||||
* @param info the ShaderGenerationInfo. |
||||
*/ |
||||
protected abstract void generateAttributes(StringBuilder source, ShaderGenerationInfo info); |
||||
|
||||
/** |
||||
* generates the varyings for the given shader type shader. Note that |
||||
* varyings are deprecated in glsl 1.3, but this method will still be called |
||||
* to generate all non global inputs and output of the shaders. |
||||
* |
||||
* @param source the source StringBuilder to append generated code. |
||||
* @param info the ShaderGenerationInfo. |
||||
* @param type the shader type the varyings have to be generated for. |
||||
*/ |
||||
protected abstract void generateVaryings(StringBuilder source, ShaderGenerationInfo info, ShaderType type); |
||||
|
||||
/** |
||||
* Appends the given shaderNode declarative part to the shader declarative |
||||
* part. If needed the sahder type can be determined by fetching the |
||||
* shaderNode's definition type. |
||||
* |
||||
* @see ShaderNode#getDefinition() |
||||
* @see ShaderNodeDefinition#getType() |
||||
* |
||||
* @param source the StringBuilder to append generated code. |
||||
* @param shaderNode the shaderNode. |
||||
* @param nodeSource the declaration part of the loaded shaderNode source. |
||||
* @param info the ShaderGenerationInfo. |
||||
*/ |
||||
protected abstract void generateDeclarativeSection(StringBuilder source, ShaderNode shaderNode, String nodeDecalarationSource, ShaderGenerationInfo info); |
||||
|
||||
/** |
||||
* generates the start of the shader main section. this method is |
||||
* responsible of appending the "void main(){" in the shader and declaring |
||||
* all global outputs of the shader |
||||
* |
||||
* @param source the StringBuilder to append generated code. |
||||
* @param info the ShaderGenerationInfo. |
||||
* @param type the shader type the section has to be generated for. |
||||
*/ |
||||
protected abstract void generateStartOfMainSection(StringBuilder source, ShaderGenerationInfo info, ShaderType type); |
||||
|
||||
/** |
||||
* generates the end of the shader main section. this method is responsible |
||||
* of appending the last "}" in the shader and mapping all global outputs of |
||||
* the shader |
||||
* |
||||
* @param source the StringBuilder to append generated code. |
||||
* @param info the ShaderGenerationInfo. |
||||
* @param type the shader type the section has to be generated for. |
||||
*/ |
||||
protected abstract void generateEndOfMainSection(StringBuilder source, ShaderGenerationInfo info, ShaderType type); |
||||
|
||||
/** |
||||
* Appends the given shaderNode main part to the shader declarative part. If |
||||
* needed the sahder type can be determined by fetching the shaderNode's |
||||
* definition type. |
||||
* |
||||
* @see ShaderNode#getDefinition() |
||||
* @see ShaderNodeDefinition#getType() |
||||
* |
||||
* @param source the StringBuilder to append generated code. |
||||
* @param shaderNode the shaderNode. |
||||
* @param nodeSource the declaration part of the loaded shaderNode source. |
||||
* @param info the ShaderGenerationInfo. |
||||
*/ |
||||
protected abstract void generateNodeMainSection(StringBuilder source, ShaderNode shaderNode, String nodeSource, ShaderGenerationInfo info); |
||||
|
||||
/** |
||||
* returns the shaderpath index according to the version of the generator. |
||||
* This allow to select the higher version of the shader that the generator |
||||
* can handle |
||||
* |
||||
* @param shaderNode the shaderNode being processed |
||||
* @param type the shaderType |
||||
* @return the index of the shader path in ShaderNodeDefinition shadersPath |
||||
* list |
||||
* @throws NumberFormatException |
||||
*/ |
||||
protected int findShaderIndexFromVersion(ShaderNode shaderNode, ShaderType type) throws NumberFormatException { |
||||
int index = 0; |
||||
List<String> lang = shaderNode.getDefinition().getShadersLanguage(); |
||||
int genVersion = Integer.parseInt(getLanguageAndVersion(type).substring(4)); |
||||
int curVersion = 0; |
||||
for (int i = 0; i < lang.size(); i++) { |
||||
int version = Integer.parseInt(lang.get(i).substring(4)); |
||||
if (version > curVersion && version <= genVersion) { |
||||
curVersion = version; |
||||
index = i; |
||||
} |
||||
} |
||||
return index; |
||||
} |
||||
} |
@ -0,0 +1,215 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shader; |
||||
|
||||
import com.jme3.export.InputCapsule; |
||||
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.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* A ShaderNode is the unit brick part of a shader program. A shader can be |
||||
* describe with several shader nodes that are plugged together through inputs |
||||
* and outputs. |
||||
* |
||||
* A ShaderNode is based on a definition that has a shader code, inputs and |
||||
* output variables. This node can be activated based on a condition, and has |
||||
* input and ouput mapping. |
||||
* |
||||
* This class is not intended to be used by JME users directly. It's the |
||||
* stucture for loading shader nodes from a J3md ùaterial definition file |
||||
* |
||||
* @author Nehon |
||||
*/ |
||||
public class ShaderNode implements Savable { |
||||
|
||||
private String name; |
||||
private ShaderNodeDefinition definition; |
||||
private String condition; |
||||
private List<VariableMapping> inputMapping = new ArrayList<VariableMapping>(); |
||||
private List<VariableMapping> outputMapping = new ArrayList<VariableMapping>(); |
||||
|
||||
/** |
||||
* creates a ShaderNode |
||||
* |
||||
* @param name the name |
||||
* @param definition the ShaderNodeDefinition |
||||
* @param condition the conditionto activate this node |
||||
*/ |
||||
public ShaderNode(String name, ShaderNodeDefinition definition, String condition) { |
||||
this.name = name; |
||||
this.definition = definition; |
||||
this.condition = condition; |
||||
} |
||||
|
||||
/** |
||||
* creates a ShaderNode |
||||
*/ |
||||
public ShaderNode() { |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the name of the node |
||||
*/ |
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
/** |
||||
* sets the name of th node |
||||
* |
||||
* @param name the name |
||||
*/ |
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
/** |
||||
* returns the definition |
||||
* |
||||
* @return the ShaderNodeDefinition |
||||
*/ |
||||
public ShaderNodeDefinition getDefinition() { |
||||
return definition; |
||||
} |
||||
|
||||
/** |
||||
* sets the definition |
||||
* |
||||
* @param definition the ShaderNodeDefinition |
||||
*/ |
||||
public void setDefinition(ShaderNodeDefinition definition) { |
||||
this.definition = definition; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the condition |
||||
*/ |
||||
public String getCondition() { |
||||
return condition; |
||||
} |
||||
|
||||
/** |
||||
* sets the ocndition |
||||
* |
||||
* @param condition the condition |
||||
*/ |
||||
public void setCondition(String condition) { |
||||
this.condition = condition; |
||||
} |
||||
|
||||
/** |
||||
* return a list of VariableMapping representing the input mappings of this |
||||
* node |
||||
* |
||||
* @return the input mappings |
||||
*/ |
||||
public List<VariableMapping> getInputMapping() { |
||||
return inputMapping; |
||||
} |
||||
|
||||
/** |
||||
* sets the input mappings |
||||
* |
||||
* @param inputMapping the input mappings |
||||
*/ |
||||
public void setInputMapping(List<VariableMapping> inputMapping) { |
||||
this.inputMapping = inputMapping; |
||||
} |
||||
|
||||
/** |
||||
* return a list of VariableMapping representing the output mappings of this |
||||
* node |
||||
* |
||||
* @return the output mappings |
||||
*/ |
||||
public List<VariableMapping> getOutputMapping() { |
||||
return outputMapping; |
||||
} |
||||
|
||||
/** |
||||
* sets the output mappings |
||||
* |
||||
* @param inputMapping the output mappings |
||||
*/ |
||||
public void setOutputMapping(List<VariableMapping> outputMapping) { |
||||
this.outputMapping = outputMapping; |
||||
} |
||||
|
||||
/** |
||||
* jme seralization |
||||
* |
||||
* @param ex the exporter |
||||
* @throws IOException |
||||
*/ |
||||
@Override |
||||
public void write(JmeExporter ex) throws IOException { |
||||
OutputCapsule oc = (OutputCapsule) ex.getCapsule(this); |
||||
oc.write(name, "name", ""); |
||||
oc.write(definition, "definition", null); |
||||
oc.write(condition, "condition", null); |
||||
oc.writeSavableArrayList((ArrayList) inputMapping, "inputMapping", new ArrayList<VariableMapping>()); |
||||
oc.writeSavableArrayList((ArrayList) outputMapping, "outputMapping", new ArrayList<VariableMapping>()); |
||||
} |
||||
|
||||
/** |
||||
* jme seralization |
||||
* |
||||
* @param im the importer |
||||
* @throws IOException |
||||
*/ |
||||
@Override |
||||
public void read(JmeImporter im) throws IOException { |
||||
InputCapsule ic = (InputCapsule) im.getCapsule(this); |
||||
name = ic.readString("name", ""); |
||||
definition = (ShaderNodeDefinition) ic.readSavable("definition", null); |
||||
condition = ic.readString("condition", null); |
||||
inputMapping = (List<VariableMapping>) ic.readSavableArrayList("inputMapping", new ArrayList<VariableMapping>()); |
||||
outputMapping = (List<VariableMapping>) ic.readSavableArrayList("outputMapping", new ArrayList<VariableMapping>()); |
||||
} |
||||
|
||||
/** |
||||
* convenience tostring |
||||
* |
||||
* @return a string |
||||
*/ |
||||
@Override |
||||
public String toString() { |
||||
return "\nShaderNode{" + "\nname=" + name + ", \ndefinition=" + definition.getName() + ", \ncondition=" + condition + ", \ninputMapping=" + inputMapping + ", \noutputMapping=" + outputMapping + '}'; |
||||
} |
||||
} |
@ -0,0 +1,253 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shader; |
||||
|
||||
import com.jme3.export.InputCapsule; |
||||
import com.jme3.export.JmeExporter; |
||||
import com.jme3.export.JmeImporter; |
||||
import com.jme3.export.OutputCapsule; |
||||
import com.jme3.export.Savable; |
||||
import com.jme3.shader.Shader.ShaderType; |
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Shader node definition structure meant for holding loaded datat from a |
||||
* material definition j3md file |
||||
* |
||||
* @author Nehon |
||||
*/ |
||||
public class ShaderNodeDefinition implements Savable { |
||||
|
||||
private String name; |
||||
private Shader.ShaderType type; |
||||
private List<String> shadersLanguage = new ArrayList<String>(); |
||||
private List<String> shadersPath = new ArrayList<String>(); |
||||
private String documentation; |
||||
private List<ShaderNodeVariable> inputs = new ArrayList<ShaderNodeVariable>(); |
||||
private List<ShaderNodeVariable> outputs = new ArrayList<ShaderNodeVariable>(); |
||||
private String path = null; |
||||
|
||||
/** |
||||
* creates a ShaderNodeDefinition |
||||
* |
||||
* @param name the name of the definition |
||||
* @param type the type of the shader |
||||
* @param shaderPath the path of the shader |
||||
* @param shaderLanguage the shader language (minimum required for this |
||||
* definition) |
||||
*/ |
||||
public ShaderNodeDefinition(String name, ShaderType type, String shaderPath, String shaderLanguage) { |
||||
this.name = name; |
||||
this.type = type; |
||||
shadersLanguage.add(shaderLanguage); |
||||
shadersPath.add(shaderPath); |
||||
} |
||||
|
||||
/** |
||||
* creates a ShaderNodeDefinition |
||||
*/ |
||||
public ShaderNodeDefinition() { |
||||
} |
||||
|
||||
/** |
||||
* returns the name of the definition |
||||
* |
||||
* @return the name |
||||
*/ |
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
/** |
||||
* sets the name of the definition |
||||
* |
||||
* @param name the name |
||||
*/ |
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the type of shader the definition applies to |
||||
*/ |
||||
public ShaderType getType() { |
||||
return type; |
||||
} |
||||
|
||||
/** |
||||
* sets the type of shader this def applies to |
||||
* |
||||
* @param type the type |
||||
*/ |
||||
public void setType(ShaderType type) { |
||||
this.type = type; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the docuentation for tthis definition |
||||
*/ |
||||
public String getDocumentation() { |
||||
return documentation; |
||||
} |
||||
|
||||
/** |
||||
* sets the dcumentation |
||||
* |
||||
* @param documentation the documentation |
||||
*/ |
||||
public void setDocumentation(String documentation) { |
||||
this.documentation = documentation; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the input variables of this definition |
||||
*/ |
||||
public List<ShaderNodeVariable> getInputs() { |
||||
return inputs; |
||||
} |
||||
|
||||
/** |
||||
* sets the input variables of this definition |
||||
* |
||||
* @param inputs the inputs |
||||
*/ |
||||
public void setInputs(List<ShaderNodeVariable> inputs) { |
||||
this.inputs = inputs; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the output variables of this definition |
||||
*/ |
||||
public List<ShaderNodeVariable> getOutputs() { |
||||
return outputs; |
||||
} |
||||
|
||||
/** |
||||
* sets the output variables of this definition |
||||
* |
||||
* @param inputs the output |
||||
*/ |
||||
public void setOutputs(List<ShaderNodeVariable> outputs) { |
||||
this.outputs = outputs; |
||||
} |
||||
|
||||
/** |
||||
* retrun the path of this definition |
||||
* @return |
||||
*/ |
||||
public String getPath() { |
||||
return path; |
||||
} |
||||
|
||||
/** |
||||
* sets the path of this definition |
||||
* @param path |
||||
*/ |
||||
public void setPath(String path) { |
||||
this.path = path; |
||||
} |
||||
|
||||
|
||||
|
||||
/** |
||||
* jme seralization (not used) |
||||
* |
||||
* @param ex the exporter |
||||
* @throws IOException |
||||
*/ |
||||
public void write(JmeExporter ex) throws IOException { |
||||
OutputCapsule oc = (OutputCapsule) ex.getCapsule(this); |
||||
oc.write(name, "name", ""); |
||||
String[] str = new String[shadersLanguage.size()]; |
||||
oc.write(shadersLanguage.toArray(str), "shadersLanguage", null); |
||||
oc.write(shadersPath.toArray(str), "shadersPath", null); |
||||
oc.write(type, "type", null); |
||||
oc.writeSavableArrayList((ArrayList) inputs, "inputs", new ArrayList<ShaderNodeVariable>()); |
||||
oc.writeSavableArrayList((ArrayList) outputs, "inputs", new ArrayList<ShaderNodeVariable>()); |
||||
} |
||||
|
||||
public List<String> getShadersLanguage() { |
||||
return shadersLanguage; |
||||
} |
||||
|
||||
public List<String> getShadersPath() { |
||||
return shadersPath; |
||||
} |
||||
|
||||
|
||||
|
||||
/** |
||||
* jme seralization (not used) |
||||
* |
||||
* @param im the importer |
||||
* @throws IOException |
||||
*/ |
||||
public void read(JmeImporter im) throws IOException { |
||||
InputCapsule ic = (InputCapsule) im.getCapsule(this); |
||||
name = ic.readString("name", ""); |
||||
|
||||
String[] str = ic.readStringArray("shadersLanguage", null); |
||||
if (str != null) { |
||||
shadersLanguage = Arrays.asList(str); |
||||
} else { |
||||
shadersLanguage = new ArrayList<String>(); |
||||
} |
||||
|
||||
str = ic.readStringArray("shadersPath", null); |
||||
if (str != null) { |
||||
shadersPath = Arrays.asList(str); |
||||
} else { |
||||
shadersPath = new ArrayList<String>(); |
||||
} |
||||
|
||||
type = ic.readEnum("type", Shader.ShaderType.class, null); |
||||
inputs = (List<ShaderNodeVariable>) ic.readSavableArrayList("inputs", new ArrayList<ShaderNodeVariable>()); |
||||
outputs = (List<ShaderNodeVariable>) ic.readSavableArrayList("outputs", new ArrayList<ShaderNodeVariable>()); |
||||
} |
||||
|
||||
/** |
||||
* convenience tostring |
||||
* |
||||
* @return a string |
||||
*/ |
||||
@Override |
||||
public String toString() { |
||||
return "\nShaderNodeDefinition{\n" + "name=" + name + "\ntype=" + type + "\nshaderPath=" + shadersPath + "\nshaderLanguage=" + shadersLanguage + "\ndocumentation=" + documentation + "\ninputs=" + inputs + ",\noutputs=" + outputs + '}'; |
||||
} |
||||
} |
@ -0,0 +1,234 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shader; |
||||
|
||||
import com.jme3.export.InputCapsule; |
||||
import com.jme3.export.JmeExporter; |
||||
import com.jme3.export.JmeImporter; |
||||
import com.jme3.export.OutputCapsule; |
||||
import com.jme3.export.Savable; |
||||
import java.io.IOException; |
||||
|
||||
/** |
||||
* A shader node variable |
||||
* |
||||
* @author Nehon |
||||
*/ |
||||
public class ShaderNodeVariable implements Savable, Cloneable { |
||||
|
||||
private String name; |
||||
private String type; |
||||
private String nameSpace; |
||||
private String condition; |
||||
private boolean shaderOutput = false; |
||||
|
||||
/** |
||||
* creates a ShaderNodeVariable |
||||
* |
||||
* @param type the glsl type of the variable |
||||
* @param name the name of the variable |
||||
*/ |
||||
public ShaderNodeVariable(String type, String name) { |
||||
this.name = name; |
||||
this.type = type; |
||||
} |
||||
|
||||
/** |
||||
* creates a ShaderNodeVariable |
||||
* |
||||
* @param type the glsl type of the variable |
||||
* @param nameSpace the nameSpace (can be the name of the shaderNode or |
||||
* Globel,Attr,MatParam,WorldParam) |
||||
* @param name the name of the variable |
||||
*/ |
||||
public ShaderNodeVariable(String type, String nameSpace, String name) { |
||||
this.name = name; |
||||
this.nameSpace = nameSpace; |
||||
this.type = type; |
||||
} |
||||
|
||||
/** |
||||
* returns the name |
||||
* |
||||
* @return the name |
||||
*/ |
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
/** |
||||
* sets the name |
||||
* |
||||
* @param name the name |
||||
*/ |
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the glsl type |
||||
*/ |
||||
public String getType() { |
||||
return type; |
||||
} |
||||
|
||||
/** |
||||
* sets the glsl type |
||||
* |
||||
* @param type the type |
||||
*/ |
||||
public void setType(String type) { |
||||
this.type = type; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the name space (can be the name of the shaderNode or |
||||
* Globel,Attr,MatParam,WorldParam) |
||||
*/ |
||||
public String getNameSpace() { |
||||
return nameSpace; |
||||
} |
||||
|
||||
/** |
||||
* sets the nameSpace (can be the name of the shaderNode or |
||||
* Globel,Attr,MatParam,WorldParam) |
||||
* |
||||
* @param nameSpace |
||||
*/ |
||||
public void setNameSpace(String nameSpace) { |
||||
this.nameSpace = nameSpace; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
int hash = 5; |
||||
return hash; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if (obj == null) { |
||||
return false; |
||||
} |
||||
if (getClass() != obj.getClass()) { |
||||
return false; |
||||
} |
||||
final ShaderNodeVariable other = (ShaderNodeVariable) obj; |
||||
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) { |
||||
return false; |
||||
} |
||||
if ((this.type == null) ? (other.type != null) : !this.type.equals(other.type)) { |
||||
return false; |
||||
} |
||||
if ((this.nameSpace == null) ? (other.nameSpace != null) : !this.nameSpace.equals(other.nameSpace)) { |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* jme seralization (not used) |
||||
* |
||||
* @param ex the exporter |
||||
* @throws IOException |
||||
*/ |
||||
public void write(JmeExporter ex) throws IOException { |
||||
OutputCapsule oc = (OutputCapsule) ex.getCapsule(this); |
||||
oc.write(name, "name", ""); |
||||
oc.write(type, "type", ""); |
||||
oc.write(nameSpace, "nameSpace", ""); |
||||
oc.write(condition, "condition", null); |
||||
oc.write(shaderOutput, "shaderOutput", false); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* jme seralization (not used) |
||||
* |
||||
* @param im the importer |
||||
* @throws IOException |
||||
*/ |
||||
public void read(JmeImporter im) throws IOException { |
||||
InputCapsule ic = (InputCapsule) im.getCapsule(this); |
||||
name = ic.readString("name", ""); |
||||
type = ic.readString("type", ""); |
||||
nameSpace = ic.readString("nameSpace", ""); |
||||
condition = ic.readString("condition", null); |
||||
shaderOutput = ic.readBoolean("shaderOutput", false); |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the condition for this variable to be declared |
||||
*/ |
||||
public String getCondition() { |
||||
return condition; |
||||
} |
||||
|
||||
/** |
||||
* sets the condition |
||||
* |
||||
* @param condition the condition |
||||
*/ |
||||
public void setCondition(String condition) { |
||||
this.condition = condition; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "\n" + type + ' ' + (nameSpace != null ? (nameSpace + '.') : "") + name; |
||||
} |
||||
|
||||
@Override |
||||
public ShaderNodeVariable clone() { |
||||
return new ShaderNodeVariable(type, nameSpace, name); |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return true if this variable is a shader output |
||||
*/ |
||||
public boolean isShaderOutput() { |
||||
return shaderOutput; |
||||
} |
||||
|
||||
/** |
||||
* sets to true if this variable is a shader output |
||||
* |
||||
* @param shaderOutput true if this variable is a shader output |
||||
*/ |
||||
public void setShaderOutput(boolean shaderOutput) { |
||||
this.shaderOutput = shaderOutput; |
||||
} |
||||
} |
@ -0,0 +1,196 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shader; |
||||
|
||||
import com.jme3.export.InputCapsule; |
||||
import com.jme3.export.JmeExporter; |
||||
import com.jme3.export.JmeImporter; |
||||
import com.jme3.export.OutputCapsule; |
||||
import com.jme3.export.Savable; |
||||
import java.io.IOException; |
||||
|
||||
/** |
||||
* represents a mapping between 2 ShaderNodeVariables |
||||
* |
||||
* @author Nehon |
||||
*/ |
||||
public class VariableMapping implements Savable { |
||||
|
||||
private ShaderNodeVariable leftVariable; |
||||
private ShaderNodeVariable rightVariable; |
||||
private String condition; |
||||
private String leftSwizzling = ""; |
||||
private String rightSwizzling = ""; |
||||
|
||||
/** |
||||
* creates a VariableMapping |
||||
*/ |
||||
public VariableMapping() { |
||||
} |
||||
|
||||
/** |
||||
* creates a VariableMapping |
||||
* |
||||
* @param leftVariable the left hand side variable of the expression |
||||
* @param leftSwizzling the swizzling of the left variable |
||||
* @param rightVariable the right hand side variable of the expression |
||||
* @param rightSwizzling the swizzling of the right variable |
||||
* @param condition the condition for this mapping |
||||
*/ |
||||
public VariableMapping(ShaderNodeVariable leftVariable, String leftSwizzling, ShaderNodeVariable rightVariable, String rightSwizzling, String condition) { |
||||
this.leftVariable = leftVariable; |
||||
this.rightVariable = rightVariable; |
||||
this.condition = condition; |
||||
this.leftSwizzling = leftSwizzling; |
||||
this.rightSwizzling = rightSwizzling; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the left variable |
||||
*/ |
||||
public ShaderNodeVariable getLeftVariable() { |
||||
return leftVariable; |
||||
} |
||||
|
||||
/** |
||||
* sets the left variable |
||||
* |
||||
* @param leftVariable the left variable |
||||
*/ |
||||
public void setLeftVariable(ShaderNodeVariable leftVariable) { |
||||
this.leftVariable = leftVariable; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the right variable |
||||
*/ |
||||
public ShaderNodeVariable getRightVariable() { |
||||
return rightVariable; |
||||
} |
||||
|
||||
/** |
||||
* sets the right variable |
||||
* |
||||
* @param leftVariable the right variable |
||||
*/ |
||||
public void setRightVariable(ShaderNodeVariable rightVariable) { |
||||
this.rightVariable = rightVariable; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the condition |
||||
*/ |
||||
public String getCondition() { |
||||
return condition; |
||||
} |
||||
|
||||
/** |
||||
* sets the condition |
||||
* |
||||
* @param condition the condition |
||||
*/ |
||||
public void setCondition(String condition) { |
||||
this.condition = condition; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the left swizzle |
||||
*/ |
||||
public String getLeftSwizzling() { |
||||
return leftSwizzling; |
||||
} |
||||
|
||||
/** |
||||
* sets the left swizzle |
||||
* |
||||
* @param leftSwizzling the left swizzle |
||||
*/ |
||||
public void setLeftSwizzling(String leftSwizzling) { |
||||
this.leftSwizzling = leftSwizzling; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return the right swizzle |
||||
*/ |
||||
public String getRightSwizzling() { |
||||
return rightSwizzling; |
||||
} |
||||
|
||||
/** |
||||
* sets the right swizzle |
||||
* |
||||
* @param leftSwizzling the right swizzle |
||||
*/ |
||||
public void setRightSwizzling(String rightSwizzling) { |
||||
this.rightSwizzling = rightSwizzling; |
||||
} |
||||
|
||||
/** |
||||
* jme seralization (not used) |
||||
* |
||||
* @param ex the exporter |
||||
* @throws IOException |
||||
*/ |
||||
public void write(JmeExporter ex) throws IOException { |
||||
OutputCapsule oc = (OutputCapsule) ex.getCapsule(this); |
||||
oc.write(leftVariable, "leftVariable", null); |
||||
oc.write(rightVariable, "rightVariable", null); |
||||
oc.write(condition, "condition", ""); |
||||
oc.write(leftSwizzling, "leftSwizzling", ""); |
||||
oc.write(rightSwizzling, "rightSwizzling", ""); |
||||
} |
||||
|
||||
/** |
||||
* jme seralization (not used) |
||||
* |
||||
* @param im the importer |
||||
* @throws IOException |
||||
*/ |
||||
public void read(JmeImporter im) throws IOException { |
||||
InputCapsule ic = (InputCapsule) im.getCapsule(this); |
||||
leftVariable = (ShaderNodeVariable) ic.readSavable("leftVariable", null); |
||||
rightVariable = (ShaderNodeVariable) ic.readSavable("rightVariable", null); |
||||
condition = ic.readString("condition", ""); |
||||
leftSwizzling = ic.readString("leftSwizzling", ""); |
||||
rightSwizzling = ic.readString("rightSwizzling", ""); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "\n{" + leftVariable.toString() + (leftSwizzling.length() > 0 ? ("." + leftSwizzling) : "") + " = " + rightVariable.getType() + " " + rightVariable.getNameSpace() + "." + rightVariable.getName() + (rightSwizzling.length() > 0 ? ("." + rightSwizzling) : "") + " : " + condition + "}"; |
||||
} |
||||
} |
Loading…
Reference in new issue