/** * Copyright 2010 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ package com.jogamp.opengl.util.glsl; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; import javax.media.opengl.GLArrayData; import javax.media.opengl.GLContext; import javax.media.opengl.GLException; import javax.media.opengl.GLUniformData; import jogamp.opengl.Debug; import com.jogamp.common.os.Platform; import com.jogamp.common.util.IntObjectHashMap; import com.jogamp.opengl.util.GLArrayDataEditable; public class ShaderState { public static final boolean DEBUG = Debug.isPropertyDefined("jogl.debug.GLSLState", true); private static final String currentStateKey = "jogamp.opengl.glsl.ShaderState" ; public ShaderState() { } public boolean verbose() { return verbose; } public void setVerbose(boolean v) { verbose=v; } /** * Fetches the current shader state from this thread (TLS) current GLContext * * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) * @see com.jogamp.opengl.util.glsl.ShaderState#getShaderState(GL) * @see com.jogamp.opengl.util.glsl.ShaderState#setShaderState(GL) * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() */ public static ShaderState getCurrentShaderState() { return getShaderState(GLContext.getCurrentGL()); } /** * Gets the shader state attached to the GL object's GLContext * * @param gl the GL object referencing the GLContext * * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) * @see com.jogamp.opengl.util.glsl.ShaderState#getShaderState(GL) * @see com.jogamp.opengl.util.glsl.ShaderState#setShaderState(GL) * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() */ public static ShaderState getShaderState(GL gl) { return (ShaderState) gl.getContext().getAttachedObject(currentStateKey); } /** * Attaches the shader state to the GL object's GLContext * * @param gl the GL object referencing the GLContext * * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) * @see com.jogamp.opengl.util.glsl.ShaderState#getShaderState(GL) * @see com.jogamp.opengl.util.glsl.ShaderState#setShaderState(GL) * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() */ public final ShaderState setShaderState(GL gl) { return (ShaderState) gl.getContext().attachObject(currentStateKey, this); } /** * Returns the attached user object for the given name to this ShaderState. */ public final Object getAttachedObject(String name) { return attachedObjectsByString.get(name); } /** * Attach user object for the given name to this ShaderState. * Returns the previously set object or null. * * @return the previous mapped object or null if none */ public final Object attachObject(String name, Object obj) { return attachedObjectsByString.put(name, obj); } /** * @param name name of the mapped object to detach * * @return the previous mapped object or null if none */ public final Object detachObject(String name) { return attachedObjectsByString.remove(name); } /** * Returns the attached user object for the given name to this ShaderState. */ public final Object getAttachedObject(int name) { return attachedObjectsByInt.get(name); } /** * Attach user object for the given name to this ShaderState. * Returns the previously set object or null. */ public final Object attachObject(int name, Object obj) { return attachedObjectsByInt.put(name, obj); } public final Object detachObject(int name) { return attachedObjectsByInt.remove(name); } /** * Turns the shader program on or off.
* Puts this ShaderState to to the thread local storage (TLS), * if on is true. * * @throws GLException if no program is attached * * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) * @see com.jogamp.opengl.util.glsl.ShaderState#getShaderState(GL) * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() */ public synchronized void useProgram(GL2ES2 gl, boolean on) throws GLException { if(null==shaderProgram) { throw new GLException("No program is attached"); } if(on) { setShaderState(gl); if(shaderProgram.linked()) { shaderProgram.useProgram(gl, true); if(resetAllShaderData) { resetAllAttributes(gl); resetAllUniforms(gl); } } else { if(resetAllShaderData) { setAllAttributes(gl); } if(!shaderProgram.link(gl, System.err)) { throw new GLException("could not link program: "+shaderProgram); } shaderProgram.useProgram(gl, true); if(resetAllShaderData) { resetAllUniforms(gl); } } resetAllShaderData = false; } else { shaderProgram.useProgram(gl, false); } } public boolean linked() { return (null!=shaderProgram)?shaderProgram.linked():false; } public boolean inUse() { return (null!=shaderProgram)?shaderProgram.inUse():false; } /** * Attach or switch a shader program * *

Attaching a shader program the first time, * as well as switching to another program on the fly, * while managing all attribute and uniform data.

* *

[Re]sets all data and use program in case of a program switch.

* *

Use program, {@link #useProgram(GL2ES2, boolean)}, * if enable is true.

* * @throws GLException if program was not linked and linking fails */ public synchronized void attachShaderProgram(GL2ES2 gl, ShaderProgram prog, boolean enable) throws GLException { if(DEBUG) { int curId = (null!=shaderProgram)?shaderProgram.id():-1; int newId = (null!=prog)?prog.id():-1; System.err.println("Info: attachShaderProgram: "+curId+" -> "+newId+" (enable: "+enable+")\n\t"+shaderProgram+"\n\t"+prog); if(verbose) { Throwable tX = new Throwable("Info: attachShaderProgram: Trace"); tX.printStackTrace(); } } if(null!=shaderProgram) { if(shaderProgram.equals(prog)) { // nothing to do .. if(DEBUG) { System.err.println("Info: attachShaderProgram: NOP: equal id: "+shaderProgram.id()); } return; } if(shaderProgram.inUse()) { if(null != prog && enable) { // new program will issue glUseProgram(..) shaderProgram.programInUse = false; } else { // no new 'enabled' program - disable useProgram(gl, false); } } resetAllShaderData = true; } // register new one shaderProgram = prog; if(null!=shaderProgram) { // [re]set all data and use program if switching program, // or use program if program is linked if(resetAllShaderData || enable) { useProgram(gl, true); // may reset all data if(!enable) { useProgram(gl, false); } } } if(DEBUG) { System.err.println("Info: attachShaderProgram: END"); } } public ShaderProgram shaderProgram() { return shaderProgram; } /** * Calls {@link #release(GL2ES2, boolean, boolean, boolean) release(gl, true, true, true)} * * @see #glReleaseAllVertexAttributes * @see #glReleaseAllUniforms * @see #release(GL2ES2, boolean, boolean, boolean) */ public synchronized void destroy(GL2ES2 gl) { release(gl, true, true, true); attachedObjectsByString.clear(); attachedObjectsByInt.clear(); } /** * Calls {@link #release(GL2ES2, boolean, boolean, boolean) release(gl, false, false, false)} * * @see #glReleaseAllVertexAttributes * @see #glReleaseAllUniforms * @see #release(GL2ES2, boolean, boolean, boolean) */ public synchronized void releaseAllData(GL2ES2 gl) { release(gl, false, false, false); } /** * @see #glReleaseAllVertexAttributes * @see #glReleaseAllUniforms * @see ShaderProgram#release(GL2ES2, boolean) */ public synchronized void release(GL2ES2 gl, boolean destroyBoundAttributes, boolean destroyShaderProgram, boolean destroyShaderCode) { if(null!=shaderProgram) { shaderProgram.useProgram(gl, false); } if(destroyBoundAttributes) { for(Iterator iter = managedAttributes.iterator(); iter.hasNext(); ) { iter.next().destroy(gl); } } releaseAllAttributes(gl); releaseAllUniforms(gl); if(null!=shaderProgram && destroyShaderProgram) { shaderProgram.release(gl, destroyShaderCode); } } // // Shader attribute handling // /** * Gets the cached location of a shader attribute. * * @return -1 if there is no such attribute available, * otherwise >= 0 * * @see #bindAttribLocation(GL2ES2, int, String) * @see #bindAttribLocation(GL2ES2, int, GLArrayData) * @see #getAttribLocation(GL2ES2, String) * @see GL2ES2#glGetAttribLocation(int, String) */ public int getCachedAttribLocation(String name) { Integer idx = activeAttribLocationMap.get(name); return (null!=idx)?idx.intValue():-1; } /** * Get the previous cached vertex attribute data. * * @return the GLArrayData object, null if not previously set. * * @see #ownAttribute(GLArrayData, boolean) * * @see #glEnableVertexAttribArray * @see #glDisableVertexAttribArray * @see #glVertexAttribPointer * @see #getVertexAttribPointer * @see #glReleaseAllVertexAttributes * @see #glResetAllVertexAttributes * @see ShaderProgram#glReplaceShader */ public GLArrayData getAttribute(String name) { return activeAttribDataMap.get(name); } /** * Binds or unbinds the {@link GLArrayData} lifecycle to this ShaderState. * *

If an attribute location is cached (ie {@link #bindAttribLocation(GL2ES2, int, String)}) * it is promoted to the {@link GLArrayData} instance.

* *

The attribute will be destroyed with {@link #destroy(GL2ES2)} * and it's location will be reset when switching shader with {@link #attachShaderProgram(GL2ES2, ShaderProgram)}.

* *

The data will not be transfered to the GPU, use {@link #vertexAttribPointer(GL2ES2, GLArrayData)} additionally.

* * @param attribute the {@link GLArrayData} which lifecycle shall be managed * @param own true if owning shall be performs, false if disowning. * * @see #bindAttribLocation(GL2ES2, int, String) * @see #getAttribute(String) */ public void ownAttribute(GLArrayData attribute, boolean own) { if(own) { final int location = getCachedAttribLocation(attribute.getName()); if(0<=location) { attribute.setLocation(location); } managedAttributes.add(managedAttributes.size(), attribute); } else { managedAttributes.remove(attribute); } } public boolean ownsAttribute(GLArrayData attribute) { return managedAttributes.contains(attribute); } /** * Binds a shader attribute to a location. * Multiple names can be bound to one location. * The value will be cached and can be retrieved via {@link #getCachedAttribLocation(String)} * before or after linking. * * @throws GLException if no program is attached * @throws GLException if the program is already linked * * @see javax.media.opengl.GL2ES2#glBindAttribLocation(int, int, String) * @see #getAttribLocation(GL2ES2, String) * @see #getCachedAttribLocation(String) */ public void bindAttribLocation(GL2ES2 gl, int location, String name) { if(null==shaderProgram) throw new GLException("No program is attached"); if(shaderProgram.linked()) throw new GLException("Program is already linked"); final Integer loc = new Integer(location); activeAttribLocationMap.put(name, loc); gl.glBindAttribLocation(shaderProgram.program(), location, name); } /** * Binds a shader {@link GLArrayData} attribute to a location. * Multiple names can be bound to one location. * The value will be cached and can be retrieved via {@link #getCachedAttribLocation(String)} * and {@link #getAttribute(String)}before or after linking. * The {@link GLArrayData}'s location will be set as well. * * @throws GLException if no program is attached * @throws GLException if the program is already linked * * @see javax.media.opengl.GL2ES2#glBindAttribLocation(int, int, String) * @see #getAttribLocation(GL2ES2, String) * @see #getCachedAttribLocation(String) * @see #getAttribute(String) */ public void bindAttribLocation(GL2ES2 gl, int location, GLArrayData data) { bindAttribLocation(gl, location, data.getName()); data.setLocation(location); activeAttribDataMap.put(data.getName(), data); } /** * Gets the location of a shader attribute.
* Uses either the cached value {@link #getCachedAttribLocation(String)} if valid, * or the GLSL queried via {@link GL2ES2#glGetAttribLocation(int, String)}.
* The location will be cached. * * @return -1 if there is no such attribute available, * otherwise >= 0 * @throws GLException if no program is attached * @throws GLException if the program is not linked and no location was cached. * * @see #getCachedAttribLocation(String) * @see #bindAttribLocation(GL2ES2, int, GLArrayData) * @see #bindAttribLocation(GL2ES2, int, String) * @see GL2ES2#glGetAttribLocation(int, String) */ public int getAttribLocation(GL2ES2 gl, String name) { if(null==shaderProgram) throw new GLException("No program is attached"); int location = getCachedAttribLocation(name); if(0>location) { if(!shaderProgram.linked()) throw new GLException("Program is not linked"); location = gl.glGetAttribLocation(shaderProgram.program(), name); if(0<=location) { Integer idx = new Integer(location); activeAttribLocationMap.put(name, idx); if(DEBUG) { System.err.println("Info: glGetAttribLocation: "+name+", loc: "+location); } } else if(verbose) { Throwable tX = new Throwable("Info: glGetAttribLocation failed, no location for: "+name+", loc: "+location); tX.printStackTrace(); } } return location; } /** * Validates and returns the location of a shader attribute.
* Uses either the cached value {@link #getCachedAttribLocation(String)} if valid, * or the GLSL queried via {@link GL2ES2#glGetAttribLocation(int, String)}.
* The location will be cached and set in the * {@link GLArrayData} object. * * @return -1 if there is no such attribute available, * otherwise >= 0 * * @throws GLException if no program is attached * @throws GLException if the program is not linked and no location was cached. * * @see #getCachedAttribLocation(String) * @see #bindAttribLocation(GL2ES2, int, GLArrayData) * @see #bindAttribLocation(GL2ES2, int, String) * @see GL2ES2#glGetAttribLocation(int, String) * @see #getAttribute(String) */ public int getAttribLocation(GL2ES2 gl, GLArrayData data) { int location = getAttribLocation(gl, data.getName()); data.setLocation(location); activeAttribDataMap.put(data.getName(), data); return location; } // // Enabled Vertex Arrays and its data // /** * @return true if the named attribute is enable */ public final boolean isVertexAttribArrayEnabled(String name) { final Boolean v = activedAttribEnabledMap.get(name); return null != v && v.booleanValue(); } /** * @return true if the {@link GLArrayData} attribute is enable */ public final boolean isVertexAttribArrayEnabled(GLArrayData data) { return isVertexAttribArrayEnabled(data.getName()); } private boolean enableVertexAttribArray(GL2ES2 gl, String name, int location) { activedAttribEnabledMap.put(name, Boolean.TRUE); if(0>location) { location = getAttribLocation(gl, name); if(0>location) { if(verbose) { Throwable tX = new Throwable("Info: glEnableVertexAttribArray failed, no index for: "+name); tX.printStackTrace(); } return false; } } if(DEBUG) { System.err.println("Info: glEnableVertexAttribArray: "+name+", loc: "+location); } gl.glEnableVertexAttribArray(location); return true; } /** * Enables a vertex attribute array. * * This method retrieves the the location via {@link #getAttribLocation(GL2ES2, GLArrayData)} * hence {@link #enableVertexAttribArray(GL2ES2, GLArrayData)} shall be preferred. * * Even if the attribute is not found in the current shader, * it is marked enabled in this state. * * @return false, if the name is not found, otherwise true * * @throws GLException if the program is not linked and no location was cached. * * @see #glEnableVertexAttribArray * @see #glDisableVertexAttribArray * @see #glVertexAttribPointer * @see #getVertexAttribPointer */ public boolean enableVertexAttribArray(GL2ES2 gl, String name) { return enableVertexAttribArray(gl, name, -1); } /** * Enables a vertex attribute array, usually invoked by {@link GLArrayDataEditable#enableBuffer(GL, boolean)}. * * This method uses the {@link GLArrayData}'s location if set * and is the preferred alternative to {@link #enableVertexAttribArray(GL2ES2, String)}. * If data location is unset it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)} set * and cached in this state. * * Even if the attribute is not found in the current shader, * it is marked enabled in this state. * * @return false, if the name is not found, otherwise true * * @throws GLException if the program is not linked and no location was cached. * * @see #glEnableVertexAttribArray * @see #glDisableVertexAttribArray * @see #glVertexAttribPointer * @see #getVertexAttribPointer * @see GLArrayDataEditable#enableBuffer(GL, boolean) */ public boolean enableVertexAttribArray(GL2ES2 gl, GLArrayData data) { if(0 > data.getLocation()) { getAttribLocation(gl, data); } else { // ensure data is the current bound one activeAttribDataMap.put(data.getName(), data); } return enableVertexAttribArray(gl, data.getName(), data.getLocation()); } private boolean disableVertexAttribArray(GL2ES2 gl, String name, int location) { activedAttribEnabledMap.put(name, Boolean.FALSE); if(0>location) { location = getAttribLocation(gl, name); if(0>location) { if(verbose) { Throwable tX = new Throwable("Info: glDisableVertexAttribArray failed, no index for: "+name); tX.printStackTrace(); } return false; } } if(DEBUG) { System.err.println("Info: glDisableVertexAttribArray: "+name); } gl.glDisableVertexAttribArray(location); return true; } /** * Disables a vertex attribute array * * This method retrieves the the location via {@link #getAttribLocation(GL2ES2, GLArrayData)} * hence {@link #disableVertexAttribArray(GL2ES2, GLArrayData)} shall be preferred. * * Even if the attribute is not found in the current shader, * it is removed from this state enabled list. * * @return false, if the name is not found, otherwise true * * @throws GLException if no program is attached * @throws GLException if the program is not linked and no location was cached. * * @see #glEnableVertexAttribArray * @see #glDisableVertexAttribArray * @see #glVertexAttribPointer * @see #getVertexAttribPointer */ public boolean disableVertexAttribArray(GL2ES2 gl, String name) { return disableVertexAttribArray(gl, name, -1); } /** * Disables a vertex attribute array * * This method uses the {@link GLArrayData}'s location if set * and is the preferred alternative to {@link #disableVertexAttribArray(GL2ES2, String)}. * If data location is unset it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)} set * and cached in this state. * * Even if the attribute is not found in the current shader, * it is removed from this state enabled list. * * @return false, if the name is not found, otherwise true * * @throws GLException if no program is attached * @throws GLException if the program is not linked and no location was cached. * * @see #glEnableVertexAttribArray * @see #glDisableVertexAttribArray * @see #glVertexAttribPointer * @see #getVertexAttribPointer */ public boolean disableVertexAttribArray(GL2ES2 gl, GLArrayData data) { if(0 > data.getLocation()) { getAttribLocation(gl, data); } return disableVertexAttribArray(gl, data.getName(), data.getLocation()); } /** * Set the {@link GLArrayData} vertex attribute data. * * This method uses the {@link GLArrayData}'s location if set. * If data location is unset it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)}, set * and cached in this state. * * @return false, if the location could not be determined, otherwise true * * @throws GLException if no program is attached * @throws GLException if the program is not linked and no location was cached. * * @see #glEnableVertexAttribArray * @see #glDisableVertexAttribArray * @see #glVertexAttribPointer * @see #getVertexAttribPointer */ public boolean vertexAttribPointer(GL2ES2 gl, GLArrayData data) { int location = data.getLocation(); if(0 > location) { location = getAttribLocation(gl, data); } if(0 <= location) { // only pass the data, if the attribute exists in the current shader if(DEBUG) { System.err.println("Info: glVertexAttribPointer: "+data); } gl.glVertexAttribPointer(data); return true; } return false; } /** * Releases all mapped vertex attribute data, * disables all enabled attributes and loses all indices * * @see #glEnableVertexAttribArray * @see #glDisableVertexAttribArray * @see #glVertexAttribPointer * @see #getVertexAttribPointer * @see #glReleaseAllVertexAttributes * @see #glResetAllVertexAttributes * @see #glResetAllVertexAttributes * @see ShaderProgram#glReplaceShader */ public void releaseAllAttributes(GL2ES2 gl) { if(null!=shaderProgram) { for(Iterator iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { if(!disableVertexAttribArray(gl, iter.next())) { throw new GLException("Internal Error: mapped vertex attribute couldn't be disabled"); } } for(Iterator iter = activedAttribEnabledMap.keySet().iterator(); iter.hasNext(); ) { if(!disableVertexAttribArray(gl, iter.next())) { throw new GLException("Internal Error: prev enabled vertex attribute couldn't be disabled"); } } } activeAttribDataMap.clear(); activedAttribEnabledMap.clear(); activeAttribLocationMap.clear(); managedAttributes.clear(); } /** * Disables all vertex attribute arrays. * * Their enabled stated will be removed from this state only * if 'removeFromState' is true. * * This method purpose is more for debugging. * * @see #glEnableVertexAttribArray * @see #glDisableVertexAttribArray * @see #glVertexAttribPointer * @see #getVertexAttribPointer * @see #glReleaseAllVertexAttributes * @see #glResetAllVertexAttributes * @see #glResetAllVertexAttributes * @see ShaderProgram#glReplaceShader */ public void disableAllVertexAttributeArrays(GL2ES2 gl, boolean removeFromState) { for(Iterator iter = activedAttribEnabledMap.keySet().iterator(); iter.hasNext(); ) { final String name = iter.next(); if(removeFromState) { activedAttribEnabledMap.remove(name); } final int index = getAttribLocation(gl, name); if(0<=index) { gl.glDisableVertexAttribArray(index); } } } private final void relocateAttribute(GL2ES2 gl, GLArrayData attribute) { // get new location .. final String name = attribute.getName(); final int loc = getAttribLocation(gl, name); attribute.setLocation(loc); if(0<=loc) { if(isVertexAttribArrayEnabled(name)) { // enable attrib, VBO and pass location/data gl.glEnableVertexAttribArray(loc); } if( attribute.isVBO() ) { gl.glBindBuffer(GL.GL_ARRAY_BUFFER, attribute.getVBOName()); } gl.glVertexAttribPointer(attribute); } } /** * Reset all previously enabled mapped vertex attribute data. * *

Attribute data is bound to the GL state

*

Attribute location is bound to the program

* *

However, since binding an attribute to a location via {@link #bindAttribLocation(GL2ES2, int, GLArrayData)} * must happen before linking and we try to promote the attributes to the new program, * we have to gather the probably new location etc.

* * @throws GLException is the program is not linked * * @see #attachShaderProgram(GL2ES2, ShaderProgram) */ private final void resetAllAttributes(GL2ES2 gl) { if(!shaderProgram.linked()) throw new GLException("Program is not linked"); activeAttribLocationMap.clear(); for(Iterator iter = managedAttributes.iterator(); iter.hasNext(); ) { iter.next().setLocation(-1); } for(Iterator iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { relocateAttribute(gl, iter.next()); } } private final void setAttribute(GL2ES2 gl, GLArrayData attribute) { // get new location .. final String name = attribute.getName(); final int loc = attribute.getLocation(); if(0<=loc) { this.bindAttribLocation(gl, loc, name); if(isVertexAttribArrayEnabled(name)) { // enable attrib, VBO and pass location/data gl.glEnableVertexAttribArray(loc); } if( attribute.isVBO() ) { gl.glBindBuffer(GL.GL_ARRAY_BUFFER, attribute.getVBOName()); } gl.glVertexAttribPointer(attribute); } } /** * preserves the attribute location .. (program not linked) */ private final void setAllAttributes(GL2ES2 gl) { for(Iterator iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { setAttribute(gl, iter.next()); } } // // Shader Uniform handling // /** * Gets the cached location of the shader uniform. * * @return -1 if there is no such uniform available, * otherwise >= 0 */ public final int getCachedUniformLocation(String name) { Integer idx = (Integer) activeUniformLocationMap.get(name); return (null!=idx)?idx.intValue():-1; } /** * Bind the {@link GLUniform} lifecycle to this ShaderState. * *

If a uniform location is cached it is promoted to the {@link GLUniformData} instance.

* *

The attribute will be destroyed with {@link #destroy(GL2ES2)} * and it's location will be reset when switching shader with {@link #attachShaderProgram(GL2ES2, ShaderProgram)}.

* *

The data will not be transfered to the GPU, use {@link #uniform(GL2ES2, GLUniformData)} additionally.

* * @param uniform the {@link GLUniformData} which lifecycle shall be managed * * @see #getUniform(String) */ public void ownUniform(GLUniformData uniform) { final int location = getCachedUniformLocation(uniform.getName()); if(0<=location) { uniform.setLocation(location); } activeUniformDataMap.put(uniform.getName(), uniform); managedUniforms.add(uniform); } public boolean ownsUniform(GLUniformData uniform) { return managedUniforms.contains(uniform); } /** * Gets the location of a shader uniform.
* Uses either the cached value {@link #getCachedUniformLocation(String)} if valid, * or the GLSL queried via {@link GL2ES2#glGetUniformLocation(int, String)}.
* The location will be cached. *

* The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)}) * must be in use ({@link #useProgram(GL2ES2, boolean) }) !

* * @return -1 if there is no such attribute available, * otherwise >= 0 * @throws GLException is the program is not linked * * @see #glGetUniformLocation * @see javax.media.opengl.GL2ES2#glGetUniformLocation * @see #getUniformLocation * @see ShaderProgram#glReplaceShader */ public final int getUniformLocation(GL2ES2 gl, String name) { if(!shaderProgram.inUse()) throw new GLException("Program is not in use"); int location = getCachedUniformLocation(name); if(0>location) { location = gl.glGetUniformLocation(shaderProgram.program(), name); if(0<=location) { Integer idx = new Integer(location); activeUniformLocationMap.put(name, idx); } else if(verbose) { Throwable tX = new Throwable("Info: glUniform failed, no location for: "+name+", index: "+location); tX.printStackTrace(); } } return location; } /** * Validates and returns the location of a shader uniform.
* Uses either the cached value {@link #getCachedUniformLocation(String)} if valid, * or the GLSL queried via {@link GL2ES2#glGetUniformLocation(int, String)}.
* The location will be cached and set in the * {@link GLUniformData} object. *

* The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)}) * must be in use ({@link #useProgram(GL2ES2, boolean) }) !

* * @return -1 if there is no such attribute available, * otherwise >= 0 * @throws GLException is the program is not linked * * @see #glGetUniformLocation * @see javax.media.opengl.GL2ES2#glGetUniformLocation * @see #getUniformLocation * @see ShaderProgram#glReplaceShader */ public int getUniformLocation(GL2ES2 gl, GLUniformData data) { int location = getUniformLocation(gl, data.getName()); data.setLocation(location); activeUniformDataMap.put(data.getName(), data); return location; } /** * Set the uniform data. * * Even if the uniform is not found in the current shader, * it is stored in this state. * * @param data the GLUniforms's name must match the uniform one, * it's index will be set with the uniforms's location, * if found. * * * @return false, if the name is not found, otherwise true * * @throws GLException if the program is not in use * * @see #glGetUniformLocation * @see javax.media.opengl.GL2ES2#glGetUniformLocation * @see javax.media.opengl.GL2ES2#glUniform * @see #getUniformLocation * @see ShaderProgram#glReplaceShader */ public boolean uniform(GL2ES2 gl, GLUniformData data) { if(!shaderProgram.inUse()) throw new GLException("Program is not in use"); int location = data.getLocation(); if(0>location) { location = getUniformLocation(gl, data); } if(0<=location) { // only pass the data, if the uniform exists in the current shader if(DEBUG) { System.err.println("Info: glUniform: "+data); } gl.glUniform(data); } return true; } /** * Get the uniform data, previously set. * * @return the GLUniformData object, null if not previously set. */ public GLUniformData getUniform(String name) { return activeUniformDataMap.get(name); } /** * Releases all mapped uniform data * and loses all indices */ public void releaseAllUniforms(GL2ES2 gl) { activeUniformDataMap.clear(); activeUniformLocationMap.clear(); managedUniforms.clear(); } /** * Reset all previously mapped uniform data * * Uniform data and location is bound to the program, * hence both are updated here * * @throws GLException is the program is not in use * * @see #attachShaderProgram(GL2ES2, ShaderProgram) */ private final void resetAllUniforms(GL2ES2 gl) { if(!shaderProgram.inUse()) throw new GLException("Program is not in use"); activeUniformLocationMap.clear(); for(Iterator iter = managedUniforms.iterator(); iter.hasNext(); ) { iter.next().setLocation(-1); } for(Iterator iter = activeUniformDataMap.values().iterator(); iter.hasNext(); ) { final GLUniformData uniform = iter.next(); uniform.setLocation(-1); uniform(gl, uniform); } } public StringBuilder toString(StringBuilder sb) { if(null==sb) { sb = new StringBuilder(); } sb.append("ShaderState[ "); sb.append(Platform.getNewline()).append(" "); if(null != shaderProgram) { shaderProgram.toString(sb); } else { sb.append("ShaderProgram: null"); } sb.append(Platform.getNewline()).append(" enabledAttributes ["); { Iterator names = activedAttribEnabledMap.keySet().iterator(); Iterator values = activedAttribEnabledMap.values().iterator(); while( names.hasNext() ) { sb.append(Platform.getNewline()).append(" ").append(names.next()).append(": ").append(values.next()); } } sb.append(Platform.getNewline()).append(" ],").append(" activeAttributes ["); for(Iterator iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { sb.append(Platform.getNewline()).append(" ").append(iter.next()); } sb.append(Platform.getNewline()).append(" ],").append(" managedAttributes ["); for(Iterator iter = managedAttributes.iterator(); iter.hasNext(); ) { sb.append(Platform.getNewline()).append(" ").append(iter.next()); } sb.append(Platform.getNewline()).append(" ],").append(" activeUniforms ["); for(Iterator iter=activeUniformDataMap.values().iterator(); iter.hasNext(); ) { sb.append(Platform.getNewline()).append(" ").append(iter.next()); } sb.append(Platform.getNewline()).append(" ],").append(" managedUniforms ["); for(Iterator iter = managedUniforms.iterator(); iter.hasNext(); ) { sb.append(Platform.getNewline()).append(" ").append(iter.next()); } sb.append(Platform.getNewline()).append(" ]").append(Platform.getNewline()).append("]"); return sb; } @Override public String toString() { return toString(null).toString(); } private boolean verbose = DEBUG ? true : false; private ShaderProgram shaderProgram=null; private HashMap activedAttribEnabledMap = new HashMap(); private HashMap activeAttribLocationMap = new HashMap(); private HashMap activeAttribDataMap = new HashMap(); private ArrayList managedAttributes = new ArrayList(); private HashMap activeUniformLocationMap = new HashMap(); private HashMap activeUniformDataMap = new HashMap(); private ArrayList managedUniforms = new ArrayList(); private HashMap attachedObjectsByString = new HashMap(); private IntObjectHashMap attachedObjectsByInt = new IntObjectHashMap(); private boolean resetAllShaderData = false; }