• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2009-2010 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 package com.jme3.shader.plugins;
34 
35 import com.jme3.asset.AssetInfo;
36 import com.jme3.asset.AssetKey;
37 import com.jme3.asset.AssetLoader;
38 import com.jme3.asset.AssetManager;
39 import java.io.BufferedReader;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.io.InputStreamReader;
43 import java.util.*;
44 
45 /**
46  * GLSL File parser that supports #import pre-processor statement
47  */
48 public class GLSLLoader implements AssetLoader {
49 
50     private AssetManager owner;
51     private Map<String, DependencyNode> dependCache = new HashMap<String, DependencyNode>();
52 
53     private class DependencyNode {
54 
55         private String shaderSource;
56         private String shaderName;
57 
58         private final Set<DependencyNode> dependsOn = new HashSet<DependencyNode>();
59         private final Set<DependencyNode> dependOnMe = new HashSet<DependencyNode>();
60 
DependencyNode(String shaderName)61         public DependencyNode(String shaderName){
62             this.shaderName = shaderName;
63         }
64 
setSource(String source)65         public void setSource(String source){
66             this.shaderSource = source;
67         }
68 
addDependency(DependencyNode node)69         public void addDependency(DependencyNode node){
70             if (this.dependsOn.contains(node))
71                 return; // already contains dependency
72 
73 //            System.out.println(shaderName + " depend on "+node.shaderName);
74             this.dependsOn.add(node);
75             node.dependOnMe.add(this);
76         }
77 
78     }
79 
80     private class GlslDependKey extends AssetKey<InputStream> {
GlslDependKey(String name)81         public GlslDependKey(String name){
82             super(name);
83         }
84         @Override
shouldCache()85         public boolean shouldCache(){
86             return false;
87         }
88     }
89 
loadNode(InputStream in, String nodeName)90     private DependencyNode loadNode(InputStream in, String nodeName) throws IOException{
91         DependencyNode node = new DependencyNode(nodeName);
92         if (in == null)
93             throw new IOException("Dependency "+nodeName+" cannot be found.");
94 
95         StringBuilder sb = new StringBuilder();
96         BufferedReader r = new BufferedReader(new InputStreamReader(in));
97         while (r.ready()){
98             String ln = r.readLine();
99             if (ln.startsWith("#import ")){
100                 ln = ln.substring(8).trim();
101                 if (ln.startsWith("\"") && ln.endsWith("\"") && ln.length() > 3){
102                     // import user code
103                     // remove quotes to get filename
104                     ln = ln.substring(1, ln.length()-1);
105                     if (ln.equals(nodeName))
106                         throw new IOException("Node depends on itself.");
107 
108                     // check cache first
109                     DependencyNode dependNode = dependCache.get(ln);
110                     if (dependNode == null){
111                         GlslDependKey key = new GlslDependKey(ln);
112                         // make sure not to register an input stream with
113                         // the cache..
114                         InputStream stream = (InputStream) owner.loadAsset(key);
115                         dependNode = loadNode(stream, ln);
116                     }
117                     node.addDependency(dependNode);
118                 }
119 //            }else if (ln.startsWith("uniform") || ln.startsWith("varying") || ln.startsWith("attribute")){
120 //                // these variables are included as dependencies as well
121 //                DependencyNode dependNode = dependCache.get(ln);
122 //                if (dependNode == null){
123 //                    // the source and name are the same for variable dependencies
124 //                    dependNode = new DependencyNode(ln);
125 //                    dependNode.setSource(ln);
126 //                    dependCache.put(ln, dependNode);
127 //                }
128 //                node.addDependency(dependNode);
129             }else{
130                 sb.append(ln).append('\n');
131             }
132         }
133         r.close();
134 
135         node.setSource(sb.toString());
136         dependCache.put(nodeName, node);
137         return node;
138     }
139 
nextIndependentNode(List<DependencyNode> checkedNodes)140     private DependencyNode nextIndependentNode(List<DependencyNode> checkedNodes){
141         Collection<DependencyNode> allNodes = dependCache.values();
142         if (allNodes == null || allNodes.isEmpty())
143             return null;
144 
145         for (DependencyNode node : allNodes){
146             if (node.dependsOn.isEmpty()){
147                 return node;
148             }
149         }
150 
151         // circular dependency found..
152         for (DependencyNode node : allNodes){
153             System.out.println(node.shaderName);
154         }
155         throw new RuntimeException("Circular dependency.");
156     }
157 
resolveDependencies(DependencyNode root)158     private String resolveDependencies(DependencyNode root){
159         StringBuilder sb = new StringBuilder();
160 
161         List<DependencyNode> checkedNodes = new ArrayList<DependencyNode>();
162         checkedNodes.add(root);
163         while (true){
164             DependencyNode indepnNode = nextIndependentNode(checkedNodes);
165             if (indepnNode == null)
166                 break;
167 
168             sb.append(indepnNode.shaderSource).append('\n');
169             dependCache.remove(indepnNode.shaderName);
170 
171             // take out this dependency
172             for (Iterator<DependencyNode> iter = indepnNode.dependOnMe.iterator();
173                  iter.hasNext();){
174                 DependencyNode dependNode = iter.next();
175                 iter.remove();
176                 dependNode.dependsOn.remove(indepnNode);
177             }
178         }
179 
180 //        System.out.println(sb.toString());
181 //        System.out.println("--------------------------------------------------");
182 
183         return sb.toString();
184     }
185 
186     /**
187      *
188      * @param owner
189      * @param in
190      * @param extension
191      * @param key
192      * @return
193      * @throws java.io.IOException
194      */
load(AssetInfo info)195     public Object load(AssetInfo info) throws IOException {
196         // The input stream provided is for the vertex shader,
197         // to retrieve the fragment shader, use the content manager
198         this.owner = info.getManager();
199         if (info.getKey().getExtension().equals("glsllib")){
200             // NOTE: Loopback, GLSLLIB is loaded by this loader
201             // and needs data as InputStream
202             return info.openStream();
203         }else{
204             // GLSLLoader wants result as String for
205             // fragment shader
206             DependencyNode rootNode = loadNode(info.openStream(), "[main]");
207             String code = resolveDependencies(rootNode);
208             dependCache.clear();
209             return code;
210         }
211     }
212 
213 }
214