Kaynağa Gözat

* Shader now stores shader language in the ShaderSources instead of the Shader itself (everything makes a lot more sense now).
^ Make sure your shader languages are appropriate for each shader type (vert / frag) in your J3MS!
* Shaders no longer have the "usable" member and the renderers don't use it either (its useless)

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9545 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

Sha..rd 13 yıl önce
ebeveyn
işleme
55d75c5abc

+ 41 - 93
engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java

@@ -590,6 +590,18 @@ public class OGLESShaderRenderer implements Renderer {
         }
     }
 
+    protected void bindProgram(Shader shader) {
+        int shaderId = shader.getId();
+        if (context.boundShaderProgram != shaderId) {
+            GLES20.glUseProgram(shaderId);
+            statistics.onShaderUse(shader, true);
+            boundShader = shader;
+            context.boundShaderProgram = shaderId;
+        } else {
+            statistics.onShaderUse(shader, false);
+        }
+    }
+    
     protected void updateUniform(Shader shader, Uniform uniform) {
         int shaderId = shader.getId();
 
@@ -698,7 +710,6 @@ public class OGLESShaderRenderer implements Renderer {
 
     protected void updateShaderUniforms(Shader shader) {
         ListMap<String, Uniform> uniforms = shader.getUniformMap();
-//        for (Uniform uniform : shader.getUniforms()){
         for (int i = 0; i < uniforms.size(); i++) {
             Uniform uniform = uniforms.getValue(i);
             if (uniform.isUpdateNeeded()) {
@@ -709,7 +720,6 @@ public class OGLESShaderRenderer implements Renderer {
 
     protected void resetUniformLocations(Shader shader) {
         ListMap<String, Uniform> uniforms = shader.getUniformMap();
-//        for (Uniform uniform : shader.getUniforms()){
         for (int i = 0; i < uniforms.size(); i++) {
             Uniform uniform = uniforms.getValue(i);
             uniform.reset(); // e.g check location again
@@ -736,27 +746,28 @@ public class OGLESShaderRenderer implements Renderer {
         }
     }
 
-    public void updateShaderSourceData(ShaderSource source, String language) {
+    public void updateShaderSourceData(ShaderSource source) {
         int id = source.getId();
         if (id == -1) {
-            // create id
+            // Create id
             id = GLES20.glCreateShader(convertShaderType(source.getType()));
             if (id <= 0) {
                 throw new RendererException("Invalid ID received when trying to create shader.");
             }
             source.setId(id);
         }
+        
+        if (!source.getLanguage().equals("GLSL100")) {
+            throw new RendererException("This shader cannot run in OpenGL ES. "
+                                      + "Only GLSL 1.0 shaders are supported.");
+        }
 
         // upload shader source
         // merge the defines and source code
-        byte[] versionData = new byte[]{};//"#version 140\n".getBytes();
-//        versionData = "#define INSTANCING 1\n".getBytes();
         byte[] definesCodeData = source.getDefines().getBytes();
         byte[] sourceCodeData = source.getSource().getBytes();
-        ByteBuffer codeBuf = BufferUtils.createByteBuffer(versionData.length
-                + definesCodeData.length
-                + sourceCodeData.length);
-        codeBuf.put(versionData);
+        ByteBuffer codeBuf = BufferUtils.createByteBuffer(definesCodeData.length
+                                                        + sourceCodeData.length);
         codeBuf.put(definesCodeData);
         codeBuf.put(sourceCodeData);
         codeBuf.flip();
@@ -791,10 +802,11 @@ public class OGLESShaderRenderer implements Renderer {
 
         if (compiledOK) {
             if (infoLog != null) {
-                logger.log(Level.INFO, "compile success: " + source.getName() + ", " + infoLog);
+                logger.log(Level.INFO, "compile success: {0}, {1}", new Object[]{source.getName(), infoLog});
             } else {
-                logger.log(Level.FINE, "compile success: " + source.getName());
+                logger.log(Level.FINE, "compile success: {0}", source.getName());
             }
+            source.clearUpdateNeeded();
         } else {
            logger.log(Level.WARNING, "Bad compile of:\n{0}",
                     new Object[]{ShaderDebug.formatShaderSource(source.getDefines(), source.getSource(),stringBuf.toString())});
@@ -804,18 +816,6 @@ public class OGLESShaderRenderer implements Renderer {
                 throw new RendererException("compile error in:" + source + " error: <not provided>");
             }
         }
-
-        source.clearUpdateNeeded();
-        // only usable if compiled
-        source.setUsable(compiledOK);
-        if (!compiledOK) {
-            // make sure to dispose id cause all program's
-            // shaders will be cleared later.
-            GLES20.glDeleteShader(id);
-        } else {
-            // register for cleanup since the ID is usable
-            objManager.registerForCleanup(source);
-        }
     }
 
     public void updateShaderData(Shader shader) {
@@ -835,15 +835,7 @@ public class OGLESShaderRenderer implements Renderer {
 
         for (ShaderSource source : shader.getSources()) {
             if (source.isUpdateNeeded()) {
-                updateShaderSourceData(source, shader.getLanguage());
-                // shader has been compiled here
-            }
-
-            if (!source.isUsable()) {
-                // it's useless.. just forget about everything..
-                shader.setUsable(false);
-                shader.clearUpdateNeeded();
-                return;
+                updateShaderSourceData(source);
             }
             GLES20.glAttachShader(id, source.getId());
         }
@@ -871,41 +863,27 @@ public class OGLESShaderRenderer implements Renderer {
             } else {
                 logger.fine("shader link success");
             }
-        } else {
-            if (infoLog != null) {
-                throw new RendererException("Shader link failure, shader:" + shader + " info:" + infoLog);
-            } else {
-                throw new RendererException("Shader link failure, shader:" + shader + " info: <not provided>");
-            }
-        }
-
-        shader.clearUpdateNeeded();
-        if (!linkOK) {
-            // failure.. forget about everything
-            shader.resetSources();
-            shader.setUsable(false);
-            deleteShader(shader);
-        } else {
-            shader.setUsable(true);
+            shader.clearUpdateNeeded();
             if (needRegister) {
+                // Register shader for clean up if it was created in this method.
                 objManager.registerForCleanup(shader);
                 statistics.onNewShader();
             } else {
                 // OpenGL spec: uniform locations may change after re-link
                 resetUniformLocations(shader);
             }
+        } else {
+            if (infoLog != null) {
+                throw new RendererException("Shader link failure, shader:" + shader + " info:" + infoLog);
+            } else {
+                throw new RendererException("Shader link failure, shader:" + shader + " info: <not provided>");
+            }
         }
     }
-
+    
     public void setShader(Shader shader) {
         if (shader == null) {
-            if (context.boundShaderProgram > 0) {
-                GLES20.glUseProgram(0);
-
-                statistics.onShaderUse(null, true);
-                context.boundShaderProgram = 0;
-                boundShader = null;
-            }
+            throw new IllegalArgumentException("Shader cannot be null");
         } else {
             if (shader.isUpdateNeeded()) {
                 updateShaderData(shader);
@@ -913,37 +891,11 @@ public class OGLESShaderRenderer implements Renderer {
 
             // NOTE: might want to check if any of the 
             // sources need an update?
-            if (!shader.isUsable()) {
-                logger.warning("shader is not usable.");
-                return;
-            }
 
             assert shader.getId() > 0;
 
             updateShaderUniforms(shader);
-            if (context.boundShaderProgram != shader.getId()) {
-                if (VALIDATE_SHADER) {
-                    // check if shader can be used
-                    // with current state
-                    GLES20.glValidateProgram(shader.getId());
-                    GLES20.glGetProgramiv(shader.getId(), GLES20.GL_VALIDATE_STATUS, intBuf1);
-
-                    boolean validateOK = intBuf1.get(0) == GLES20.GL_TRUE;
-                    if (validateOK) {
-                        logger.fine("shader validate success");
-                    } else {
-                        logger.warning("shader validate failure");
-                    }
-                }
-
-                GLES20.glUseProgram(shader.getId());
-
-                statistics.onShaderUse(shader, true);
-                context.boundShaderProgram = shader.getId();
-                boundShader = shader;
-            } else {
-                statistics.onShaderUse(shader, false);
-            }
+            bindProgram(shader);
         }
     }
 
@@ -952,9 +904,8 @@ public class OGLESShaderRenderer implements Renderer {
             logger.warning("Shader source is not uploaded to GPU, cannot delete.");
             return;
         }
-        source.setUsable(false);
+        
         source.clearUpdateNeeded();
-
         GLES20.glDeleteShader(source.getId());
         source.resetObject();
     }
@@ -964,20 +915,17 @@ public class OGLESShaderRenderer implements Renderer {
             logger.warning("Shader is not uploaded to GPU, cannot delete.");
             return;
         }
+        
         for (ShaderSource source : shader.getSources()) {
             if (source.getId() != -1) {
                 GLES20.glDetachShader(shader.getId(), source.getId());
-                // the next part is done by the GLObjectManager automatically
-//                glDeleteShader(source.getId());
+                deleteShaderSource(source);
             }
         }
-        // kill all references so sources can be collected
-        // if needed.
-        shader.resetSources();
-
+        
         GLES20.glDeleteProgram(shader.getId());
-
         statistics.onDeleteShader();
+        shader.resetObject();
     }
 
     /*********************************************************************\

+ 1 - 6
engine/src/core-effects/Common/MatDefs/Post/BloomFinal.j3md

@@ -8,7 +8,7 @@ MaterialDef Bloom Final {
     }
 
     Technique {
-        VertexShader GLSL100:   Common/MatDefs/Post/Post15.vert
+        VertexShader GLSL150:   Common/MatDefs/Post/Post15.vert
         FragmentShader GLSL150: Common/MatDefs/Post/bloomFinal15.frag
 
         WorldParameters {
@@ -28,9 +28,4 @@ MaterialDef Bloom Final {
             WorldViewProjectionMatrix
         }
     }
-
-
-
-    Technique FixedFunc {
-    }
 }

+ 14 - 34
engine/src/core/com/jme3/asset/DesktopAssetManager.java

@@ -336,11 +336,6 @@ public class DesktopAssetManager implements AssetManager {
         return loadAsset(new AssetKey(name));
     }
 
-    /**
-     * Loads a texture.
-     *
-     * @return the texture
-     */
     public Texture loadTexture(TextureKey key){
         return (Texture) loadAsset(key);
     }
@@ -365,18 +360,16 @@ public class DesktopAssetManager implements AssetManager {
         return loadAudio(new AudioKey(name, false));
     }
 
-    /**
-     * Loads a bitmap font with the given name.
-     *
-     * @param name
-     * @return the loaded {@link BitmapFont}
-     */
     public BitmapFont loadFont(String name){
         return (BitmapFont) loadAsset(new AssetKey(name));
     }
 
-    public InputStream loadGLSLLibrary(AssetKey key){
-        return (InputStream) loadAsset(key);
+    public Spatial loadModel(ModelKey key){
+        return (Spatial) loadAsset(key);
+    }
+
+    public Spatial loadModel(String name){
+        return loadModel(new ModelKey(name));
     }
 
     /**
@@ -389,34 +382,21 @@ public class DesktopAssetManager implements AssetManager {
         // cache abuse in method
         // that doesn't use loaders/locators
         AssetCache cache = handler.getCache(SimpleAssetCache.class);
-        Shader s = (Shader) cache.getFromCache(key);
-        if (s == null){
+        Shader shader = (Shader) cache.getFromCache(key);
+        if (shader == null){
             String vertName = key.getVertName();
             String fragName = key.getFragName();
 
             String vertSource = (String) loadAsset(new AssetKey(vertName));
             String fragSource = (String) loadAsset(new AssetKey(fragName));
 
-            s = new Shader(key.getLanguage());
-            s.addSource(Shader.ShaderType.Vertex,   vertName, vertSource, key.getDefines().getCompiled());
-            s.addSource(Shader.ShaderType.Fragment, fragName, fragSource, key.getDefines().getCompiled());
+            shader = new Shader();
+            shader.initialize();
+            shader.addSource(Shader.ShaderType.Vertex,   vertName, vertSource, key.getDefines().getCompiled(), key.getVertexShaderLanguage());
+            shader.addSource(Shader.ShaderType.Fragment, fragName, fragSource, key.getDefines().getCompiled(), key.getFragmentShaderLanguage());
 
-            cache.addToCache(key, s);
+            cache.addToCache(key, shader);
         }
-        return s;
-    }
-
-    public Spatial loadModel(ModelKey key){
-        return (Spatial) loadAsset(key);
-    }
-
-    /**
-     * Load a model.
-     *
-     * @param name
-     * @return the loaded model
-     */
-    public Spatial loadModel(String name){
-        return loadModel(new ModelKey(name));
+        return shader;
     }
 }

+ 9 - 4
engine/src/core/com/jme3/material/Technique.java

@@ -32,9 +32,7 @@
 package com.jme3.material;
 
 import com.jme3.asset.AssetManager;
-import com.jme3.export.*;
 import com.jme3.shader.*;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -43,7 +41,7 @@ import java.util.logging.Logger;
 /**
  * Represents a technique instance.
  */
-public class Technique implements Savable {
+public class Technique /* implements Savable */ {
 
     private static final Logger logger = Logger.getLogger(Technique.class.getName());
     private TechniqueDef def;
@@ -133,6 +131,10 @@ public class Technique implements Savable {
     }
 
     void updateUniformParam(String paramName, VarType type, Object value) {
+        if (paramName == null) {
+            throw new IllegalArgumentException();
+        }
+        
         Uniform u = shader.getUniform(paramName);
         switch (type) {
             case TextureBuffer:
@@ -208,7 +210,8 @@ public class Technique implements Savable {
         ShaderKey key = new ShaderKey(def.getVertexShaderName(),
                                       def.getFragmentShaderName(),
                                       allDefines,
-                                      def.getShaderLanguage());
+                                      def.getVertexShaderLanguage(),
+                                      def.getFragmentShaderLanguage());
         shader = manager.loadShader(key);
 
         // register the world bound uniforms
@@ -225,6 +228,7 @@ public class Technique implements Savable {
         needReload = false;
     }
     
+    /*
     public void write(JmeExporter ex) throws IOException {
         OutputCapsule oc = ex.getCapsule(this);
         oc.write(def, "def", null);
@@ -240,4 +244,5 @@ public class Technique implements Savable {
         defines = (DefineList) ic.readSavable("defines", null);
         shader = (Shader) ic.readSavable("shader", null);
     }
+    */
 }

+ 51 - 17
engine/src/core/com/jme3/material/TechniqueDef.java

@@ -51,6 +51,11 @@ import java.util.List;
  */
 public class TechniqueDef implements Savable {
 
+    /**
+     * Version #1: Separate shader language for each shader source.
+     */
+    public static final int SAVABLE_VERSION = 1;
+    
     /**
      * Describes light rendering mode.
      */
@@ -101,7 +106,9 @@ public class TechniqueDef implements Savable {
 
     private String vertName;
     private String fragName;
-    private String shaderLang;
+    private String vertLanguage;
+    private String fragLanguage;
+    
     private DefineList presetDefines;
     private boolean usesShaders;
 
@@ -227,13 +234,16 @@ public class TechniqueDef implements Savable {
      * @param fragmentShader The name of the fragment shader
      * @param shaderLanguage The shader language
      */
-    public void setShaderFile(String vertexShader, String fragmentShader, String shaderLanguage){
+    public void setShaderFile(String vertexShader, String fragmentShader, String vertLanguage, String fragLanguage){
         this.vertName = vertexShader;
         this.fragName = fragmentShader;
-        this.shaderLang = shaderLanguage;
+        this.vertLanguage = vertLanguage;
+        this.fragLanguage = fragLanguage;
 
-        Caps langCap = Caps.valueOf(shaderLanguage);
-        requiredCaps.add(langCap);
+        Caps vertCap = Caps.valueOf(vertLanguage);
+        requiredCaps.add(vertCap);
+        Caps fragCap = Caps.valueOf(fragLanguage);
+        requiredCaps.add(fragCap);
 
         usesShaders = true;
     }
@@ -247,9 +257,9 @@ public class TechniqueDef implements Savable {
      * @see #addShaderParamDefine(java.lang.String, java.lang.String) 
      */
     public String getShaderParamDefine(String paramName){
-        if (defineParams == null)
+        if (defineParams == null) {
             return null;
-        
+        }
         return defineParams.get(paramName);
     }
 
@@ -266,9 +276,9 @@ public class TechniqueDef implements Savable {
      * @param defineName The name of the define parameter, e.g. USE_LIGHTING
      */
     public void addShaderParamDefine(String paramName, String defineName){
-        if (defineParams == null)
+        if (defineParams == null) {
             defineParams = new HashMap<String, String>();
-
+        }
         defineParams.put(paramName, defineName);
     }
 
@@ -297,9 +307,9 @@ public class TechniqueDef implements Savable {
      * @param value The value of the define
      */
     public void addShaderPresetDefine(String defineName, VarType type, Object value){
-        if (presetDefines == null)
+        if (presetDefines == null) {
             presetDefines = new DefineList();
-
+        }
         presetDefines.set(defineName, type, value);
     }
 
@@ -325,14 +335,27 @@ public class TechniqueDef implements Savable {
     }
 
     /**
-     * Returns the shader language of the shaders used in this technique.
-     * 
-     * @return the shader language of the shaders used in this technique.
+     * @deprecated Use {@link #getVertexShaderLanguage() } instead.
      */
+    @Deprecated
     public String getShaderLanguage() {
-        return shaderLang;
+        return vertLanguage;
     }
 
+    /**
+     * Returns the language of the fragment shader used in this technique.
+     */
+    public String getFragmentShaderLanguage() {
+        return fragLanguage;
+    }
+    
+    /**
+     * Returns the language of the vertex shader used in this technique.
+     */
+    public String getVertexShaderLanguage() {
+        return vertLanguage;
+    }
+    
     /**
      * Adds a new world parameter by the given name.
      * 
@@ -368,12 +391,14 @@ public class TechniqueDef implements Savable {
         oc.write(name, "name", null);
         oc.write(vertName, "vertName", null);
         oc.write(fragName, "fragName", null);
-        oc.write(shaderLang, "shaderLang", null);
+        oc.write(vertLanguage, "vertLanguage", null);
+        oc.write(vertLanguage, "fragLanguage", null);
         oc.write(presetDefines, "presetDefines", null);
         oc.write(lightMode, "lightMode", LightMode.Disable);
         oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
         oc.write(renderState, "renderState", null);
         oc.write(usesShaders, "usesShaders", false);
+        
         // TODO: Finish this when Map<String, String> export is available
 //        oc.write(defineParams, "defineParams", null);
         // TODO: Finish this when List<Enum> export is available
@@ -385,12 +410,21 @@ public class TechniqueDef implements Savable {
         name = ic.readString("name", null);
         vertName = ic.readString("vertName", null);
         fragName = ic.readString("fragName", null);
-        shaderLang = ic.readString("shaderLang", null);
         presetDefines = (DefineList) ic.readSavable("presetDefines", null);
         lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
         shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
         renderState = (RenderState) ic.readSavable("renderState", null);
         usesShaders = ic.readBoolean("usesShaders", false);
+        
+        if (ic.getSavableVersion(TechniqueDef.class) == 0) {
+            // Old version
+            vertLanguage = ic.readString("shaderLang", null);
+            fragLanguage = vertLanguage;
+        } else {
+            // New version
+            vertLanguage = ic.readString("vertLanguage", null);
+            fragLanguage = ic.readString("fragLanguage", null);;
+        }
     }
     
 }

+ 136 - 161
engine/src/core/com/jme3/shader/Shader.java

@@ -32,37 +32,33 @@
 
 package com.jme3.shader;
 
-import com.jme3.export.*;
 import com.jme3.renderer.Renderer;
 import com.jme3.scene.VertexBuffer;
 import com.jme3.util.IntMap;
 import com.jme3.util.IntMap.Entry;
 import com.jme3.util.ListMap;
 import com.jme3.util.NativeObject;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 
-public final class Shader extends NativeObject implements Savable {
-
-    private String language;
+public final class Shader extends NativeObject {
 
     /**
-     * True if the shader is fully compiled & linked.
-     * (e.g no GL error will be invoked if used).
+     * 
+     * @deprecated shader language now specified per shader source. See
+     * {@link ShaderSource#setLanguage(String)
      */
-    private boolean usable = false;
-
+    @Deprecated
+    private String language;
+    
     /**
-     * A list of all shaders currently attached.
+     * A list of all shader sources currently attached.
      */
-    private ArrayList<ShaderSource> shaderList;
+    private ArrayList<ShaderSource> shaderSourceList;
 
     /**
      * Maps uniform name to the uniform variable.
      */
-//    private HashMap<String, Uniform> uniforms;
     private ListMap<String, Uniform> uniforms;
 
     /**
@@ -94,50 +90,32 @@ public final class Shader extends NativeObject implements Savable {
      * Shader source describes a shader object in OpenGL. Each shader source
      * is assigned a certain pipeline which it controls (described by it's type).
      */
-    public static class ShaderSource extends NativeObject implements Savable {
+    public static class ShaderSource extends NativeObject {
 
-        ShaderType shaderType;
-
-        boolean usable = false;
-        String name = null;
-        String source = null;
-        String defines = null;
+        ShaderType sourceType;
+        String language;
+        String name;
+        String source;
+        String defines;
 
         public ShaderSource(ShaderType type){
             super(ShaderSource.class);
-            this.shaderType = type;
-            if (type == null)
+            this.sourceType = type;
+            if (type == null) {
                 throw new NullPointerException("The shader type must be specified");
+            }
         }
         
         protected ShaderSource(ShaderSource ss){
             super(ShaderSource.class, ss.id);
-            this.shaderType = ss.shaderType;
-            usable = false;
-            name = ss.name;
-            // forget source & defines
+            // No data needs to be copied.
+            // (This is a destructable clone)
         }
 
         public ShaderSource(){
             super(ShaderSource.class);
         }
 
-        public void write(JmeExporter ex) throws IOException{
-            OutputCapsule oc = ex.getCapsule(this);
-            oc.write(shaderType, "shaderType", null);
-            oc.write(name, "name", null);
-            oc.write(source, "source", null);
-            oc.write(defines, "defines", null);
-        }
-
-        public void read(JmeImporter im) throws IOException{
-            InputCapsule ic = im.getCapsule(this);
-            shaderType = ic.readEnum("shaderType", ShaderType.class, null);
-            name = ic.readString("name", null);
-            source = ic.readString("source", null);
-            defines = ic.readString("defines", null);
-        }
-
         public void setName(String name){
             this.name = name;
         }
@@ -147,21 +125,33 @@ public final class Shader extends NativeObject implements Savable {
         }
 
         public ShaderType getType() {
-            return shaderType;
+            return sourceType;
+        }
+
+        public String getLanguage() {
+            return language;
+        }
+
+        public void setLanguage(String language) {
+            if (language == null) {
+                throw new NullPointerException("Shader language cannot be null");
+            }
+            this.language = language;
+            setUpdateNeeded();
         }
 
         public void setSource(String source){
-            if (source == null)
+            if (source == null) {
                 throw new NullPointerException("Shader source cannot be null");
-
+            }
             this.source = source;
             setUpdateNeeded();
         }
 
         public void setDefines(String defines){
-            if (defines == null)
+            if (defines == null) {
                 throw new NullPointerException("Shader defines cannot be null");
-
+            }
             this.defines = defines;
             setUpdateNeeded();
         }
@@ -174,14 +164,6 @@ public final class Shader extends NativeObject implements Savable {
             return defines;
         }
         
-        public boolean isUsable(){
-            return usable;
-        }
-
-        public void setUsable(boolean usable){
-            this.usable = usable;
-        }
-
         @Override
         public String toString(){
             String nameTxt = "";
@@ -192,12 +174,11 @@ public final class Shader extends NativeObject implements Savable {
             
 
             return getClass().getSimpleName() + "["+nameTxt+"type="
-                                              + shaderType.name()+"]";
+                                              + sourceType.name()+", language=" + language + "]";
         }
 
         public void resetObject(){
             id = -1;
-            usable = false;
             setUpdateNeeded();
         }
 
@@ -211,110 +192,103 @@ public final class Shader extends NativeObject implements Savable {
     }
 
     /**
-     * Create an empty shader.
+     * @deprecated Shader sources are now associated with the shader
+     * language.
      */
+    @Deprecated
     public Shader(String language){
         super(Shader.class);
         this.language = language;
-        shaderList = new ArrayList<ShaderSource>();
-//        uniforms = new HashMap<String, Uniform>();
+        initialize();
+    }
+
+    /**
+     * Initializes the shader for use, must be called after the 
+     * constructor without arguments is used.
+     */
+    public void initialize() {
+        shaderSourceList = new ArrayList<ShaderSource>();
         uniforms = new ListMap<String, Uniform>();
         attribs = new IntMap<Attribute>();
     }
-
+    
     /**
-     * Do not use this constructor. Serialization purposes only.
+     * Creates a new shader, {@link #initialize() } must be called
+     * after this constructor for the shader to be usable.
      */
     public Shader(){
         super(Shader.class);
     }
 
+    /**
+     * Do not use this constructor. Used for destructable clones only.
+     */
     protected Shader(Shader s){
         super(Shader.class, s.id);
-        shaderList = new ArrayList<ShaderSource>();
-        //uniforms = new ListMap<String, Uniform>();
-        //attribs = new IntMap<Attribute>();
         
-        // NOTE: Because ShaderSources are registered separately with
-        // the GLObjectManager
-        for (ShaderSource source : s.shaderList){
-            shaderList.add( (ShaderSource)source.createDestructableClone() );
+        // Shader sources cannot be shared, therefore they must
+        // be destroyed together with the parent shader.
+        shaderSourceList = new ArrayList<ShaderSource>();
+        for (ShaderSource source : s.shaderSourceList){
+            shaderSourceList.add( (ShaderSource)source.createDestructableClone() );
         }
     }
 
-    public void write(JmeExporter ex) throws IOException{
-        OutputCapsule oc = ex.getCapsule(this);
-        oc.write(language, "language", null);
-        oc.writeSavableArrayList(shaderList, "shaderList", null);
-        oc.writeIntSavableMap(attribs, "attribs", null);
-        oc.writeStringSavableMap(uniforms, "uniforms", null);
-    }
-
-    public void read(JmeImporter im) throws IOException{
-        InputCapsule ic = im.getCapsule(this);
-        language = ic.readString("language", null);
-        shaderList = ic.readSavableArrayList("shaderList", null);
-        attribs = (IntMap<Attribute>) ic.readIntSavableMap("attribs", null);
-
-        HashMap<String, Uniform> uniMap = (HashMap<String, Uniform>) ic.readStringSavableMap("uniforms", null);
-        uniforms = new ListMap<String, Uniform>(uniMap);
-    }
-
     /**
-     * Creates a deep clone of the shader, where the sources are available
-     * but have not been compiled yet. Does not copy the uniforms or attribs.
-     * @return
+     * @deprecated Use the method that takes a language argument instead. 
+     * {@link #addSource(com.jme3.shader.Shader.ShaderType, java.lang.String, java.lang.String, java.lang.String, java.lang.String) }
      */
-//    public Shader createDeepClone(String defines){
-//        Shader newShader = new Shader(language);
-//        for (ShaderSource source : shaderList){
-//            if (!source.getDefines().equals(defines)){
-//                // need to clone the shadersource so
-//                // the correct defines can be placed
-//                ShaderSource newSource = new ShaderSource(source.getType());
-//                newSource.setSource(source.getSource());
-//                newSource.setDefines(defines);
-//                newShader.addSource(newSource);
-//            }else{
-//                // no need to clone source, also saves
-//                // having to compile the shadersource
-//                newShader.addSource(source);
-//            }
-//        }
-//        return newShader;
-//    }
-
+    @Deprecated
+    public void addSource(ShaderType type, String name, String source, String defines) {
+        addSource(type, name, source, defines, this.language);
+    }
+    
     /**
-     * Adds source code to a certain pipeline.
-     *
-     * @param type The pipeline to control
-     * @param source The shader source code (in GLSL).
+     * @deprecated Use the method that takes a language argument instead. 
+     * {@link #addSource(com.jme3.shader.Shader.ShaderType, java.lang.String, java.lang.String, java.lang.String, java.lang.String) }
      */
-    public void addSource(ShaderType type, String name, String source, String defines){
-        ShaderSource shader = new ShaderSource(type);
-        shader.setSource(source);
-        shader.setName(name);
-        if (defines != null)
-            shader.setDefines(defines);
-        
-        shaderList.add(shader);
-        setUpdateNeeded();
-    }
-
+    @Deprecated
     public void addSource(ShaderType type, String source, String defines){
         addSource(type, null, source, defines);
     }
 
+    /**
+     * @deprecated Use the method that takes a language argument instead. 
+     * {@link #addSource(com.jme3.shader.Shader.ShaderType, java.lang.String, java.lang.String, java.lang.String, java.lang.String) }
+     */
+    @Deprecated
     public void addSource(ShaderType type, String source){
         addSource(type, source, null);
     }
 
     /**
-     * Adds an existing shader source to this shader.
-     * @param source
+     * @deprecated Shader sources may not be shared.
+     * {@link #addSource(com.jme3.shader.Shader.ShaderType, java.lang.String, java.lang.String, java.lang.String, java.lang.String) }
      */
+    @Deprecated
     private void addSource(ShaderSource source){
-        shaderList.add(source);
+        shaderSourceList.add(source);
+        setUpdateNeeded();
+    }
+    
+    /**
+     * Adds source code to a certain pipeline.
+     *
+     * @param type The pipeline to control
+     * @param source The shader source code (in GLSL).
+     * @param defines Preprocessor defines (placed at the beginning of the shader)
+     * @param language The shader source language, currently accepted is GLSL###
+     * where ### is the version, e.g. GLSL100 = GLSL 1.0, GLSL330 = GLSL 3.3, etc.
+     */
+    public void addSource(ShaderType type, String name, String source, String defines, String language){
+        ShaderSource shaderSource = new ShaderSource(type);
+        shaderSource.setSource(source);
+        shaderSource.setName(name);
+        shaderSource.setLanguage(language);
+        if (defines != null) {
+            shaderSource.setDefines(defines);
+        }
+        shaderSourceList.add(shaderSource);
         setUpdateNeeded();
     }
 
@@ -343,69 +317,73 @@ public final class Shader extends NativeObject implements Savable {
         return attrib;
     }
 
-//    public Collection<Uniform> getUniforms(){
-//        return uniforms.values();
-//    }
-
     public ListMap<String, Uniform> getUniformMap(){
         return uniforms;
     }
 
-//    public Collection<Attribute> getAttributes() {
-//        return attribs.
-//    }
-
     public Collection<ShaderSource> getSources(){
-        return shaderList;
+        return shaderSourceList;
     }
 
+    /**
+     * @deprecated Shaders no longer have a language variable, 
+     * use {@link ShaderSource#getLanguage() } instead.
+     */
+    @Deprecated
     public String getLanguage(){
         return language;
     }
 
     @Override
-    public String toString(){
-        return getClass().getSimpleName() + "[language="+language
-                                           + ", numSources="+shaderList.size()
-                                           + ", numUniforms="+uniforms.size()
-                                           + ", shaderSources="+getSources()+"]";
+    public String toString() {
+        return getClass().getSimpleName() + 
+                "[numSources=" + shaderSourceList.size() +
+                ", numUniforms=" + uniforms.size() +
+                ", shaderSources=" + getSources() + "]";
     }
 
     /**
-     * Clears all sources. Assuming that they have already been detached and
-     * removed on the GL side.
+     * @deprecated This method is not needed since deleting
+     * a shader causes the sources to delete as well, thus its not required
+     * for them to be GC'd to be removed from GL.
      */
+    @Deprecated
     public void resetSources(){
-        shaderList.clear();
+        shaderSourceList.clear();
     }
 
     /**
-     * Returns true if this program and all it's shaders have been compiled,
-     * linked and validated successfuly.
+     * @deprecated Unusable shaders cause the renderer to crash, 
+     * therefore this field no longer serves any purpose.
      */
+    @Deprecated
     public boolean isUsable(){
-        return usable;
+        return true;
     }
 
     /**
-     * Sets if the program can be used. Should only be called by the Renderer.
-     * @param usable
+     * @deprecated Unusable shaders cause the renderer to crash, 
+     * therefore this field no longer serves any purpose.
      */
+    @Deprecated
     public void setUsable(boolean usable){
-        this.usable = usable;
     }
 
     /**
      * Usually called when the shader itself changes or during any
-     * time when the var locations need to be refreshed.
+     * time when the variable locations need to be refreshed.
      */
-    public void resetLocations(){
-        // NOTE: Shader sources will be reset seperately from the shader itself.
-        for (Uniform uniform : uniforms.values()){
-            uniform.reset(); // fixes issue with re-initialization
+    public void resetLocations() {
+        if (uniforms != null) {
+            // NOTE: Shader sources will be reset seperately from the shader itself.
+            for (Uniform uniform : uniforms.values()) {
+                uniform.reset(); // fixes issue with re-initialization
+            }
         }
-        for (Entry<Attribute> entry : attribs){
-            entry.getValue().location = -2;
+        if (attribs != null) {
+            for (Entry<Attribute> entry : attribs) {
+                entry.getValue().location = ShaderVariable.LOC_UNKNOWN;
+            }
         }
     }
 
@@ -422,12 +400,9 @@ public final class Shader extends NativeObject implements Savable {
     @Override
     public void resetObject() {
         this.id = -1;
-        this.usable = false;
-        
-        for (ShaderSource source : shaderList){
+        for (ShaderSource source : shaderSourceList){
             source.resetObject();
         }
-        
         setUpdateNeeded();
     }
 

+ 20 - 7
engine/src/core/com/jme3/shader/ShaderKey.java

@@ -43,16 +43,18 @@ public class ShaderKey extends AssetKey<Shader> {
 
     protected String fragName;
     protected DefineList defines;
-    protected String language;
+    protected String vertLanguage;
+    protected String fragLanguage;
 
     public ShaderKey(){
     }
 
-    public ShaderKey(String vertName, String fragName, DefineList defines, String lang){
+    public ShaderKey(String vertName, String fragName, DefineList defines, String vertLanguage, String fragLanguage){
         super(vertName);
         this.fragName = fragName;
         this.defines = defines;
-        this.language = lang;
+        this.vertLanguage = vertLanguage;
+        this.fragLanguage = fragLanguage;
     }
 
     @Override
@@ -71,7 +73,6 @@ public class ShaderKey extends AssetKey<Shader> {
 
         final ShaderKey other = (ShaderKey) obj;
         if (name.equals(other.name) && fragName.equals(other.fragName)){
-//            return true;
             if (defines != null && other.defines != null)
                 return defines.getCompiled().equals(other.defines.getCompiled());
             else if (defines != null || other.defines != null)
@@ -103,8 +104,20 @@ public class ShaderKey extends AssetKey<Shader> {
         return fragName;
     }
 
+    /**
+     * @deprecated Use {@link #getVertexShaderLanguage() } instead.
+     */
+    @Deprecated
     public String getLanguage() {
-        return language;
+        return vertLanguage;
+    }
+    
+    public String getVertexShaderLanguage() { 
+        return vertLanguage;
+    }
+    
+    public String getFragmentShaderLanguage() {
+        return fragLanguage;
     }
 
     @Override
@@ -112,7 +125,7 @@ public class ShaderKey extends AssetKey<Shader> {
         super.write(ex);
         OutputCapsule oc = ex.getCapsule(this);
         oc.write(fragName, "fragment_name", null);
-        oc.write(language, "language", null);
+        oc.write(vertLanguage, "language", null);
     }
 
     @Override
@@ -120,7 +133,7 @@ public class ShaderKey extends AssetKey<Shader> {
         super.read(im);
         InputCapsule ic = im.getCapsule(this);
         fragName = ic.readString("fragment_name", null);
-        language = ic.readString("language", null);
+        vertLanguage = ic.readString("language", null);
     }
 
 }

+ 5 - 14
engine/src/core/com/jme3/shader/ShaderVariable.java

@@ -32,15 +32,15 @@
 
 package com.jme3.shader;
 
-import com.jme3.export.*;
-import java.io.IOException;
-
-public class ShaderVariable implements Savable {
+public class ShaderVariable {
 
+    public static final int LOC_UNKNOWN = -2,
+                            LOC_NOT_DEFINED = -1;
+    
     // if -2, location not known
     // if -1, not defined in shader
     // if >= 0, uniform defined and available.
-    protected int location = -2;
+    protected int location = LOC_UNKNOWN;
 
     /**
      * Name of the uniform as was declared in the shader.
@@ -54,15 +54,6 @@ public class ShaderVariable implements Savable {
      */
     protected boolean updateNeeded = true;;
 
-    public void write(JmeExporter ex) throws IOException{
-        OutputCapsule oc = ex.getCapsule(this);
-        oc.write(name, "name", null);
-    }
-
-    public void read(JmeImporter im) throws IOException{
-        InputCapsule ic = im.getCapsule(this);
-        name = ic.readString("name", null);
-    }
 
     public void setLocation(int location){
         this.location = location;

+ 27 - 107
engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java

@@ -53,7 +53,10 @@ import com.jme3.texture.FrameBuffer.RenderBuffer;
 import com.jme3.texture.Image;
 import com.jme3.texture.Texture;
 import com.jme3.texture.Texture.WrapAxis;
-import com.jme3.util.*;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.ListMap;
+import com.jme3.util.NativeObjectManager;
+import com.jme3.util.SafeArrayList;
 import java.nio.*;
 import java.util.EnumSet;
 import java.util.List;
@@ -61,7 +64,6 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 import jme3tools.converters.MipMapGenerator;
 import jme3tools.shader.ShaderDebug;
-import org.lwjgl.opengl.*;
 import static org.lwjgl.opengl.ARBTextureMultisample.*;
 import static org.lwjgl.opengl.EXTFramebufferBlit.*;
 import static org.lwjgl.opengl.EXTFramebufferMultisample.*;
@@ -72,6 +74,7 @@ import static org.lwjgl.opengl.GL13.*;
 import static org.lwjgl.opengl.GL14.*;
 import static org.lwjgl.opengl.GL15.*;
 import static org.lwjgl.opengl.GL20.*;
+import org.lwjgl.opengl.*;
 //import static org.lwjgl.opengl.ARBDrawInstanced.*;
 
 public class LwjglRenderer implements Renderer {
@@ -889,7 +892,6 @@ public class LwjglRenderer implements Renderer {
 
     protected void updateShaderUniforms(Shader shader) {
         ListMap<String, Uniform> uniforms = shader.getUniformMap();
-//        for (Uniform uniform : shader.getUniforms()){
         for (int i = 0; i < uniforms.size(); i++) {
             Uniform uniform = uniforms.getValue(i);
             if (uniform.isUpdateNeeded()) {
@@ -900,7 +902,6 @@ public class LwjglRenderer implements Renderer {
 
     protected void resetUniformLocations(Shader shader) {
         ListMap<String, Uniform> uniforms = shader.getUniformMap();
-//        for (Uniform uniform : shader.getUniforms()){
         for (int i = 0; i < uniforms.size(); i++) {
             Uniform uniform = uniforms.getValue(i);
             uniform.reset(); // e.g check location again
@@ -927,10 +928,10 @@ public class LwjglRenderer implements Renderer {
         }
     }
 
-    public void updateShaderSourceData(ShaderSource source, String language) {
+    public void updateShaderSourceData(ShaderSource source) {
         int id = source.getId();
         if (id == -1) {
-            // create id
+            // Create id
             id = glCreateShader(convertShaderType(source.getType()));
             if (id <= 0) {
                 throw new RendererException("Invalid ID received when trying to create shader.");
@@ -941,9 +942,9 @@ public class LwjglRenderer implements Renderer {
             throw new RendererException("Cannot recompile shader source");
         }
 
-        // upload shader source
-        // merge the defines and source code
-
+        // Upload shader source.
+        // Merge the defines and source code.
+        String language = source.getLanguage();
         stringBuf.setLength(0);
         if (language.startsWith("GLSL")) {
             int version = Integer.parseInt(language.substring(4));
@@ -999,6 +1000,7 @@ public class LwjglRenderer implements Renderer {
             } else {
                 logger.log(Level.FINE, "{0} compile success", source.getName());
             }
+            source.clearUpdateNeeded();
         } else {
             logger.log(Level.WARNING, "Bad compile of:\n{0}",
                     new Object[]{ShaderDebug.formatShaderSource(source.getDefines(), source.getSource(), stringBuf.toString())});
@@ -1008,21 +1010,6 @@ public class LwjglRenderer implements Renderer {
                 throw new RendererException("compile error in:" + source + " error: <not provided>");
             }
         }
-
-        source.clearUpdateNeeded();
-        // only usable if compiled
-        source.setUsable(compiledOK);
-        if (!compiledOK) {
-            // make sure to dispose id cause all program's
-            // shaders will be cleared later.
-            glDeleteShader(id);
-        } else {
-            // register for cleanup since the ID is usable
-            // NOTE: From now on cleanup is handled
-            // by the parent shader object so no need
-            // to register.
-            //objManager.registerForCleanup(source);
-        }
     }
 
     public void updateShaderData(Shader shader) {
@@ -1041,15 +1028,7 @@ public class LwjglRenderer implements Renderer {
 
         for (ShaderSource source : shader.getSources()) {
             if (source.isUpdateNeeded()) {
-                updateShaderSourceData(source, shader.getLanguage());
-                // shader has been compiled here
-            }
-
-            if (!source.isUsable()) {
-                // it's useless.. just forget about everything..
-                shader.setUsable(false);
-                shader.clearUpdateNeeded();
-                return;
+                updateShaderSourceData(source);
             }
             glAttachShader(id, source.getId());
         }
@@ -1057,13 +1036,16 @@ public class LwjglRenderer implements Renderer {
         if (caps.contains(Caps.OpenGL30)) {
             // Check if GLSL version is 1.5 for shader
             GL30.glBindFragDataLocation(id, 0, "outFragColor");
+            // For MRT
             for(int i = 0 ; i < maxMRTFBOAttachs ; i++) {
                 GL30.glBindFragDataLocation(id, i, "outFragData[" + i + "]");
             }
         }
 
-        // link shaders to program
+        // Link shaders to program
         glLinkProgram(id);
+        
+        // Check link status
         glGetProgram(id, GL_LINK_STATUS, intBuf1);
         boolean linkOK = intBuf1.get(0) == GL_TRUE;
         String infoLog = null;
@@ -1089,53 +1071,35 @@ public class LwjglRenderer implements Renderer {
             } else {
                 logger.fine("shader link success");
             }
-        } else {
-            if (infoLog != null) {
-                throw new RendererException("Shader link failure, shader:" + shader + " info:" + infoLog);
-            } else {
-                throw new RendererException("Shader link failure, shader:" + shader + " info: <not provided>");
-            }
-        }
-
-        shader.clearUpdateNeeded();
-        if (!linkOK) {
-            // failure.. forget about everything
-            shader.resetSources();
-            shader.setUsable(false);
-            deleteShader(shader);
-        } else {
-            shader.setUsable(true);
+            shader.clearUpdateNeeded();
             if (needRegister) {
+                // Register shader for clean up if it was created in this method.
                 objManager.registerForCleanup(shader);
                 statistics.onNewShader();
             } else {
                 // OpenGL spec: uniform locations may change after re-link
                 resetUniformLocations(shader);
             }
+        } else {
+            if (infoLog != null) {
+                throw new RendererException("Shader link failure, shader:" + shader + " info:" + infoLog);
+            } else {
+                throw new RendererException("Shader link failure, shader:" + shader + " info: <not provided>");
+            }
         }
     }
 
     public void setShader(Shader shader) {
         if (shader == null) {
-            throw new IllegalArgumentException("shader cannot be null");
-//            if (context.boundShaderProgram > 0) {
-//                glUseProgram(0);
-//                statistics.onShaderUse(null, true);
-//                context.boundShaderProgram = 0;
-//                boundShader = null;
-//            }
+            throw new IllegalArgumentException("Shader cannot be null");
         } else {
             if (shader.isUpdateNeeded()) {
                 updateShaderData(shader);
             }
-
+            
             // NOTE: might want to check if any of the
             // sources need an update?
 
-            if (!shader.isUsable()) {
-                return;
-            }
-
             assert shader.getId() > 0;
 
             updateShaderUniforms(shader);
@@ -1148,7 +1112,6 @@ public class LwjglRenderer implements Renderer {
             logger.warning("Shader source is not uploaded to GPU, cannot delete.");
             return;
         }
-        source.setUsable(false);
         source.clearUpdateNeeded();
         glDeleteShader(source.getId());
         source.resetObject();
@@ -1167,12 +1130,9 @@ public class LwjglRenderer implements Renderer {
             }
         }
 
-        // kill all references so sources can be collected
-        // if needed.
-        shader.resetSources();
         glDeleteProgram(shader.getId());
-
         statistics.onDeleteShader();
+        shader.resetObject();
     }
 
     /*********************************************************************\
@@ -2116,46 +2076,6 @@ public class LwjglRenderer implements Renderer {
                     throw new UnsupportedOperationException("Unknown buffer format.");
             }
         }
-//        }else{
-//            if (created || vb.hasDataSizeChanged()){
-//                glBufferData(target, vb.getData().capacity() * vb.getFormat().getComponentSize(), usage);
-//            }
-//
-//            ByteBuffer buf = glMapBuffer(target,
-//                                         GL_WRITE_ONLY,
-//                                         vb.getMappedData());
-//
-//            if (buf != vb.getMappedData()){
-//                buf = buf.order(ByteOrder.nativeOrder());
-//                vb.setMappedData(buf);
-//            }
-//
-//            buf.clear();
-//
-//            switch (vb.getFormat()){
-//                case Byte:
-//                case UnsignedByte:
-//                    buf.put( (ByteBuffer) vb.getData() );
-//                    break;
-//                case Short:
-//                case UnsignedShort:
-//                    buf.asShortBuffer().put( (ShortBuffer) vb.getData() );
-//                    break;
-//                case Int:
-//                case UnsignedInt:
-//                    buf.asIntBuffer().put( (IntBuffer) vb.getData() );
-//                    break;
-//                case Float:
-//                    buf.asFloatBuffer().put( (FloatBuffer) vb.getData() );
-//                    break;
-//                case Double:
-//                    break;
-//                default:
-//                    throw new RuntimeException("Unknown buffer format.");
-//            }
-//
-//            glUnmapBuffer(target);
-//        }
 
         vb.clearUpdateNeeded();
     }