Shader Nodes implementation :

Core system

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10432 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
rem..om 12 years ago
parent 9e3a98931a
commit 55d065ab78
  1. 112
      engine/src/core-plugins/com/jme3/material/plugins/ConditionParser.java
  2. 74
      engine/src/core-plugins/com/jme3/material/plugins/J3MLoader.java
  3. 90
      engine/src/core-plugins/com/jme3/material/plugins/MatParseException.java
  4. 85
      engine/src/core-plugins/com/jme3/material/plugins/ShaderNodeDefinitionLoader.java
  5. 937
      engine/src/core-plugins/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java
  6. 16
      engine/src/core/com/jme3/asset/AssetManager.java
  7. 1
      engine/src/core/com/jme3/asset/Desktop.cfg
  8. 31
      engine/src/core/com/jme3/asset/DesktopAssetManager.java
  9. 89
      engine/src/core/com/jme3/asset/ShaderNodeDefinitionKey.java
  10. 6
      engine/src/core/com/jme3/material/Material.java
  11. 190
      engine/src/core/com/jme3/material/ShaderGenerationInfo.java
  12. 29
      engine/src/core/com/jme3/material/Technique.java
  13. 43
      engine/src/core/com/jme3/material/TechniqueDef.java
  14. 42
      engine/src/core/com/jme3/renderer/queue/GeometryList.java
  15. 572
      engine/src/core/com/jme3/shader/Glsl100ShaderGenerator.java
  16. 146
      engine/src/core/com/jme3/shader/Glsl150ShaderGenerator.java
  17. 290
      engine/src/core/com/jme3/shader/ShaderGenerator.java
  18. 215
      engine/src/core/com/jme3/shader/ShaderNode.java
  19. 253
      engine/src/core/com/jme3/shader/ShaderNodeDefinition.java
  20. 234
      engine/src/core/com/jme3/shader/ShaderNodeVariable.java
  21. 67
      engine/src/core/com/jme3/shader/ShaderUtils.java
  22. 69
      engine/src/core/com/jme3/shader/UniformBinding.java
  23. 50
      engine/src/core/com/jme3/shader/VarType.java
  24. 196
      engine/src/core/com/jme3/shader/VariableMapping.java
  25. 22
      engine/src/core/com/jme3/util/blockparser/Statement.java

@ -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;
}
}

@ -56,6 +56,9 @@ import java.util.logging.Logger;
public class J3MLoader implements AssetLoader {
private static final Logger logger = Logger.getLogger(J3MLoader.class.getName());
// private ErrorLogger errors;
private ShaderNodeLoaderDelegate nodesLoaderDelegate;
boolean isUseNodes = false;
private AssetManager assetManager;
private AssetKey key;
@ -76,13 +79,6 @@ public class J3MLoader implements AssetLoader {
public J3MLoader(){
}
private void throwIfNequal(String expected, String got) throws IOException {
if (expected == null)
throw new IOException("Expected a statement, got '"+got+"'!");
if (!expected.equals(got))
throw new IOException("Expected '"+expected+"', got '"+got+"'!");
}
// <TYPE> <LANG> : <SOURCE>
private void readShaderStatement(String statement) throws IOException {
@ -311,8 +307,8 @@ public class J3MLoader implements AssetLoader {
return word != null && word.equals("On");
}
private void readRenderStateStatement(String statement) throws IOException{
String[] split = statement.split(whitespacePattern);
private void readRenderStateStatement(Statement statement) throws IOException{
String[] split = statement.getLine().split(whitespacePattern);
if (split[0].equals("Wireframe")){
renderState.setWireframe(parseBoolean(split[1]));
}else if (split[0].equals("FaceCull")){
@ -335,14 +331,14 @@ public class J3MLoader implements AssetLoader {
}else if (split[0].equals("PointSprite")){
renderState.setPointSprite(parseBoolean(split[1]));
} else {
throwIfNequal(null, split[0]);
throw new MatParseException(null, split[0], statement);
}
}
private void readAdditionalRenderState(List<Statement> renderStates) throws IOException{
renderState = material.getAdditionalRenderState();
for (Statement statement : renderStates){
readRenderStateStatement(statement.getLine());
readRenderStateStatement(statement);
}
renderState = null;
}
@ -350,7 +346,7 @@ public class J3MLoader implements AssetLoader {
private void readRenderState(List<Statement> renderStates) throws IOException{
renderState = new RenderState();
for (Statement statement : renderStates){
readRenderStateStatement(statement.getLine());
readRenderStateStatement(statement);
}
technique.setRenderState(renderState);
renderState = null;
@ -359,7 +355,7 @@ public class J3MLoader implements AssetLoader {
private void readForcedRenderState(List<Statement> renderStates) throws IOException{
renderState = new RenderState();
for (Statement statement : renderStates){
readRenderStateStatement(statement.getLine());
readRenderStateStatement(statement);
}
technique.setForcedRenderState(renderState);
renderState = null;
@ -402,8 +398,23 @@ public class J3MLoader implements AssetLoader {
readForcedRenderState(statement.getContents());
}else if (split[0].equals("Defines")){
readDefines(statement.getContents());
} else if (split[0].equals("ShaderNodesDefinitions")) {
initNodesLoader();
if (isUseNodes) {
nodesLoaderDelegate.readNodesDefinitions(statement.getContents());
}
} else if (split[0].equals("VertexShaderNodes")) {
initNodesLoader();
if (isUseNodes) {
nodesLoaderDelegate.readVertexShaderNodes(statement.getContents());
}
} else if (split[0].equals("FragmentShaderNodes")) {
initNodesLoader();
if (isUseNodes) {
nodesLoaderDelegate.readFragmentShaderNodes(statement.getContents());
}
} else {
throwIfNequal(null, split[0]);
throw new MatParseException(null, split[0], statement);
}
}
@ -448,6 +459,7 @@ public class J3MLoader implements AssetLoader {
}
private void loadFromRoot(List<Statement> roots) throws IOException{
isUseNodes = false;
if (roots.size() == 2){
Statement exception = roots.get(0);
String line = exception.getLine();
@ -476,32 +488,33 @@ public class J3MLoader implements AssetLoader {
String[] split = materialName.split(":", 2);
if (materialName.equals("")){
throw new IOException("Material name cannot be empty");
throw new MatParseException("Material name cannot be empty", materialStat);
}
if (split.length == 2){
if (!extending){
throw new IOException("Must use 'Material' when extending.");
throw new MatParseException("Must use 'Material' when extending.", materialStat);
}
String extendedMat = split[1].trim();
MaterialDef def = (MaterialDef) assetManager.loadAsset(new AssetKey(extendedMat));
if (def == null)
throw new IOException("Extended material "+extendedMat+" cannot be found.");
if (def == null) {
throw new MatParseException("Extended material " + extendedMat + " cannot be found.", materialStat);
}
material = new Material(def);
material.setKey(key);
// material.setAssetName(fileName);
}else if (split.length == 1){
if (extending){
throw new IOException("Expected ':', got '{'");
throw new MatParseException("Expected ':', got '{'", materialStat);
}
materialDef = new MaterialDef(assetManager, materialName);
// NOTE: pass file name for defs so they can be loaded later
materialDef.setAssetName(key.getName());
}else{
throw new IOException("Cannot use colon in material name/path");
throw new MatParseException("Cannot use colon in material name/path", materialStat);
}
for (Statement statement : materialStat.getContents()){
@ -521,7 +534,7 @@ public class J3MLoader implements AssetLoader {
}else if (statType.equals("MaterialParameters")){
readMaterialParams(statement.getContents());
}else{
throw new IOException("Expected material statement, got '"+statType+"'");
throw new MatParseException("Expected material statement, got '"+statType+"'", statement);
}
}
}
@ -552,4 +565,23 @@ public class J3MLoader implements AssetLoader {
}
}
public MaterialDef loadMaterialDef(List<Statement> roots, AssetManager manager, AssetKey key) throws IOException {
this.key = key;
this.assetManager = manager;
loadFromRoot(roots);
return materialDef;
}
protected void initNodesLoader() {
if (!isUseNodes) {
isUseNodes = fragName == null && vertName == null;
if (isUseNodes) {
nodesLoaderDelegate = new ShaderNodeLoaderDelegate();
nodesLoaderDelegate.setTechniqueDef(technique);
nodesLoaderDelegate.setMaterialDef(materialDef);
nodesLoaderDelegate.setAssetManager(assetManager);
}
}
}
}

@ -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;
}
}

@ -38,12 +38,15 @@ import com.jme3.audio.AudioKey;
import com.jme3.font.BitmapFont;
import com.jme3.material.Material;
import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.Caps;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.OBJLoader;
import com.jme3.shader.Shader;
import com.jme3.shader.ShaderGenerator;
import com.jme3.shader.ShaderKey;
import com.jme3.texture.Texture;
import com.jme3.texture.plugins.TGALoader;
import java.util.EnumSet;
import java.util.List;
/**
@ -364,4 +367,17 @@ public interface AssetManager {
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/
public FilterPostProcessor loadFilter(String name);
/**
* Sets the shaderGenerator to generate shaders based on shaderNodes.
* @param generator the shaderGenerator
*/
public void setShaderGenerator(ShaderGenerator generator);
/**
* Returns the shaderGenerator responsible for generating the shaders
* @return the shaderGenerator
*/
public ShaderGenerator getShaderGenerator(EnumSet<Caps> caps);
}

@ -6,6 +6,7 @@ LOADER com.jme3.audio.plugins.OGGLoader : ogg
LOADER com.jme3.cursors.plugins.CursorLoader : ani, cur, ico
LOADER com.jme3.material.plugins.J3MLoader : j3m
LOADER com.jme3.material.plugins.J3MLoader : j3md
LOADER com.jme3.material.plugins.ShaderNodeDefinitionLoader : j3sn
LOADER com.jme3.font.plugins.BitmapFontLoader : fnt
LOADER com.jme3.texture.plugins.DDSLoader : dds
LOADER com.jme3.texture.plugins.PFMLoader : pfm

@ -38,8 +38,12 @@ import com.jme3.audio.AudioKey;
import com.jme3.font.BitmapFont;
import com.jme3.material.Material;
import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.Caps;
import com.jme3.scene.Spatial;
import com.jme3.shader.Glsl100ShaderGenerator;
import com.jme3.shader.Glsl150ShaderGenerator;
import com.jme3.shader.Shader;
import com.jme3.shader.ShaderGenerator;
import com.jme3.shader.ShaderKey;
import com.jme3.texture.Texture;
import java.io.IOException;
@ -48,6 +52,7 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
@ -62,6 +67,7 @@ import java.util.logging.Logger;
public class DesktopAssetManager implements AssetManager {
private static final Logger logger = Logger.getLogger(AssetManager.class.getName());
private ShaderGenerator shaderGenerator;
private final ImplHandler handler = new ImplHandler(this);
@ -407,4 +413,29 @@ public class DesktopAssetManager implements AssetManager {
}
return shader;
}
/**
* {@inheritDoc}
*/
@Override
public ShaderGenerator getShaderGenerator(EnumSet<Caps> caps) {
if (shaderGenerator == null) {
if(caps.contains(Caps.GLSL150)){
shaderGenerator = new Glsl150ShaderGenerator(this);
}else{
shaderGenerator = new Glsl100ShaderGenerator(this);
}
}
return shaderGenerator;
}
/**
* {@inheritDoc}
*/
@Override
public void setShaderGenerator(ShaderGenerator shaderGenerator) {
this.shaderGenerator = shaderGenerator;
}
}

@ -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;
}
}

@ -873,10 +873,10 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
public void selectTechnique(String name, RenderManager renderManager) {
// check if already created
Technique tech = techniques.get(name);
if (tech == null) {
// When choosing technique, we choose one that
// supports all the caps.
EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps();
if (tech == null) {
if (name.equals("Default")) {
List<TechniqueDef> techDefs = def.getDefaultTechniques();
@ -923,7 +923,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
}
technique = tech;
tech.makeCurrent(def.getAssetManager(), true);
tech.makeCurrent(def.getAssetManager(), true, rendererCaps);
// shader was changed
sortingId = -1;
@ -933,7 +933,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
if (technique == null) {
selectTechnique("Default", rm);
} else {
technique.makeCurrent(def.getAssetManager(), false);
technique.makeCurrent(def.getAssetManager(), false, rm.getRenderer().getCaps());
}
}

@ -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);
}
}

@ -32,9 +32,11 @@
package com.jme3.material;
import com.jme3.asset.AssetManager;
import com.jme3.renderer.Caps;
import com.jme3.shader.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.logging.Logger;
@ -170,7 +172,7 @@ public class Technique /* implements Savable */ {
*
* @param assetManager The asset manager to use for loading shaders.
*/
public void makeCurrent(AssetManager assetManager, boolean techniqueSwitched) {
public void makeCurrent(AssetManager assetManager, boolean techniqueSwitched, EnumSet<Caps> rendererCaps) {
if (!def.isUsingShaders()) {
// No shaders are used, no processing is neccessary.
return;
@ -197,23 +199,23 @@ public class Technique /* implements Savable */ {
}
if (needReload) {
loadShader(assetManager);
loadShader(assetManager,rendererCaps);
}
}
private void loadShader(AssetManager manager) {
// recompute define list
DefineList allDefines = new DefineList();
allDefines.addFrom(def.getShaderPresetDefines());
allDefines.addFrom(defines);
private void loadShader(AssetManager manager,EnumSet<Caps> rendererCaps) {
if (getDef().isUsingShaderNodes()) {
shader = manager.getShaderGenerator(rendererCaps).generateShader(this);
} else {
ShaderKey key = new ShaderKey(def.getVertexShaderName(),
def.getFragmentShaderName(),
allDefines,
getAllDefines(),
def.getVertexShaderLanguage(),
def.getFragmentShaderLanguage());
shader = manager.loadShader(key);
}
// register the world bound uniforms
worldBindUniforms.clear();
if (def.getWorldBindings() != null) {
@ -228,6 +230,17 @@ public class Technique /* implements Savable */ {
needReload = false;
}
/**
* Computes the define list
* @return the complete define list
*/
public DefineList getAllDefines() {
DefineList allDefines = new DefineList();
allDefines.addFrom(def.getShaderPresetDefines());
allDefines.addFrom(defines);
return allDefines;
}
/*
public void write(JmeExporter ex) throws IOException {
OutputCapsule oc = ex.getCapsule(this);

@ -35,6 +35,7 @@ import com.jme3.export.*;
import com.jme3.renderer.Caps;
import com.jme3.renderer.Renderer;
import com.jme3.shader.DefineList;
import com.jme3.shader.ShaderNode;
import com.jme3.shader.UniformBinding;
import com.jme3.shader.VarType;
import java.io.IOException;
@ -110,6 +111,9 @@ public class TechniqueDef implements Savable {
private DefineList presetDefines;
private boolean usesShaders;
private boolean usesNodes = false;
private List<ShaderNode> shaderNodes;
private ShaderGenerationInfo shaderGenerationInfo;
private RenderState renderState;
private RenderState forcedRenderState;
@ -218,6 +222,16 @@ public class TechniqueDef implements Savable {
return usesShaders;
}
/**
* Returns true if this technique uses Shader Nodes, false otherwise.
*
* @return true if this technique uses Shader Nodes, false otherwise.
*
*/
public boolean isUsingShaderNodes(){
return usesNodes;
}
/**
* Gets the {@link Caps renderer capabilities} that are required
* by this technique.
@ -408,6 +422,9 @@ public class TechniqueDef implements Savable {
oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
oc.write(renderState, "renderState", null);
oc.write(usesShaders, "usesShaders", false);
oc.write(usesNodes, "usesNodes", false);
oc.writeSavableArrayList((ArrayList)shaderNodes,"shaderNodes", null);
oc.write(shaderGenerationInfo, "shaderGenerationInfo", null);
// TODO: Finish this when Map<String, String> export is available
// oc.write(defineParams, "defineParams", null);
@ -435,6 +452,32 @@ public class TechniqueDef implements Savable {
vertLanguage = ic.readString("vertLanguage", null);
fragLanguage = ic.readString("fragLanguage", null);;
}
usesNodes = ic.readBoolean("usesNodes", false);
shaderNodes = ic.readSavableArrayList("shaderNodes", null);
shaderGenerationInfo = (ShaderGenerationInfo) ic.readSavable("shaderGenerationInfo", null);
}
public List<ShaderNode> getShaderNodes() {
return shaderNodes;
}
public void setShaderNodes(List<ShaderNode> shaderNodes) {
this.shaderNodes = shaderNodes;
usesNodes = true;
usesShaders = true;
}
public ShaderGenerationInfo getShaderGenerationInfo() {
return shaderGenerationInfo;
}
public void setShaderGenerationInfo(ShaderGenerationInfo shaderGenerationInfo) {
this.shaderGenerationInfo = shaderGenerationInfo;
}
@Override
public String toString() {
return "TechniqueDef{" + "requiredCaps=" + requiredCaps + ", name=" + name + ", vertName=" + vertName + ", fragName=" + fragName + ", vertLanguage=" + vertLanguage + ", fragLanguage=" + fragLanguage + ", presetDefines=" + presetDefines + ", usesShaders=" + usesShaders + ", usesNodes=" + usesNodes + ", shaderNodes=" + shaderNodes + ", shaderGenerationInfo=" + shaderGenerationInfo + ", renderState=" + renderState + ", forcedRenderState=" + forcedRenderState + ", lightMode=" + lightMode + ", shadowMode=" + shadowMode + ", defineParams=" + defineParams + ", worldBinds=" + worldBinds + '}';
}
}

@ -33,6 +33,7 @@ package com.jme3.renderer.queue;
import com.jme3.renderer.Camera;
import com.jme3.scene.Geometry;
import com.jme3.util.ListSort;
import com.jme3.util.SortUtil;
/**
@ -48,10 +49,16 @@ public class GeometryList {
private static final int DEFAULT_SIZE = 32;
private Geometry[] geometries;
private Geometry[] geometries2;
// private Geometry[] geometries2;
private ListSort listSort;
private int size;
private GeometryComparator comparator;
/*static private int count =0;
static private int cpt =0;
static private long time = 0;
*/
/**
* Initializes the GeometryList to use the given {@link GeometryComparator}
* to use for comparing geometries.
@ -61,8 +68,9 @@ public class GeometryList {
public GeometryList(GeometryComparator comparator) {
size = 0;
geometries = new Geometry[DEFAULT_SIZE];
geometries2 = new Geometry[DEFAULT_SIZE];
// geometries2 = new Geometry[DEFAULT_SIZE];
this.comparator = comparator;
listSort = new ListSort<Geometry>();
}
/**
@ -115,7 +123,7 @@ public class GeometryList {
System.arraycopy(geometries, 0, temp, 0, size);
geometries = temp; // original list replaced by double-size list
geometries2 = new Geometry[size * 2];
// geometries2 = new Geometry[size * 2];
}
geometries[size++] = g;
}
@ -137,14 +145,34 @@ public class GeometryList {
public void sort() {
if (size > 1) {
// sort the spatial list using the comparator
// count++;
// long t = System.nanoTime();
// SortUtil.qsort(geometries, 0, size, comparator);
// Arrays.sort(geometries, 0, size, comparator);
if(listSort.getLength() != size){
listSort.allocateStack(size);
}
listSort.sort(geometries,comparator);
System.arraycopy(geometries, 0, geometries2, 0, size);
SortUtil.msort(geometries2, geometries, 0, size-1, comparator);
// time += System.nanoTime() - t;
// count++;
// long t = System.nanoTime();
// System.arraycopy(geometries, 0, geometries2, 0, size);
// SortUtil.msort(geometries2, geometries, 0, size-1, comparator);
// time += System.nanoTime() - t;
//
// count++;
// long t = System.nanoTime();
// Arrays.sort(geometries,0,size, comparator);
// time += System.nanoTime() - t;
}
// if( count>50){
// count = 0;
// cpt++;
// System.err.println(50*cpt+"\t"+time/1000000);
// }
}
}

@ -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 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;
}
}

@ -46,4 +46,71 @@ public class ShaderUtils {
return sb.toString();
}
/**
* Check if a mapping is valid by checking the types and swizzle of both of
* the variables
*
* @param mapping the mapping
* @return true if this mapping is valid
*/
public static boolean typesMatch(VariableMapping mapping) {
String leftType = mapping.getLeftVariable().getType();
String rightType = mapping.getRightVariable().getType();
String leftSwizzling = mapping.getLeftSwizzling();
String rightSwizzling = mapping.getRightSwizzling();
//types match : no error
if (leftType.equals(rightType) && leftSwizzling.length() == rightSwizzling.length()) {
return true;
}
if (isSwizzlable(leftType) && isSwizzlable(rightType)) {
if (getCardinality(leftType, leftSwizzling) == getCardinality(rightType, rightSwizzling)) {
return true;
}
}
return false;
}
/**
* return the cardinality of a type and a swizzle example : vec4 cardinality
* is 4 float cardinality is 1 vec4.xyz cardinality is 3. sampler2D
* cardinality is 0
*
* @param type the glsl type
* @param swizzling the swizzling of a variable
* @return the cardinality
*/
public static int getCardinality(String type, String swizzling) {
int card = 0;
if (isSwizzlable(type)) {
if (type.equals("float")) {
card = 1;
if (swizzling.length() != 0) {
card = 0;
}
} else {
card = Integer.parseInt(type.replaceAll("vec", ""));
if (swizzling.length() > 0) {
if (card >= swizzling.length()) {
card = swizzling.length();
} else {
card = 0;
}
}
}
}
return card;
}
/**
* returns true if a variable of the given type can have a swizzle
*
* @param type the glsl type
* @return true if a variable of the given type can have a swizzle
*/
public static boolean isSwizzlable(String type) {
return type.equals("vec4") || type.equals("vec3") || type.equals("vec2") || type.equals("float");
}
}

@ -37,63 +37,63 @@ public enum UniformBinding {
* The world matrix. Converts Model space to World space.
* Type: mat4
*/
WorldMatrix,
WorldMatrix("mat4"),
/**
* The view matrix. Converts World space to View space.
* Type: mat4
*/
ViewMatrix,
ViewMatrix("mat4"),
/**
* The projection matrix. Converts View space to Clip/Projection space.
* Type: mat4
*/
ProjectionMatrix,
ProjectionMatrix("mat4"),
/**
* The world view matrix. Converts Model space to View space.
* Type: mat4
*/
WorldViewMatrix,
WorldViewMatrix("mat4"),
/**
* The normal matrix. The inverse transpose of the worldview matrix.
* Converts normals from model space to view space.
* Type: mat3
*/
NormalMatrix,
NormalMatrix("mat3"),
/**
* The world view projection matrix. Converts Model space to Clip/Projection
* space.
* Type: mat4
*/
WorldViewProjectionMatrix,
WorldViewProjectionMatrix("mat4"),
/**
* The view projection matrix. Converts World space to Clip/Projection
* space.
* Type: mat4
*/
ViewProjectionMatrix,
ViewProjectionMatrix("mat4"),
/**
* The world matrix inverse transpose. Converts a normals from Model space
* to world space.
* Type: mat3
*/
WorldMatrixInverseTranspose,
WorldMatrixInverseTranspose("mat3"),
WorldMatrixInverse,
ViewMatrixInverse,
ProjectionMatrixInverse,
ViewProjectionMatrixInverse,
WorldViewMatrixInverse,
NormalMatrixInverse,
WorldViewProjectionMatrixInverse,
WorldMatrixInverse("mat4"),
ViewMatrixInverse("mat4"),
ProjectionMatrixInverse("mat4"),
ViewProjectionMatrixInverse("mat4"),
WorldViewMatrixInverse("mat4"),
NormalMatrixInverse("mat3"),
WorldViewProjectionMatrixInverse("mat4"),
/**
* Contains the four viewport parameters in this order:
@ -103,7 +103,7 @@ public enum UniformBinding {
* W = Bottom.
* Type: vec4
*/
ViewPort,
ViewPort("vec4"),
/**
* The near and far values for the camera frustum.
@ -111,65 +111,80 @@ public enum UniformBinding {
* Y = Far.
* Type: vec2
*/
FrustumNearFar,
FrustumNearFar("vec2"),
/**
* The width and height of the camera.
* Type: vec2
*/
Resolution,
Resolution("vec2"),
/**
* The inverse of the resolution, 1/width and 1/height.
* Type: vec2
*/
ResolutionInverse,
ResolutionInverse("vec2"),
/**
* Aspect ratio of the resolution currently set. Width/Height.
* Type: float
*/
Aspect,
Aspect("float"),
/**
* Camera position in world space.
* Type: vec3
*/
CameraPosition,
CameraPosition("vec3"),
/**
* Direction of the camera.
* Type: vec3
*/
CameraDirection,
CameraDirection("vec3"),
/**
* Left vector of the camera.
* Type: vec3
*/
CameraLeft,
CameraLeft("vec3"),
/**
* Up vector of the camera.
* Type: vec3
*/
CameraUp,
CameraUp("vec3"),
/**
* Time in seconds since the application was started.
* Type: float
*/
Time,
Time("float"),
/**
* Time in seconds that the last frame took.
* Type: float
*/
Tpf,
Tpf("float"),
/**
* Frames per second.
* Type: float
*/
FrameRate,
FrameRate("float");
String glslType;
private UniformBinding() {
}
private UniformBinding(String glslType) {
this.glslType = glslType;
}
public String getGlslType() {
return glslType;
}
}

@ -33,40 +33,44 @@ package com.jme3.shader;
public enum VarType {
Float,
Vector2,
Vector3,
Vector4,
Float("float"),
Vector2("vec2"),
Vector3("vec3"),
Vector4("vec4"),
FloatArray(true,false),
Vector2Array(true,false),
Vector3Array(true,false),
Vector4Array(true,false),
FloatArray(true,false,"float[]"),
Vector2Array(true,false,"vec2[]"),
Vector3Array(true,false,"vec3[]"),
Vector4Array(true,false,"vec4[]"),
Boolean,
Boolean("bool"),
Matrix3(true,false),
Matrix4(true,false),
Matrix3(true,false,"mat3"),
Matrix4(true,false,"mat4"),
Matrix3Array(true,false),
Matrix4Array(true,false),
Matrix3Array(true,false,"mat3[]"),
Matrix4Array(true,false,"mat4[]"),
TextureBuffer(false,true),
Texture2D(false,true),
Texture3D(false,true),
TextureArray(false,true),
TextureCubeMap(false,true),
Int;
TextureBuffer(false,true,"sampler1D|sampler1DShadow"),
Texture2D(false,true,"sampler2D|sampler2DShadow"),
Texture3D(false,true,"sampler3D"),
TextureArray(false,true,"sampler2DArray"),
TextureCubeMap(false,true,"samplerCube"),
Int("int");
private boolean usesMultiData = false;
private boolean textureType = false;
private String glslType;
VarType(){
VarType(String glslType){
this.glslType = glslType;
}
VarType(boolean multiData, boolean textureType){
VarType(boolean multiData, boolean textureType,String glslType){
usesMultiData = multiData;
this.textureType = textureType;
this.glslType = glslType;
}
public boolean isTextureType() {
@ -77,4 +81,8 @@ public enum VarType {
return usesMultiData;
}
public String getGlslType() {
return glslType;
}
}

@ -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 + "}";
}
}

@ -36,22 +36,23 @@ import java.util.List;
public class Statement {
private int lineNumber;
private String line;
private List<Statement> contents = new ArrayList<Statement>();
protected int lineNumber;
protected String line;
protected List<Statement> contents = new ArrayList<Statement>();
Statement(int lineNumber, String line) {
protected Statement(int lineNumber, String line) {
this.lineNumber = lineNumber;
this.line = line;
}
void addStatement(Statement statement){
// if (contents == null){
// contents = new ArrayList<Statement>();
// }
protected void addStatement(Statement statement) {
contents.add(statement);
}
protected void addStatement(int index, Statement statement) {
contents.add(index, statement);
}
public int getLineNumber() {
return lineNumber;
}
@ -64,11 +65,11 @@ public class Statement {
return contents;
}
private String getIndent(int indent){
protected String getIndent(int indent) {
return " ".substring(0, indent);
}
private String toString(int indent){
protected String toString(int indent) {
StringBuilder sb = new StringBuilder();
sb.append(getIndent(indent));
sb.append(line);
@ -88,5 +89,4 @@ public class Statement {
public String toString() {
return toString(0);
}
}

Loading…
Cancel
Save