package com.osufx.sunpy; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL20; import org.newdawn.slick.util.Log; import org.newdawn.slick.util.ResourceLoader; import java.io.InputStream; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.*; public class Shader { public String name; public int programId = -1; public boolean loaded = false; public boolean started = false; public PropertyCollection Properties; public static HashMap globalProperties = new HashMap<>(); public static List loadedShaders = new ArrayList<>(); public static int currentShader = 0; private HashMap attribLoc = new HashMap<>(); private static final String[] attribs = new String[] { "m_Position", "m_Color", "m_TexCoord", "m_Time", "m_Direction" }; private HashMap shaderParts = new HashMap<>(); private int previousShader; public Shader(String vertexShader, String fragmentShader) { String vertexString; String fragmentString; try { InputStream vertexStream = ResourceLoader.getResourceAsStream(vertexShader); vertexString = convertStreamToString(vertexStream); InputStream fragmentStream = ResourceLoader.getResourceAsStream(fragmentShader); fragmentString = convertStreamToString(fragmentStream); } catch (Exception e){ e.printStackTrace(); return; } this.name = String.format("%s/%s", vertexShader, fragmentShader); this.Properties = new PropertyCollection(this); this.programId = GL20.glCreateProgram(); this.shaderParts.put(GL20.glCreateShader(GL20.GL_VERTEX_SHADER), vertexString); this.shaderParts.put(GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER), fragmentString); this.CompileParts(); this.Compile(); } public Shader(String shader, final int shaderType) { if (shaderType != GL20.GL_FRAGMENT_SHADER && shaderType != GL20.GL_VERTEX_SHADER){ System.out.println("Invalid shader type"); return; } String shaderString; try { InputStream shaderStream = ResourceLoader.getResourceAsStream(shader); shaderString = convertStreamToString(shaderStream); } catch (Exception e) { e.printStackTrace(); return; } this.name = String.format("%s/%s", shaderType == GL20.GL_FRAGMENT_SHADER ? "Fragment" : "Vertex", shader); this.Properties = new PropertyCollection(this); this.programId = GL20.glCreateProgram(); this.shaderParts.put(GL20.glCreateShader(shaderType), shaderString); this.CompileParts(); this.Compile(); } public void CompileParts() { for (Map.Entry entry : this.shaderParts.entrySet()) { int handle = entry.getKey(); String source = entry.getValue(); GL20.glShaderSource(handle, source); GL20.glCompileShader(handle); int res = GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS); if (res != GL11.GL_TRUE) { String error = GL20.glGetShaderInfoLog(handle, 1024); Log.error("Shader compilation failed.", new Exception(error)); } } } public void Compile() { this.Properties.Clear(); for (int handler : this.shaderParts.keySet()) { GL20.glAttachShader(this.programId, handler); } GL20.glLinkProgram(this.programId); int res = GL20.glGetProgrami(this.programId, GL20.GL_LINK_STATUS); if (res != GL11.GL_TRUE) { String error = GL20.glGetProgramInfoLog(this.programId, 1024); Log.error("Program linking failed.", new Exception(error)); } for (int handler : this.shaderParts.keySet()) { GL20.glDeleteShader(handler); } for (int i = 0; i < attribs.length; i++) { int attrib = GL20.glGetAttribLocation(this.programId, attribs[i]); this.attribLoc.put(attribs[i], attrib); //GL20.glBindAttribLocation(this.programId, i, attribLoc[i]); } /*for (int handler : this.shaderParts.keySet()) { GL20.glDetachShader(this.programId, handler); }*/ this.loaded = res == GL11.GL_TRUE; if (this.loaded) { int uniCount = GL20.glGetProgrami(this.programId, GL20.GL_ACTIVE_UNIFORMS); for (int i = 0; i < uniCount; i++) { IntBuffer length = BufferUtils.createIntBuffer(1); IntBuffer size = BufferUtils.createIntBuffer(1); IntBuffer type = BufferUtils.createIntBuffer(1); ByteBuffer name = ByteBuffer.allocateDirect(64); GL20.glGetActiveUniform(this.programId, i, length, size, type, name); byte[] buf = new byte[length.get(0)]; name.get(buf, 0, buf.length); String propName = new String(buf); int propLocation = GL20.glGetUniformLocation(programId, propName); UniformType propUniformType = UniformType.map.get(type.get(0)); Class propType = ShaderProperty.ConvertType(propUniformType.name()); System.out.println(String.format("propName:%s PropUniformType:%s PropType:%s", propName, propUniformType, propType)); this.Properties.Add(new ShaderProperty( propName, propLocation, propType, propUniformType )); } for (Map.Entry entry : globalProperties.entrySet()) { this.Properties.Replace(entry.getKey(), entry.getValue()); } loadedShaders.add(this); } } public void Begin(){ if (this.started || !this.loaded) return; GL20.glUseProgram(this.programId); this.previousShader = currentShader; currentShader = this.programId; this.started = true; } public void End() { if (!this.started) return; GL20.glUseProgram(this.previousShader); currentShader = this.previousShader; this.started = false; } static String convertStreamToString(InputStream is) { Scanner s = new Scanner(is).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; } }