/** * Copyright (c) 2012, Matt DesLauriers All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * 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. * * * Neither the name of the Matt DesLauriers nor the names * of his contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER 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. */ package mdesl.graphics.glutils; import static org.lwjgl.opengl.GL20.GL_ACTIVE_ATTRIBUTES; import static org.lwjgl.opengl.GL20.GL_ACTIVE_ATTRIBUTE_MAX_LENGTH; import static org.lwjgl.opengl.GL20.GL_ACTIVE_UNIFORMS; import static org.lwjgl.opengl.GL20.GL_ACTIVE_UNIFORM_MAX_LENGTH; import static org.lwjgl.opengl.GL20.GL_COMPILE_STATUS; import static org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER; import static org.lwjgl.opengl.GL20.GL_INFO_LOG_LENGTH; import static org.lwjgl.opengl.GL20.GL_LINK_STATUS; import static org.lwjgl.opengl.GL20.GL_VERTEX_SHADER; import static org.lwjgl.opengl.GL20.glAttachShader; import static org.lwjgl.opengl.GL20.glBindAttribLocation; import static org.lwjgl.opengl.GL20.glCompileShader; import static org.lwjgl.opengl.GL20.glCreateProgram; import static org.lwjgl.opengl.GL20.glCreateShader; import static org.lwjgl.opengl.GL20.glDeleteProgram; import static org.lwjgl.opengl.GL20.glDeleteShader; import static org.lwjgl.opengl.GL20.glDetachShader; import static org.lwjgl.opengl.GL20.glGetActiveAttrib; import static org.lwjgl.opengl.GL20.glGetActiveAttribSize; import static org.lwjgl.opengl.GL20.glGetActiveAttribType; import static org.lwjgl.opengl.GL20.glGetActiveUniform; import static org.lwjgl.opengl.GL20.glGetAttribLocation; import static org.lwjgl.opengl.GL20.glGetProgramInfoLog; import static org.lwjgl.opengl.GL20.glGetProgrami; import static org.lwjgl.opengl.GL20.glGetShaderInfoLog; import static org.lwjgl.opengl.GL20.glGetShaderi; import static org.lwjgl.opengl.GL20.glGetUniform; import static org.lwjgl.opengl.GL20.glGetUniformLocation; import static org.lwjgl.opengl.GL20.glLinkProgram; import static org.lwjgl.opengl.GL20.glShaderSource; import static org.lwjgl.opengl.GL20.glUniform1f; import static org.lwjgl.opengl.GL20.glUniform1i; import static org.lwjgl.opengl.GL20.glUniform2f; import static org.lwjgl.opengl.GL20.glUniform2i; import static org.lwjgl.opengl.GL20.glUniform3f; import static org.lwjgl.opengl.GL20.glUniform3i; import static org.lwjgl.opengl.GL20.glUniform4f; import static org.lwjgl.opengl.GL20.glUniform4i; import static org.lwjgl.opengl.GL20.glUniformMatrix3; import static org.lwjgl.opengl.GL20.glUniformMatrix4; import static org.lwjgl.opengl.GL20.glUseProgram; import static org.lwjgl.opengl.GL32.GL_GEOMETRY_SHADER; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.HashMap; import java.util.List; import mdesl.test.Util; import org.lwjgl.BufferUtils; import org.lwjgl.LWJGLException; import org.lwjgl.opengl.ContextCapabilities; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GLContext; import org.lwjgl.util.vector.Matrix3f; import org.lwjgl.util.vector.Matrix4f; import org.lwjgl.util.vector.Vector2f; import org.lwjgl.util.vector.Vector3f; import org.lwjgl.util.vector.Vector4f; /** A complete ShaderProgram utility wrapper. * * @author davedes */ public class ShaderProgram { private static FloatBuffer fbuf16; private static IntBuffer ibuf4; // a simple struct for attrib data; ideally we should find the // component count to utilize our VertexAttrib class protected static class Attrib { String name = null; int type = -1; int size = 0; // for arrays int location = -1; } /** The vertex shader type (GL_VERTEX_SHADER). */ public static final int VERTEX_SHADER = GL_VERTEX_SHADER; /** The fragment shader type (GL_FRAGMENT_SHADER). */ public static final int FRAGMENT_SHADER = GL_FRAGMENT_SHADER; private static boolean strict = false; /** Returns true if the extensions GL_ARB_shader_objects, * GL_ARB_vertex_shader, and GL_ARB_fragment shader are present. * * @return true if shaders are supported */ public static boolean isSupported() { ContextCapabilities c = GLContext.getCapabilities(); return c.GL_ARB_shader_objects && c.GL_ARB_vertex_shader && c.GL_ARB_fragment_shader; // return c.OpenGL20; } /** Whether shader programs are to use "strict" uniform/attribute name * checking (default: disabled). That is, when strict mode is enabled, * trying to modify or retrieve uniform/attribute data by name will fail and * throw an IllegalArgumentException if there exists no 'active' * uniforms/attributes by the given name. (In GLSL, declared uniforms might * still be "inactive" if they are not used.) If strict mode is disabled, * getting/setting uniform/attribute data will fail silently if the name is * not found. * * @param enabled true to enable strict mode */ public static void setStrictMode(boolean enabled) { strict = enabled; } /** Returns <tt>true</tt> if shader programs are to use "strict" * uniform/attribute name checking (default: disabled). That is, when strict * mode is enabled, trying to modify or retrieve uniform/attribute data by * name will fail and throw an IllegalArgumentException if there exists no * 'active' uniforms/attributes by the given name. (In GLSL, declared * uniforms might still be "inactive" if they are not used.) If strict mode * is disabled, getting/setting uniform/attribute data will fail silently if * the name is not found. * * @return true if strict mode is enabled */ public static boolean isStrictMode() { return strict; } /** The OpenGL handle for this shader program object. */ protected int program; /** The log for this program. */ protected String log = ""; /** A map of uniforms by <name, int>. */ protected HashMap<String, Integer> uniforms = new HashMap<String, Integer>(); /** A list of attribute data. */ protected Attrib[] attributes; /** The vertex shader source. */ protected String vertShaderSource; /** The fragment shader source. */ protected String fragShaderSource; /** The OpenGL handle for this program's vertex shader object. */ protected int vert; /** The OpenGL handle for this program's fragment shader object. */ protected int frag; /** Creates a new shader program with the given vertex and fragment shader * source code. The given source code is compiled, then the shaders attached * and linked. * * If shaders are not supported on this system (isSupported returns false), * a LWJGLException will be thrown. * * If one of the shaders does not compile successfully, a LWJGLException * will be thrown. * * If there was a problem in linking the shaders to the program, a * LWJGLException will be thrown and the program will be deleted. * * Before linking, the specified attribLocations will be bound using * glBindAttribLocation. * * @param vertexShaderSource the shader code to compile, attach and link * @param fragShaderSource the frag code to compile, attach and link * @param attribLocations the attribute locations to bind * @throws LWJGLException if there was an issue * @throws IllegalArgumentException if there was an issue */ public ShaderProgram(String vertexShaderSource, String fragShaderSource, List<VertexAttrib> attribLocations) throws LWJGLException { if (vertexShaderSource == null || fragShaderSource == null) throw new IllegalArgumentException("shader source must be non-null"); if (!isSupported()) throw new LWJGLException("no shader support found; shaders require OpenGL 2.0"); this.vertShaderSource = vertexShaderSource; this.fragShaderSource = fragShaderSource; vert = compileShader(VERTEX_SHADER, vertexShaderSource); frag = compileShader(FRAGMENT_SHADER, fragShaderSource); program = createProgram(); try { linkProgram(attribLocations); } catch (LWJGLException e) { dispose(); throw e; } //TODO: for convenience it might be nice to warn non-critical errors in a log //but ideally the user should do that himself // if (log != null && log.length() != 0) // Util.warn(log); } /** Creates a new shader program with the given vertex and fragment shader * source code. The given source code is compiled, then the shaders attached * and linked. * * If shaders are not supported on this system (isSupported returns false), * a LWJGLException will be thrown. * * If one of the shaders does not compile successfully, a LWJGLException * will be thrown. * * If there was a problem in linking the shaders to the program, a * LWJGLException will be thrown and the program will be deleted. * * @param vertexShaderSource the shader code to compile, attach and link * @param fragShaderSource the frag code to compile, attach and link * @throws LWJGLException if there was an issue * @throws IllegalArgumentException if there was an issue */ public ShaderProgram(String vertexShaderSource, String fragShaderSource) throws LWJGLException { this(vertexShaderSource, fragShaderSource, null); } /** Subclasses may wish to implement this to manually handle program/shader * creation, compiling, and linking. This constructor does nothing; users * will need to call compileShader, createProgram and linkProgram manually. * * @throws SlimException */ protected ShaderProgram() { } /** Creates a shader program and returns its OpenGL handle. If the result is * zero, an exception will be thrown. * * @return the OpenGL handle for the newly created shader program * @throws SlimException if the result is zero */ protected int createProgram() throws LWJGLException { int program = glCreateProgram(); if (program == 0) throw new LWJGLException("could not create program; check ShaderProgram.isSupported()"); return program; } /** Used only for clearer debug messages. */ private String shaderTypeString(int type) { if (type == FRAGMENT_SHADER) return "FRAGMENT_SHADER"; else if (type == VERTEX_SHADER) return "VERTEX_SHADER"; else if (type == GL_GEOMETRY_SHADER) return "GEOMETRY_SHADER"; else return "shader"; } /** Compiles a shader from source and returns its handle. If the compilation * failed, a SlimException will be thrown. If the compilation had error, * info or warnings messages, they will be appended to this program's log. * * @param type the type to use in compilation * @param source the source code to compile * @return the resulting ID * @throws SlimException if compilation was unsuccessful */ protected int compileShader(int type, String source) throws LWJGLException { int shader = glCreateShader(type); if (shader == 0) throw new LWJGLException( "could not create shader object; check ShaderProgram.isSupported()"); glShaderSource(shader, source); glCompileShader(shader); int comp = glGetShaderi(shader, GL_COMPILE_STATUS); int len = glGetShaderi(shader, GL_INFO_LOG_LENGTH); String t = shaderTypeString(type); String err = glGetShaderInfoLog(shader, len); if (err != null && err.length() != 0) log += t + " compile log:\n" + err + "\n"; if (comp == GL11.GL_FALSE) throw new LWJGLException(log.length()!=0 ? log : "Could not compile "+shaderTypeString(type)); return shader; } /** Called to attach vertex and fragment; users may override this for more * specific purposes. */ protected void attachShaders() { glAttachShader(getID(), vert); glAttachShader(getID(), frag); } /** Tries to bind the given attributes by location, then calls * attachShaders() and links the program. * * @param attribs tries to bind the given attributes in their order of * appearance * @throws SlimException if this program is invalid (released) or if the * link was unsuccessful */ protected void linkProgram(List<VertexAttrib> attribLocations) throws LWJGLException { if (!valid()) throw new LWJGLException("trying to link an invalid (i.e. released) program"); uniforms.clear(); // bind user-defined attribute locations if (attribLocations != null) { for (VertexAttrib a : attribLocations) { if (a != null) glBindAttribLocation(program, a.location, a.name); } } attachShaders(); glLinkProgram(program); int comp = glGetProgrami(program, GL_LINK_STATUS); int len = glGetProgrami(program, GL_INFO_LOG_LENGTH); String err = glGetProgramInfoLog(program, len); if (err != null && err.length() != 0) log = err + "\n" + log; if (log != null) log = log.trim(); if (comp == GL11.GL_FALSE) throw new LWJGLException(log.length()!=0 ? log : "Could not link program"); fetchUniforms(); fetchAttributes(); } /** Returns the full log of compiling/linking errors, info, warnings, etc. * * @return the full log of this ShaderProgram */ public String getLog() { return log; } /** Enables this shader for use -- only one shader can be bound at a time. * Calling bind() when another program is bound will simply make this object * the active program. * * @throw IllegalStateException if this program is invalid */ public void use() { if (!valid()) throw new IllegalStateException("trying to enable a program that is not valid"); glUseProgram(program); } /** Detaches and releases the shaders * associated with this program. This can be called after linking a program * in order to free up memory (as the shaders are no longer needed), * however, since it is not a commonly used feature and thus not well tested * on all drivers, it should be used with caution. Shaders shouldn't be used * after being released. */ public void disposeShaders() { if (vert != 0) { glDetachShader(getID(), vert); glDeleteShader(vert); vert = 0; } if (frag != 0) { glDetachShader(getID(), frag); glDeleteShader(frag); frag = 0; } } /** If this program has not yet been released, this will releases * this program and its shaders. To only release the * shaders (not the program itself), call disposeShaders(). Programs will be * considered "invalid" after being released, and should no longer be used. */ public void dispose() { if (program != 0) { disposeShaders(); glDeleteProgram(program); program = 0; } } /** Returns the OpenGL handle for this program's vertex shader. * * @return the vertex ID */ public int getVertexShaderID() { return vert; } /** Returns the OpenGL handle for this program's fragment shader. * * @return the fragment ID */ public int getFragmentShaderID() { return frag; } /** Returns the source code for the vertex shader. * * @return the source code */ public String getVertexShaderSource() { return vertShaderSource; } /** Returns the source code for the fragment shader. * * @return the source code */ public String getFragmentShaderSource() { return fragShaderSource; } /** Returns the OpenGL handle for this shader program * * @return the program ID */ public int getID() { return program; } /** A shader program is "valid" if it's ID is not zero. Upon releasing a * program, the ID will be set to zero. * * @return whether this program is valid */ public boolean valid() { return program != 0; } private void fetchUniforms() { int len = glGetProgrami(program, GL_ACTIVE_UNIFORMS); // max length of all uniforms stored in program int strLen = glGetProgrami(program, GL_ACTIVE_UNIFORM_MAX_LENGTH); for (int i = 0; i < len; i++) { String name = glGetActiveUniform(program, i, strLen); int id = glGetUniformLocation(program, name); uniforms.put(name, id); } } private void fetchAttributes() { int len = glGetProgrami(program, GL_ACTIVE_ATTRIBUTES); // max length of all uniforms stored in program int strLen = glGetProgrami(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH); attributes = new Attrib[len]; for (int i = 0; i < len; i++) { Attrib a = new Attrib(); // TODO: use proper FloatBuffer method instead of these convenience // methods a.name = glGetActiveAttrib(program, i, strLen); a.size = glGetActiveAttribSize(program, i); a.type = glGetActiveAttribType(program, i); a.location = glGetAttribLocation(program, a.name); attributes[i] = a; } } /** Returns the location of the uniform by name. If the uniform is not found * and we are in strict mode, an IllegalArgumentException will be thrown, * otherwise -1 will be returned if no active uniform by that name exists. * * @param name the uniform name * @return the ID (location) in the shader program */ public int getUniformLocation(String name) { int location = -1; Integer locI = uniforms.get(name); if (locI == null) { // maybe it's not yet cached? location = glGetUniformLocation(program, name); uniforms.put(name, location); } else location = locI.intValue(); // throw an exception if not found... if (location == -1 && strict) throw new IllegalArgumentException("no active uniform by name '" + name + "' " + "(disable strict compiling to suppress warnings)"); return location; } Attrib attrib(String name) { for (int i = 0; i < attributes.length; i++) { if (name.equals(attributes[i].name)) return attributes[i]; } // throw an exception if not found... if (strict) throw new IllegalArgumentException("no active attribute by name '" + name + "' " + "(disable strict compiling to suppress warnings)"); return null; } /** Returns the location of the attribute by name. If the attribute is not * found and we are in strict mode, an IllegalArgumentException will be * thrown, otherwise -1 will be returned if no active attribute by that name * exists. * * @param name the attribute name * @return the ID (location) in the shader program */ public int getAttributeLocation(String name) { Attrib a = attrib(name); return a != null ? a.location : -1; } /** Returns the type of the attribute by name. If the attribute is not found * and we are in strict mode, an IllegalArgumentException will be thrown, * otherwise -1 will be returned if no active attribute by that name exists. * * @param name the attribute name * @return the ID (location) in the shader program */ public int getAttributeType(String name) { Attrib a = attrib(name); return a != null ? a.type : -1; } /** Returns the size of the attribute by name (i.e. for arrays). If the * attribute is not found and we are in strict mode, an * IllegalArgumentException will be thrown, otherwise -1 will be returned if * no active attribute by that name exists. * * @param name the attribute name * @return the ID (location) in the shader program */ public int getAttributeSize(String name) { Attrib a = attrib(name); return a != null ? a.size : -1; } /** Creates and returns an array for all active attributes that were found * when linking the program. * * @return an array list of active uniform names */ public String[] getAttributeNames() { String[] s = new String[attributes.length]; for (int i = 0; i < attributes.length; i++) { s[i] = attributes[i].name; } return s; } /** Creates and returns an array for all active uniforms that were found when * linking the program. * * @return an array list of active uniform names */ public String[] getUniformNames() { return uniforms.keySet().toArray(new String[uniforms.size()]); } /** Returns true if an active uniform by the given name was found when * linking. * * @param name the active uniform name * @return true if the uniform was found */ public boolean hasUniform(String name) { return uniforms.containsKey(name); } /** Returns true if an active attribute by the given name was found when * linking. * * @param name the active attribute name * @return true if the attribute was found */ public boolean hasAttribute(String name) { for (int i = 0; i < attributes.length; i++) if (name.equals(attributes[i].name)) return true; return false; } /** ----- UNIFORM GETTERS ----- */ private FloatBuffer uniformf(int loc) { if (fbuf16 == null) fbuf16 = BufferUtils.createFloatBuffer(16); fbuf16.clear(); if (loc == -1) return fbuf16; getUniform(loc, fbuf16); return fbuf16; } private IntBuffer uniformi(int loc) { if (ibuf4 == null) ibuf4 = BufferUtils.createIntBuffer(4); ibuf4.clear(); if (loc == -1) return ibuf4; getUniform(loc, ibuf4); return ibuf4; } /** Retrieves data from a uniform and places it in the given buffer. * * @param loc the location of the uniform * @param buf the buffer to place the data */ public void getUniform(int loc, FloatBuffer buf) { glGetUniform(program, loc, buf); } /** Retrieves data from a uniform and places it in the given buffer. * * @param loc the location of the uniform * @param buf the buffer to place the data */ public void getUniform(int loc, IntBuffer buf) { glGetUniform(program, loc, buf); } /** Retrieves data from a uniform and places it in the given buffer. If * strict mode is enabled, this will throw an IllegalArgumentException if * the given uniform is not 'active' -- i.e. if GLSL determined that the * shader isn't using it. If strict mode is disabled, this method will * return <tt>true</tt> if the uniform was found, and <tt>false</tt> * otherwise. * * @param name the name of the uniform * @param buf the buffer to place the data * @return true if the uniform was found, false if there is no active * uniform by that name */ public boolean getUniform(String name, FloatBuffer buf) { int id = getUniformLocation(name); if (id == -1) return false; getUniform(id, buf); return true; } /** Retrieves data from a uniform and places it in the given buffer. If * strict mode is enabled, this will throw an IllegalArgumentException if * the given uniform is not 'active' -- i.e. if GLSL determined that the * shader isn't using it. If strict mode is disabled, this method will * return <tt>true</tt> if the uniform was found, and <tt>false</tt> * otherwise. * * @param name the name of the uniform * @param buf the buffer to place the data * @return true if the uniform was found, false if there is no active * uniform by that name */ public boolean getUniform(String name, IntBuffer buf) { int id = getUniformLocation(name); if (id == -1) return false; getUniform(id, buf); return true; } /** A convenience method to retrieve an integer/sampler2D uniform. The return * values are undefined if the uniform is not found. * * @param loc the uniform location * @return the value */ public int getUniform1i(int loc) { return uniformi(loc).get(0); } /** A convenience method to retrieve an integer/sampler2D uniform. The return * values are undefined if the uniform is not found. * * @param name the uniform location * @return the value */ public int getUniform1i(String name) { return getUniform1i(getUniformLocation(name)); } /** A convenience method to retrieve an ivec2 uniform; for maximum * performance and memory efficiency you should use getUniform(int, * IntBuffer) with a shared buffer. * * @param loc the uniform location * @return a newly created int[] array with 2 elements; e.g. (x, y) */ public int[] getUniform2i(int loc) { IntBuffer buf = uniformi(loc); return new int[] { buf.get(0), buf.get(1) }; } /** A convenience method to retrieve an ivec2 uniform; for maximum * performance and memory efficiency you should use getUniform(int, * IntBuffer) with a shared buffer. The return values are undefined if the * uniform is not found. * * @param name the uniform name * @return a newly created int[] array with 2 elements; e.g. (x, y) */ public int[] getUniform2i(String name) { return getUniform2i(getUniformLocation(name)); } /** A convenience method to retrieve an ivec3 uniform; for maximum * performance and memory efficiency you should use getUniform(String, * IntBuffer) with a shared buffer. * * @param loc the name of the uniform * @return a newly created int[] array with 3 elements; e.g. (x, y, z) */ public int[] getUniform3i(int loc) { IntBuffer buf = uniformi(loc); return new int[] { buf.get(0), buf.get(1), buf.get(2) }; } /** A convenience method to retrieve an ivec3 uniform; for maximum * performance and memory efficiency you should use getUniform(String, * IntBuffer) with a shared buffer. The return values are undefined if the * uniform is not found. * * @param name the name of the uniform * @return a newly created int[] array with 3 elements; e.g. (x, y, z) */ public int[] getUniform3i(String name) { return getUniform3i(getUniformLocation(name)); } /** A convenience method to retrieve an ivec4 uniform; for maximum * performance and memory efficiency you should use getUniform(String, * IntBuffer) with a shared buffer. * * @param loc the location of the uniform * @return a newly created int[] array with 2 elements; e.g. (r, g, b, a) */ public int[] getUniform4i(int loc) { IntBuffer buf = uniformi(loc); return new int[] { buf.get(0), buf.get(1), buf.get(2), buf.get(3) }; } /** A convenience method to retrieve an ivec4 uniform; for maximum * performance and memory efficiency you should use getUniform(String, * IntBuffer) with a shared buffer. The return values are undefined if the * uniform is not found. * * @param name the name of the uniform * @return a newly created int[] array with 2 elements; e.g. (r, g, b, a) */ public int[] getUniform4i(String name) { return getUniform4i(getUniformLocation(name)); } /** A convenience method to retrieve a float uniform. * * @param location the location of the uniform * @return the value */ public float getUniform1f(int loc) { return uniformf(loc).get(0); } /** A convenience method to retrieve a float uniform. The return values are undefined if the * uniform is not found. * * @param name the uniform name * @return the value */ public float getUniform1f(String name) { return getUniform1f(getUniformLocation(name)); } /** A convenience method to retrieve a vec2 uniform; for maximum performance * and memory efficiency you should use getUniform(String, FloatBuffer) with * a shared buffer. * * @param location the location of the uniform * @return a newly created float[] array with 2 elements; e.g. (x, y) */ public float[] getUniform2f(int loc) { FloatBuffer buf = uniformf(loc); return new float[] { buf.get(0), buf.get(1) }; } /** A convenience method to retrieve a vec2 uniform; for maximum performance * and memory efficiency you should use getUniform(String, FloatBuffer) with * a shared buffer. The return values are undefined if the * uniform is not found. * * @param name the name of the uniform * @return a newly created float[] array with 2 elements; e.g. (x, y) */ public float[] getUniform2f(String name) { return getUniform2f(getUniformLocation(name)); } /** A convenience method to retrieve a vec3 uniform; for maximum performance * and memory efficiency you should use getUniform(String, FloatBuffer) with * a shared buffer. * * @param location the location of the uniform * @return a newly created float[] array with 3 elements; e.g. (x, y, z) */ public float[] getUniform3f(int loc) { FloatBuffer buf = uniformf(loc); return new float[] { buf.get(0), buf.get(1), buf.get(2) }; } /** A convenience method to retrieve a vec3 uniform; for maximum performance * and memory efficiency you should use getUniform(String, FloatBuffer) with * a shared buffer. The return values are undefined if the * uniform is not found. * * @param name the name of the uniform * @return a newly created float[] array with 3 elements; e.g. (x, y, z) */ public float[] getUniform3f(String name) { return getUniform3f(getUniformLocation(name)); } /** A convenience method to retrieve a vec4 uniform; for maximum performance * and memory efficiency you should use getUniform(String, FloatBuffer) with * a shared buffer. * * @param location the location of the uniform * @return a newly created float[] array with 4 elements; e.g. (r, g, b, a) */ public float[] getUniform4f(int loc) { FloatBuffer buf = uniformf(loc); return new float[] { buf.get(0), buf.get(1), buf.get(2), buf.get(3) }; } /** A convenience method to retrieve a vec4 uniform; for maximum performance * and memory efficiency you should use getUniform(String, FloatBuffer) with * a shared buffer. The return values are undefined if the * uniform is not found. * * @param name the name of the uniform * @return a newly created float[] array with 4 elements; e.g. (r, g, b, a) */ public float[] getUniform4f(String name) { return getUniform4f(getUniformLocation(name)); } /** ----- UNIFORM LOCATION SETTERS ----- */ /** * Sets the value of a float uniform. * @param loc the location of the uniform * @param f the float value */ public void setUniformf(int loc, float f) { if (loc==-1) return; glUniform1f(loc, f); } /** * Sets the value of a vec2 uniform. * @param loc the location of the uniform * @param a vec.x / tex.s * @param b vec.y / tex.t */ public void setUniformf(int loc, float a, float b) { if (loc==-1) return; glUniform2f(loc, a, b); } /** * Sets the value of a vec3 uniform. * @param loc the location of the uniform * @param a vec.x / color.r / tex.s * @param b vec.y / color.g / tex.t * @param c vec.z / color.b / tex.p */ public void setUniformf(int loc, float a, float b, float c) { if (loc==-1) return; glUniform3f(loc, a, b, c); } /** * Sets the value of a vec4 uniform. * @param loc the location of the uniform * @param a vec.x / color.r * @param b vec.y / color.g * @param c vec.z / color.b * @param d vec.w / color.a */ public void setUniformf(int loc, float a, float b, float c, float d) { if (loc==-1) return; glUniform4f(loc, a, b, c, d); } /** * Sets the value of an int or sampler2D uniform. * @param loc the location of the uniform * @param i the integer / active texture (e.g. 0 for TEXTURE0) */ public void setUniformi(int loc, int i) { if (loc==-1) return; glUniform1i(loc, i); } /** * Sets the value of a ivec2 uniform. * @param loc the location of the uniform * @param a vec.x / tex.s * @param b vec.y / tex.t */ public void setUniformi(int loc, int a, int b) { if (loc==-1) return; glUniform2i(loc, a, b); } /** * Sets the value of a ivec3 uniform. * @param loc the location of the uniform * @param a vec.x / color.r * @param b vec.y / color.g * @param c vec.z / color.b */ public void setUniformi(int loc, int a, int b, int c) { if (loc==-1) return; glUniform3i(loc, a, b, c); } /** * Sets the value of a ivec4 uniform. * @param loc the location of the uniform * @param a vec.x / color.r * @param b vec.y / color.g * @param c vec.z / color.b * @param d vec.w / color.a */ public void setUniformi(int loc, int a, int b, int c, int d) { if (loc==-1) return; glUniform4i(loc, a, b, c, d); } /** ----- UNIFORM STRING SETTERS ----- */ /** * Sets the value of a float uniform. * @param name the name of the uniform * @param f the float value */ public void setUniformf(String name, float f) { setUniformf(getUniformLocation(name), f); } /** * Sets the value of a vec2 uniform. * @param name the name of the uniform * @param a vec.x / tex.s * @param b vec.y / tex.t */ public void setUniformf(String name, float a, float b) { setUniformf(getUniformLocation(name), a, b); } /** * Sets the value of a vec3 uniform. * @param name the name of the uniform * @param a vec.x / color.r / tex.s * @param b vec.y / color.g / tex.t * @param c vec.z / color.b / tex.p */ public void setUniformf(String name, float a, float b, float c) { setUniformf(getUniformLocation(name), a, b, c); } /** * Sets the value of a vec4 uniform. * @param name the name of the uniform * @param a vec.x / color.r * @param b vec.y / color.g * @param c vec.z / color.b * @param d vec.w / color.a */ public void setUniformf(String name, float a, float b, float c, float d) { setUniformf(getUniformLocation(name), a, b, c, d); } /** * Sets the value of an int or sampler2D uniform. * @param name the name of the uniform * @param i the integer / active texture (e.g. 0 for TEXTURE0) */ public void setUniformi(String name, int i) { setUniformi(getUniformLocation(name), i); } /** * Sets the value of a ivec2 uniform. * @param name the name of the uniform * @param a vec.x / tex.s * @param b vec.y / tex.t */ public void setUniformi(String name, int a, int b) { setUniformi(getUniformLocation(name), a, b); } /** * Sets the value of a ivec3 uniform. * @param name the name of the uniform * @param a vec.x / color.r * @param b vec.y / color.g * @param c vec.z / color.b */ public void setUniformi(String name, int a, int b, int c) { setUniformi(getUniformLocation(name), a, b, c); } /** * Sets the value of a ivec4 uniform. * @param name the name of the uniform * @param a vec.x / color.r * @param b vec.y / color.g * @param c vec.z / color.b * @param d vec.w / color.a */ public void setUniformi(String name, int a, int b, int c, int d) { setUniformi(getUniformLocation(name), a, b, c, d); } /** ----- MATRIX SETTERS ----- */ public void setUniformMatrix(String name, boolean transpose, Matrix3f m) { setUniformMatrix(getUniformLocation(name), transpose, m); } public void setUniformMatrix(String name, boolean transpose, Matrix4f m) { setUniformMatrix(getUniformLocation(name), transpose, m); } public void setUniformMatrix(int loc, boolean transpose, Matrix3f m) { if (loc==-1) return; if (fbuf16==null) fbuf16 = BufferUtils.createFloatBuffer(16); fbuf16.clear(); m.store(fbuf16); fbuf16.flip(); glUniformMatrix3(loc, transpose, fbuf16); } public void setUniformMatrix(int loc, boolean transpose, Matrix4f m) { if (loc==-1) return; if (fbuf16==null) fbuf16 = BufferUtils.createFloatBuffer(16); fbuf16.clear(); m.store(fbuf16); fbuf16.flip(); glUniformMatrix4(loc, transpose, fbuf16); } /** ----- VECTOR SETTERS ----- */ public void setUniformf(String name, Vector2f v) { setUniformf(getUniformLocation(name), v); } public void setUniformf(String name, Vector3f v) { setUniformf(getUniformLocation(name), v); } public void setUniformf(String name, Vector4f v) { setUniformf(getUniformLocation(name), v); } public void setUniformf(int loc, Vector2f v) { if (loc==-1) return; setUniformf(loc, v.x, v.y); } public void setUniformf(int loc, Vector3f v) { if (loc==-1) return; setUniformf(loc, v.x, v.y, v.z); } public void setUniformf(int loc, Vector4f v) { if (loc==-1) return; setUniformf(loc, v.x, v.y, v.z, v.w); } }