From 00daf4c542496c3968e8c30060171c5177307868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20=C5=A0pan=C4=9Bl?= Date: Tue, 6 Oct 2020 15:58:12 +0200 Subject: [PATCH] Added pipeline (quad and glyph) from https://github.com/adbrown85/jogl (https://github.com/sgothel/jogl/pull/47) --- .../pipeline/AbstractGlyphRenderer.java | 415 +++++++++++++++++ .../opengl/pipeline/AbstractQuadPipeline.java | 417 ++++++++++++++++++ .../opengrabeso/opengl/pipeline/Glyph.java | 308 +++++++++++++ .../opengl/pipeline/GlyphRenderer.java | 163 +++++++ .../opengl/pipeline/GlyphRendererGL2.java | 168 +++++++ .../opengl/pipeline/GlyphRendererGL3.java | 219 +++++++++ .../opengl/pipeline/Mat4Uniform.java | 70 +++ .../net/opengrabeso/opengl/pipeline/Quad.java | 82 ++++ .../opengl/pipeline/QuadPipeline.java | 132 ++++++ .../opengl/pipeline/QuadPipelineGL15.java | 145 ++++++ .../opengl/pipeline/QuadPipelineGL30.java | 231 ++++++++++ .../opengl/pipeline/ShaderLoader.java | 156 +++++++ .../opengl/pipeline/ShaderUtil.java | 68 +++ .../opengrabeso/opengl/pipeline/Uniform.java | 72 +++ .../opengl/pipeline/Vec4Uniform.java | 66 +++ 15 files changed, 2712 insertions(+) create mode 100644 src/main/java/net/opengrabeso/opengl/pipeline/AbstractGlyphRenderer.java create mode 100644 src/main/java/net/opengrabeso/opengl/pipeline/AbstractQuadPipeline.java create mode 100644 src/main/java/net/opengrabeso/opengl/pipeline/Glyph.java create mode 100644 src/main/java/net/opengrabeso/opengl/pipeline/GlyphRenderer.java create mode 100644 src/main/java/net/opengrabeso/opengl/pipeline/GlyphRendererGL2.java create mode 100644 src/main/java/net/opengrabeso/opengl/pipeline/GlyphRendererGL3.java create mode 100644 src/main/java/net/opengrabeso/opengl/pipeline/Mat4Uniform.java create mode 100644 src/main/java/net/opengrabeso/opengl/pipeline/Quad.java create mode 100644 src/main/java/net/opengrabeso/opengl/pipeline/QuadPipeline.java create mode 100644 src/main/java/net/opengrabeso/opengl/pipeline/QuadPipelineGL15.java create mode 100644 src/main/java/net/opengrabeso/opengl/pipeline/QuadPipelineGL30.java create mode 100644 src/main/java/net/opengrabeso/opengl/pipeline/ShaderLoader.java create mode 100644 src/main/java/net/opengrabeso/opengl/pipeline/ShaderUtil.java create mode 100644 src/main/java/net/opengrabeso/opengl/pipeline/Uniform.java create mode 100644 src/main/java/net/opengrabeso/opengl/pipeline/Vec4Uniform.java diff --git a/src/main/java/net/opengrabeso/opengl/pipeline/AbstractGlyphRenderer.java b/src/main/java/net/opengrabeso/opengl/pipeline/AbstractGlyphRenderer.java new file mode 100644 index 00000000..bb3bac58 --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/pipeline/AbstractGlyphRenderer.java @@ -0,0 +1,415 @@ +/* + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package net.opengrabeso.opengl.pipeline; + +import com.github.opengrabeso.jaagl.GL2GL3; +import net.opengrabeso.opengl.util.texture.TextureCoords; + +import java.util.ArrayList; +import java.util.List; + + +/** + * Skeletal implementation of {@link GlyphRenderer}. + */ +abstract class AbstractGlyphRenderer implements GlyphRenderer, QuadPipeline.EventListener { + + // Default color + private static final float DEFAULT_RED = 1.0f; + private static final float DEFAULT_GREEN = 1.0f; + private static final float DEFAULT_BLUE = 1.0f; + private static final float DEFAULT_ALPHA = 1.0f; + + /** + * Listeners to send events to. + */ + /*@Nonnull*/ + private final List listeners = new ArrayList(); + + /** + * Quad to send to pipeline. + */ + /*@Nonnull*/ + private final Quad quad = new Quad(); + + /** + * Buffer of quads. + */ + /*@CheckForNull*/ + private QuadPipeline pipeline = null; + + /** + * Whether pipeline needs to be flushed. + */ + private boolean pipelineDirty = true; + + /** + * True if between begin and end calls. + */ + private boolean inRenderCycle = false; + + /** + * True if orthographic. + */ + private boolean orthoMode = false; + + /** + * Red component of color. + */ + private float r = DEFAULT_RED; + + /** + * Green component of color. + */ + private float g = DEFAULT_GREEN; + + /** + * Blue component of color. + */ + private float b = DEFAULT_BLUE; + + /** + * Alpha component of color. + */ + private float a = DEFAULT_ALPHA; + + /** + * True if color needs to be updated. + */ + private boolean colorDirty = true; + + /** + * Transformation matrix for 3D mode. + */ + /*@Nonnull*/ + private final float[] transform = new float[16]; + + /** + * Whether transformation matrix is in row-major order instead of column-major. + */ + private boolean transposed = false; + + // TODO: Should `transformDirty` start out as true? + /** + * Whether transformation matrix needs to be updated. + */ + private boolean transformDirty = false; + + /** + * Constructs an {@link AbstractGlyphRenderer}. + */ + AbstractGlyphRenderer() { + // empty + } + + @Override + public final void addListener(/*@Nonnull*/ final EventListener listener) { + + listeners.add(listener); + } + + @Override + public final void beginRendering(/*@Nonnull*/ final GL2GL3 gl, + final boolean ortho, + /*@Nonnegative*/ final int width, + /*@Nonnegative*/ final int height, + final boolean disableDepthTest, final boolean gl3) { + + // Perform hook + doBeginRendering(gl, ortho, width, height, disableDepthTest); + + assert !ortho; + // Store text renderer state + inRenderCycle = true; + orthoMode = ortho; + + // Make sure the pipeline is made + if (pipelineDirty) { + setPipeline(gl, doCreateQuadPipeline(gl)); + } + + // Pass to quad renderer + pipeline.beginRendering(gl); + + // Make sure color is correct + if (colorDirty) { + doSetColor(gl, r, g, b, a); + colorDirty = false; + } + + // Make sure transform is correct + if (transformDirty) { + doSetTransform3d(gl, transform, transposed); + transformDirty = false; + } + } + + /** + * Requests that the pipeline be replaced on the next call to {@link #beginRendering}. + */ + protected final void dirtyPipeline() { + pipelineDirty = true; + } + + @Override + public final void dispose(/*@Nonnull*/ final GL2GL3 gl) { + + doDispose(gl); + listeners.clear(); + pipeline.dispose(gl); + } + + /** + * Actually starts a render cycle. + * + * @param gl Current OpenGL context + * @param ortho True if using orthographic projection + * @param width Width of current OpenGL viewport + * @param height Height of current OpenGL viewport + * @param disableDepthTest True if should ignore depth values + */ + protected abstract void doBeginRendering(/*@Nonnull*/ final GL2GL3 gl, + final boolean ortho, + /*@Nonnegative*/ final int width, + /*@Nonnegative*/ final int height, + final boolean disableDepthTest); + + /** + * Actually creates the quad pipeline for rendering quads. + * + * @param gl Current OpenGL context + * @return Quad pipeline to render quads with + */ + protected abstract QuadPipeline doCreateQuadPipeline(/*@Nonnull*/ final GL2GL3 gl); + + /** + * Actually frees resources used by the renderer. + * + * @param gl Current OpenGL context + */ + protected abstract void doDispose(/*@Nonnull*/ final GL2GL3 gl); + + /** + * Actually finishes a render cycle. + * + * @param gl Current OpenGL context + */ + protected abstract void doEndRendering(/*@Nonnull*/ final GL2GL3 gl); + + /** + * Actually changes the color when user calls {@link #setColor}. + * + * @param gl Current OpenGL context + * @param r Red component of color + * @param g Green component of color + * @param b Blue component of color + * @param a Alpha component of color + */ + protected abstract void doSetColor(/*@Nonnull*/ final GL2GL3 gl, + float r, + float g, + float b, + float a); + + /** + * Actually changes the MVP matrix when using an arbitrary projection. + * + * @param gl Current OpenGL context + * @param value Matrix as float array + * @param transpose True if in row-major order + */ + protected abstract void doSetTransform3d(/*@Nonnull*/ GL2GL3 gl, + /*@Nonnull*/ float[] value, + boolean transpose); + + @Override + public final float drawGlyph(/*@Nonnull*/ final GL2GL3 gl, + /*@Nonnull*/ final Glyph glyph, + /*@CheckForSigned*/ final float x, + /*@CheckForSigned*/ final float y, + /*@CheckForSigned*/ final float z, + /*@CheckForSigned*/ final float scale, + final boolean verticalFlip, + /*@Nonnull*/ final TextureCoords coords) { + + // Compute position and size + float yScale = verticalFlip ? -scale : scale; + quad.xl = x + (scale * glyph.kerning); + quad.xr = quad.xl + (scale * glyph.width); + quad.yb = y - (yScale * glyph.descent); + quad.yt = quad.yb + (yScale * glyph.height); + quad.z = z; + quad.sl = coords.left(); + quad.sr = coords.right(); + quad.tb = coords.bottom(); + quad.tt = coords.top(); + + // Draw quad + pipeline.addQuad(gl, quad); + + // Return distance to next character + return glyph.advance; + } + + @Override + public final void endRendering(/*@Nonnull*/ final GL2GL3 gl) { + // Store text renderer state + inRenderCycle = false; + + // Pass to quad renderer + pipeline.endRendering(gl); + + // Perform hook + doEndRendering(gl); + } + + /** + * Fires an event to all observers. + * + * @param type Kind of event + * @throws NullPointerException if type is null + */ + protected final void fireEvent(/*@Nonnull*/ final EventType type) { + for (final EventListener listener : listeners) { + assert listener != null : "addListener rejects null"; + listener.onGlyphRendererEvent(type); + } + } + + @Override + public final void flush(/*@Nonnull*/ final GL2GL3 gl) { + pipeline.flush(gl); + gl.glFlush(); + } + + /** + * Determines if a color is the same one that is stored. + * + * @param r Red component of color + * @param g Green component of color + * @param b Blue component of color + * @param a Alpha component of color + * @return True if each component matches + */ + final boolean hasColor(final float r, final float g, final float b, final float a) { + return (this.r == r) && (this.g == g) && (this.b == b) && (this.a == a); + } + + // TODO: Rename to `isOrthographic`? + /** + * Checks if this {@link GlyphRenderer} using an orthographic projection. + * + * @return True if this renderer is using an orthographic projection + */ + final boolean isOrthoMode() { + return orthoMode; + } + + @Override + public final void onQuadPipelineEvent(/*@Nonnull*/ final QuadPipeline.EventType type) { + if (type == QuadPipeline.EventType.AUTOMATIC_FLUSH) { + fireEvent(EventType.AUTOMATIC_FLUSH); + } + } + + @Override + public final void setColor(final GL2GL3 gl, final float r, final float g, final float b, final float a) { + + // Check if already has the color + if (hasColor(r, g, b, a)) { + return; + } + + // Render any outstanding quads first + if (pipeline != null && !pipeline.isEmpty()) { + fireEvent(EventType.AUTOMATIC_FLUSH); + flush(gl); + } + + // Store the color + this.r = r; + this.g = g; + this.b = b; + this.a = a; + + // Change the color + if (inRenderCycle) { + doSetColor(gl, r, g, b, a); + } else { + colorDirty = true; + } + } + + /** + * Changes the quad pipeline. + * + * @param gl Current OpenGL context + * @param pipeline Quad pipeline to change to + */ + private final void setPipeline(/*@Nonnull*/ final GL2GL3 gl, + /*@Nonnull*/ final QuadPipeline pipeline) { + + assert gl != null : "GL should not be null"; + assert pipeline != null : "Pipeline should not be null"; + + final QuadPipeline oldPipeline = this.pipeline; + final QuadPipeline newPipeline = pipeline; + + // Remove the old pipeline + if (oldPipeline != null) { + oldPipeline.removeListener(this); + oldPipeline.dispose(gl); + this.pipeline = null; + } + + // Store the new pipeline + newPipeline.addListener(this); + this.pipeline = newPipeline; + pipelineDirty = false; + } + + @Override + public final void setTransform(final GL2GL3 gl, /*@Nonnull*/ final float[] value, final boolean transpose) { + // Render any outstanding quads first + if (pipeline != null && !pipeline.isEmpty()) { + fireEvent(EventType.AUTOMATIC_FLUSH); + flush(gl); + } + + // Store the transform + System.arraycopy(value, 0, this.transform, 0, value.length); + this.transposed = transpose; + + // Change the transform + if (inRenderCycle) { + doSetTransform3d(gl, value, transpose); + } else { + transformDirty = true; + } + } +} diff --git a/src/main/java/net/opengrabeso/opengl/pipeline/AbstractQuadPipeline.java b/src/main/java/net/opengrabeso/opengl/pipeline/AbstractQuadPipeline.java new file mode 100644 index 00000000..f5dfa18e --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/pipeline/AbstractQuadPipeline.java @@ -0,0 +1,417 @@ +/* + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package net.opengrabeso.opengl.pipeline; + +import com.github.opengrabeso.jaagl.GL2GL3; +import com.jogamp.common.nio.Buffers; + +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.List; + + +/** + * Skeletal implementation of {@link QuadPipeline}. + */ +abstract class AbstractQuadPipeline implements QuadPipeline { + + /** + * Number of bytes in one float. + */ + /*@Nonnegative*/ + static final int SIZEOF_FLOAT = 4; + + /** + * Number of bytes in one int. + */ + /*@Nonnegative*/ + static final int SIZEOF_INT = 4; + + /** + * Maximum number of quads in the buffer. + */ + /*@Nonnegative*/ + static final int QUADS_PER_BUFFER = 100; + + /** + * Number of components in a point attribute. + */ + /*@Nonnegative*/ + static final int FLOATS_PER_POINT = 3; + + /** + * Number of components in a texture coordinate attribute + */ + /*@Nonnegative*/ + static final int FLOATS_PER_COORD = 2; + + /** + * Total components in vertex. + */ + /*@Nonnegative*/ + static final int FLOATS_PER_VERT = FLOATS_PER_POINT + FLOATS_PER_COORD; + + /** + * Size of a point attribute in bytes. + */ + /*@Nonnegative*/ + static final int BYTES_PER_POINT = FLOATS_PER_POINT * SIZEOF_FLOAT; + + /** + * Size of a texture coordinate attribute in bytes. + */ + /*@Nonnegative*/ + static final int BYTES_PER_COORD = FLOATS_PER_COORD * SIZEOF_FLOAT; + + /** + * Total size of a vertex in bytes. + */ + /*@Nonnegative*/ + static final int BYTES_PER_VERT = BYTES_PER_POINT + BYTES_PER_COORD; + + /** + * Number of bytes before first point attribute in buffer. + */ + /*@Nonnegative*/ + static final int POINT_OFFSET = 0; + + /** + * Number of bytes before first texture coordinate in buffer. + */ + /*@Nonnegative*/ + static final int COORD_OFFSET = BYTES_PER_POINT; + + /** + * Number of bytes between successive values for the same attribute. + */ + /*@Nonnegative*/ + static final int STRIDE = BYTES_PER_POINT + BYTES_PER_COORD; + + /** + * Maximum buffer size in floats. + */ + /*@Nonnegative*/ + final int FLOATS_PER_BUFFER; + + /** + * Maximum buffer size in bytes. + */ + /*@Nonnegative*/ + final int BYTES_PER_BUFFER; + + /** + * Number of vertices per primitive. + */ + /*@Nonnegative*/ + final int VERTS_PER_PRIM; + + /** + * Maximum buffer size in primitives. + */ + /*@Nonnegative*/ + final int PRIMS_PER_BUFFER; + + /** + * Maximum buffer size in vertices. + */ + /*@Nonnegative*/ + final int VERTS_PER_BUFFER; + + /** + * Size of a quad in vertices. + */ + /*@Nonnegative*/ + final int VERTS_PER_QUAD; + + /** + * Size of a quad in bytes. + */ + /*@Nonnegative*/ + final int BYTES_PER_QUAD; + + /** + * Size of a quad in primitives. + */ + /*@Nonnegative*/ + final int PRIMS_PER_QUAD; + + /** + * Observers of events. + */ + /*@Nonnull*/ + private final List listeners = new ArrayList(); + + /** + * Buffer of vertices. + */ + /*@Nonnull*/ + private final FloatBuffer data; + + /** + * Number of outstanding quads in the buffer. + */ + /*@Nonnegative*/ + private int size = 0; + + /** + * Constructs an abstract quad pipeline. + * + * @param vertsPerPrim Number of vertices per primitive + * @param primsPerQuad Number of primitives per quad + * @throws IllegalArgumentException if vertices or primitives is less than one + */ + AbstractQuadPipeline(/*@Nonnegative*/ final int vertsPerPrim, + /*@Nonnegative*/ final int primsPerQuad) { + + assert vertsPerPrim > 0; + assert primsPerQuad > 0; + + VERTS_PER_PRIM = vertsPerPrim; + PRIMS_PER_QUAD = primsPerQuad; + PRIMS_PER_BUFFER = primsPerQuad * QUADS_PER_BUFFER; + VERTS_PER_QUAD = vertsPerPrim * primsPerQuad; + VERTS_PER_BUFFER = PRIMS_PER_BUFFER * VERTS_PER_PRIM; + FLOATS_PER_BUFFER = FLOATS_PER_VERT * VERTS_PER_BUFFER; + BYTES_PER_BUFFER = BYTES_PER_VERT * VERTS_PER_BUFFER; + BYTES_PER_QUAD = BYTES_PER_VERT * VERTS_PER_QUAD; + + this.data = Buffers.newDirectFloatBuffer(FLOATS_PER_BUFFER); + } + + /** + * Adds a texture coordinate to the pipeline. + * + * @param s Texture coordinate for X axis + * @param t Texture coordinate for Y axis + */ + protected final void addCoord(final float s, final float t) { + data.put(s).put(t); + } + + /** + * Adds a point to the pipeline. + * + * @param x Position on X axis + * @param y Position on Y axis + * @param z Position on Z axis + */ + protected final void addPoint(final float x, final float y, final float z) { + data.put(x).put(y).put(z); + } + + @Override + public final void addListener(/*@Nonnull*/ final EventListener listener) { + + listeners.add(listener); + } + + @Override + public final void addQuad(/*@Nonnull*/ final GL2GL3 gl, /*@Nonnull*/ final Quad quad) { + + doAddQuad(quad); + if (++size >= QUADS_PER_BUFFER) { + fireEvent(EventType.AUTOMATIC_FLUSH); + flush(gl); + } + } + + @Override + public void beginRendering(/*@Nonnull*/ final GL2GL3 gl) { + } + + /** + * Rewinds the buffer and resets the number of outstanding quads. + */ + protected final void clear() { + data.rewind(); + size = 0; + } + + /** + * Creates a vertex buffer object for use with a pipeline. + * + * @param gl Current OpenGL context + * @param size Size in bytes of buffer + * @return OpenGL handle to vertex buffer object + * @throws NullPointerException if context is null + * @throws IllegalArgumentException if size is negative + */ + /*@Nonnegative*/ + protected static int createVertexBufferObject(/*@Nonnull*/ final GL2GL3 gl, + /*@Nonnegative*/ final int size) { + + // Generate + final int[] handles = new int[1]; + gl.glGenBuffers(handles); + final int vbo = handles[0]; + + // Allocate + gl.glBindBuffer(gl.GL_ARRAY_BUFFER(), vbo); + gl.glBufferData( + gl.GL_ARRAY_BUFFER(), // target + size, // size + null, // data + gl.GL_STREAM_DRAW()); // usage + gl.glBindBuffer(gl.GL_ARRAY_BUFFER(), 0); + + return vbo; + } + + @Override + public void dispose(/*@Nonnull*/ final GL2GL3 gl) { + + listeners.clear(); + } + + /** + * Actually adds vertices from a quad to the buffer. + * + * @param quad Quad to add + * @throws NullPointerException if quad is null + */ + protected void doAddQuad(/*@Nonnull*/ final Quad quad) { + + addPoint(quad.xr, quad.yt, quad.z); + addCoord(quad.sr, quad.tt); + addPoint(quad.xl, quad.yt, quad.z); + addCoord(quad.sl, quad.tt); + addPoint(quad.xl, quad.yb, quad.z); + addCoord(quad.sl, quad.tb); + addPoint(quad.xr, quad.yb, quad.z); + addCoord(quad.sr, quad.tb); + } + + /** + * Actually draws everything in the pipeline. + * + * @param gl Current OpenGL context + */ + protected abstract void doFlush(/*@Nonnull*/ final GL2GL3 gl); + + @Override + public void endRendering(/*@Nonnull*/ final GL2GL3 gl) { + + flush(gl); + } + + /** + * Fires an event to all observers. + * + * @param type Type of event to send to observers + */ + protected final void fireEvent(/*@Nonnull*/ final EventType type) { + + for (final EventListener listener : listeners) { + assert listener != null : "addListener rejects null"; + listener.onQuadPipelineEvent(type); + } + } + + @Override + public final void flush(/*@Nonnull*/ final GL2GL3 gl) { + + if (size > 0) { + doFlush(gl); + } + } + + /** + * Returns NIO buffer backing the pipeline. + */ + /*@Nonnull*/ + protected final FloatBuffer getData() { + return data; + } + + /** + * Returns next float in the pipeline. + */ + /*@CheckForSigned*/ + protected final float getFloat() { + return data.get(); + } + + /*@Nonnegative*/ + @Override + public final int getSize() { + return size; + } + + @Override + public final boolean isEmpty() { + return size == 0; + } + + /** + * Returns size of vertices in the pipeline in bytes. + */ + /*@Nonnegative*/ + public final int getSizeInBytes() { + return size * BYTES_PER_QUAD; + } + + /** + * Returns number of primitives in the pipeline. + */ + /*@Nonnegative*/ + public final int getSizeInPrimitives() { + return size * PRIMS_PER_QUAD; + } + + /** + * Returns number of vertices in the pipeline. + */ + /*@Nonnegative*/ + public final int getSizeInVertices() { + return size * VERTS_PER_QUAD; + } + + /** + * Changes the buffer's position. + * + * @param position Location in buffer to move to + * @throws IllegalArgumentException if position is out-of-range + */ + protected final void position(/*@Nonnegative*/ final int position) { + data.position(position); + } + + @Override + public final void removeListener(/*@CheckForNull*/ final EventListener listener) { + if (listener != null) { + listeners.remove(listener); + } + } + + /** + * Rewinds the data buffer. + */ + protected final void rewind() { + data.rewind(); + } +} diff --git a/src/main/java/net/opengrabeso/opengl/pipeline/Glyph.java b/src/main/java/net/opengrabeso/opengl/pipeline/Glyph.java new file mode 100644 index 00000000..fd7d76d8 --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/pipeline/Glyph.java @@ -0,0 +1,308 @@ +/* + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package net.opengrabeso.opengl.pipeline; + +import net.opengrabeso.opengl.util.packrect.Rect; +import net.opengrabeso.opengl.util.texture.TextureCoords; + +import java.awt.font.GlyphVector; +import java.awt.geom.Rectangle2D; + + +/** + * Representation of one or multiple unicode characters to be drawn. + * + *

+ * The reason for the dual behavior is so that we can take in a sequence of unicode characters and + * partition them into runs of individual glyphs, but if we encounter complex text and/or unicode + * sequences we don't understand, we can render them using the string-by-string method. + * + *

Positioning

+ * + *

+ * In an effort to make positioning glyphs more intuitive for both Java2D's and OpenGL's coordinate + * systems, {@code Glyph} now stores its measurements differently. This new way is patterned off + * of HTML's box model. + * + *

+ * Of course, as expected each glyph maintains its width and height. For spacing however, rather + * than storing positions in Java2D space that must be manipulated on a case-by-case basis, + * {@code Glyph} stores two separate pre-computed boundaries representing space around the text. + * Each of the boundaries has separate top, bottom, left, and right components. These components + * should generally be considered positive, but negative values are sometimes necessary in rare + * situations. + * + *

+ * The first boundary is called padding. Padding is the space between the actual glyph + * itself and its border. It is included in the width and height of the glyph. The second + * boundary that a glyph stores is called margin, which is extra space around the glyph's + * border. The margin is generally used for separating the glyph from other glyphs when it's + * stored. + * + *

+ * The diagram below shows the boundaries of a glyph and how they relate to its width and height. + * The inner rectangle is the glyph's boundary, and the outer rectangle is the edge of the margin. + * + *

+ * +--------------------------------------+
+ * |             top margin               |
+ * |                                      |
+ * |        |------ WIDTH -------|        |
+ * |     -  +--------------------+        |
+ * |     |  |    top padding     |        |
+ * |     |  | l    ________    r |        |
+ * | l   |  | e   /        \   i |      r |
+ * | e      | f  |          |  g |      i |
+ * | f   H  | t  |          |  h |      g |
+ * | t   E  |    |          |  t |      h |
+ * |     I  | p  |     _____     |      t |
+ * | m   G  | a  |          |  p |        |
+ * | a   H  | d  |          |  a |      m |
+ * | r   T  | d  |          |  d |      a |
+ * | g      | i  |          |  d |      r |
+ * | i   |  | n  |          |  i |      g |
+ * | n   |  | g   \________/   n |      i |
+ * |     |  |                  g |      n |
+ * |     |  |   bottom padding   |        |
+ * |     -  +--------------------+        |
+ * |                                      |
+ * |                                      |
+ * |            bottom margin             |
+ * +--------------------------------------+
+ * 
+ * + *

+ * In addition, {@code Glyph} also keeps a few other measurements useful for positioning. + * Ascent is the distance between the baseline and the top border, while descent is + * the distance between the baseline and the bottom border. Kerning is the distance between + * the vertical baseline and the left border. Note that in some cases some of these fields can + * match up with padding components, but in general they should be considered separate. + * + *

+ * Below is a diagram showing ascent, descent, and kerning. + * + *

+ * +--------------------+   -
+ * |                    |   |
+ * |      ________      |   |
+ * |     /        \     |   |
+ * |    |          |    |   |
+ * |    |          |    |   |
+ * |    |          |    |   |
+ * |    |     _____     |   | ascent
+ * |    |          |    |   |
+ * |    |          |    |   |
+ * |    |          |    |   |
+ * |    |          |    |   |
+ * |    |          |    |   |
+ * |     \________/     |   -
+ * |                    |   |
+ * |                    |   | descent
+ * +--------------------+   -
+ *
+ * |--| kerning
+ * 
+ */ +/*@NotThreadSafe*/ +public final class Glyph { + + // TODO: Create separate Glyph implementations -- one for character one for string? + + /** + * Unicode ID if this glyph represents a single character, otherwise -1. + */ + /*@CheckForSigned*/ + final int id; + + /** + * String if this glyph represents multiple characters, otherwise null. + */ + /*@CheckForNull*/ + final String str; + + /** + * Font's identifier of glyph. + */ + final int code; + + /** + * Distance to next glyph. + */ + final float advance; + + /** + * Java2D shape of glyph. + */ + /*@Nonnull*/ + final GlyphVector glyphVector; + + /** + * Actual character if this glyph represents a single character, otherwise NUL. + */ + final char character; + + /** + * Width of text with inner padding. + */ + /*@VisibleForTesting*/ + public float width; + + /** + * Height of text with inner padding. + */ + /*@VisibleForTesting*/ + public float height; + + /** + * Length from baseline to top border. + */ + float ascent; + + /** + * Length from baseline to bottom border. + */ + float descent; + + /** + * Length from baseline to left padding. + */ + float kerning; + + /** + * Outer boundary excluded from size. + */ + /*@CheckForNull*/ + Boundary margin; + + /** + * Inner boundary included in size. + */ + /*@CheckForNull*/ + Boundary padding; + + /** + * Position of this glyph in texture. + */ + /*@CheckForNull*/ + public Rect location; + + /** + * Coordinates of this glyph in texture. + */ + /*@CheckForNull*/ + TextureCoords coordinates; + + /** + * Cached bounding box of glyph. + */ + /*@CheckForNull*/ + Rectangle2D bounds; + + /** + * Constructs a {@link Glyph} representing an individual Unicode character. + * + * @param id Unicode ID of character + * @param gv Vector shape of character + * @throws IllegalArgumentException if ID is negative + * @throws NullPointerException if glyph is null + */ + public Glyph(/*@Nonnegative*/ final int id, /*@Nonnull*/ final GlyphVector gv) { + + this.id = id; + this.str = null; + this.code = gv.getGlyphCode(0); + this.advance = gv.getGlyphMetrics(0).getAdvance(); + this.glyphVector = gv; + this.character = (char) id; + } + + /** + * Constructs a {@link Glyph} representing a sequence of characters. + * + * @param str Sequence of characters + * @param gv Vector shape of sequence + * @throws NullPointerException if string or glyph vector is null + */ + public Glyph(/*@Nonnull*/ final String str, /*@Nonnull*/ final GlyphVector gv) { + + this.id = -1; + this.str = str; + this.code = -1; + this.advance = 0; + this.glyphVector = gv; + this.character = '\0'; + } + + /*@Nonnull*/ + @Override + public String toString() { + return (str != null) ? str : Character.toString(character); + } + + /** + * Space around a rectangle. + */ + /*@Immutable*/ + static final class Boundary { + + /** + * Space above rectangle. + */ + final int top; + + /** + * Space below rectangle. + */ + final int bottom; + + /** + * Space beside rectangle to left. + */ + final int left; + + /** + * Space beside rectangle to right. + */ + final int right; + + /** + * Constructs a {@link Boundary} by computing the distances between two rectangles. + * + * @param large Outer rectangle + * @param small Inner rectangle + * @throws NullPointerException if either rectangle is null + */ + Boundary(/*@Nonnull*/ final Rectangle2D large, /*@Nonnull*/ final Rectangle2D small) { + + top = (int) (large.getMinY() - small.getMinY()) * -1; + left = (int) (large.getMinX() - small.getMinX()) * -1; + bottom = (int) (large.getMaxY() - small.getMaxY()); + right = (int) (large.getMaxX() - small.getMaxX()); + } + } +} diff --git a/src/main/java/net/opengrabeso/opengl/pipeline/GlyphRenderer.java b/src/main/java/net/opengrabeso/opengl/pipeline/GlyphRenderer.java new file mode 100644 index 00000000..f85b5eaf --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/pipeline/GlyphRenderer.java @@ -0,0 +1,163 @@ +/* + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package net.opengrabeso.opengl.pipeline; + +import com.github.opengrabeso.jaagl.GL2GL3; +import net.opengrabeso.opengl.util.texture.TextureCoords; + + +/** + * Utility for drawing glyphs. + */ +public interface GlyphRenderer { + + /** + * Registers an {@link EventListener} with this {@link GlyphRenderer}. + * + * @param listener Listener to register + * @throws NullPointerException if listener is null + */ + void addListener(/*@Nonnull*/ EventListener listener); + + /** + * Starts a render cycle with this {@link GlyphRenderer}. + * + * @param gl Current OpenGL context + * @param ortho True if using orthographic projection + * @param width Width of current OpenGL viewport + * @param height Height of current OpenGL viewport + * @param disableDepthTest True if should ignore depth values + */ + void beginRendering(/*@Nonnull*/ GL2GL3 gl, + boolean ortho, + /*@Nonnegative*/ int width, + /*@Nonnegative*/ int height, + boolean disableDepthTest, boolean gl3); + + /** + * Frees resources used by this {@link GlyphRenderer}. + * + * @param gl Current OpenGL context + */ + void dispose(/*@Nonnull*/ GL2GL3 gl); + + /** + * Draws a glyph with this {@link GlyphRenderer}. + * + * @param gl Current OpenGL context + * @param glyph Visual representation of a character + * @param x Position to draw on X axis, which may be negative + * @param y Position to draw on Y axis, which may be negative + * @param z Position to draw on Z axis, which may be negative + * @param scale Relative size of glyph, which may be negative + * @param coords Texture coordinates of glyph + */ + /*@CheckForSigned*/ + float drawGlyph(/*@Nonnull*/ GL2GL3 gl, + /*@Nonnull*/ Glyph glyph, + /*@CheckForSigned*/ float x, + /*@CheckForSigned*/ float y, + /*@CheckForSigned*/ float z, + /*@CheckForSigned*/ float scale, + boolean verticalFlip, + /*@Nonnull*/ TextureCoords coords); + + /** + * Finishes a render cycle with this {@link GlyphRenderer}. + * + * @param gl Current OpenGL context + */ + void endRendering(/*@Nonnull*/ GL2GL3 gl); + + /** + * Forces all stored text to be rendered. + * + * @param gl Current OpenGL context + */ + void flush(/*@Nonnull*/ GL2GL3 gl); + + /** + * Checks if this {@link GlyphRenderer} is using vertex arrays. + * + * @return True if this renderer is using vertex arrays + */ + boolean getUseVertexArrays(); + + /** + * Changes the color used to draw the text. + * + * @param r Red component of color + * @param g Green component of color + * @param b Blue component of color + * @param a Alpha component of color + */ + void setColor(final GL2GL3 gl, float r, float g, float b, float a); + + /** + * Changes the transformation matrix for drawing in 3D. + * + * @param gl Current OpenGL context + * @param value Matrix as float array + * @param transpose True if array is in in row-major order + * @throws IndexOutOfBoundsException if value's length is less than sixteen + * @throws IllegalStateException if in orthographic mode + */ + void setTransform(final GL2GL3 gl, /*@Nonnull*/ float[] value, boolean transpose); + + /** + * Changes whether vertex arrays are in use. + * + * @param useVertexArrays true to use vertex arrays + */ + void setUseVertexArrays(boolean useVertexArrays); + + /** + * Observer of a {@link GlyphRenderer}. + */ + public interface EventListener { + + /** + * Responds to an event from a glyph renderer. + * + * @param type Type of event + * @throws NullPointerException if event type is null + */ + public void onGlyphRendererEvent(EventType type); + } + + /** + * Type of event fired from the renderer. + */ + public static enum EventType { + + /** + * Renderer is automatically flushing queued glyphs, e.g., when it's full or color changes. + */ + AUTOMATIC_FLUSH; + } +} diff --git a/src/main/java/net/opengrabeso/opengl/pipeline/GlyphRendererGL2.java b/src/main/java/net/opengrabeso/opengl/pipeline/GlyphRendererGL2.java new file mode 100644 index 00000000..1b53f760 --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/pipeline/GlyphRendererGL2.java @@ -0,0 +1,168 @@ +/* + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package net.opengrabeso.opengl.pipeline; + +import com.github.opengrabeso.jaagl.GL2GL3; +import com.github.opengrabeso.jaagl.GL2; + + +/** + * {@link GlyphRenderer} for use with OpenGL 2. + */ +/*@VisibleForTesting*/ +/*@NotThreadSafe*/ +public final class GlyphRendererGL2 extends AbstractGlyphRenderer { + + /** + * True if using vertex arrays. + */ + private boolean useVertexArrays = true; + + /** + * Constructs a {@link GlyphRendererGL2}. + */ + /*@VisibleForTesting*/ + public GlyphRendererGL2() { + // empty + } + + @Override + protected void doBeginRendering(/*@Nonnull*/ final GL2GL3 gl, + final boolean ortho, + /*@Nonnegative*/ final int width, + /*@Nonnegative*/ final int height, + final boolean disableDepthTest) { + + final GL2 gl2 = gl.getGL2(); + + // Change general settings + gl2.glPushAttrib(getAttribMask(gl2, ortho)); + gl2.glDisable(gl2.GL_LIGHTING()); + gl2.glEnable(gl2.GL_BLEND()); + gl2.glDisable(gl2.GL_SCISSOR_TEST()); + gl2.glBlendFunc(gl2.GL_SRC_ALPHA(), gl2.GL_ONE_MINUS_SRC_ALPHA()); + gl2.glEnable(gl2.GL_TEXTURE_2D()); + gl2.glTexEnvi(gl2.GL_TEXTURE_ENV(), gl2.GL_TEXTURE_ENV_MODE(), gl2.GL_MODULATE()); + + // Set up transformations + if (ortho) { + if (disableDepthTest) { + gl2.glDisable(gl2.GL_DEPTH_TEST()); + } + gl2.glDisable(gl2.GL_CULL_FACE()); + gl2.glMatrixMode(gl2.GL_PROJECTION()); + gl2.glPushMatrix(); + gl2.glLoadIdentity(); + gl2.glOrtho(0, width, 0, height, -1, +1); + gl2.glMatrixMode(gl2.GL_MODELVIEW()); + gl2.glPushMatrix(); + gl2.glLoadIdentity(); + gl2.glMatrixMode(gl2.GL_TEXTURE()); + gl2.glPushMatrix(); + gl2.glLoadIdentity(); + } + } + + /*@Nonnull*/ + protected QuadPipeline doCreateQuadPipeline(/*@Nonnull*/ final GL2GL3 gl) { + + final GL2 gl2 = gl.getGL2(); + + return new QuadPipelineGL15(gl2); + } + + protected void doDispose(/*@Nonnull*/ final GL2GL3 gl) { + } + + @Override + protected void doEndRendering(/*@Nonnull*/ final GL2GL3 gl) { + + final GL2 gl2 = gl.getGL2(); + + // Reset transformations + if (isOrthoMode()) { + gl2.glMatrixMode(gl2.GL_PROJECTION()); + gl2.glPopMatrix(); + gl2.glMatrixMode(gl2.GL_MODELVIEW()); + gl2.glPopMatrix(); + gl2.glMatrixMode(gl2.GL_TEXTURE()); + gl2.glPopMatrix(); + } + + // Reset general settings + gl2.glPopAttrib(); + } + + @Override + protected void doSetColor(/*@Nonnull*/ final GL2GL3 gl, + final float r, + final float g, + final float b, + final float a) { + + final GL2 gl2 = gl.getGL2(); + + gl2.glColor4f(r, g, b, a); + } + + @Override + protected void doSetTransform3d(/*@Nonnull*/ final GL2GL3 gl, + /*@Nonnull*/ final float[] value, + final boolean transpose) { + + // FIXME: Could implement this... + throw new UnsupportedOperationException("Use standard GL instead"); + } + + /** + * Returns attribute bits for {@code glPushAttrib} calls. + * + * @param ortho True if using orthographic projection + * @return Attribute bits for {@code glPushAttrib} calls + */ + private static int getAttribMask(final GL2 gl2, final boolean ortho) { + return gl2.GL_ENABLE_BIT() | + gl2.GL_TEXTURE_BIT() | + gl2.GL_COLOR_BUFFER_BIT() | + gl2.GL_SCISSOR_BIT() | + (ortho ? (gl2.GL_DEPTH_BUFFER_BIT() | gl2.GL_TRANSFORM_BIT()) : 0); + } + + @Override + public boolean getUseVertexArrays() { + return useVertexArrays; + } + + @Override + public void setUseVertexArrays(final boolean useVertexArrays) { + if (useVertexArrays != this.useVertexArrays) { + dirtyPipeline(); + this.useVertexArrays = useVertexArrays; + } + } +} diff --git a/src/main/java/net/opengrabeso/opengl/pipeline/GlyphRendererGL3.java b/src/main/java/net/opengrabeso/opengl/pipeline/GlyphRendererGL3.java new file mode 100644 index 00000000..c76d583c --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/pipeline/GlyphRendererGL3.java @@ -0,0 +1,219 @@ +/* + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package net.opengrabeso.opengl.pipeline; + +import com.github.opengrabeso.jaagl.GL2GL3; + + +/** + * Utility for drawing glyphs with OpenGL 3. + */ +/*@VisibleForTesting*/ +/*@NotThreadSafe*/ +public final class GlyphRendererGL3 extends AbstractGlyphRenderer { + + /** + * Source code of vertex shader. + */ + /*@Nonnull*/ + private static final String VERT_SOURCE = + "#version 140\n" + + "uniform mat4 MVPMatrix;\n" + + "in vec4 MCVertex;\n" + + "in vec2 TexCoord0;\n" + + "out vec2 Coord0;\n" + + "void main() {\n" + + " gl_Position = MVPMatrix * MCVertex;\n" + + " Coord0 = TexCoord0;\n" + + "}\n"; + + /** + * Source code of fragment shader. + */ + /*@Nonnull*/ + private static final String FRAG_SOURCE = + "#version 140\n" + + "uniform sampler2D Texture;\n" + + "uniform vec4 Color=vec4(1,1,1,1);\n" + + "in vec2 Coord0;\n" + + "out vec4 FragColor;\n" + + "void main() {\n" + + " float sample;\n" + + " sample = texture(Texture,Coord0).r;\n" + + " FragColor = Color * sample;\n" + + "}\n"; + + /** + * True if blending needs to be reset. + */ + private boolean restoreBlending; + + private boolean restoreScissor; + + /** + * True if depth test needs to be reset. + */ + private boolean restoreDepthTest; + + /** + * Shader program. + */ + /*@Nonnegative*/ + private final int program; + + /** + * Uniform for modelview projection. + */ + /*@Nonnull*/ + private final Mat4Uniform transform; + + /** + * Uniform for color of glyphs. + */ + /*@Nonnull*/ + private final Vec4Uniform color; + + /** + * Width of last orthographic render. + */ + /*@Nonnegative*/ + private int lastWidth = 0; + + /** + * Height of last orthographic render + */ + /*@Nonnegative*/ + private int lastHeight = 0; + + /** + * Constructs a {@link GlyphRendererGL3}. + * + * @param gl Current OpenGL context + * @throws NullPointerException if context is null + */ + /*@VisibleForTesting*/ + public GlyphRendererGL3(/*@Nonnull*/ final GL2GL3 gl) { + + this.program = ShaderLoader.loadProgram(gl, VERT_SOURCE, FRAG_SOURCE); + this.transform = new Mat4Uniform(gl, program, "MVPMatrix"); + this.color = new Vec4Uniform(gl, program, "Color"); + } + + @Override + protected void doBeginRendering(/*@Nonnull*/ final GL2GL3 gl, + final boolean ortho, + /*@Nonnegative*/ final int width, + /*@Nonnegative*/ final int height, + final boolean disableDepthTest) { + + // Activate program + gl.glUseProgram(program); + + // Check blending and depth test + restoreBlending = false; + if (!gl.glIsEnabled(gl.GL_BLEND())) { + gl.glEnable(gl.GL_BLEND()); + gl.glBlendFunc(gl.GL_ONE(), gl.GL_ONE_MINUS_SRC_ALPHA()); + restoreBlending = true; + } + restoreDepthTest = false; + if (disableDepthTest && gl.glIsEnabled(gl.GL_DEPTH_TEST())) { + gl.glDisable(gl.GL_DEPTH_TEST()); + restoreDepthTest = true; + } + restoreScissor = false; + if (gl.glIsEnabled(gl.GL_SCISSOR_TEST())) { + gl.glDisable(gl.GL_SCISSOR_TEST()); + restoreScissor = true; + } + + assert !ortho; + } + + @Override + protected QuadPipeline doCreateQuadPipeline(/*@Nonnull*/ final GL2GL3 gl) { + + return new QuadPipelineGL30(gl, program); + } + + protected void doDispose(/*@Nonnull*/ final GL2GL3 gl) { + gl.glUseProgram(0); + gl.glDeleteProgram(program); + } + + @Override + protected void doEndRendering(/*@Nonnull*/ final GL2GL3 gl) { + // Deactivate program + gl.glUseProgram(0); + + // Check blending and depth test + if (restoreBlending) { + gl.glDisable(gl.GL_BLEND()); + } + if (restoreScissor) { + gl.glEnable(gl.GL_SCISSOR_TEST()); + } + if (restoreDepthTest) { + gl.glEnable(gl.GL_DEPTH_TEST()); + } + } + + @Override + protected void doSetColor(/*@Nonnull*/ final GL2GL3 gl, + final float r, + final float g, + final float b, + final float a) { + + color.value[0] = r; + color.value[1] = g; + color.value[2] = b; + color.value[3] = a; + color.update(gl); + } + + @Override + protected void doSetTransform3d(/*@Nonnull*/ final GL2GL3 gl, + /*@Nonnull*/ final float[] value, + final boolean transpose) { + if (transform.location >= 0) { + gl.glUniformMatrix4fv(transform.location, 1, transpose, value, 0); + } + transform.dirty = true; + } + + @Override + public boolean getUseVertexArrays() { + return true; + } + + @Override + public void setUseVertexArrays(final boolean useVertexArrays) { + // empty + } +} diff --git a/src/main/java/net/opengrabeso/opengl/pipeline/Mat4Uniform.java b/src/main/java/net/opengrabeso/opengl/pipeline/Mat4Uniform.java new file mode 100644 index 00000000..e8d578cd --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/pipeline/Mat4Uniform.java @@ -0,0 +1,70 @@ + +/* + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package net.opengrabeso.opengl.pipeline; + +import com.github.opengrabeso.jaagl.GL2GL3; + + +/** + * Uniform for a {@code mat4}. + */ +/*@NotThreadSafe*/ +final class Mat4Uniform extends Uniform { + + /** + * Local copy of matrix values. + */ + final float[] value = new float[16]; + + /** + * True if matrix is stored in row-major order. + */ + boolean transpose; + + /** + * Constructs a UniformMatrix + * + * @param gl Current OpenGL context + * @param program OpenGL handle to shader program + * @param name Name of the uniform in shader source code + * @throws NullPointerException if context is null + */ + Mat4Uniform(/*@Nonnull*/ final GL2GL3 gl, + /*@Nonnegative*/ final int program, + /*@Nonnull*/ final String name) { + super(gl, program, name); + } + + @Override + void update(/*@Nonnull*/ final GL2GL3 gl) { + if (location >= 0) { + gl.glUniformMatrix4fv(location, 1, transpose, value, 0); + } + } +} diff --git a/src/main/java/net/opengrabeso/opengl/pipeline/Quad.java b/src/main/java/net/opengrabeso/opengl/pipeline/Quad.java new file mode 100644 index 00000000..3c90a733 --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/pipeline/Quad.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package net.opengrabeso.opengl.pipeline; + + +/** + * Structure for points and coordinates. + */ +/*@VisibleForTesting*/ +/*@NotThreadSafe*/ +public final class Quad { + + /** + * Position of left side. + */ + public float xl; + + /** + * Position of right side. + */ + public float xr; + + /** + * Position of bottom side. + */ + public float yb; + + /** + * Position of top side. + */ + public float yt; + + /** + * Depth. + */ + public float z; + + /** + * Left texture coordinate. + */ + public float sl; + + /** + * Right texture coordinate. + */ + public float sr; + + /** + * Bottom texture coordinate. + */ + public float tb; + + /** + * Top texture coordinate. + */ + public float tt; +} diff --git a/src/main/java/net/opengrabeso/opengl/pipeline/QuadPipeline.java b/src/main/java/net/opengrabeso/opengl/pipeline/QuadPipeline.java new file mode 100644 index 00000000..0112814c --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/pipeline/QuadPipeline.java @@ -0,0 +1,132 @@ +/* + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package net.opengrabeso.opengl.pipeline; + +import com.github.opengrabeso.jaagl.GL2GL3; + +/** + * Utility for drawing a stream of quads. + * Adapted from https://github.com/adbrown85/jogl + */ +/*@VisibleForTesting*/ +public interface QuadPipeline { + + /** + * Registers an {@link EventListener} with this {@link QuadPipeline}. + * + * @param listener Listener to register + * @throws NullPointerException if listener is null + */ + void addListener(/*@Nonnull*/ EventListener listener); + + /** + * Adds a quad to this {@link QuadPipeline}. + * + * @param gl Current OpenGL context + * @param quad Quad to add to pipeline + * @throws NullPointerException if context or quad is null + */ + void addQuad(/*@Nonnull*/ GL2GL3 gl, /*@Nonnull*/ Quad quad); + + /** + * Starts a render cycle with this {@link QuadPipeline}. + * + * @param gl Current OpenGL context + * @throws NullPointerException if context is null + */ + void beginRendering(/*@Nonnull*/ GL2GL3 gl); + + /** + * Frees resources used by this {@link QuadPipeline}. + * + * @param gl Current OpenGL context + */ + void dispose(/*@Nonnull*/ GL2GL3 gl); + + /** + * Finishes a render cycle with this {@link QuadPipeline}. + * + * @param gl Current OpenGL context + */ + void endRendering(/*@Nonnull*/ GL2GL3 gl); + + /** + * Draws all vertices in this {@link QuadPipeline}. + * + * @param gl Current OpenGL context + */ + void flush(/*@Nonnull*/ GL2GL3 gl); + + // TODO: Rename to `size`? + /** + * Returns number of quads in this {@link QuadPipeline}. + * + * @return Number of quads in this pipeline, not negative + */ + /*@Nonnegative*/ + int getSize(); + + /** + * Checks if there aren't any quads in this {@link QuadPipeline}. + * + * @return True if there aren't any quads in this pipeline + */ + boolean isEmpty(); + + /** + * Deregisters an {@link EventListener} from this {@link QuadPipeline}. + * + * @param listener Listener to deregister, ignored if null or unregistered + */ + void removeListener(/*@CheckForNull*/ EventListener listener); + + /** + * Observer of a {@link QuadPipeline}. + */ + interface EventListener { + + /** + * Responds to an event from a {@link QuadPipeline}. + * + * @param type Type of event + * @throws NullPointerException if event type is null + */ + void onQuadPipelineEvent(/*@Nonnull*/ EventType type); + } + + /** + * Kind of event. + */ + enum EventType { + + /** + * Pipeline is automatically flushing all queued quads, e.g., when it's full. + */ + AUTOMATIC_FLUSH; + } +} diff --git a/src/main/java/net/opengrabeso/opengl/pipeline/QuadPipelineGL15.java b/src/main/java/net/opengrabeso/opengl/pipeline/QuadPipelineGL15.java new file mode 100644 index 00000000..b1b5c39c --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/pipeline/QuadPipelineGL15.java @@ -0,0 +1,145 @@ +/* + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package net.opengrabeso.opengl.pipeline; + +import com.github.opengrabeso.jaagl.GL2GL3; +import com.github.opengrabeso.jaagl.GL2; + +/** + * {@link QuadPipeline} for use with OpenGL 1.5. + */ +/*@VisibleForTesting*/ +/*@NotThreadSafe*/ +public final class QuadPipelineGL15 extends AbstractQuadPipeline { + + /** + * Number of vertices per primitive. + */ + /*@Nonnegative*/ + private static final int VERTS_PER_PRIM = 4; + + /** + * Number of primitives per quad. + */ + /*@Nonnegative*/ + private static final int PRIMS_PER_QUAD = 1; + + /** + * OpenGL handle to vertex buffer. + */ + /*@Nonnegative*/ + private final int vbo; + + /** + * Constructs a {@link QuadPipelineGL15}. + * + * @param gl Current OpenGL context + * @throws NullPointerException if context is null + */ + /*@VisibleForTesting*/ + public QuadPipelineGL15(/*@Nonnull*/ final GL2 gl) { + + super(VERTS_PER_PRIM, PRIMS_PER_QUAD); + + this.vbo = createVertexBufferObject(gl, BYTES_PER_BUFFER); + } + + @Override + public void beginRendering(/*@Nonnull*/ final GL2GL3 gl) { + + super.beginRendering(gl); + + final GL2 gl2 = gl.getGL2(); + + // Change state + gl2.glPushClientAttrib((int) gl2.GL_ALL_CLIENT_ATTRIB_BITS()); + gl2.glBindBuffer(gl2.GL_ARRAY_BUFFER(), vbo); + + // Points + gl2.glEnableClientState(gl2.GL_VERTEX_ARRAY()); + gl2.glVertexPointer( + FLOATS_PER_POINT, // size + gl2.GL_FLOAT(), // type + STRIDE, // stride + POINT_OFFSET); // offset + + // Coordinates + gl2.glEnableClientState(gl2.GL_TEXTURE_COORD_ARRAY()); + gl2.glTexCoordPointer( + FLOATS_PER_COORD, // size + gl2.GL_FLOAT(), // type + STRIDE, // stride + COORD_OFFSET); // offset + } + + @Override + public void dispose(/*@Nonnull*/ final GL2GL3 gl) { + + super.dispose(gl); + + final GL2 gl2 = gl.getGL2(); + + // Delete the vertex buffer object + final int[] handles = new int[] { vbo }; + gl2.glDeleteBuffers(handles); + } + + @Override + protected void doFlush(/*@Nonnull*/ final GL2GL3 gl) { + + final GL2 gl2 = gl.getGL2(); + + // Upload data + rewind(); + gl2.glBufferSubData( + gl2.GL_ARRAY_BUFFER(), // target + 0, // offset + getSizeInBytes(), // size + getData()); // data + + // Draw + gl2.glDrawArrays( + gl2.GL_QUADS(), // mode + 0, // first + getSizeInVertices()); // count + + clear(); + } + + @Override + public void endRendering(/*@Nonnull*/ final GL2GL3 gl) { + + super.endRendering(gl); + + final GL2 gl2 = gl.getGL2(); + + // Restore state + gl2.glBindBuffer(gl2.GL_ARRAY_BUFFER(), 0); + gl2.glPopClientAttrib(); + } +} diff --git a/src/main/java/net/opengrabeso/opengl/pipeline/QuadPipelineGL30.java b/src/main/java/net/opengrabeso/opengl/pipeline/QuadPipelineGL30.java new file mode 100644 index 00000000..2a28f307 --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/pipeline/QuadPipelineGL30.java @@ -0,0 +1,231 @@ +/* + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package net.opengrabeso.opengl.pipeline; + +import com.github.opengrabeso.jaagl.GL2GL3; + +/** + * {@link QuadPipeline} for use with OpenGL 3. + * + *

+ * {@code QuadPipelineGL30} draws quads using OpenGL 3 features. It uses a Vertex Buffer Object to + * store vertices in graphics memory and a Vertex Array Object to quickly switch which vertex + * attributes are enabled. + * + *

+ * Since {@code GL_QUAD} has been deprecated in OpenGL 3, this implementation uses two triangles to + * represent one quad. An alternative implementation using one {@code GL_FAN} per quad was also + * tested, but proved slower in most cases. Apparently the penalty imposed by the extra work + * required by the driver outweighed the benefit of transferring less vertices. + */ +/*@VisibleForTesting*/ +/*@NotThreadSafe*/ +public final class QuadPipelineGL30 extends AbstractQuadPipeline { + + /** + * Name of point attribute in shader program. + */ + /*@Nonnull*/ + private static final String POINT_ATTRIB_NAME = "MCVertex"; + + /** + * Name of texture coordinate attribute in shader program. + */ + /*@Nonnull*/ + private static final String COORD_ATTRIB_NAME = "TexCoord0"; + + /** + * Number of vertices per primitive. + */ + /*@Nonnegative*/ + private static final int VERTS_PER_PRIM = 3; + + /** + * Number of primitives per quad. + */ + /*@Nonnegative*/ + private static final int PRIMS_PER_QUAD = 2; + + /** + * Vertex Buffer Object with vertex data. + */ + /*@Nonnegative*/ + private final int vbo; + + /** + * Vertex Array Object with vertex attribute state. + */ + /*@Nonnegative*/ + private final int vao; + + /** + * Constructs a {@link QuadPipelineGL30}. + * + * @param gl Current OpenGL context + * @param shaderProgram Shader program to render quads with + * @throws NullPointerException if context is null + * @throws IllegalArgumentException if shader program is less than one + */ + /*@VisibleForTesting*/ + public QuadPipelineGL30(/*@Nonnull*/ final GL2GL3 gl, /*@Nonnegative*/ final int shaderProgram) { + + super(VERTS_PER_PRIM, PRIMS_PER_QUAD); + + this.vbo = createVertexBufferObject(gl, BYTES_PER_BUFFER); + this.vao = createVertexArrayObject(gl, shaderProgram, vbo); + } + + @Override + public void beginRendering(/*@Nonnull*/ final GL2GL3 gl) { + + super.beginRendering(gl); + + // Bind the VBO and VAO + gl.glBindBuffer(gl.GL_ARRAY_BUFFER(), vbo); + gl.glBindVertexArray(vao); + } + + /** + * Creates a vertex array object for use with the pipeline. + * + * @param gl Current OpenGL context, assumed not null + * @param program OpenGL handle to the shader program, assumed not negative + * @param vbo OpenGL handle to VBO holding vertices, assumed not negative + * @return OpenGL handle to resulting VAO + */ + /*@Nonnegative*/ + private static int createVertexArrayObject(/*@Nonnull*/ final GL2GL3 gl, + /*@Nonnegative*/ final int program, + /*@Nonnegative*/ final int vbo) { + + // Generate + final int[] handles = new int[1]; + gl.glGenVertexArrays(handles); + final int vao = handles[0]; + + // Bind + gl.glBindVertexArray(vao); + gl.glBindBuffer(gl.GL_ARRAY_BUFFER(), vbo); + + // Points + final int pointLoc = gl.glGetAttribLocation(program, POINT_ATTRIB_NAME); + if (pointLoc == -1) { + throw new IllegalStateException("Could not find point attribute location!"); + } else { + gl.glEnableVertexAttribArray(pointLoc); + gl.glVertexAttribPointer( + pointLoc, // location + FLOATS_PER_POINT, // number of components + gl.GL_FLOAT(), // type + false, // normalized + STRIDE, // stride + POINT_OFFSET); // offset + } + + // Coords + final int coordLoc = gl.glGetAttribLocation(program, COORD_ATTRIB_NAME); + if (coordLoc != -1) { + gl.glEnableVertexAttribArray(coordLoc); + gl.glVertexAttribPointer( + coordLoc, // location + FLOATS_PER_COORD, // number of components + gl.GL_FLOAT(), // type + false, // normalized + STRIDE, // stride + COORD_OFFSET); // offset + } + + // Unbind + gl.glBindBuffer(gl.GL_ARRAY_BUFFER(), 0); + gl.glBindVertexArray(0); + + return vao; + } + + @Override + public void dispose(/*@Nonnull*/ final GL2GL3 gl) { + + super.dispose(gl); + + // Delete VBO and VAO + final int[] handles = new int[1]; + handles[0] = vbo; + gl.glDeleteBuffers(handles); + handles[0] = vao; + gl.glDeleteVertexArrays(handles); + } + + @Override + protected void doAddQuad(/*@Nonnull*/ final Quad quad) { + + // Add upper-left triangle + addPoint(quad.xr, quad.yt, quad.z); + addCoord(quad.sr, quad.tt); + addPoint(quad.xl, quad.yt, quad.z); + addCoord(quad.sl, quad.tt); + addPoint(quad.xl, quad.yb, quad.z); + addCoord(quad.sl, quad.tb); + + // Add lower-right triangle + addPoint(quad.xr, quad.yt, quad.z); + addCoord(quad.sr, quad.tt); + addPoint(quad.xl, quad.yb, quad.z); + addCoord(quad.sl, quad.tb); + addPoint(quad.xr, quad.yb, quad.z); + addCoord(quad.sr, quad.tb); + } + + @Override + protected void doFlush(/*@Nonnull*/ final GL2GL3 gl) { + + // Upload data + rewind(); + gl.glBufferSubData( + gl.GL_ARRAY_BUFFER(), // target + 0, // offset + getSizeInBytes(), // size + getData()); // data + + // Draw + gl.glDrawArrays( + gl.GL_TRIANGLES(), // mode + 0, // first + getSizeInVertices()); // count + clear(); + } + + @Override + public void endRendering(/*@Nonnull*/ final GL2GL3 gl) { + + super.endRendering(gl); + + // Unbind the VBO and VAO + gl.glBindBuffer(gl.GL_ARRAY_BUFFER(), 0); + gl.glBindVertexArray(0); + } +} diff --git a/src/main/java/net/opengrabeso/opengl/pipeline/ShaderLoader.java b/src/main/java/net/opengrabeso/opengl/pipeline/ShaderLoader.java new file mode 100644 index 00000000..302da254 --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/pipeline/ShaderLoader.java @@ -0,0 +1,156 @@ +/* + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package net.opengrabeso.opengl.pipeline; + +import com.github.opengrabeso.jaagl.GL2GL3; + + +/** + * Utility to load shaders from files, URLs, and strings. + * + *

+ * {@code ShaderLoader} is a simple utility for loading shaders. It takes shaders directly as + * strings. It will create and compile the shaders, and link them together into a program. Both + * compiling and linking are verified. + * + *

+ * Note it is highly recommended that if the developer passes the strings directly to {@code + * ShaderLoader} that they contain newlines. That way if any errors do occur their line numbers + * will be reported correctly. This means that if the shader is to be embedded in Java code, a + * "\n" should be appended to every line. + */ +/*@VisibleForTesting*/ +/*@ThreadSafe*/ +public final class ShaderLoader { + + /** + * Prevents instantiation. + */ + private ShaderLoader() { + // empty + } + + /** + * Checks that a shader was compiled correctly. + * + * @param gl OpenGL context, assumed not null + * @param shader OpenGL handle to a shader + * @return True if shader was compiled without errors + */ + private static boolean isShaderCompiled(/*@Nonnull*/ final GL2GL3 gl, final int shader) { + return ShaderUtil.isShaderStatusValid(gl, shader, gl.GL_COMPILE_STATUS(), null); + } + + /** + * Checks that a shader program was linked successfully. + * + * @param gl OpenGL context, assumed not null + * @param program OpenGL handle to a shader program + * @return True if program was linked successfully + */ + private static boolean isProgramLinked(/*@Nonnull*/ final GL2GL3 gl, final int program) { + return ShaderUtil.isProgramStatusValid(gl, program, gl.GL_LINK_STATUS()); + } + + /** + * Checks that a shader program was validated successfully. + * + * @param gl OpenGL context, assumed not null + * @param program OpenGL handle to a shader program + * @return True if program was validated successfully + */ + private static boolean isProgramValidated(/*@Nonnull*/ final GL2GL3 gl, final int program) { + return ShaderUtil.isProgramStatusValid(gl, program, gl.GL_VALIDATE_STATUS()); + } + + /** + * Loads a shader program from a pair of strings. + * + * @param gl Current OpenGL context + * @param vss Vertex shader source + * @param fss Fragment shader source + * @return OpenGL handle to the shader program, not negative + * @throws NullPointerException if context or either source is null + * @throws IllegalArgumentException if either source is empty + */ + /*@Nonnegative*/ + public static int loadProgram(/*@Nonnull*/ final GL2GL3 gl, + /*@Nonnull*/ final String vss, + /*@Nonnull*/ final String fss) { + + // Create the shaders + final int vs = loadShader(gl, vss, gl.GL_VERTEX_SHADER()); + final int fs = loadShader(gl, fss, gl.GL_FRAGMENT_SHADER()); + + // Create a program and attach the shaders + final int program = gl.glCreateProgram(); + gl.glAttachShader(program, vs); + gl.glAttachShader(program, fs); + + // Link and validate the program + gl.glLinkProgram(program); + gl.glValidateProgram(program); + if ((!isProgramLinked(gl, program)) || (!isProgramValidated(gl, program))) { + final String log = ShaderUtil.getProgramInfoLog(gl, program); + throw gl.newGLException(log); + } + + // Clean up the shaders + gl.glDeleteShader(vs); + gl.glDeleteShader(fs); + + return program; + } + + /** + * Loads a shader from a string. + * + * @param gl Current OpenGL context, assumed not null + * @param source Source code of the shader as one long string, assumed not null or empty + * @param type Type of shader, assumed valid + * @return OpenGL handle to the shader, not negative + */ + /*@Nonnegative*/ + private static int loadShader(/*@Nonnull*/ final GL2GL3 gl, + /*@Nonnull*/ final String source, + final int type) { + + // Create and read source + final int shader = gl.glCreateShader(type); + gl.glShaderSource(shader, source); + + // Compile + gl.glCompileShader(shader); + if (!isShaderCompiled(gl, shader)) { + final String log = ShaderUtil.getShaderInfoLog(gl, shader); + throw gl.newGLException(log); + } + + return shader; + } +} diff --git a/src/main/java/net/opengrabeso/opengl/pipeline/ShaderUtil.java b/src/main/java/net/opengrabeso/opengl/pipeline/ShaderUtil.java new file mode 100644 index 00000000..dbc8dec3 --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/pipeline/ShaderUtil.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package net.opengrabeso.opengl.pipeline; + +import com.github.opengrabeso.jaagl.GL2GL3; + +import java.io.PrintStream; + +public class ShaderUtil { + public static String getShaderInfoLog(final GL2GL3 gl, final int shaderObj) { + return gl.glGetShaderInfoLog(shaderObj); + } + + public static String getProgramInfoLog(final GL2GL3 gl, final int programObj) { + return gl.glGetProgramInfoLog(programObj); + } + + public static boolean isShaderStatusValid(final GL2GL3 gl, final int shaderObj, final int name, final PrintStream verboseOut) { + final int[] ires = new int[1]; + gl.glGetShaderiv(shaderObj, name, ires, 0); + + final boolean res = ires[0]==1; + if(!res && null!=verboseOut) { + verboseOut.println("Shader status invalid: "+ getShaderInfoLog(gl, shaderObj)); + } + return res; + } + + public static boolean isProgramStatusValid(final GL2GL3 gl, final int programObj, final int name) { + final int[] ires = new int[1]; + gl.glGetProgramiv(programObj, name, ires, 0); + + return ires[0]==1; + } + + +} diff --git a/src/main/java/net/opengrabeso/opengl/pipeline/Uniform.java b/src/main/java/net/opengrabeso/opengl/pipeline/Uniform.java new file mode 100644 index 00000000..6c2b7710 --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/pipeline/Uniform.java @@ -0,0 +1,72 @@ +/* + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package net.opengrabeso.opengl.pipeline; + +import com.github.opengrabeso.jaagl.GL2GL3; + + +/** + * Uniform variable in a shader. + */ +abstract class Uniform { + + /** + * Index of uniform in shader. + */ + /*@Nonnegative*/ + final int location; + + /** + * True if local value should be pushed. + */ + boolean dirty; + + /** + * Constructs a {@link Uniform}. + * + * @param gl Current OpenGL context + * @param program OpenGL handle to shader program + * @param name Name of the uniform in shader source code + * @throws NullPointerException if context or name is null + * @throws IllegalArgumentException if program is negative + */ + Uniform(/*@Nonnull*/ final GL2GL3 gl, + /*@Nonnegative*/ final int program, + /*@Nonnull*/ final String name) { + + location = gl.glGetUniformLocation(program, name); + } + + /** + * Pushes the local value to the shader program. + * + * @param gl Current OpenGL context + * @throws NullPointerException if context is null + */ + abstract void update(/*@Nonnull*/ GL2GL3 gl); +} diff --git a/src/main/java/net/opengrabeso/opengl/pipeline/Vec4Uniform.java b/src/main/java/net/opengrabeso/opengl/pipeline/Vec4Uniform.java new file mode 100644 index 00000000..15a2c648 --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/pipeline/Vec4Uniform.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package net.opengrabeso.opengl.pipeline; + +import com.github.opengrabeso.jaagl.GL2GL3; + + +/** + * Uniform for a {@code vec4}. + */ +/*@NotThreadSafe*/ +final class Vec4Uniform extends Uniform { + + /** + * Local copy of vector values. + */ + /*@Nonnull*/ + final float[] value = new float[4]; + + /** + * Constructs a uniform vector. + * + * @param gl Current OpenGL context + * @param program OpenGL handle to shader program + * @param name Name of the uniform in shader source code + * @throws NullPointerException if context is null + */ + Vec4Uniform(/*@Nonnull*/ final GL2GL3 gl, + /*@Nonnegative*/ final int program, + /*@Nonnull*/ final String name) { + super(gl, program, name); + } + + @Override + void update(/*@Nonnull*/ final GL2GL3 gl) { + Check.notNull(gl, "GL cannot be null"); + if (location >= 0) { + gl.glUniform4fv(location, 1, value, 0); + } + } +}