package chu.engine.anim; import static org.lwjgl.opengl.GL11.GL_FALSE; import static org.lwjgl.opengl.GL11.GL_LINES; import static org.lwjgl.opengl.GL11.GL_NEAREST; import static org.lwjgl.opengl.GL11.GL_QUADS; import static org.lwjgl.opengl.GL11.GL_SCISSOR_TEST; import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D; import static org.lwjgl.opengl.GL11.GL_TEXTURE_MAG_FILTER; import static org.lwjgl.opengl.GL11.GL_TEXTURE_MIN_FILTER; import static org.lwjgl.opengl.GL11.GL_TRIANGLES; import static org.lwjgl.opengl.GL11.glBegin; import static org.lwjgl.opengl.GL11.glColor4f; import static org.lwjgl.opengl.GL11.glDisable; import static org.lwjgl.opengl.GL11.glEnable; import static org.lwjgl.opengl.GL11.glEnd; import static org.lwjgl.opengl.GL11.glLineWidth; import static org.lwjgl.opengl.GL11.glPopMatrix; import static org.lwjgl.opengl.GL11.glPushMatrix; import static org.lwjgl.opengl.GL11.glRotatef; import static org.lwjgl.opengl.GL11.glScalef; import static org.lwjgl.opengl.GL11.glScissor; import static org.lwjgl.opengl.GL11.glTexCoord2f; import static org.lwjgl.opengl.GL11.glTexParameteri; import static org.lwjgl.opengl.GL11.glTranslatef; import static org.lwjgl.opengl.GL11.glVertex3f; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Stack; import net.fe.FEResources; import org.lwjgl.opengl.ARBFragmentShader; import org.lwjgl.opengl.ARBShaderObjects; import org.lwjgl.opengl.ARBVertexShader; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL20; import org.newdawn.slick.Color; import org.newdawn.slick.opengl.Texture; import org.newdawn.slick.opengl.TextureLoader; import org.newdawn.slick.util.ResourceLoader; import chu.engine.Game; public class Renderer { private static Camera camera; private static RectClip clip; private static final int SCALE_FILTER = GL_NEAREST; private static Color color; private static Stack<RendererState> stateStack; private static HashMap<String, Integer> programs; static { System.out.println(GL11.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION)); System.out.println(GL11.glGetInteger(GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)); stateStack = new Stack<RendererState>(); programs = new HashMap<String, Integer>(); camera = new Camera(null, 0, 0); clip = null; color = Color.white; programs.put("default", createProgram("default", "default")); programs.put("greyscale", createProgram("default", "greyscale")); programs.put("lighten", createProgram("default", "lighten")); programs.put("paletteSwap", createProgram("betterSwap", "betterSwap")); } /*** * Draws the given subtexture at the given coordinates. * * @param t * Texture to be drawn * @param tx0 * First texture x coord * @param ty0 * First texture y coord * @param tx1 * Second texture x coord * @param ty1 * Second texture y coord * @param x0 * First render x coord * @param y0 * First render y coord * @param x1 * Second render x coord * @param y1 * Second render y coord */ public static void render(Texture t, float tx0, float ty0, float tx1, float ty1, float x0, float y0, float x1, float y1, float depth) { render(t, tx0, ty0, tx1, ty1, x0, y0, x1, y1, depth, null, new ShaderArgs()); } public static void render(Texture t, float tx0, float ty0, float tx1, float ty1, float x0, float y0, float x1, float y1, float depth, Transform transform) { render(t, tx0, ty0, tx1, ty1, x0, y0, x1, y1, depth, transform, new ShaderArgs()); } public static void render(Texture t, float tx0, float ty0, float tx1, float ty1, float x0, float y0, float x1, float y1, float depth, Transform transform, ShaderArgs shader) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, SCALE_FILTER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, SCALE_FILTER); int program = programs.get(shader.programName); ARBShaderObjects.glUseProgramObjectARB(program); shader.bindArgs(program); t.bind(); glPushMatrix(); if(transform != null) { Color c = transform.color.multiply(color); glColor4f(c.r, c.g, c.b, c.a); glTranslatef(x0, y0, depth); glTranslatef(transform.translateX, transform.translateY, 0); glScalef(transform.scaleX, transform.scaleY, 0); glTranslatef(-x0 + (x0 + x1) / 2, -y0 + (y0 + y1) / 2, 0); glRotatef(transform.rotation / (float) Math.PI * 180, 0, 0, 1); glTranslatef(-(x0 + x1) / 2, -(y0 + y1) / 2, -depth); // do flip operations if (transform.flipHorizontal) { float temp = tx0; tx0 = tx1; tx1 = temp; } if (transform.flipVertical) { float temp = ty0; ty0 = ty1; ty1 = temp; } } else { glColor4f(color.r, color.g, color.b, color.a); } // Compensation for non power of 2 images float txi = tx0*t.getImageWidth()/t.getTextureWidth(); float tyi = ty0*t.getImageHeight()/t.getTextureHeight(); float txf = tx1*t.getImageWidth()/t.getTextureWidth(); float tyf = ty1*t.getImageHeight()/t.getTextureHeight(); // draw quad glBegin(GL_QUADS); glTexCoord2f(txi, tyi); glVertex3f(x0, y0, depth); glTexCoord2f(txf, tyi); glVertex3f(x1, y0, depth); glTexCoord2f(txf, tyf); glVertex3f(x1, y1, depth); glTexCoord2f(txi, tyf); glVertex3f(x0, y1, depth); glEnd(); glPopMatrix(); if(clip != null && !clip.persistent) clip.destroy(); ARBShaderObjects.glUseProgramObjectARB(0); } public static void drawSquare(float x, float y, float s, float depth, Color c) { drawRectangle(x, y, x + s, y + s, depth, c); } public static void drawRectangle(float x0, float y0, float x1, float y1, float depth, Color c) { drawRectangle(x0, y0, x1, y1, depth, c, c, c, c); } public static void drawBorderedRectangle(float x0, float y0, float x1, float y1, float depth, Color... c){ for(int i = c.length - 1; i >= 0; i--){ drawRectangle(x0 - i, y0 - i, x1 + i, y1 + i, depth, c[i]); } } public static void drawRectangle(float x0, float y0, float x1, float y1, float depth, Color c0, Color c1, Color c2, Color c3) { glDisable(GL_TEXTURE_2D); glBegin(GL_QUADS); c0 = c0.multiply(color); c1 = c1.multiply(color); c2 = c2.multiply(color); c3 = c3.multiply(color); glColor4f(c0.r, c0.g, c0.b, c0.a); glVertex3f(x0, y0, depth); glColor4f(c1.r, c1.g, c1.b, c1.a); glVertex3f(x1, y0, depth); glColor4f(c2.r, c2.g, c2.b, c2.a); glVertex3f(x1, y1, depth); glColor4f(c3.r, c3.g, c3.b, c3.a); glVertex3f(x0, y1, depth); glEnd(); glEnable(GL_TEXTURE_2D); if(clip != null && !clip.persistent) clip.destroy(); } public static void drawLine(float x0, float y0, float x, float y, float width, float depth, Color c1, Color c2) { glDisable(GL_TEXTURE_2D); glLineWidth(width); // glLoadIdentity(); glBegin(GL_LINES); c1 = c1.multiply(color); c2 = c2.multiply(color); glColor4f(c1.r, c1.g, c1.b, c1.a); glVertex3f(x0, y0, depth); glColor4f(c2.r, c2.g, c2.b, c2.a); glVertex3f(x, y, depth); glEnd(); glEnable(GL_TEXTURE_2D); if(clip != null && !clip.persistent) clip.destroy(); } public static void drawTriangle(float x0, float y0, float x, float y, float x2, float y2, float depth, Color c) { c.bind(); glDisable(GL_TEXTURE_2D); c = c.multiply(color); glColor4f(c.r, c.g, c.b, c.a); glBegin(GL_TRIANGLES); glVertex3f(x0, y0, depth); glVertex3f(x, y, depth); glVertex3f(x2, y2, depth); glEnd(); glEnable(GL_TEXTURE_2D); if(clip != null && !clip.persistent) clip.destroy(); } public static void drawString(String fontName, int num, float x, float y, float depth) { drawString(fontName, num+"", x, y, depth); } public static void drawString(String fontName, String string, float x, float y, float depth) { drawString(fontName, string, x, y, depth, null); } public static void drawString(String fontName, String string, float x, float y, float depth, Transform t) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, SCALE_FILTER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, SCALE_FILTER); FEResources.getBitmapFont(fontName).render(string, x, y, depth, t); if(clip != null && !clip.persistent) clip.destroy(); } public static void translate(float x, float y) { glTranslatef(x, y, 0); } public static void scale(float x, float y) { glScalef(x, y, 0); } public static void setColor(Color c) { if(c == null) color = Color.white; else color = c; } public static void pushMatrix() { glPushMatrix(); RendererState state = new RendererState(); state.color = color; state.clip = clip; stateStack.push(state); } public static void popMatrix() { glPopMatrix(); RendererState state = stateStack.pop(); color = state.color; clip = state.clip; stateStack.push(state); } public static void setCamera(Camera c) { camera = c; } public static Camera getCamera() { return camera; } public static void addClip(float f, float g, float h, float i, boolean persistent) { clip = new RectClip(f, g, h, i, persistent); } public static void removeClip() { if(clip != null) clip.destroy(); } /* * With the exception of syntax, setting up vertex and fragment shaders * is the same. * @param the name and path to the vertex shader */ private static int createShader(String filename, int shaderType) throws Exception { int shader = 0; try { shader = ARBShaderObjects.glCreateShaderObjectARB(shaderType); if(shader == 0) return 0; ARBShaderObjects.glShaderSourceARB(shader, readFileAsString(filename)); ARBShaderObjects.glCompileShaderARB(shader); if (ARBShaderObjects.glGetObjectParameteriARB(shader, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB) == GL_FALSE) throw new RuntimeException("Error creating shader: " + getLogInfo(shader)); return shader; } catch(Exception exc) { ARBShaderObjects.glDeleteObjectARB(shader); throw exc; } } private static int createProgram(String vertShader, String fragShader) { // Create program object int prog = ARBShaderObjects.glCreateProgramObjectARB(); // Create vertex shader try { int vertexShader = createShader("shaders/"+vertShader+".vert",ARBVertexShader.GL_VERTEX_SHADER_ARB); if(vertexShader == 0) throw new Exception(); ARBShaderObjects.glAttachObjectARB(prog, vertexShader); } catch (Exception e) { System.err.println("Unable to create vertex shader: "+vertShader); e.printStackTrace(); } // Create fragment shader try { int fragmentShader = createShader("shaders/"+fragShader+".frag",ARBFragmentShader.GL_FRAGMENT_SHADER_ARB); if(fragmentShader == 0) throw new Exception(); ARBShaderObjects.glAttachObjectARB(prog, fragmentShader); } catch (Exception e) { System.err.println("Unable to create fragment shader: "+fragShader); e.printStackTrace(); } // Link program ARBShaderObjects.glLinkProgramARB(prog); if (ARBShaderObjects.glGetObjectParameteriARB(prog, ARBShaderObjects.GL_OBJECT_LINK_STATUS_ARB) == GL_FALSE) { System.err.println(getLogInfo(prog)); System.out.println("crap"); return -1; } // Use program ARBShaderObjects.glUseProgramObjectARB(prog); // Validate program ARBShaderObjects.glValidateProgramARB(prog); if (ARBShaderObjects.glGetObjectParameteriARB(prog, ARBShaderObjects.GL_OBJECT_VALIDATE_STATUS_ARB) == GL_FALSE) { System.err.println(getLogInfo(prog)); System.out.println("shit"); return -1; } // Bind texture units to uniforms int loc = GL20.glGetUniformLocation(prog, "texture1"); GL20.glUniform1i(loc, 0); int loc2 = GL20.glGetUniformLocation(prog, "texture2"); GL20.glUniform1i(loc2, 8); return prog; } private static String getLogInfo(int obj) { return ARBShaderObjects.glGetInfoLogARB(obj, ARBShaderObjects.glGetObjectParameteriARB(obj, ARBShaderObjects.GL_OBJECT_INFO_LOG_LENGTH_ARB)); } /** * I copied this method from the lwjgl website but this code makes me want * to kill myself. I might fix later * @param filename * @return * @throws Exception */ private static String readFileAsString(String filename) throws Exception { StringBuilder source = new StringBuilder(); InputStream in = ResourceLoader.getResourceAsStream(filename); Exception exception = null; BufferedReader reader; try{ reader = new BufferedReader(new InputStreamReader(in,"UTF-8")); Exception innerExc= null; try { String line; while((line = reader.readLine()) != null) source.append(line).append('\n'); } catch(Exception exc) { exception = exc; } finally { try { reader.close(); } catch(Exception exc) { if(innerExc == null) innerExc = exc; else exc.printStackTrace(); } } if(innerExc != null) throw innerExc; } catch(Exception exc) { exception = exc; } finally { try { in.close(); } catch(Exception exc) { if(exception == null) exception = exc; else exc.printStackTrace(); } if(exception != null) throw exception; } return source.toString(); } public static void addProgram(String name, String vertShader, String fragShader) { programs.put(name, createProgram(vertShader, fragShader)); } static class RectClip { boolean persistent; public RectClip(float x0, float y0, float w, float h, boolean p) { persistent = p; glEnable(GL_SCISSOR_TEST); glScissor( Game.getScaleX()*(int)x0, (int)(Game.getWindowHeight()-Game.getScaleY()*(y0+h)), Game.getScaleX()*(int)w, Game.getScaleY()*(int)h); } public void destroy() { glDisable(GL_SCISSOR_TEST); } } static class RendererState { Color color; RectClip clip; } }