* Improvements to GLSL loader handling of libraries. The order of #import is maintained, it is also possible to #import in the body of the shader (but it will not be included twice). Fixed issue 523.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9752 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
Sha..rd 13 years ago
parent bd7c10ba80
commit 243bba48ff
  1. 188
      engine/src/core-plugins/com/jme3/shader/plugins/GLSLLoader.java
  2. 72
      engine/src/core-plugins/com/jme3/shader/plugins/ShaderDependencyNode.java

@ -34,13 +34,17 @@ package com.jme3.shader.plugins;
import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetKey; import com.jme3.asset.AssetKey;
import com.jme3.asset.AssetLoadException;
import com.jme3.asset.AssetLoader; import com.jme3.asset.AssetLoader;
import com.jme3.asset.AssetManager; import com.jme3.asset.AssetManager;
import com.jme3.asset.cache.AssetCache; import com.jme3.asset.cache.AssetCache;
import com.jme3.asset.plugins.ClasspathLocator;
import com.jme3.system.JmeSystem;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader;
import java.util.*; import java.util.*;
/** /**
@ -48,39 +52,16 @@ import java.util.*;
*/ */
public class GLSLLoader implements AssetLoader { public class GLSLLoader implements AssetLoader {
private AssetManager owner; private AssetManager assetManager;
private Map<String, DependencyNode> dependCache = new HashMap<String, DependencyNode>(); private Map<String, ShaderDependencyNode> dependCache = new HashMap<String, ShaderDependencyNode>();
private class DependencyNode { /**
* Used to load {@link ShaderDependencyNode}s.
private String shaderSource; * Asset caching is disabled.
private String shaderName; */
private class ShaderDependencyKey extends AssetKey<Reader> {
private final Set<DependencyNode> dependsOn = new HashSet<DependencyNode>();
private final Set<DependencyNode> dependOnMe = new HashSet<DependencyNode>();
public DependencyNode(String shaderName){
this.shaderName = shaderName;
}
public void setSource(String source){
this.shaderSource = source;
}
public void addDependency(DependencyNode node){
if (this.dependsOn.contains(node))
return; // already contains dependency
// System.out.println(shaderName + " depend on "+node.shaderName);
this.dependsOn.add(node);
node.dependOnMe.add(this);
}
}
private class GlslDependKey extends AssetKey<InputStream> {
public GlslDependKey(String name) { public ShaderDependencyKey(String name) {
super(name); super(name);
} }
@ -91,124 +72,119 @@ public class GLSLLoader implements AssetLoader {
} }
} }
private DependencyNode loadNode(InputStream in, String nodeName) throws IOException{ /**
DependencyNode node = new DependencyNode(nodeName); * Creates a {@link ShaderDependencyNode} from a stream representing shader code.
if (in == null) *
throw new IOException("Dependency "+nodeName+" cannot be found."); * @param in The input stream containing shader code
* @param nodeName
* @return
* @throws IOException
*/
private ShaderDependencyNode loadNode(Reader reader, String nodeName) {
ShaderDependencyNode node = new ShaderDependencyNode(nodeName);
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
BufferedReader r = new BufferedReader(new InputStreamReader(in)); BufferedReader bufReader = new BufferedReader(reader);
while (r.ready()){ try {
String ln = r.readLine(); while (bufReader.ready()) {
if (ln.startsWith("#import ")){ String ln = bufReader.readLine();
ln = ln.substring(8).trim(); if (ln.trim().startsWith("#import ")) {
ln = ln.trim().substring(8).trim();
if (ln.startsWith("\"") && ln.endsWith("\"") && ln.length() > 3) { if (ln.startsWith("\"") && ln.endsWith("\"") && ln.length() > 3) {
// import user code // import user code
// remove quotes to get filename // remove quotes to get filename
ln = ln.substring(1, ln.length() - 1); ln = ln.substring(1, ln.length() - 1);
if (ln.equals(nodeName)) if (ln.equals(nodeName)) {
throw new IOException("Node depends on itself."); throw new IOException("Node depends on itself.");
}
// check cache first // check cache first
DependencyNode dependNode = dependCache.get(ln); ShaderDependencyNode dependNode = dependCache.get(ln);
if (dependNode == null) { if (dependNode == null) {
GlslDependKey key = new GlslDependKey(ln); Reader dependNodeReader = assetManager.loadAsset(new ShaderDependencyKey(ln));
// make sure not to register an input stream with dependNode = loadNode(dependNodeReader, ln);
// the cache.. }
InputStream stream = (InputStream) owner.loadAsset(key);
dependNode = loadNode(stream, ln); node.addDependency(sb.length(), dependNode);
} }
node.addDependency(dependNode);
}
// }else if (ln.startsWith("uniform") || ln.startsWith("varying") || ln.startsWith("attribute")){
// // these variables are included as dependencies as well
// DependencyNode dependNode = dependCache.get(ln);
// if (dependNode == null){
// // the source and name are the same for variable dependencies
// dependNode = new DependencyNode(ln);
// dependNode.setSource(ln);
// dependCache.put(ln, dependNode);
// }
// node.addDependency(dependNode);
} else { } else {
sb.append(ln).append('\n'); sb.append(ln).append('\n');
} }
} }
r.close(); } catch (IOException ex) {
if (bufReader != null) {
try {
bufReader.close();
} catch (IOException ex1) {
}
}
throw new AssetLoadException("Failed to load shader node: " + nodeName, ex);
}
node.setSource(sb.toString()); node.setSource(sb.toString());
dependCache.put(nodeName, node); dependCache.put(nodeName, node);
return node; return node;
} }
private DependencyNode nextIndependentNode(List<DependencyNode> checkedNodes){ private ShaderDependencyNode nextIndependentNode() throws IOException {
Collection<DependencyNode> allNodes = dependCache.values(); Collection<ShaderDependencyNode> allNodes = dependCache.values();
if (allNodes == null || allNodes.isEmpty())
if (allNodes == null || allNodes.isEmpty()) {
return null; return null;
}
for (DependencyNode node : allNodes){ for (ShaderDependencyNode node : allNodes) {
if (node.dependsOn.isEmpty()){ if (node.getDependOnMe().isEmpty()) {
return node; return node;
} }
} }
// circular dependency found.. // Circular dependency found..
for (DependencyNode node : allNodes){ for (ShaderDependencyNode node : allNodes){
System.out.println(node.shaderName); System.out.println(node.getName());
} }
throw new RuntimeException("Circular dependency.");
}
private String resolveDependencies(DependencyNode root){
StringBuilder sb = new StringBuilder();
List<DependencyNode> checkedNodes = new ArrayList<DependencyNode>(); throw new IOException("Circular dependency.");
checkedNodes.add(root); }
while (true){
DependencyNode indepnNode = nextIndependentNode(checkedNodes);
if (indepnNode == null)
break;
sb.append(indepnNode.shaderSource).append('\n');
dependCache.remove(indepnNode.shaderName);
// take out this dependency private String resolveDependencies(ShaderDependencyNode node, Set<ShaderDependencyNode> alreadyInjectedSet) {
for (Iterator<DependencyNode> iter = indepnNode.dependOnMe.iterator(); if (alreadyInjectedSet.contains(node)) {
iter.hasNext();){ return "// " + node.getName() + " was already injected at the top.\n";
DependencyNode dependNode = iter.next(); } else {
iter.remove(); alreadyInjectedSet.add(node);
dependNode.dependsOn.remove(indepnNode);
} }
if (node.getDependencies().isEmpty()) {
return node.getSource();
} else {
StringBuilder sb = new StringBuilder(node.getSource());
List<String> resolvedShaderNodes = new ArrayList<String>();
for (ShaderDependencyNode dependencyNode : node.getDependencies()) {
resolvedShaderNodes.add( resolveDependencies(dependencyNode, alreadyInjectedSet) );
}
List<Integer> injectIndices = node.getDependencyInjectIndices();
for (int i = resolvedShaderNodes.size() - 1; i >= 0; i--) {
// Must insert them backwards ..
sb.insert(injectIndices.get(i), resolvedShaderNodes.get(i));
} }
// System.out.println(sb.toString());
// System.out.println("--------------------------------------------------");
return sb.toString(); return sb.toString();
} }
}
/**
*
* @param info
* @return String of GLSL code
* @throws java.io.IOException
*/
public Object load(AssetInfo info) throws IOException { public Object load(AssetInfo info) throws IOException {
// The input stream provided is for the vertex shader, // The input stream provided is for the vertex shader,
// to retrieve the fragment shader, use the content manager // to retrieve the fragment shader, use the content manager
this.owner = info.getManager(); this.assetManager = info.getManager();
Reader reader = new InputStreamReader(info.openStream());
if (info.getKey().getExtension().equals("glsllib")) { if (info.getKey().getExtension().equals("glsllib")) {
// NOTE: Loopback, GLSLLIB is loaded by this loader // NOTE: Loopback, GLSLLIB is loaded by this loader
// and needs data as InputStream // and needs data as InputStream
return info.openStream(); return reader;
} else { } else {
// GLSLLoader wants result as String for ShaderDependencyNode rootNode = loadNode(reader, "[main]");
// fragment shader String code = resolveDependencies(rootNode, new HashSet<ShaderDependencyNode>());
DependencyNode rootNode = loadNode(info.openStream(), "[main]");
String code = resolveDependencies(rootNode);
dependCache.clear(); dependCache.clear();
return code; return code;
} }
} }
} }

@ -0,0 +1,72 @@
package com.jme3.shader.plugins;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class ShaderDependencyNode {
private String shaderSource;
private String shaderName;
private final List<ShaderDependencyNode> dependencies = new ArrayList<ShaderDependencyNode>();
private final List<Integer> dependencyInjectIndices = new ArrayList<Integer>();
private final List<ShaderDependencyNode> dependOnMe = new ArrayList<ShaderDependencyNode>();
public ShaderDependencyNode(String shaderName){
this.shaderName = shaderName;
}
public String getSource() {
return shaderSource;
}
public void setSource(String shaderSource) {
this.shaderSource = shaderSource;
}
public String getName() {
return shaderName;
}
public void setName(String shaderName) {
this.shaderName = shaderName;
}
public void addDependency(int index, ShaderDependencyNode node){
if (this.dependencies.contains(node)) {
// already contains dependency ..
return;
}
this.dependencies.add(node);
this.dependencyInjectIndices.add(index);
node.dependOnMe.add(this);
}
public void removeDependency(ShaderDependencyNode node) {
int positionInList = this.dependencies.indexOf(node);
if (positionInList == -1) {
throw new IllegalArgumentException("The given node " +
node.getName() +
" is not in this node's (" +
getName() +
") dependency list");
}
this.dependencies.remove(positionInList);
this.dependencyInjectIndices.remove(positionInList);
}
public List<ShaderDependencyNode> getDependOnMe() {
return Collections.unmodifiableList(dependOnMe);
}
public List<ShaderDependencyNode> getDependencies() {
return Collections.unmodifiableList(dependencies);
}
public List<Integer> getDependencyInjectIndices() {
return Collections.unmodifiableList(dependencyInjectIndices);
}
}
Loading…
Cancel
Save