package net.fourbytes.shadow.utils; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.glutils.ShaderProgram; import com.badlogic.gdx.utils.ObjectMap; import net.fourbytes.shadow.Shadow; public final class ShaderHelper { private ShaderHelper() { } private static ObjectMap<String, ShaderProgram> shaders = new ObjectMap<String, ShaderProgram>(); private static ObjectMap<String, Object[]> defaults = new ObjectMap<String, Object[]>(); private static ObjectMap<String, Object[]> values = new ObjectMap<String, Object[]>(); private static ShaderProgram current; private static String currentName; public static ShaderProgram getCurrentShader() { return current; } public static String getCurrentShaderName() { return currentName; } public static void resetCurrentShader() { setCurrentShader(""); } public static void setCurrentShader(String name) { current = getShader(name); if (current == null) { ShaderProgram shader = ShaderHelper.loadShader("shaders/"+name); ShaderHelper.addShader(shader, name); current = shader; } currentName = name; Shadow.spriteBatch.setShader(current); Shadow.spriteCache.setShader(current); setAll(); } public static ShaderProgram getDefaultShader() { return getShader(""); } public static ShaderProgram getShader(String name) { return shaders.get(name); } public static void addShader(ShaderProgram shader) { addShader(shader, ""); } public static void addShader(ShaderProgram shader, String name) { shaders.put(name, shader); for (ObjectMap.Entry entry : values.entries()) { values.remove((String) entry.key); set((String) entry.key, entry.value); } } public static void setDefault(boolean set, String setting, Object... values) { if (values[0] instanceof Object[]) { values = (Object[]) values[0]; } defaults.put(setting, values); if (set) { set(setting, (Object[]) values); } } public static void set(String setting, Object... values) { if (values[0] instanceof Object[]) { values = (Object[]) values[0]; } Object[] rawoldvals = ShaderHelper.values.get(setting); if (rawoldvals != null && rawoldvals.length == values.length) { MultiObject oldvals = Garbage.multiobjs.getNext(); Object[] oldobjects = oldvals.objects; oldvals.objects = rawoldvals; MultiObject newvals = Garbage.multiobjs.getNext(); Object[] newobjects = newvals.objects; newvals.objects = values; boolean equals = oldvals.equals(newvals); oldvals.objects = oldobjects; newvals.objects = newobjects; if (equals) { return; } } ShaderHelper.values.put(setting, values); //1 float or 1 int if (values.length == 1) { //1 boolean try { boolean value; if (values[0] instanceof Boolean) { value = (Boolean)values[0]; } else { value = Boolean.parseBoolean((String)values[0]); } for (ShaderProgram shader : shaders.values()) { shader.begin(); shader.setUniformi(setting, value?1:0); shader.end(); } return; } catch (Exception e) { } //1 int try { int value; if (values[0] instanceof Integer) { value = (Integer)values[0]; } else { value = Integer.parseInt((String)values[0]); } for (ShaderProgram shader : shaders.values()) { shader.begin(); shader.setUniformi(setting, value); shader.end(); } return; } catch (Exception e) { } //1 float try { float value; if (values[0] instanceof Float) { value = (Float)values[0]; } else { value = Float.parseFloat((String)values[0]); } for (ShaderProgram shader : shaders.values()) { shader.begin(); shader.setUniformf(setting, value); shader.end(); } return; } catch (Exception e) { } System.err.println("1: Can not set ["+values[0]+"] to "+setting+": Unknown input type"); return; } //2 floats if (values.length == 2) { try { float value1; if (values[0] instanceof Float) { value1 = (Float)values[0]; } else { value1 = Float.parseFloat((String)values[0]); } float value2; if (values[1] instanceof Float) { value2 = (Float)values[1]; } else { value2 = Float.parseFloat((String)values[1]); } for (ShaderProgram shader : shaders.values()) { shader.begin(); shader.setUniformf(setting, value1, value2); shader.end(); } return; } catch (Exception e) { } System.err.println("2: Can not set ["+values[0]+", "+values[1]+"] to "+setting+": Unknown input type"); return; } //3 floats if (values.length == 3) { try { float value1; if (values[0] instanceof Float) { value1 = (Float)values[0]; } else { value1 = Float.parseFloat((String)values[0]); } float value2; if (values[1] instanceof Float) { value2 = (Float)values[1]; } else { value2 = Float.parseFloat((String)values[1]); } float value3; if (values[2] instanceof Float) { value3 = (Float)values[2]; } else { value3 = Float.parseFloat((String)values[2]); } for (ShaderProgram shader : shaders.values()) { shader.begin(); shader.setUniformf(setting, value1, value2, value3); shader.end(); } return; } catch (Exception e) { } System.err.println("3: Can not set ["+values[0]+", "+values[1]+", "+values[2]+"] to "+setting+": Unknown input type"); return; } //4 floats if (values.length == 4) { try { float value1; if (values[0] instanceof Float) { value1 = (Float)values[0]; } else { value1 = Float.parseFloat((String)values[0]); } float value2; if (values[1] instanceof Float) { value2 = (Float)values[1]; } else { value2 = Float.parseFloat((String)values[1]); } float value3; if (values[2] instanceof Float) { value3 = (Float)values[2]; } else { value3 = Float.parseFloat((String)values[2]); } float value4; if (values[3] instanceof Float) { value4 = (Float)values[3]; } else { value4 = Float.parseFloat((String)values[3]); } for (ShaderProgram shader : shaders.values()) { shader.begin(); shader.setUniformf(setting, value1, value2, value3, value4); shader.end(); } return; } catch (Exception e) { } System.err.println("4: Can not set ["+values[0]+", "+values[1]+", "+values[2]+", "+values[3]+"] to "+setting+": Unknown input type"); return; } System.err.println("?: Can not set value of "+setting+": Unknown input type"); } public static void setAll() { for (ObjectMap.Entry entry : values.entries()) { set((String)entry.key, entry.value); } } public static void reset(String setting) { set(setting, defaults.get(setting)); } public static Object[] getDefault(String setting) { return defaults.get(setting); } public static Object[] get(String setting) { return values.get(setting); } public static String loadImports(String shader, String imports_root) { String finalShader = ""; String[] lines = shader.split("\n"); for (int i = 0; i < lines.length; i++) { String input = lines[i]; String output; if (input.startsWith("#import ")) { String importPath = input.substring(8, input.indexOf(';')); String subshader = Gdx.files.internal(imports_root + "/" + importPath + ".glsl").readString(); output = loadImports(subshader, imports_root); } else if (input.startsWith("#copy ")) { String importPath = input.substring(6, input.indexOf(';')); String subshader = Gdx.files.internal(importPath).readString(); output = loadImports(subshader, imports_root); } else { output = input; } finalShader += output; if (i < lines.length - 1) { finalShader += "\n"; } } finalShader = finalShader.trim(); return finalShader; } public static String setupShaderSource(String shader, String imports_root) { shader = loadImports(shader, imports_root); String finalShader = ""; String[] lines = shader.split("\n"); for (int i = 0; i < lines.length; i++) { String input = lines[i]; String output; if (input.startsWith("#setting ")) { String postPrefix = input.substring(9, input.indexOf(';')); String setting = postPrefix.trim(); String type = setting.substring(0, setting.indexOf(' ')).trim(); setting = setting.substring(setting.indexOf(' ')+1).trim(); Object[] values = {-1}; if (setting.contains("=")) { String rawValue = setting.substring(setting.indexOf('=')+1).trim(); if (rawValue.contains(",")) { values = rawValue.split(","); for (int ii = 0; ii < values.length; ii++) { values[ii] = ((String)values[ii]).trim(); } } else { values[0] = rawValue; } setting = setting.substring(0, setting.indexOf('=')).trim(); } setDefault(false, setting, values); output = "uniform "+type+" "+setting+";"; } else { output = input; } finalShader += output; if (i < lines.length - 1) { finalShader += "\n"; } } finalShader = finalShader.trim(); return finalShader; } public static ShaderProgram loadShader(String path) { String vertex = Gdx.files.internal(path+".vert.glsl").readString(); String fragment = Gdx.files.internal(path+".frag.glsl").readString(); vertex = ShaderHelper.setupShaderSource(vertex, "shaders/imports"); fragment = ShaderHelper.setupShaderSource(fragment, "shaders/imports"); ShaderProgram shader = new ShaderProgram(vertex, fragment); if (shader.getLog().length()!=0) { System.err.println("--------SHADER ERROR--------"); System.err.print(shader.getLog()); System.err.println("--------SOURCES--------"); String[] lines; System.err.println("--------VERTEX--------"); lines = vertex.split("\n"); for (int i = 0; i < lines.length; i++) { System.err.println((i+1)+": "+lines[i]); } System.err.println("--------FRAGMENT--------"); lines = fragment.split("\n"); for (int i = 0; i < lines.length; i++) { System.err.println((i+1)+": "+lines[i]); } System.err.println("--------END--------"); } return shader; } }