First commit

Former-commit-id: 9340873f9cfb15264004c32d6e4b8f8bd6828d94
Former-commit-id: 1916747c109876aa064412e01204c3aeda9bbbc0
This commit is contained in:
Song Minjae
2016-02-05 13:36:35 +09:00
commit d5c99aad5e
1340 changed files with 298157 additions and 0 deletions

284
src/shader/MultiTex.java Executable file
View File

@@ -0,0 +1,284 @@
package shader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import org.newdawn.slick.Color;
import org.newdawn.slick.Renderable;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.opengl.InternalTextureLoader;
import org.newdawn.slick.opengl.Texture;
/**
* Class to support the concept of a single artifact being
* comprised of multiple image resources.</br>
* For example a colourmap, normalmap, diffusemap, and specularmap.
* This is currently extremely buggy, and I don't know why some
* things have to be the way the are.
* @author Chronocide (Jeremy Klix)
*
*
* All drawing is done starting from the top left vertex and
* moving counter clockwise.</br>
*/
//TODO Make interface feel a little more like the familiar Image class
//TODO Determine a method of dealing with the case were textures
//are not all the same size. For instance should textures be
//stretched, tiled, clamped?
//TODO Way of handling images larger then the supporting cards
//max texture size ala Slicks BigImage class.
//TODO Needs way more attention to documenting inheritance.
//TODO Test using different numbers of textures.
public class MultiTex implements Renderable{
private static int units = -1;
/** The top left corner identifier */
public static final int TOP_LEFT = 0;
/** The top right corner identifier */
public static final int BOTTOM_LEFT = 1;
/** The bottom left corner identifier */
public static final int BOTTOM_RIGHT = 3;
/** The bottom right corner identifier */
public static final int TOP_RIGHT = 2;
private List<Texture> textures;
private int primaryTextureIndex = 0;
//Width and height based off primary texture loaded
private float imgWidth, imgHeight;
//Primary texture width and height clamped between 0 and 1
private float texWidth, texHeight;
private float[] normals = new float[]{0,0,1,
0,0,1,
0,0,1,
0,0,1};
private float[] colours = new float[]{1,1,1,1,
1,1,1,1,
1,1,1,1,
1,1,1,1};
/**
* Constructs a new <tt>MultiTex</tt> object using the textures
* identified in <tt>textures</tt>.</br>
* The index of the textures in the list will be the texture unit
* that the texture is bound to.</br>
* @param textures a list of paths to the textures to use.
* @throws SlickException If <tt>textures.size()</tt> is greater
* than the maximum number of texture units.
*/
public MultiTex(List<String> textures)throws SlickException{
//Check how many texture units are supported
if(units==-1){
units = GL11.glGetInteger(GL20.GL_MAX_TEXTURE_IMAGE_UNITS);
System.out.println(units);
}
if(units < textures.size()){
throw new UnsupportedOperationException("You attempted to " +
"create an artifact with " + textures.size() +
" textures, but your environment only supports " +
units + " texure image units.");
}
//Create texture list
this.textures = new ArrayList<Texture>(textures.size());
//Load textures into texture list.
InternalTextureLoader itl = InternalTextureLoader.get();
for(int i = 0; i<textures.size(); i++){
GL13.glActiveTexture(GL13.GL_TEXTURE0 + i);
GL11.glEnable(GL11.GL_TEXTURE_2D);
try{
this.textures.add(itl.getTexture(textures.get(i),
false,
GL11.GL_LINEAR,
null));
}catch(IOException e){
throw new SlickException(e.getMessage());
}
GL11.glDisable(GL11.GL_TEXTURE_2D);
}
//FIXME pretty sure there is a rather serious problem here.
//Since the TextureLoader used keeps track of previously loaded
//textures, and binds them to a unit at creation. If a single
//image is loaded twice to two different Texture Units, it may
//not actually be associated with the correct unit on the
//second load. This is because the TextureLoader will simply
//return the earlier loaded texture.
//Reset current texture unit to 0
GL13.glActiveTexture(GL13.GL_TEXTURE0);
imgWidth = this.textures.get(primaryTextureIndex).getImageWidth();
imgHeight = this.textures.get(primaryTextureIndex).getImageHeight();
texWidth = this.textures.get(primaryTextureIndex).getWidth();
texHeight = this.textures.get(primaryTextureIndex).getHeight();
}
public MultiTex(String[] textures) throws SlickException{
this(Arrays.asList(textures));
}
public MultiTex(String t1, String t2)throws SlickException{
this(new String[]{t1,t2});
}
/**
* When extending please note that this method relies on the
* private method drawEmbedded.</br>
*/
public void draw(float x, float y){
draw(x, y, x + imgWidth, y+imgHeight,
0, 0, imgWidth, imgHeight);
}
/**
* Draw a section of this MultTex at a particular location and
* scale on the screen.</br>
*
* This is the draw method that all other overloaded draw
* methods eventually evoke.</br>
*
* @param x1
* @param y1
* @param x2
* @param y2
* @param sx1
* @param sy1
* @param sx2
* @param sy2
*/
public void draw(float x1, float y1, float x2, float y2,
float sx1, float sy1, float sx2, float sy2){
//Bind textures to their correct locations
for(int i = 0; i < textures.size(); i++){
GL13.glActiveTexture(GL13.GL_TEXTURE0 + i);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textures.get(i).getTextureID());
}
GL11.glBegin(GL11.GL_QUADS);
drawEmbedded(x1, y1, x2, y2,
sx1, sy1, sx2, sy2);
GL11.glEnd();
//Clean up texture setting to allow basic slick to operate correctly.
for(int i = textures.size()-1; i>=0; i--){
GL13.glActiveTexture(GL13.GL_TEXTURE0+i);
GL11.glDisable(GL11.GL_TEXTURE_2D);
}
GL11.glEnable(GL11.GL_TEXTURE_2D);
}
public void draw(float x1, float y1, float x2, float y2,
float sx1, float sy1, float sx2, float sy2,
Color c){
float[] bu = colours;//Save the colour state
setColour(c);
draw(x1, y1, x2, y2, sx1, sy1, sx2, sy2);
colours = bu;//Restore the colour state
}
/**
* Sets the colour of a given corner.</br>
* Note that this will have an effect only if: the
* fixed pixel pipeline is being used; or the applied shader
* takes the vertex colour into account.</br>
* @param corner
* @param c
*/
public void setColour(int corner, Color c){
colours[corner*4 + 0] = c.r;
colours[corner*4 + 1] = c.g;
colours[corner*4 + 2] = c.b;
colours[corner*4 + 3] = c.a;
}
/**
* Sets the colour of all four corners.</br>
* @param c
*/
public void setColour(Color c){
for(int i=0; i<4; i++){
setColour(i, c);
}
}
private void drawEmbedded(float x1, float y1, float x2, float y2,
float sx1, float sy1, float sx2, float sy2){
//TODO reduce code duplication need to produce sequence 0,1,3,2
//TOP LEFT
for(int i=0; i<textures.size(); i++){
GL13.glMultiTexCoord2f(GL13.GL_TEXTURE0 + i,
(sx1/imgWidth) * texWidth,
(sy1/imgHeight)* texHeight);
}
GL11.glColor4f(colours[0], colours[1], colours[2], colours[3]);
GL11.glNormal3f(normals[0], normals[1], normals[2]);
GL11.glVertex3f(x1, y1, 0);
//BOTTOM LEFT
for(int i=0; i<textures.size(); i++){
GL13.glMultiTexCoord2f(GL13.GL_TEXTURE0 + i,
(sx1/imgWidth) * texWidth,
(sy2/imgHeight)* texHeight);
}
GL11.glColor4f(colours[3], colours[5], colours[6], colours[7]);
GL11.glNormal3f(normals[3], normals[4], normals[5]);
GL11.glVertex3f(x1, y2, 0);
//BOTTOM RIGHT
for(int i=0; i<textures.size(); i++){
GL13.glMultiTexCoord2f(GL13.GL_TEXTURE0 + i,
(sx2/imgWidth) * texWidth,
(sy2/imgHeight)* texHeight);
}
GL11.glColor4f(colours[8], colours[9], colours[10], colours[11]);
GL11.glNormal3f(normals[6], normals[7], normals[8]);
GL11.glVertex3f(x2, y2, 0);
//TOP RIGHT
for(int i=0; i<textures.size(); i++){
GL13.glMultiTexCoord2f(GL13.GL_TEXTURE0 + i,
(sx2/imgWidth) * texWidth,
(sy1/imgHeight)* texHeight);
}
GL11.glColor4f(colours[12], colours[13], colours[14], colours[15]);
GL11.glNormal3f(normals[9], normals[10], normals[11]);
GL11.glVertex3f(x2, y1, 0);
}
}

453
src/shader/Shader.java Executable file
View File

@@ -0,0 +1,453 @@
package shader;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.ResourceLoader;
/**
* Class used to use and access shaders without having to deal
* with all of the fidly openGL bits.
* @author Chronocide (Jeremy Klix)
*
*/
public class Shader {
public static final int BRIEF = 128;
public static final int MODERATE = 512;
public static final int VERBOSE = 1024;
private static final int NOT_LOADED = -1;
private static final String ERR_LOCATION =
"Warning: variable %s could not be found. " +
"Ensure the name is spelled correctly\n";
private static int logging = MODERATE;
private ShaderResourceManager srm;
/**
* ID of the <tt>Shader</tt>. A Shader may have programID of
* -1 only before construction is completed, or
* after the <tt>Shader</tt> is deleted
*/
private int programID = NOT_LOADED;
private Map<String, ShaderVariable> vars = new HashMap<String, ShaderVariable>();
private Shader(ShaderResourceManager srm,
Collection<String> vertex,
Collection<String> fragment)throws SlickException{
this.srm = srm;
StringBuilder errorMessage = new StringBuilder();
programID = GL20.glCreateProgram();
int[] shaderIds = new int[vertex.size() + fragment.size()];
int index = 0;
//Load Vertex Shaders
for(String vertShader: vertex){
int vsid = srm.getVertexShaderID(vertShader);
srm.createProgramShaderDependancy(programID, vsid);
//Add to shader ids array
shaderIds[index] = vsid;
index++;
//Check for errors with shader
if(!compiledSuccessfully(vsid)){
errorMessage.append("Vertex Shader ");
errorMessage.append(vertShader);
errorMessage.append(" failed to compile.\n");
errorMessage.append(getShaderInfoLog(vsid));
errorMessage.append("\n\n");
}
scanSource(vertShader);
}
//Load Fragment Shaders
for(String fragShader: fragment){
int fsid = srm.getFragementShaderID(fragShader);
srm.createProgramShaderDependancy(programID, fsid);
//Add to shader ids array
shaderIds[index] = fsid;
index++;
//Check for errors with shader
if(!compiledSuccessfully(fsid)){
errorMessage.append("Fragment Shader ");
errorMessage.append(fragShader);
errorMessage.append(" failed to compile.\n");
errorMessage.append(getShaderInfoLog(fsid));
errorMessage.append("\n\n");
}
scanSource(fragShader);
}
//Attach shaders to program
for(int i=0; i<index; i++){
GL20.glAttachShader(programID, shaderIds[i]);
}
//Link program
GL20.glLinkProgram(programID);
if(!linkedSuccessfully()){
errorMessage.append("Linking Error\n");
errorMessage.append(getProgramInfoLog());
errorMessage.append("\n\n");
}
if(errorMessage.length()!=0){
errorMessage.insert(0, "Could not compile shader.\n");
srm.removeProgram(programID);
programID = -1;
errorMessage.append("Stack Trace:");
throw new SlickException(errorMessage.toString());
}
}
/**
* Factory method to create a new Shader.
* @param vertexFileName
* @param fragmentFileName
* @return
* @throws SlickException
*/
public static Shader makeShader(String vertexFileName,
String fragmentFileName)throws SlickException{
ArrayList<String> l1 = new ArrayList<String>();
l1.add(vertexFileName);
ArrayList<String> l2 = new ArrayList<String>();
l2.add(fragmentFileName);
return new Shader(ShaderResourceManagerImpl.getSRM(),
l1,
l2);
}
/**
* Reverts GL context back to the fixed pixel pipeline.<br>
*/
public static void forceFixedShader(){
GL20.glUseProgram(0);
}
/**
* Sets the number of characters to be returned when printing
* errors.</br> Suggested values are the constants
* <tt>BRIEF</tt>, <tt>MODERATE</tt>, and <tt>VERBOSE</tt>.</br>
* @param detailLevel number of characters to display for error
* messages.
*/
public static void setLoggingDetail(int detailLevel){
logging = detailLevel;
}
/**
* Deletes this shader and unloads all free resources.</br>
* TODO should this be called from <tt>finalise()</tt>, or is
* that just asking for trouble?
*/
public void deleteShader(){
srm.removeProgram(programID);
programID = NOT_LOADED;
}
/**
* Returns true if this <tt>Shader</tt> has been deleted.</br>
* @return true if this <tt>Shader</tt> has been deleted.</br>
*/
public boolean isDeleted(){
return programID == NOT_LOADED;
}
/**
* Activates the shader.</br>
*/
public void startShader(){
if(programID == NOT_LOADED){
throw new IllegalStateException("Cannot start shader; this" +
" Shader has been deleted");
}
forceFixedShader(); //Not sure why this is necessary but it is.
GL20.glUseProgram(programID);
}
//UNIFORM SETTERS
/**
* Sets the value of the uniform integer Variable <tt>name</tt>.</br>
* @param name the variable to set.
* @param value the value to be set.
*/
public Shader setUniformIntVariable(String name, int value){
return setUniformIntVariable(name, new int[]{value});
}
public Shader setUniformIntVariable(String name, int v0, int v1){
return setUniformIntVariable(name, new int[]{v0, v1});
}
public Shader setUniformIntVariable(String name,
int v0, int v1, int v2){
return setUniformIntVariable(name, new int[]{v0, v1, v2});
}
public Shader setUniformIntVariable(String name,
int v0, int v1, int v2, int v3){
return setUniformIntVariable(name, new int[]{v0, v1, v2, v3});
}
public Shader setUniformIntVariable(String name, int[] values){
ShaderVariable var = vars.get(name);
if(var==null){
printError(name);
}else{
var.setUniformValue(values);
}
return this;
}
/**
* Sets the value of the uniform integer Variable
* <tt>name</tt>.</br>
* @param name the variable to set.
* @param value the value to be set.
*/
public Shader setUniformFloatVariable(String name, float value){
return setUniformFloatVariable(name, new float[]{value});
}
public Shader setUniformFloatVariable(String name,
float v0, float v1){
return setUniformFloatVariable(name, new float[]{v0, v1});
}
public Shader setUniformFloatVariable(String name,
float v0, float v1, float v2){
return setUniformFloatVariable(name, new float[]{v0, v1, v2});
}
public Shader setUniformFloatVariable(String name,
float v0, float v1,
float v2, float v3){
return setUniformFloatVariable(name, new float[]{v0, v1, v2, v3});
}
public Shader setUniformFloatVariable(String name, float[] values){
ShaderVariable var = vars.get(name);
if(var==null){
printError(name);
}else{
var.setUniformValue(values);
}
return this;
}
//TODO implement using ShaderVariable
//TODO Test
public Shader setUniformMatrix(String name,
boolean transpose,
float[][] matrix){
//Convert matrix format
FloatBuffer matBuffer = matrixPrepare(matrix);
//Get uniform location
int location = GL20.glGetUniformLocation(programID, name);
printError(name);
//determine correct matrixSetter
switch(matrix.length){
case 2: GL20.glUniformMatrix2(location, transpose, matBuffer);
break;
case 3: GL20.glUniformMatrix3(location, transpose, matBuffer);
break;
case 4: GL20.glUniformMatrix4(location, transpose, matBuffer);
break;
}
return this;
}
private FloatBuffer matrixPrepare(float[][] matrix){
//Check argument validity
if(matrix==null){
throw new IllegalArgumentException("The matrix may not be null");
}
int row = matrix.length;
if(row<2){
throw new IllegalArgumentException("The matrix must have at least 2 rows.");
}
int col = matrix[0].length;
if(col!=row){
throw new IllegalArgumentException("The matrix must have an equal number of rows and columns.");
}
float[] unrolled = new float[row*col];
for(int i=0;i<row;i++){
for(int j=0;j<col;j++){
unrolled[i*col+j] = matrix[i][j];
}
}
//TODO FloatBuffer creation here is probably broken
return FloatBuffer.wrap(unrolled);
}
private void printError(String varName){
System.err.printf(ERR_LOCATION, varName);
}
/**
* Returns true if the shader compiled successfully.</br>
* @param shaderID
* @return true if the shader compiled successfully.</br>
*/
private boolean compiledSuccessfully(int shaderID){
return GL20.glGetShader(shaderID, GL20.GL_COMPILE_STATUS)==GL11.GL_TRUE;
}
/**
* Returns true if the shader program linked successfully.</br>
* @return true if the shader program linked successfully.</br>
*/
private boolean linkedSuccessfully(){
int test = GL20.glGetShader(programID, GL20.GL_LINK_STATUS);
return true;
// return GL20.glGetShader(programID, GL20.GL_LINK_STATUS)==GL11.GL_TRUE;
}
private String getShaderInfoLog(int shaderID){
return GL20.glGetShaderInfoLog(shaderID, logging).trim();
}
private String getProgramInfoLog(){
return GL20.glGetProgramInfoLog(programID, logging).trim();
}
private void scrapeVariables(String varLine){
ShaderVariable.Qualifier qualifier = null;
ShaderVariable.Type type = null;
String name = "";
int vecSize = 1; // if a vector the
int size = 1; //If array size of array
String str;
Scanner scanner = new Scanner(varLine);
scanner.useDelimiter("[\\s,]++");
//Determine qualifier
qualifier = ShaderVariable.Qualifier.fromString(scanner.next());
//Determine type
str = scanner.next();
if(str.equals("float")){
type = ShaderVariable.Type.FLOAT;
}else if(str.matches("[u]?int|sampler[123]D")){
type = ShaderVariable.Type.INTEGER;
}else if(str.equals("bool")){
type = ShaderVariable.Type.BOOLEAN;
}else if(str.matches("[bdiu]?vec[234]")){
char c = str.charAt(0);
switch(c){
case 'b':
type = ShaderVariable.Type.BOOLEAN; break;
case 'd':
type = ShaderVariable.Type.DOUBLE; break;
case 'i':
case 'u':
type = ShaderVariable.Type.INTEGER; break;
case 'v':
type = ShaderVariable.Type.FLOAT; break;
}
str = str.substring(str.length()-1);
vecSize = Integer.parseInt(str);
}
//Determine variable names
while(scanner.hasNext("[\\w_]+[\\w\\d_]*(\\[\\d+\\])?")){
name = scanner.next();
if(name.contains("]")){
String sub = name.substring(name.indexOf('[')+1, name.length()-1);
size = Integer.parseInt(sub);
name = name.substring(0, name.indexOf('[')).trim();
}
ShaderVariable var =
new ShaderVariable(programID,
name, qualifier, type, vecSize, size);
vars.put(var.name, var);
}
}
private void scanSource(String filename){
Scanner scanner = new Scanner(ResourceLoader.getResourceAsStream(filename));
scanner.useDelimiter(";|\\{|\\}");
while(scanner.hasNext()){
while(scanner.hasNext("\\s*?(uniform|attribute|varying).*")){
scrapeVariables(scanner.next().trim());
}
scanner.next();
}
}
}

197
src/shader/ShaderManagerImpl.java Executable file
View File

@@ -0,0 +1,197 @@
package shader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL20;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.ResourceLoader;
/**
* A simple class used to prevent duplicate shaders from
* being loaded and compiled onto the video card.</br>
* @author Chronocide (Jeremy Klix)
*/
class ShaderResourceManagerImpl implements ShaderResourceManager{
private static final ShaderResourceManager SRM = new ShaderResourceManagerImpl();
private Map<String, Integer> shaderMap = new HashMap<String, Integer>();
private Map<Integer, Set<Integer>> shaderToPrograms = new HashMap<Integer, Set<Integer>>(); //for every shader lists all the programs that use it
private Map<Integer, Set<Integer>> programToShaders = new HashMap<Integer, Set<Integer>>(); //for every program lists the shaders it uses
//Constructor
private ShaderResourceManagerImpl(){
//Private Constructor to prevent external extension
}
//Factory Method
static ShaderResourceManager getSRM(){
return SRM;
}
/**
* Fetches a shader id for a given fragment shader, generating
* a new id if necessary.</br>
*
* @param fragmentFileName the fragment shader to fetch an id for.
* @returns shaderID for a given fragment shader.</br>
*/
public int getFragementShaderID(String fragmentFileName)throws SlickException{
Integer id = shaderMap.get(fragmentFileName);
if(id==null){
id = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
shaderMap.put(fragmentFileName, id);
GL20.glShaderSource(id, getProgramCode(fragmentFileName));
GL20.glCompileShader(id);
}
return id;
}
/**
* Fetches a shader id for a given vertex shader, generating
* a new id if necessary.</br>
*
* @param vertexFileName the vertex shader to fetch an id for.
* @returns shaderID for a given vertex shader.</br>
*/
public int getVertexShaderID(String vertexFileName)throws SlickException{
Integer id = shaderMap.get(vertexFileName);
if(id==null){
id = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
shaderMap.put(vertexFileName, id);
GL20.glShaderSource(id, getProgramCode(vertexFileName));
GL20.glCompileShader(id);
}
return id;
}
/**
* Link a shader that the shader program depends on to operate.</br>
* @param programID
* @param shaderID
*/
public void createProgramShaderDependancy(int programID,
int shaderID){
if(!shaderMap.containsValue(shaderID)){
throw new IllegalArgumentException("Cannot link a shader " +
"id that does not exist.");
}
//Add Shader to list of shaders used by program
Set<Integer> shaders = programToShaders.get(programID);
if(shaders==null){
shaders = new HashSet<Integer>();
programToShaders.put(programID, shaders);
}
shaders.add(shaderID);
//Add program to list of programs used by Shader
Set<Integer> programs = shaderToPrograms.get(shaderID);
if(programs==null){
programs = new HashSet<Integer>();
shaderToPrograms.put(shaderID, programs);
}
programs.add(programID);
}
public void createProgramShaderDependancies(int programID,
Iterable<Integer> ids){
for(int id : ids){
createProgramShaderDependancy(programID, id);
}
}
/**
* Removes the program.</br>
* After calling this method the program specified will no longer
* be loaded, nor will any shaders that were still in use only by
* only that program.</br>
*
* @params programID the ID of the program to remove.
*/
//This is a rather inefficient implementation however, it need not
//be fast and this representation is very simple to follow and
//debug.
public void removeProgram(int programID){
Set<Integer> shaders = programToShaders.get(programID);
if(shaders==null){
throw new IllegalArgumentException("The programID " +
programID +
"does not exist");
}
//detach Shaders from program
for(int id : shaders){
GL20.glDetachShader(programID, id);
}
//Delete unused shaders
for(int id : shaders){
Set<Integer> progs = shaderToPrograms.get(id);
progs.remove(programID);
if(progs.isEmpty()){
GL20.glDeleteShader(id);
shaderToPrograms.remove(id);
}
}
//Delete Program
GL20.glDeleteProgram(programID);
programToShaders.remove(programID);
}
/**
* Gets the program code from the file <tt>filename</tt> and puts
* it into a <tt>ByteBuffer</tt>.</br>
* @param filename the full name of the file.
* @return a ByteBuffer containing the program code.
* @throws SlickException
*/
private ByteBuffer getProgramCode(String filename)throws SlickException{
InputStream fileInputStream = null;
byte[] shaderCode = null;
fileInputStream = ResourceLoader.getResourceAsStream(filename);
DataInputStream dataStream = new DataInputStream(fileInputStream);
try{
dataStream.readFully(shaderCode = new byte[fileInputStream.available()]);
fileInputStream.close();
dataStream.close();
}catch (IOException e) {
throw new SlickException(e.getMessage());
}
ByteBuffer shaderPro = BufferUtils.createByteBuffer(shaderCode.length);
shaderPro.put(shaderCode);
shaderPro.flip();
return shaderPro;
}
}

View File

@@ -0,0 +1,25 @@
package shader;
import org.newdawn.slick.SlickException;
/**
* Simply interface for component that manages shaders source files.
* @author Chronocide (Jeremy Klix)
*
*/
public interface ShaderResourceManager{
int getFragementShaderID(String fragmentFileName)throws SlickException;
int getVertexShaderID(String vertexFileName)throws SlickException;
/**
* Link a shader that the shader program depends on to operate.</br>
* @param programID
* @param shaderID
*/
void createProgramShaderDependancy(int programID, int shaderID);
void removeProgram(int programID);
}

234
src/shader/ShaderVariable.java Executable file
View File

@@ -0,0 +1,234 @@
package shader;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL20;
/**
* Class used to keep track of variables associated with this
* shader.</br>
*
* @author Chronocide (Jeremy Klix)
*
*/
class ShaderVariable{
public enum Qualifier{
ATTRIBUTE("attribute"), UNIFORM("uniform"), VARYING("varying");
private static final Map<String, ShaderVariable.Qualifier> stringToEnum =
new HashMap<String, ShaderVariable.Qualifier>();
static{
for(ShaderVariable.Qualifier qual : values()){
stringToEnum.put(qual.toString(),qual);
}
}
private String name;
Qualifier(String name){
this.name = name;
}
public String toString(){
return name;
}
public static ShaderVariable.Qualifier fromString(String token){
return stringToEnum.get(token);
}
}
public enum Type{
BOOLEAN("boolean"), DOUBLE("double"), FLOAT("float"),
INTEGER("integer");
private String name;
Type(String name){
this.name = name;
}
public String toString(){
return name;
}
}
private static final String TYPE_WARN =
"Warning!\nProblem setting %s variable. " +
"Expected type %s but got type %s instead.\n";
private static final String QUAL_WARN =
"Warning!\nProblem setting %s variable. " +
"Expected qualifier %s but got %s instead.\n";
private ShaderVariable.Qualifier qualifier = null;
private ShaderVariable.Type type = null;
private int vecSize; //size of vector
private int size; //size of array non arrays are size 1
private int programID;
private int location = -1;
/**Set true if GLSL has removed this unused variable*/
private boolean isCulled = false;
String name = "";
ShaderVariable(int programID, String name,
ShaderVariable.Qualifier qual,
ShaderVariable.Type type,
int vecSize,
int size){
this.programID = programID;
this.name = name;
this.qualifier = qual;
this.type = type;
if(vecSize<1){
throw new IllegalArgumentException("size of elements must be greater than 0");
}
this.vecSize = vecSize;
if(size<1){
throw new IllegalArgumentException("number of elements must be greater than 0");
}
this.size = size;
}
public String toString(){
return name;
}
public boolean equals(Object obj){
if(obj instanceof ShaderVariable){
return this.toString().equals(obj.toString());
}
return false;
}
public int hashCode(){
return name.hashCode();
}
//TODO TEST
void setUniformValue(boolean[] vals){
if(this.type!=Type.BOOLEAN){
System.err.printf(TYPE_WARN, this.name, this.type, Type.BOOLEAN);
}
if(this.qualifier!=Qualifier.UNIFORM){
System.err.printf(QUAL_WARN, this.name, this.qualifier, Qualifier.UNIFORM);
}
if(vals.length!=vecSize){
throw new AssertionError("Incorrect number of arguments.");
}
//No GL methods to set boolean uniforms exist
if(location==-1){
CharSequence param = new StringBuffer(name);
location = GL20.glGetUniformLocation(programID, param);
locationCheck();
}
IntBuffer fb = BufferUtils.createIntBuffer(vals.length);
for(boolean b : vals){
fb.put(b? 1 : 0);
}
fb.flip();
switch(vecSize){
case 1: GL20.glUniform1(location, fb); break;
case 2: GL20.glUniform2(location, fb); break;
case 3: GL20.glUniform3(location, fb); break;
case 4: GL20.glUniform4(location, fb); break;
}
}
void setUniformValue(float[] vals){
if(this.type!=Type.FLOAT){
System.err.printf(TYPE_WARN, this.name, this.type, Type.FLOAT);
}
if(this.qualifier!=Qualifier.UNIFORM){
System.err.printf(QUAL_WARN, this.name, this.qualifier, Qualifier.UNIFORM);
}
if(vals.length!=vecSize*size){
throw new AssertionError("Incorrect number of values.\n" +
"Expected " + vecSize*size + " vlaues but got " +
vals.length + " values instead.");
}
if(location==-1){
CharSequence param = new StringBuffer(name);
location = GL20.glGetUniformLocation(programID, param);
locationCheck();
}
FloatBuffer fb = BufferUtils.createFloatBuffer(vals.length);
fb.put(vals);
fb.flip();
switch(vecSize){
case 1: GL20.glUniform1(location, fb); break;
case 2: GL20.glUniform2(location, fb); break;
case 3: GL20.glUniform3(location, fb); break;
case 4: GL20.glUniform4(location, fb); break;
}
}
void setUniformValue(int[] vals){
if(this.type!=Type.INTEGER){
System.err.printf(TYPE_WARN, this.type, Type.INTEGER);
}
if(this.qualifier!=Qualifier.UNIFORM){
System.err.printf(QUAL_WARN, this.name, this.qualifier, Qualifier.UNIFORM);
}
if(vals.length!=vecSize*size){
throw new AssertionError("Incorrect number of values.\n" +
"Expected " + vecSize*size + " vlaues but got " +
vals.length + " values instead.");
}
if(location==-1){
CharSequence param = new StringBuffer(name);
location = GL20.glGetUniformLocation(programID, param);
locationCheck();
}
IntBuffer fb = BufferUtils.createIntBuffer(vals.length);
fb.put(vals);
fb.flip();
switch(vecSize){
case 1: GL20.glUniform1(location, fb); break;
case 2: GL20.glUniform2(location, fb); break;
case 3: GL20.glUniform3(location, fb); break;
case 4: GL20.glUniform4(location, fb); break;
}
}
private void locationCheck(){
if(location==-1 && !isCulled){
System.err.println("Location for variable " + name +
"could not be found.\nGLSL may remove " +
"any vairable that does not contribute " +
"to an output. Check and ensure " +
"that " + name + "is an active variable.\n");
isCulled = true;
}
}
}