Home | History | Annotate | Download | only in plugins
      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 
     61         public DependencyNode(String shaderName){
     62             this.shaderName = shaderName;
     63         }
     64 
     65         public void setSource(String source){
     66             this.shaderSource = source;
     67         }
     68 
     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> {
     81         public GlslDependKey(String name){
     82             super(name);
     83         }
     84         @Override
     85         public boolean shouldCache(){
     86             return false;
     87         }
     88     }
     89 
     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 
    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 
    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      */
    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