Files
processing4/android/core/src/processing/opengl/PShader.java
2012-12-16 18:14:33 +00:00

932 lines
26 KiB
Java

/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2011-12 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.opengl;
import processing.core.*;
import java.io.IOException;
import java.net.URL;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
/**
* This class encapsulates a GLSL shader program, including a vertex
* and a fragment shader. Based on the GLSLShader class from GLGraphics, which
* in turn was originally based in the code by JohnG:
* http://processing.org/discourse/beta/num_1159494801.html
*
* @webref rendering:shaders
*/
public class PShader {
// shaders constants
static protected final int COLOR = 0;
static protected final int LIGHT = 1;
static protected final int TEXTURE = 2;
static protected final int TEXLIGHT = 3;
static protected final int LINE = 4;
static protected final int POINT = 5;
protected PApplet parent;
// The main renderer associated to the parent PApplet.
protected PGraphicsOpenGL pgMain;
// We need a reference to the renderer since a shader might
// be called by different renderers within a single application
// (the one corresponding to the main surface, or other offscreen
// renderers).
protected PGraphicsOpenGL pgCurrent;
protected PGL pgl;
protected PGL.Context context; // The context that created this shader.
public int glProgram;
public int glVertex;
public int glFragment;
protected URL vertexURL;
protected URL fragmentURL;
protected String vertexFilename;
protected String fragmentFilename;
protected String vertexShaderSource;
protected String fragmentShaderSource;
protected boolean bound;
protected HashMap<Integer, UniformValue> uniformValues = null;
protected HashMap<Integer, Texture> textures;
protected int firstTexUnit;
protected int lastTexUnit;
// Direct buffers to pass shader dat to GL
protected IntBuffer intBuffer;
protected FloatBuffer floatBuffer;
public PShader() {
parent = null;
pgMain = null;
pgl = null;
context = null;
this.vertexURL = null;
this.fragmentURL = null;
this.vertexFilename = null;
this.fragmentFilename = null;
glProgram = 0;
glVertex = 0;
glFragment = 0;
firstTexUnit = 0;
intBuffer = PGL.allocateIntBuffer(1);
floatBuffer = PGL.allocateFloatBuffer(1);
bound = false;
}
public PShader(PApplet parent) {
this();
this.parent = parent;
pgMain = (PGraphicsOpenGL) parent.g;
pgl = pgMain.pgl;
context = pgl.createEmptyContext();
}
/**
* Creates a shader program using the specified vertex and fragment
* shaders.
*
* @param parent the parent program
* @param vertFilename name of the vertex shader
* @param fragFilename name of the fragment shader
*/
public PShader(PApplet parent, String vertFilename, String fragFilename) {
this.parent = parent;
pgMain = (PGraphicsOpenGL) parent.g;
pgl = pgMain.pgl;
this.vertexURL = null;
this.fragmentURL = null;
this.vertexFilename = vertFilename;
this.fragmentFilename = fragFilename;
glProgram = 0;
glVertex = 0;
glFragment = 0;
intBuffer = PGL.allocateIntBuffer(1);
floatBuffer = PGL.allocateFloatBuffer(1);
}
/**
* @param vertURL network location of the vertex shader
* @param fragURL network location of the fragment shader
*/
public PShader(PApplet parent, URL vertURL, URL fragURL) {
this.parent = parent;
pgMain = (PGraphicsOpenGL) parent.g;
pgl = pgMain.pgl;
this.vertexURL = vertURL;
this.fragmentURL = fragURL;
this.vertexFilename = null;
this.fragmentFilename = null;
glProgram = 0;
glVertex = 0;
glFragment = 0;
intBuffer = PGL.allocateIntBuffer(1);
floatBuffer = PGL.allocateFloatBuffer(1);
}
@Override
protected void finalize() throws Throwable {
try {
if (glVertex != 0) {
pgMain.finalizeGLSLVertShaderObject(glVertex, context.id());
}
if (glFragment != 0) {
pgMain.finalizeGLSLFragShaderObject(glFragment, context.id());
}
if (glProgram != 0) {
pgMain.finalizeGLSLProgramObject(glProgram, context.id());
}
} finally {
super.finalize();
}
}
public void setVertexShader(String vertFilename) {
this.vertexFilename = vertFilename;
}
public void setVertexShader(URL vertURL) {
this.vertexURL = vertURL;
}
public void setFragmentShader(String fragFilename) {
this.fragmentFilename = fragFilename;
}
public void setFragmentShader(URL fragURL) {
this.fragmentURL = fragURL;
}
/**
* Initializes (if needed) and binds the shader program.
*/
public void bind() {
init();
pgl.useProgram(glProgram);
bound = true;
consumeUniforms();
bindTextures();
}
/**
* Unbinds the shader program.
*/
public void unbind() {
unbindTextures();
pgl.useProgram(0);
bound = false;
}
/**
* Returns true if the shader is bound, false otherwise.
*/
public boolean bound() {
return bound;
}
/**
* @webref rendering:shaders
* @brief Sets a variable within the shader
* @param name the name of the uniform variable to modify
* @param x first component of the variable to modify
*/
public void set(String name, int x) {
setUniformImpl(name, UniformValue.INT1, new int[] { x });
}
/**
* @param y second component of the variable to modify. The variable has to be declared with an array/vector type in the shader (i.e.: int[2], vec2)
*/
public void set(String name, int x, int y) {
setUniformImpl(name, UniformValue.INT2, new int[] { x, y });
}
/**
* @param z third component of the variable to modify. The variable has to be declared with an array/vector type in the shader (i.e.: int[3], vec3)
*/
public void set(String name, int x, int y, int z) {
setUniformImpl(name, UniformValue.INT3, new int[] { x, y, z });
}
/**
* @param w fourth component of the variable to modify. The variable has to be declared with an array/vector type in the shader (i.e.: int[4], vec4)
*/
public void set(String name, int x, int y, int z, int w) {
setUniformImpl(name, UniformValue.INT4, new int[] { x, y, z });
}
public void set(String name, float x) {
setUniformImpl(name, UniformValue.FLOAT1, new float[] { x });
}
public void set(String name, float x, float y) {
setUniformImpl(name, UniformValue.FLOAT2, new float[] { x, y });
}
public void set(String name, float x, float y, float z) {
setUniformImpl(name, UniformValue.FLOAT3, new float[] { x, y, z });
}
public void set(String name, float x, float y, float z, float w) {
setUniformImpl(name, UniformValue.FLOAT4, new float[] { x, y, z, w });
}
/**
* @param vec modifies all the components of an array/vector uniform variable. PVector can only be used if the type of the variable is vec3.
*/
public void set(String name, PVector vec) {
setUniformImpl(name, UniformValue.FLOAT3,
new float[] { vec.x, vec.y, vec.z });
}
public void set(String name, int[] vec) {
set(name, vec, 1);
}
/**
* @param ncoords number of coordinates per element, max 4
*/
public void set(String name, int[] vec, int ncoords) {
if (ncoords == 1) {
setUniformImpl(name, UniformValue.INT1VEC, vec);
} else if (ncoords == 2) {
setUniformImpl(name, UniformValue.INT2VEC, vec);
} else if (ncoords == 3) {
setUniformImpl(name, UniformValue.INT3VEC, vec);
} else if (ncoords == 4) {
setUniformImpl(name, UniformValue.INT4VEC, vec);
} else if (4 < ncoords) {
PGraphics.showWarning("Only up to 4 coordinates per element are " +
"supported.");
} else {
PGraphics.showWarning("Wrong number of coordinates: it is negative!");
}
}
public void set(String name, float[] vec) {
set(name, vec, 1);
}
public void set(String name, float[] vec, int ncoords) {
if (ncoords == 1) {
setUniformImpl(name, UniformValue.FLOAT1VEC, vec);
} else if (ncoords == 2) {
setUniformImpl(name, UniformValue.FLOAT2VEC, vec);
} else if (ncoords == 3) {
setUniformImpl(name, UniformValue.FLOAT3VEC, vec);
} else if (ncoords == 4) {
setUniformImpl(name, UniformValue.FLOAT4VEC, vec);
} else if (4 < ncoords) {
PGraphics.showWarning("Only up to 4 coordinates per element are " +
"supported.");
} else {
PGraphics.showWarning("Wrong number of coordinates: it is negative!");
}
}
/**
* @param mat matrix of values
*/
public void set(String name, PMatrix2D mat) {
float[] matv = { mat.m00, mat.m01,
mat.m10, mat.m11 };
setUniformImpl(name, UniformValue.MAT2, matv);
}
public void set(String name, PMatrix3D mat) {
set(name, mat, false);
}
/**
* @param use3x3 enforces the matrix is 3 x 3
*/
public void set(String name, PMatrix3D mat, boolean use3x3) {
if (use3x3) {
float[] matv = { mat.m00, mat.m01, mat.m02,
mat.m10, mat.m11, mat.m12,
mat.m20, mat.m21, mat.m22 };
setUniformImpl(name, UniformValue.MAT3, matv);
} else {
float[] matv = { mat.m00, mat.m01, mat.m02, mat.m03,
mat.m10, mat.m11, mat.m12, mat.m13,
mat.m20, mat.m21, mat.m22, mat.m23,
mat.m30, mat.m31, mat.m32, mat.m33 };
setUniformImpl(name, UniformValue.MAT4, matv);
}
}
/**
* @param tex sets the sampler uniform variable to read from this image texture
*/
public void set(String name, PImage tex) {
setUniformImpl(name, UniformValue.SAMPLER2D, tex);
}
/**
* Returns the ID location of the attribute parameter given its name.
*
* @param name String
* @return int
*/
protected int getAttributeLoc(String name) {
init();
return pgl.getAttribLocation(glProgram, name);
}
/**
* Returns the ID location of the uniform parameter given its name.
*
* @param name String
* @return int
*/
protected int getUniformLoc(String name) {
init();
return pgl.getUniformLocation(glProgram, name);
}
protected void setAttributeVBO(int loc, int vboId, int size, int type,
boolean normalized, int stride, int offset) {
if (-1 < loc) {
pgl.bindBuffer(PGL.ARRAY_BUFFER, vboId);
pgl.vertexAttribPointer(loc, size, type, normalized, stride, offset);
}
}
protected void setUniformValue(int loc, int x) {
if (-1 < loc) {
pgl.uniform1i(loc, x);
}
}
protected void setUniformValue(int loc, int x, int y) {
if (-1 < loc) {
pgl.uniform2i(loc, x, y);
}
}
protected void setUniformValue(int loc, int x, int y, int z) {
if (-1 < loc) {
pgl.uniform3i(loc, x, y, z);
}
}
protected void setUniformValue(int loc, int x, int y, int z, int w) {
if (-1 < loc) {
pgl.uniform4i(loc, x, y, z, w);
}
}
protected void setUniformValue(int loc, float x) {
if (-1 < loc) {
pgl.uniform1f(loc, x);
}
}
protected void setUniformValue(int loc, float x, float y) {
if (-1 < loc) {
pgl.uniform2f(loc, x, y);
}
}
protected void setUniformValue(int loc, float x, float y, float z) {
if (-1 < loc) {
pgl.uniform3f(loc, x, y, z);
}
}
protected void setUniformValue(int loc, float x, float y, float z, float w) {
if (-1 < loc) {
pgl.uniform4f(loc, x, y, z, w);
}
}
protected void setUniformVector(int loc, int[] vec, int ncoords,
int length) {
if (-1 < loc) {
updateIntBuffer(vec);
if (ncoords == 1) {
pgl.uniform1iv(loc, length, intBuffer);
} else if (ncoords == 2) {
pgl.uniform2iv(loc, length, intBuffer);
} else if (ncoords == 3) {
pgl.uniform3iv(loc, length, intBuffer);
} else if (ncoords == 4) {
pgl.uniform3iv(loc, length, intBuffer);
}
}
}
protected void setUniformVector(int loc, float[] vec, int ncoords,
int length) {
if (-1 < loc) {
updateFloatBuffer(vec);
if (ncoords == 1) {
pgl.uniform1fv(loc, length, floatBuffer);
} else if (ncoords == 2) {
pgl.uniform2fv(loc, length, floatBuffer);
} else if (ncoords == 3) {
pgl.uniform3fv(loc, length, floatBuffer);
} else if (ncoords == 4) {
pgl.uniform4fv(loc, length, floatBuffer);
}
}
}
protected void setUniformMatrix(int loc, float[] mat) {
if (-1 < loc) {
updateFloatBuffer(mat);
if (mat.length == 4) {
pgl.uniformMatrix2fv(loc, 1, false, floatBuffer);
} else if (mat.length == 9) {
pgl.uniformMatrix3fv(loc, 1, false, floatBuffer);
} else if (mat.length == 16) {
pgl.uniformMatrix4fv(loc, 1, false, floatBuffer);
}
}
}
protected void setUniformTex(int loc, Texture tex) {
// get unit from last value in bindTextures ...
// pgl.activeTexture(PGL.TEXTURE0 + unit);
tex.bind();
}
/*
// The individual attribute setters are not really needed, read this:
// http://stackoverflow.com/questions/7718976/what-is-glvertexattrib-versus-glvertexattribpointer-used-for
// except for setting a constant vertex attribute value.
public void set1FloatAttribute(int loc, float x) {
if (-1 < loc) {
pgl.glVertexAttrib1f(loc, x);
}
}
public void set2FloatAttribute(int loc, float x, float y) {
if (-1 < loc) {
pgl.glVertexAttrib2f(loc, x, y);
}
}
public void set3FloatAttribute(int loc, float x, float y, float z) {
if (-1 < loc) {
pgl.glVertexAttrib3f(loc, x, y, z);
}
}
public void set4FloatAttribute(int loc, float x, float y, float z, float w) {
if (-1 < loc) {
pgl.glVertexAttrib4f(loc, x, y, z, w);
}
}
*/
protected void setUniformImpl(String name, int type, Object value) {
int loc = getUniformLoc(name);
if (-1 < loc) {
if (uniformValues == null) {
uniformValues = new HashMap<Integer, UniformValue>();
}
uniformValues.put(loc, new UniformValue(type, value));
} else {
PGraphics.showWarning("The shader doesn't have a uniform called \"" +
name + "\"");
}
}
protected void consumeUniforms() {
if (uniformValues != null && 0 < uniformValues.size()) {
lastTexUnit = firstTexUnit;
for (Integer loc: uniformValues.keySet()) {
UniformValue val = uniformValues.get(loc);
if (val.type == UniformValue.INT1) {
int[] v = ((int[])val.value);
pgl.uniform1i(loc, v[0]);
} else if (val.type == UniformValue.INT2) {
int[] v = ((int[])val.value);
pgl.uniform2i(loc, v[0], v[1]);
} else if (val.type == UniformValue.INT3) {
int[] v = ((int[])val.value);
pgl.uniform3i(loc, v[0], v[1], v[2]);
} else if (val.type == UniformValue.INT4) {
int[] v = ((int[])val.value);
pgl.uniform4i(loc, v[0], v[1], v[2], v[4]);
} else if (val.type == UniformValue.FLOAT1) {
float[] v = ((float[])val.value);
pgl.uniform1f(loc, v[0]);
} else if (val.type == UniformValue.FLOAT2) {
float[] v = ((float[])val.value);
pgl.uniform2f(loc, v[0], v[1]);
} else if (val.type == UniformValue.FLOAT3) {
float[] v = ((float[])val.value);
pgl.uniform3f(loc, v[0], v[1], v[2]);
} else if (val.type == UniformValue.FLOAT4) {
float[] v = ((float[])val.value);
pgl.uniform4f(loc, v[0], v[1], v[2], v[3]);
} else if (val.type == UniformValue.INT1VEC) {
int[] v = ((int[])val.value);
updateIntBuffer(v);
pgl.uniform1iv(loc, v.length, intBuffer);
} else if (val.type == UniformValue.INT2VEC) {
int[] v = ((int[])val.value);
updateIntBuffer(v);
pgl.uniform2iv(loc, v.length / 2, intBuffer);
} else if (val.type == UniformValue.INT3VEC) {
int[] v = ((int[])val.value);
updateIntBuffer(v);
pgl.uniform3iv(loc, v.length / 3, intBuffer);
} else if (val.type == UniformValue.INT4VEC) {
int[] v = ((int[])val.value);
updateIntBuffer(v);
pgl.uniform4iv(loc, v.length / 4, intBuffer);
} else if (val.type == UniformValue.FLOAT1VEC) {
float[] v = ((float[])val.value);
updateFloatBuffer(v);
pgl.uniform1fv(loc, v.length, floatBuffer);
} else if (val.type == UniformValue.FLOAT2VEC) {
float[] v = ((float[])val.value);
updateFloatBuffer(v);
pgl.uniform2fv(loc, v.length / 2, floatBuffer);
} else if (val.type == UniformValue.FLOAT3VEC) {
float[] v = ((float[])val.value);
updateFloatBuffer(v);
pgl.uniform3fv(loc, v.length / 3, floatBuffer);
} else if (val.type == UniformValue.FLOAT4VEC) {
float[] v = ((float[])val.value);
updateFloatBuffer(v);
pgl.uniform4fv(loc, v.length / 4, floatBuffer);
} else if (val.type == UniformValue.MAT2) {
float[] v = ((float[])val.value);
updateFloatBuffer(v);
pgl.uniformMatrix2fv(loc, 1, false, floatBuffer);
} else if (val.type == UniformValue.MAT3) {
float[] v = ((float[])val.value);
updateFloatBuffer(v);
pgl.uniformMatrix3fv(loc, 1, false, floatBuffer);
} else if (val.type == UniformValue.MAT4) {
float[] v = ((float[])val.value);
updateFloatBuffer(v);
pgl.uniformMatrix4fv(loc, 1, false, floatBuffer);
} else if (val.type == UniformValue.SAMPLER2D) {
PImage img = (PImage)val.value;
Texture tex = pgMain.getTexture(img);
pgl.uniform1i(loc, lastTexUnit);
if (textures == null) {
textures = new HashMap<Integer, Texture>();
}
textures.put(lastTexUnit, tex);
lastTexUnit++;
}
}
uniformValues.clear();
}
}
protected void updateIntBuffer(int[] vec) {
intBuffer = PGL.updateIntBuffer(intBuffer, vec, false);
}
protected void updateFloatBuffer(float[] vec) {
floatBuffer = PGL.updateFloatBuffer(floatBuffer, vec, false);
}
protected void bindTextures() {
if (textures != null) {
for (int unit: textures.keySet()) {
Texture tex = textures.get(unit);
pgl.activeTexture(PGL.TEXTURE0 + unit);
tex.bind();
}
}
}
protected void unbindTextures() {
if (textures != null) {
for (int unit: textures.keySet()) {
Texture tex = textures.get(unit);
pgl.activeTexture(PGL.TEXTURE0 + unit);
tex.unbind();
}
pgl.activeTexture(PGL.TEXTURE0);
}
}
protected void init() {
if (glProgram == 0 || contextIsOutdated()) {
context = pgl.getCurrentContext();
glProgram = pgMain.createGLSLProgramObject(context.id());
boolean hasVert = false;
if (vertexFilename != null) {
hasVert = loadVertexShader(vertexFilename);
} else if (vertexURL != null) {
hasVert = loadVertexShader(vertexURL);
} else {
PGraphics.showException("Vertex shader filenames and URLs are " +
"both null!");
}
boolean hasFrag = false;
if (fragmentFilename != null) {
hasFrag = loadFragmentShader(fragmentFilename);
} else if (fragmentURL != null) {
hasFrag = loadFragmentShader(fragmentURL);
} else {
PGraphics.showException("Fragment shader filenames and URLs are " +
"both null!");
}
boolean vertRes = true;
if (hasVert) {
vertRes = compileVertexShader();
}
boolean fragRes = true;
if (hasFrag) {
fragRes = compileFragmentShader();
}
if (vertRes && fragRes) {
if (hasVert) {
pgl.attachShader(glProgram, glVertex);
}
if (hasFrag) {
pgl.attachShader(glProgram, glFragment);
}
pgl.linkProgram(glProgram);
pgl.getProgramiv(glProgram, PGL.LINK_STATUS, intBuffer);
boolean linked = intBuffer.get(0) == 0 ? false : true;
if (!linked) {
PGraphics.showException("Cannot link shader program:\n" +
pgl.getProgramInfoLog(glProgram));
}
pgl.validateProgram(glProgram);
pgl.getProgramiv(glProgram, PGL.VALIDATE_STATUS, intBuffer);
boolean validated = intBuffer.get(0) == 0 ? false : true;
if (!validated) {
PGraphics.showException("Cannot validate shader program:\n" +
pgl.getProgramInfoLog(glProgram));
}
}
}
}
protected boolean contextIsOutdated() {
boolean outdated = !pgl.contextIsCurrent(context);
if (outdated) {
pgMain.removeGLSLProgramObject(glProgram, context.id());
pgMain.removeGLSLVertShaderObject(glVertex, context.id());
pgMain.removeGLSLFragShaderObject(glFragment, context.id());
glProgram = 0;
glVertex = 0;
glFragment = 0;
}
return outdated;
}
/**
* Loads and compiles the vertex shader contained in file.
*
* @param file String
*/
protected boolean loadVertexShader(String filename) {
vertexShaderSource = PApplet.join(parent.loadStrings(filename), "\n");
return vertexShaderSource != null;
}
/**
* Loads and compiles the vertex shader contained in the URL.
*
* @param file String
*/
protected boolean loadVertexShader(URL url) {
try {
vertexShaderSource = PApplet.join(PApplet.loadStrings(url.openStream()),
"\n");
return vertexShaderSource != null;
} catch (IOException e) {
PGraphics.showException("Cannot load vertex shader " + url.getFile());
return false;
}
}
/**
* Loads and compiles the fragment shader contained in file.
*
* @param file String
*/
protected boolean loadFragmentShader(String filename) {
fragmentShaderSource = PApplet.join(parent.loadStrings(filename), "\n");
return fragmentShaderSource != null;
}
/**
* Loads and compiles the fragment shader contained in the URL.
*
* @param url URL
*/
protected boolean loadFragmentShader(URL url) {
try {
fragmentShaderSource = PApplet.join(PApplet.loadStrings(url.openStream()),
"\n");
return fragmentShaderSource != null;
} catch (IOException e) {
PGraphics.showException("Cannot load fragment shader " + url.getFile());
return false;
}
}
/**
* @param shaderSource a string containing the shader's code
*/
protected boolean compileVertexShader() {
glVertex = pgMain.createGLSLVertShaderObject(context.id());
pgl.shaderSource(glVertex, vertexShaderSource);
pgl.compileShader(glVertex);
pgl.getShaderiv(glVertex, PGL.COMPILE_STATUS, intBuffer);
boolean compiled = intBuffer.get(0) == 0 ? false : true;
if (!compiled) {
PGraphics.showException("Cannot compile vertex shader:\n" +
pgl.getShaderInfoLog(glVertex));
return false;
} else {
return true;
}
}
/**
* @param shaderSource a string containing the shader's code
*/
protected boolean compileFragmentShader() {
glFragment = pgMain.createGLSLFragShaderObject(context.id());
pgl.shaderSource(glFragment, fragmentShaderSource);
pgl.compileShader(glFragment);
pgl.getShaderiv(glFragment, PGL.COMPILE_STATUS, intBuffer);
boolean compiled = intBuffer.get(0) == 0 ? false : true;
if (!compiled) {
PGraphics.showException("Cannot compile fragment shader:\n" +
pgl.getShaderInfoLog(glFragment));
return false;
} else {
return true;
}
}
protected void setRenderer(PGraphicsOpenGL pg) {
pgCurrent = pg;
}
protected void loadAttributes() { }
protected void loadUniforms() { }
protected void release() {
if (glVertex != 0) {
pgMain.deleteGLSLVertShaderObject(glVertex, context.id());
glVertex = 0;
}
if (glFragment != 0) {
pgMain.deleteGLSLFragShaderObject(glFragment, context.id());
glFragment = 0;
}
if (glProgram != 0) {
pgMain.deleteGLSLProgramObject(glProgram, context.id());
glProgram = 0;
}
}
// Class to store a user-specified value for a uniform parameter
// in the shader
protected class UniformValue {
static final int INT1 = 0;
static final int INT2 = 1;
static final int INT3 = 2;
static final int INT4 = 3;
static final int FLOAT1 = 4;
static final int FLOAT2 = 5;
static final int FLOAT3 = 6;
static final int FLOAT4 = 7;
static final int INT1VEC = 8;
static final int INT2VEC = 9;
static final int INT3VEC = 10;
static final int INT4VEC = 11;
static final int FLOAT1VEC = 12;
static final int FLOAT2VEC = 13;
static final int FLOAT3VEC = 14;
static final int FLOAT4VEC = 15;
static final int MAT2 = 16;
static final int MAT3 = 17;
static final int MAT4 = 18;
static final int SAMPLER2D = 19;
int type;
Object value;
UniformValue(int type, Object value) {
this.type = type;
this.value = value;
}
}
}