• 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;
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 
ShaderSource(ShaderType type)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 
ShaderSource(ShaderSource ss)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 
ShaderSource()121         public ShaderSource(){
122             super(ShaderSource.class);
123         }
124 
write(JmeExporter ex)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 
read(JmeImporter im)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 
setName(String name)141         public void setName(String name){
142             this.name = name;
143         }
144 
getName()145         public String getName(){
146             return name;
147         }
148 
getType()149         public ShaderType getType() {
150             return shaderType;
151         }
152 
setSource(String source)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 
setDefines(String defines)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 
getSource()169         public String getSource(){
170             return source;
171         }
172 
getDefines()173         public String getDefines(){
174             return defines;
175         }
176 
isUsable()177         public boolean isUsable(){
178             return usable;
179         }
180 
setUsable(boolean usable)181         public void setUsable(boolean usable){
182             this.usable = usable;
183         }
184 
185         @Override
toString()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 
resetObject()198         public void resetObject(){
199             id = -1;
200             usable = false;
201             setUpdateNeeded();
202         }
203 
deleteObject(Object rendererObject)204         public void deleteObject(Object rendererObject){
205             ((Renderer)rendererObject).deleteShaderSource(ShaderSource.this);
206         }
207 
createDestructableClone()208         public NativeObject createDestructableClone(){
209             return new ShaderSource(ShaderSource.this);
210         }
211     }
212 
213     /**
214      * Create an empty shader.
215      */
Shader(String language)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      */
Shader()228     public Shader(){
229         super(Shader.class);
230     }
231 
Shader(Shader s)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 
write(JmeExporter ex)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 
read(JmeImporter im)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      */
addSource(ShaderType type, String name, String source, String defines)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 
addSource(ShaderType type, String source, String defines)304     public void addSource(ShaderType type, String source, String defines){
305         addSource(type, null, source, defines);
306     }
307 
addSource(ShaderType type, String source)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      */
addSource(ShaderSource source)316     private void addSource(ShaderSource source){
317         shaderList.add(source);
318         setUpdateNeeded();
319     }
320 
getUniform(String name)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 
removeUniform(String name)331     public void removeUniform(String name){
332         uniforms.remove(name);
333     }
334 
getAttribute(VertexBuffer.Type attribType)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 
getUniformMap()350     public ListMap<String, Uniform> getUniformMap(){
351         return uniforms;
352     }
353 
354 //    public Collection<Attribute> getAttributes() {
355 //        return attribs.
356 //    }
357 
getSources()358     public Collection<ShaderSource> getSources(){
359         return shaderList;
360     }
361 
getLanguage()362     public String getLanguage(){
363         return language;
364     }
365 
366     @Override
toString()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      */
resetSources()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      */
isUsable()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      */
setUsable(boolean usable)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      */
resetLocations()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
setUpdateNeeded()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
resetObject()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
deleteObject(Object rendererObject)435     public void deleteObject(Object rendererObject) {
436         ((Renderer)rendererObject).deleteShader(this);
437     }
438 
createDestructableClone()439     public NativeObject createDestructableClone(){
440         return new Shader(this);
441     }
442 
443 }
444