From e9db0c1043be735f0bd68d19058bf2e7a915f7ff Mon Sep 17 00:00:00 2001 From: Brandon Borkholder Date: Fri, 30 Dec 2011 18:02:05 -0500 Subject: [PATCH] New shader pipeline --- src/main/java/glg2d/G2DGLStringDrawer.java | 14 +- src/main/java/glg2d/shaders/BasicShader.java | 138 ++++++++++++++++++ src/main/java/glg2d/shaders/FixedFuncShader.f | 3 + src/main/java/glg2d/shaders/FixedFuncShader.v | 4 + .../glg2d/shaders/G2DShaderImageDrawer.java | 34 +++++ .../glg2d/shaders/G2DShaderStringDrawer.java | 33 +++++ .../glg2d/shaders/GLShaderGraphics2D.java | 31 ++++ src/main/java/glg2d/shaders/Shader.java | 13 ++ .../java/glg2d/shaders/ShaderException.java | 25 ++++ .../java/glg2d/shaders/ShaderRegistry.java | 75 ++++++++++ src/main/java/glg2d/shaders/TextureShader.f | 9 ++ src/main/java/glg2d/shaders/TextureShader.v | 5 + 12 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 src/main/java/glg2d/shaders/BasicShader.java create mode 100644 src/main/java/glg2d/shaders/FixedFuncShader.f create mode 100644 src/main/java/glg2d/shaders/FixedFuncShader.v create mode 100644 src/main/java/glg2d/shaders/G2DShaderImageDrawer.java create mode 100644 src/main/java/glg2d/shaders/G2DShaderStringDrawer.java create mode 100644 src/main/java/glg2d/shaders/GLShaderGraphics2D.java create mode 100644 src/main/java/glg2d/shaders/Shader.java create mode 100644 src/main/java/glg2d/shaders/ShaderException.java create mode 100644 src/main/java/glg2d/shaders/ShaderRegistry.java create mode 100644 src/main/java/glg2d/shaders/TextureShader.f create mode 100644 src/main/java/glg2d/shaders/TextureShader.v diff --git a/src/main/java/glg2d/G2DGLStringDrawer.java b/src/main/java/glg2d/G2DGLStringDrawer.java index 53af2580..f3ba69ce 100644 --- a/src/main/java/glg2d/G2DGLStringDrawer.java +++ b/src/main/java/glg2d/G2DGLStringDrawer.java @@ -130,7 +130,14 @@ protected void setTextColorRespectComposite(TextRenderer renderer, Color color) public void drawString(String string, Color color, int x, int y) { TextRenderer renderer = getRenderer(getFont()); - setTextColorRespectComposite(renderer, color); + + begin(renderer, color); + renderer.draw3D(string, x, g2d.getHeight() - y, 0, 1); + end(renderer); + } + + protected void begin(TextRenderer renderer, Color textColor) { + setTextColorRespectComposite(renderer, textColor); GL gl = g2d.getGLContext().getGL(); gl.glMatrixMode(GL.GL_MODELVIEW); @@ -139,9 +146,12 @@ public void drawString(String string, Color color, int x, int y) { gl.glTranslatef(0, -g2d.getHeight(), 0); renderer.begin3DRendering(); - renderer.draw3D(string, x, g2d.getHeight() - y, 0, 1); + } + + protected void end(TextRenderer renderer) { renderer.end3DRendering(); + GL gl = g2d.getGLContext().getGL(); gl.glPopMatrix(); } diff --git a/src/main/java/glg2d/shaders/BasicShader.java b/src/main/java/glg2d/shaders/BasicShader.java new file mode 100644 index 00000000..df7f2441 --- /dev/null +++ b/src/main/java/glg2d/shaders/BasicShader.java @@ -0,0 +1,138 @@ +package glg2d.shaders; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +import javax.media.opengl.GL; + +public class BasicShader implements Shader { + protected String[] vertexShaderSrc; + protected String[] fragmentShaderSrc; + + protected int vertexShaderId; + protected int fragmentShaderId; + + protected GL gl; + + protected int programId; + + public BasicShader(GL gl) { + this.gl = gl; + } + + public void readSources(String name) { + try { + vertexShaderSrc = readShader(name + ".v"); + fragmentShaderSrc = readShader(name + ".f"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void createAndAttach() { + programId = gl.glCreateProgram(); + gl.glAttachShader(programId, vertexShaderId); + gl.glAttachShader(programId, fragmentShaderId); + gl.glLinkProgram(programId); + checkProgramThrowException(GL.GL_LINK_STATUS); + + gl.glValidateProgram(programId); + checkProgramThrowException(GL.GL_VALIDATE_STATUS); + } + + @Override + public void use(boolean use) { + gl.glUseProgram(use ? programId : 0); + } + + @Override + public void delete() { + gl.glDeleteShader(fragmentShaderId); + gl.glDeleteShader(vertexShaderId); + gl.glDeleteProgram(programId); + } + + @Override + public void compileVertexShader() { + int[] lengths = lengths(vertexShaderSrc); + + vertexShaderId = gl.glCreateShader(GL.GL_VERTEX_SHADER); + gl.glShaderSource(vertexShaderId, vertexShaderSrc.length, vertexShaderSrc, lengths, 0); + gl.glCompileShader(vertexShaderId); + + checkShaderThrowException(vertexShaderId); + } + + @Override + public void compileFragmentShader() { + int[] lengths = lengths(fragmentShaderSrc); + + fragmentShaderId = gl.glCreateShader(GL.GL_FRAGMENT_SHADER); + gl.glShaderSource(fragmentShaderId, fragmentShaderSrc.length, fragmentShaderSrc, lengths, 0); + gl.glCompileShader(fragmentShaderId); + + checkShaderThrowException(fragmentShaderId); + } + + protected void checkShaderThrowException(int shader) { + int[] result = new int[1]; + gl.glGetShaderiv(shader, GL.GL_COMPILE_STATUS, result, 0); + if (result[0] == GL.GL_TRUE) { + return; + } + + gl.glGetShaderiv(shader, GL.GL_INFO_LOG_LENGTH, result, 0); + int size = result[0]; + byte[] data = new byte[size]; + gl.glGetShaderInfoLog(shader, size, result, 0, data, 0); + + String error = new String(data, 0, result[0]); + throw new ShaderException(error); + } + + protected void checkProgramThrowException(int status) { + int[] result = new int[1]; + gl.glGetProgramiv(programId, status, result, 0); + if (result[0] == GL.GL_TRUE) { + return; + } + + gl.glGetProgramiv(programId, GL.GL_INFO_LOG_LENGTH, result, 0); + int size = result[0]; + byte[] data = new byte[size]; + gl.glGetProgramInfoLog(programId, size, result, 0, data, 0); + + String error = new String(data, 0, result[0]); + throw new ShaderException(error); + } + + protected int[] lengths(String[] lines) { + int[] lengths = new int[lines.length]; + for (int i = 0; i < lengths.length; i++) { + lengths[i] = lines[i].length(); + } + + return lengths; + } + + protected String[] readShader(String name) throws IOException { + InputStream stream = BasicShader.class.getResourceAsStream(name); + if (stream == null) { + throw new IOException("Shader " + name + " not found"); + } + + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + String line = null; + List lines = new ArrayList(); + while ((line = reader.readLine()) != null) { + lines.add(line); + } + + return lines.toArray(new String[lines.size()]); + } +} diff --git a/src/main/java/glg2d/shaders/FixedFuncShader.f b/src/main/java/glg2d/shaders/FixedFuncShader.f new file mode 100644 index 00000000..502806b8 --- /dev/null +++ b/src/main/java/glg2d/shaders/FixedFuncShader.f @@ -0,0 +1,3 @@ +void main() { + gl_FragColor = gl_Color; +} \ No newline at end of file diff --git a/src/main/java/glg2d/shaders/FixedFuncShader.v b/src/main/java/glg2d/shaders/FixedFuncShader.v new file mode 100644 index 00000000..fe1deaca --- /dev/null +++ b/src/main/java/glg2d/shaders/FixedFuncShader.v @@ -0,0 +1,4 @@ +void main() { + gl_Position = ftransform(); + gl_FrontColor = gl_Color; +} \ No newline at end of file diff --git a/src/main/java/glg2d/shaders/G2DShaderImageDrawer.java b/src/main/java/glg2d/shaders/G2DShaderImageDrawer.java new file mode 100644 index 00000000..76d37e00 --- /dev/null +++ b/src/main/java/glg2d/shaders/G2DShaderImageDrawer.java @@ -0,0 +1,34 @@ +package glg2d.shaders; + +import glg2d.G2DGLImageDrawer; +import glg2d.GLGraphics2D; + +import java.awt.Color; +import java.awt.geom.AffineTransform; + +import com.sun.opengl.util.texture.Texture; + +public class G2DShaderImageDrawer extends G2DGLImageDrawer { + protected Shader shader; + + @Override + public void setG2D(GLGraphics2D g2d) { + super.setG2D(g2d); + + if (shader == null) { + shader = ((GLShaderGraphics2D) g2d).getShaderRegistry().getTextureShader(); + } + } + + @Override + protected void begin(Texture texture, AffineTransform xform, Color bgcolor) { + super.begin(texture, xform, bgcolor); + shader.use(true); + } + + @Override + protected void end(Texture texture) { + shader.use(false); + super.end(texture); + } +} diff --git a/src/main/java/glg2d/shaders/G2DShaderStringDrawer.java b/src/main/java/glg2d/shaders/G2DShaderStringDrawer.java new file mode 100644 index 00000000..e0fa759d --- /dev/null +++ b/src/main/java/glg2d/shaders/G2DShaderStringDrawer.java @@ -0,0 +1,33 @@ +package glg2d.shaders; + +import glg2d.G2DGLStringDrawer; +import glg2d.GLGraphics2D; + +import java.awt.Color; + +import com.sun.opengl.util.j2d.TextRenderer; + +public class G2DShaderStringDrawer extends G2DGLStringDrawer { + protected Shader shader; + + @Override + public void setG2D(GLGraphics2D g2d) { + super.setG2D(g2d); + + if (shader == null) { + shader = ((GLShaderGraphics2D) g2d).getShaderRegistry().getTextureShader(); + } + } + + @Override + protected void begin(TextRenderer renderer, Color textColor) { + super.begin(renderer, textColor); + shader.use(true); + } + + @Override + protected void end(TextRenderer renderer) { + shader.use(false); + super.end(renderer); + } +} diff --git a/src/main/java/glg2d/shaders/GLShaderGraphics2D.java b/src/main/java/glg2d/shaders/GLShaderGraphics2D.java new file mode 100644 index 00000000..3016d6f2 --- /dev/null +++ b/src/main/java/glg2d/shaders/GLShaderGraphics2D.java @@ -0,0 +1,31 @@ +package glg2d.shaders; + +import glg2d.GLGraphics2D; + +import javax.media.opengl.GLAutoDrawable; + +public class GLShaderGraphics2D extends GLGraphics2D { + protected ShaderRegistry shaders; + + public GLShaderGraphics2D(int width, int height) { + super(width, height); + + shaders = new ShaderRegistry(); + + imageDrawer = new G2DShaderImageDrawer(); + stringDrawer = new G2DShaderStringDrawer(); + } + + @Override + protected void setCanvas(GLAutoDrawable drawable) { + glContext = drawable.getContext(); + gl = glContext.getGL(); + + shaders.setG2D(this); + super.setCanvas(drawable); + } + + public ShaderRegistry getShaderRegistry() { + return shaders; + } +} diff --git a/src/main/java/glg2d/shaders/Shader.java b/src/main/java/glg2d/shaders/Shader.java new file mode 100644 index 00000000..bc4b9a7b --- /dev/null +++ b/src/main/java/glg2d/shaders/Shader.java @@ -0,0 +1,13 @@ +package glg2d.shaders; + +public interface Shader { + void compileVertexShader(); + + void compileFragmentShader(); + + void createAndAttach(); + + void use(boolean use); + + void delete(); +} \ No newline at end of file diff --git a/src/main/java/glg2d/shaders/ShaderException.java b/src/main/java/glg2d/shaders/ShaderException.java new file mode 100644 index 00000000..165782f1 --- /dev/null +++ b/src/main/java/glg2d/shaders/ShaderException.java @@ -0,0 +1,25 @@ +package glg2d.shaders; + +public class ShaderException extends RuntimeException { + private static final long serialVersionUID = 829519650852350876L; + + public ShaderException() { + super(); + } + + public ShaderException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public ShaderException(String message, Throwable cause) { + super(message, cause); + } + + public ShaderException(String message) { + super(message); + } + + public ShaderException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/glg2d/shaders/ShaderRegistry.java b/src/main/java/glg2d/shaders/ShaderRegistry.java new file mode 100644 index 00000000..39cf70e2 --- /dev/null +++ b/src/main/java/glg2d/shaders/ShaderRegistry.java @@ -0,0 +1,75 @@ +package glg2d.shaders; + +import glg2d.G2DDrawingHelper; +import glg2d.GLGraphics2D; + +public class ShaderRegistry implements G2DDrawingHelper { + protected Shader fixedFunction; + protected Shader texture; + + protected boolean needsRecompile; + + @Override + public void setG2D(GLGraphics2D g2d) { + if (fixedFunction == null) { + fixedFunction = createFixedFunctionShader(g2d); + needsRecompile = true; + } + + if (texture == null) { + texture = createTextureShader(g2d); + needsRecompile = true; + } + + if (needsRecompile) { + fixedFunction.compileVertexShader(); + fixedFunction.compileFragmentShader(); + fixedFunction.createAndAttach(); + + texture.compileVertexShader(); + texture.compileFragmentShader(); + texture.createAndAttach(); + + needsRecompile = false; + } + } + + @Override + public void dispose() { + fixedFunction.delete(); + texture.delete(); + needsRecompile = true; + } + + public Shader getFixedFunctionShader() { + return fixedFunction; + } + + public Shader getTextureShader() { + return texture; + } + + protected Shader createFixedFunctionShader(GLGraphics2D g2d) { + BasicShader shader = new BasicShader(g2d.getGLContext().getGL()); + shader.readSources("FixedFuncShader"); + + return shader; + } + + protected Shader createTextureShader(GLGraphics2D g2d) { + BasicShader shader = new BasicShader(g2d.getGLContext().getGL()); + shader.readSources("TextureShader"); + + return shader; + } + + @Override + public void pop(GLGraphics2D parentG2d) { + // nop + } + + @Override + public void push(GLGraphics2D newG2d) { + // nop + } +} diff --git a/src/main/java/glg2d/shaders/TextureShader.f b/src/main/java/glg2d/shaders/TextureShader.f new file mode 100644 index 00000000..478b89c4 --- /dev/null +++ b/src/main/java/glg2d/shaders/TextureShader.f @@ -0,0 +1,9 @@ +uniform sampler2D tex; + +void main() { + vec4 texel; + vec4 color = gl_Color; + + texel = texture2D(tex, gl_TexCoord[0].st); + gl_FragColor = vec4(color.rgb * texel.rgb, texel.a); +} \ No newline at end of file diff --git a/src/main/java/glg2d/shaders/TextureShader.v b/src/main/java/glg2d/shaders/TextureShader.v new file mode 100644 index 00000000..aa5d4c2d --- /dev/null +++ b/src/main/java/glg2d/shaders/TextureShader.v @@ -0,0 +1,5 @@ +void main() { + gl_Position = ftransform(); + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + gl_FrontColor = gl_Color; +} \ No newline at end of file