package me.hammerle.snuviengine.api; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Scanner; import static org.lwjgl.opengl.GL11.GL_NO_ERROR; import static org.lwjgl.opengl.GL11.glGetError; 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_LINK_STATUS; import static org.lwjgl.opengl.GL20.GL_VERTEX_SHADER; import static org.lwjgl.opengl.GL20.glAttachShader; 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.glDeleteShader; 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.glLinkProgram; import static org.lwjgl.opengl.GL20.glShaderSource; public class Shader { public static int createShaderProgram(String vertex, String fragment) { int vertexShader = readAndCompileShader(vertex, GL_VERTEX_SHADER); if(vertexShader == -1) { return -1; } int fragmentShader = readAndCompileShader(fragment, GL_FRAGMENT_SHADER); if(fragmentShader == -1) { return -1; } return createShaderProgram(vertexShader, fragmentShader); } private static int readAndCompileShader(String file, int shaderType) { String[] shaderSource = readFile(file); if(shaderSource == null) { return -1; } return compileShader(file, shaderSource, shaderType); } private static String[] readFile(String name) { URL url = Renderer.class.getClassLoader().getResource("me/hammerle/snuviengine/shader/" + name); if(url == null) { printError("failed reading shader '%s'", name); return null; } return readUrl(url); } private static String[] readUrl(URL url) { try(Scanner scanner = new Scanner(url.openStream())) { return readScanner(scanner); } catch(IOException ex) { printError("failed reading shader '%s'", url); return null; } } private static String[] readScanner(Scanner scanner) { ArrayList strings = new ArrayList<>(); while(scanner.hasNext()) { strings.add(scanner.nextLine() + "\n"); } return strings.toArray(new String[strings.size()]); } private static int compileShader(String name, String[] shaderSource, int shaderType) { int shader = glCreateShader(shaderType); glShaderSource(shader, shaderSource); glCompileShader(shader); if(checkForShaderError(name, shader)) { return -1; } return shader; } private static boolean checkForShaderError(String name, int shader) { int error = glGetError(); if(error != GL_NO_ERROR) { printError("failed compiling shader '%s' with error code: %d", name, error); return true; } int compiled = glGetShaderi(shader, GL_COMPILE_STATUS); if(compiled != 1) { printError("failed compiling shader '%s': %s", name, glGetShaderInfoLog(shader)); return true; } return false; } private static void printError(String format, Object... o) { System.out.println(String.format(format, o)); } private static int createShaderProgram(int vertexShader, int fragmentShader) { int program = glCreateProgram(); glAttachShader(program, vertexShader); glAttachShader(program, fragmentShader); glLinkProgram(program); if(checkForLinkingError(program)) { return -1; } return program; } private static boolean checkForLinkingError(int program) { int error = glGetError(); if(error != GL_NO_ERROR) { printError("failed linking shaders with error code: %d", error); return true; } int compiled = glGetProgrami(program, GL_LINK_STATUS); if(compiled != 1) { printError("failed linking shaders: %s", glGetProgramInfoLog(program)); return true; } return false; } }