Home | History | Annotate | Download | only in shader
      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;
     34 
     35 import com.jme3.export.*;
     36 import com.jme3.renderer.Renderer;
     37 import com.jme3.scene.VertexBuffer;
     38 import com.jme3.util.IntMap;
     39 import com.jme3.util.IntMap.Entry;
     40 import com.jme3.util.ListMap;
     41 import com.jme3.util.NativeObject;
     42 import java.io.IOException;
     43 import java.util.ArrayList;
     44 import java.util.Collection;
     45 import java.util.HashMap;
     46 
     47 public final class Shader extends NativeObject implements Savable {
     48 
     49     private String language;
     50 
     51     /**
     52      * True if the shader is fully compiled & linked.
     53      * (e.g no GL error will be invoked if used).
     54      */
     55     private boolean usable = false;
     56 
     57     /**
     58      * A list of all shaders currently attached.
     59      */
     60     private ArrayList<ShaderSource> shaderList;
     61 
     62     /**
     63      * Maps uniform name to the uniform variable.
     64      */
     65 //    private HashMap<String, Uniform> uniforms;
     66     private ListMap<String, Uniform> uniforms;
     67 
     68     /**
     69      * Maps attribute name to the location of the attribute in the shader.
     70      */
     71     private IntMap<Attribute> attribs;
     72 
     73     /**
     74      * Type of shader. The shader will control the pipeline of it's type.
     75      */
     76     public static enum ShaderType {
     77         /**
     78          * Control fragment rasterization. (e.g color of pixel).
     79          */
     80         Fragment,
     81 
     82         /**
     83          * Control vertex processing. (e.g transform of model to clip space)
     84          */
     85         Vertex,
     86 
     87         /**
     88          * Control geometry assembly. (e.g compile a triangle list from input data)
     89          */
     90         Geometry;
     91     }
     92 
     93     /**
     94      * Shader source describes a shader object in OpenGL. Each shader source
     95      * is assigned a certain pipeline which it controls (described by it's type).
     96      */
     97     public static class ShaderSource extends NativeObject implements Savable {
     98 
     99         ShaderType shaderType;
    100 
    101         boolean usable = false;
    102         String name = null;
    103         String source = null;
    104         String defines = null;
    105 
    106         public ShaderSource(ShaderType type){
    107             super(ShaderSource.class);
    108             this.shaderType = type;
    109             if (type == null)
    110                 throw new NullPointerException("The shader type must be specified");
    111         }
    112 
    113         protected ShaderSource(ShaderSource ss){
    114             super(ShaderSource.class, ss.id);
    115             this.shaderType = ss.shaderType;
    116             usable = false;
    117             name = ss.name;
    118             // forget source & defines
    119         }
    120 
    121         public ShaderSource(){
    122             super(ShaderSource.class);
    123         }
    124 
    125         public void write(JmeExporter ex) throws IOException{
    126             OutputCapsule oc = ex.getCapsule(this);
    127             oc.write(shaderType, "shaderType", null);
    128             oc.write(name, "name", null);
    129             oc.write(source, "source", null);
    130             oc.write(defines, "defines", null);
    131         }
    132 
    133         public void read(JmeImporter im) throws IOException{
    134             InputCapsule ic = im.getCapsule(this);
    135             shaderType = ic.readEnum("shaderType", ShaderType.class, null);
    136             name = ic.readString("name", null);
    137             source = ic.readString("source", null);
    138             defines = ic.readString("defines", null);
    139         }
    140 
    141         public void setName(String name){
    142             this.name = name;
    143         }
    144 
    145         public String getName(){
    146             return name;
    147         }
    148 
    149         public ShaderType getType() {
    150             return shaderType;
    151         }
    152 
    153         public void setSource(String source){
    154             if (source == null)
    155                 throw new NullPointerException("Shader source cannot be null");
    156 
    157             this.source = source;
    158             setUpdateNeeded();
    159         }
    160 
    161         public void setDefines(String defines){
    162             if (defines == null)
    163                 throw new NullPointerException("Shader defines cannot be null");
    164 
    165             this.defines = defines;
    166             setUpdateNeeded();
    167         }
    168 
    169         public String getSource(){
    170             return source;
    171         }
    172 
    173         public String getDefines(){
    174             return defines;
    175         }
    176 
    177         public boolean isUsable(){
    178             return usable;
    179         }
    180 
    181         public void setUsable(boolean usable){
    182             this.usable = usable;
    183         }
    184 
    185         @Override
    186         public String toString(){
    187             String nameTxt = "";
    188             if (name != null)
    189                 nameTxt = "name="+name+", ";
    190             if (defines != null)
    191                 nameTxt += "defines, ";
    192 
    193 
    194             return getClass().getSimpleName() + "["+nameTxt+"type="
    195                                               + shaderType.name()+"]";
    196         }
    197 
    198         public void resetObject(){
    199             id = -1;
    200             usable = false;
    201             setUpdateNeeded();
    202         }
    203 
    204         public void deleteObject(Object rendererObject){
    205             ((Renderer)rendererObject).deleteShaderSource(ShaderSource.this);
    206         }
    207 
    208         public NativeObject createDestructableClone(){
    209             return new ShaderSource(ShaderSource.this);
    210         }
    211     }
    212 
    213     /**
    214      * Create an empty shader.
    215      */
    216     public Shader(String language){
    217         super(Shader.class);
    218         this.language = language;
    219         shaderList = new ArrayList<ShaderSource>();
    220 //        uniforms = new HashMap<String, Uniform>();
    221         uniforms = new ListMap<String, Uniform>();
    222         attribs = new IntMap<Attribute>();
    223     }
    224 
    225     /**
    226      * Do not use this constructor. Serialization purposes only.
    227      */
    228     public Shader(){
    229         super(Shader.class);
    230     }
    231 
    232     protected Shader(Shader s){
    233         super(Shader.class, s.id);
    234         shaderList = new ArrayList<ShaderSource>();
    235         //uniforms = new ListMap<String, Uniform>();
    236         //attribs = new IntMap<Attribute>();
    237 
    238         // NOTE: Because ShaderSources are registered separately with
    239         // the GLObjectManager
    240         for (ShaderSource source : s.shaderList){
    241             shaderList.add( (ShaderSource)source.createDestructableClone() );
    242         }
    243     }
    244 
    245     public void write(JmeExporter ex) throws IOException{
    246         OutputCapsule oc = ex.getCapsule(this);
    247         oc.write(language, "language", null);
    248         oc.writeSavableArrayList(shaderList, "shaderList", null);
    249         oc.writeIntSavableMap(attribs, "attribs", null);
    250         oc.writeStringSavableMap(uniforms, "uniforms", null);
    251     }
    252 
    253     public void read(JmeImporter im) throws IOException{
    254         InputCapsule ic = im.getCapsule(this);
    255         language = ic.readString("language", null);
    256         shaderList = ic.readSavableArrayList("shaderList", null);
    257         attribs = (IntMap<Attribute>) ic.readIntSavableMap("attribs", null);
    258 
    259         HashMap<String, Uniform> uniMap = (HashMap<String, Uniform>) ic.readStringSavableMap("uniforms", null);
    260         uniforms = new ListMap<String, Uniform>(uniMap);
    261     }
    262 
    263     /**
    264      * Creates a deep clone of the shader, where the sources are available
    265      * but have not been compiled yet. Does not copy the uniforms or attribs.
    266      * @return
    267      */
    268 //    public Shader createDeepClone(String defines){
    269 //        Shader newShader = new Shader(language);
    270 //        for (ShaderSource source : shaderList){
    271 //            if (!source.getDefines().equals(defines)){
    272 //                // need to clone the shadersource so
    273 //                // the correct defines can be placed
    274 //                ShaderSource newSource = new ShaderSource(source.getType());
    275 //                newSource.setSource(source.getSource());
    276 //                newSource.setDefines(defines);
    277 //                newShader.addSource(newSource);
    278 //            }else{
    279 //                // no need to clone source, also saves
    280 //                // having to compile the shadersource
    281 //                newShader.addSource(source);
    282 //            }
    283 //        }
    284 //        return newShader;
    285 //    }
    286 
    287     /**
    288      * Adds source code to a certain pipeline.
    289      *
    290      * @param type The pipeline to control
    291      * @param source The shader source code (in GLSL).
    292      */
    293     public void addSource(ShaderType type, String name, String source, String defines){
    294         ShaderSource shader = new ShaderSource(type);
    295         shader.setSource(source);
    296         shader.setName(name);
    297         if (defines != null)
    298             shader.setDefines(defines);
    299 
    300         shaderList.add(shader);
    301         setUpdateNeeded();
    302     }
    303 
    304     public void addSource(ShaderType type, String source, String defines){
    305         addSource(type, null, source, defines);
    306     }
    307 
    308     public void addSource(ShaderType type, String source){
    309         addSource(type, source, null);
    310     }
    311 
    312     /**
    313      * Adds an existing shader source to this shader.
    314      * @param source
    315      */
    316     private void addSource(ShaderSource source){
    317         shaderList.add(source);
    318         setUpdateNeeded();
    319     }
    320 
    321     public Uniform getUniform(String name){
    322         Uniform uniform = uniforms.get(name);
    323         if (uniform == null){
    324             uniform = new Uniform();
    325             uniform.name = name;
    326             uniforms.put(name, uniform);
    327         }
    328         return uniform;
    329     }
    330 
    331     public void removeUniform(String name){
    332         uniforms.remove(name);
    333     }
    334 
    335     public Attribute getAttribute(VertexBuffer.Type attribType){
    336         int ordinal = attribType.ordinal();
    337         Attribute attrib = attribs.get(ordinal);
    338         if (attrib == null){
    339             attrib = new Attribute();
    340             attrib.name = attribType.name();
    341             attribs.put(ordinal, attrib);
    342         }
    343         return attrib;
    344     }
    345 
    346 //    public Collection<Uniform> getUniforms(){
    347 //        return uniforms.values();
    348 //    }
    349 
    350     public ListMap<String, Uniform> getUniformMap(){
    351         return uniforms;
    352     }
    353 
    354 //    public Collection<Attribute> getAttributes() {
    355 //        return attribs.
    356 //    }
    357 
    358     public Collection<ShaderSource> getSources(){
    359         return shaderList;
    360     }
    361 
    362     public String getLanguage(){
    363         return language;
    364     }
    365 
    366     @Override
    367     public String toString(){
    368         return getClass().getSimpleName() + "[language="+language
    369                                            + ", numSources="+shaderList.size()
    370                                            + ", numUniforms="+uniforms.size()
    371                                            + ", shaderSources="+getSources()+"]";
    372     }
    373 
    374     /**
    375      * Clears all sources. Assuming that they have already been detached and
    376      * removed on the GL side.
    377      */
    378     public void resetSources(){
    379         shaderList.clear();
    380     }
    381 
    382     /**
    383      * Returns true if this program and all it's shaders have been compiled,
    384      * linked and validated successfuly.
    385      */
    386     public boolean isUsable(){
    387         return usable;
    388     }
    389 
    390     /**
    391      * Sets if the program can be used. Should only be called by the Renderer.
    392      * @param usable
    393      */
    394     public void setUsable(boolean usable){
    395         this.usable = usable;
    396     }
    397 
    398     /**
    399      * Usually called when the shader itself changes or during any
    400      * time when the var locations need to be refreshed.
    401      */
    402     public void resetLocations(){
    403         // NOTE: Shader sources will be reset seperately from the shader itself.
    404         for (Uniform uniform : uniforms.values()){
    405             uniform.reset(); // fixes issue with re-initialization
    406         }
    407         for (Entry<Attribute> entry : attribs){
    408             entry.getValue().location = -2;
    409         }
    410     }
    411 
    412     @Override
    413     public void setUpdateNeeded(){
    414         super.setUpdateNeeded();
    415         resetLocations();
    416     }
    417 
    418     /**
    419      * Called by the object manager to reset all object IDs. This causes
    420      * the shader to be reuploaded to the GPU incase the display was restarted.
    421      */
    422     @Override
    423     public void resetObject() {
    424         this.id = -1;
    425         this.usable = false;
    426 
    427         for (ShaderSource source : shaderList){
    428             source.resetObject();
    429         }
    430 
    431         setUpdateNeeded();
    432     }
    433 
    434     @Override
    435     public void deleteObject(Object rendererObject) {
    436         ((Renderer)rendererObject).deleteShader(this);
    437     }
    438 
    439     public NativeObject createDestructableClone(){
    440         return new Shader(this);
    441     }
    442 
    443 }
    444