In retrospective: I used scala’s call-by-name functionality to simplify some imperative-style OpenGL code, and used scala’s awesome type system to build some highly-composable building blocks for Vertex data classes.

One of my recent hobby projects has been to play with LWJGL, which is a thin Java layer over the native OpenGL API. I’ve been going through the examples on the wiki and translating them to Scala. There are a few benefits to doing this, I’ve found:

  1. Actually going through the source line-by-line instead of just copy-pasting the examples has given me a much better understanding of the material that is covered.
  2. Translating the code to Scala ends up revealing a lot of places where certain java/c-isms could be replaced by some of the more “functional” aspects of Scala. These places are what I want to talk about in this post.

Start, End, Bind, Unbind

It’s basically guaranteed that, if you decide to use OpenGL, you’ll see something like this:

glBegin(GL_QUADS);
//do a bunch of operations
glEnd();

Or

glBindBuffer(GL_ARRAY_BUFFER, bufferId);
//do a bunch of stuff while the buffer is bound
glBindBuffer(GL_ARRAY_BUFFER, 0);

Once the “do a bunch of stuff” starts growing beyond a few lines, it gets harder and harder to distinguish where the “stuff” begins and ends. Because of this, I’ve noticed that many examples will indent the “stuff” as if the “begin” and “end” commands are brackets.

With Scala, you can define a function that makes this kind of modification make sense! For example, to abstract away the glBegin/glEnd calls, we can define a function that takes a call-by-name parameter:

def glDoThings[T](mode: Int)(body: => T): T = {
    glBegin(mode)
    try { body }
    finally { glEnd }
}

So from now on, you don’t need to worry about adding glEnd.

glDoThings(GL_QUADS){
    //do a bunch of operations
}

Releasing Resources

Disclaimer: admittedly, the stuff in this section could probably be done with a nice abstraction in Java, but I’m going over it anyway!

In many of the examples, you create a buffer and give it to the GPU, and all you’re left with is a “handle”, which is generally just an Int. Later on, when you’re done with it, you’ve got to release it with a special call. This just means you need to hang on to the Int handle later on. This can be a pain, since having an Int doesn’t really tell you anything about what it represents, how you use it, or how you release it.

To help with this, I made some named classes to wrap a “handle”. For example, the class for a vertex array object looks like

case class VAOHandle(handle: Int) {
    def bind[T](body: => T) = {
        GL30 glBindVertexArray handle
        try{ body }
        finally{ GL30 glBindVertexArray 0 }
    }
    def release = {
        GL30 glBindVertexArray 0
        GL30 glDeleteVertexArrays handle
    }
}

All I have to do to use it is create the array handle as I normally would through OpenGL, then store the VAOHandle(handle). The bind method takes care of the bind/unbind aspect, and the release method takes care of disposing of the array when I’m done with it. All this without needing to interact with any actual GL calls.

Type Composition

One of the tutorials, titled The Quad interleaved, introduced the idea of storing multiple vertex attributes in a single buffer, by interleaving the attributes. For example, a Vertex that had both position and color attributes would store its elements in a buffer as {X, Y, Z, R, G, B, A}, so that multiple vertices could be represented in a single buffer like {X1, Y1, Z1, R1, G1, B1, A1, X2, Y2, ...} and so on.

To help with this, the tutorial created a Vertex class.

public class Vertex {
    // Vertex data
    private float[] xyzw = new float[] {0f, 0f, 0f, 1f};
    private float[] rgba = new float[] {1f, 1f, 1f, 1f};

    // The amount of elements that a vertex has
    public static final int elementCount = 8;
    // The amount of bytes an element has
    public static final int elementBytes = 4;
    // The size of a vertex in bytes, like in C/C++: sizeof(Vertex)
    public static final int sizeInBytes = elementBytes * elementCount;

    // Setters
    public void setXYZ(float x, float y, float z) {
        this.setXYZW(x, y, z, 1f);
    }

    public void setRGB(float r, float g, float b) {
        this.setRGBA(r, g, b, 1f);
    }

    public void setXYZW(float x, float y, float z, float w) {
        this.xyzw = new float[] {x, y, z, w};
    }

    public void setRGBA(float r, float g, float b, float a) {
        this.rgba = new float[] {r, g, b, 1f};
    }

    // Getters
    public float[] getXYZW() {
        return new float[] {this.xyzw[0], this.xyzw[1], this.xyzw[2], this.xyzw[3]};
    }

    public float[] getRGBA() {
        return new float[] {this.rgba[0], this.rgba[1], this.rgba[2], this.rgba[3]};
    }
}

This seems like a nice abstraction, and it makes things a bit nicer to look at than just seeing a bunch of raw array data in your editor of choice. Use it as follows:

// We'll define our quad using 4 vertices of the custom 'Vertex' class
Vertex v0 = new Vertex(); v0.setXYZ(-0.5f, 0.5f, 0f); v0.setRGB(1, 0, 0);
Vertex v1 = new Vertex(); v1.setXYZ(-0.5f, -0.5f, 0f); v1.setRGB(0, 1, 0);
Vertex v2 = new Vertex(); v2.setXYZ(0.5f, -0.5f, 0f); v2.setRGB(0, 0, 1);
Vertex v3 = new Vertex(); v3.setXYZ(0.5f, 0.5f, 0f); v3.setRGB(1, 1, 1);

Vertex[] vertices = new Vertex[] {v0, v1, v2, v3};
// Put each 'Vertex' in one FloatBuffer
FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length * Vertex.elementCount);
for (int i = 0; i < vertices.length; i++) {
    verticesBuffer.put(vertices[i].getXYZW());
    verticesBuffer.put(vertices[i].getRGBA());
}
verticesBuffer.flip();

The very next tutorial introduced texture coordinates into the Vertex class, creating the TexturedVertex class. As you can probably imagine, it was essentially the same as the original Vertex class, but with a few additional fields and methods.

I imagine that it would be nice to have any number of combinations of these different Vertex classes available. Vertex, ColoredVertex, TexturedVertex, ColoredTexturedVertex, and probably many more when you start getting into vertex normals and other more advanced things. But trying to define all of these combinations will lead to quite a lot of duplicated code. I don’t like that, so I made my own set of building blocks that, to the best of my knowledge, are a usage of the Cake Pattern.

Since the original class has both static and non-static methods, I decided to split the components into “Base” and “Meta” traits. The “Base” trait knows about a certain attribute (e.g. just position or just color) and how to get and set them. The “Meta” trait knows how the attributes get laid out in a buffer, so it takes care of the stride and numElements features that were defined as static fields in the tutorial’s example.

// some of the "Base" traits

trait VertexPositionAttribute { self =>
    private val xyzw = Array[Float](0, 0, 0, 1)
    def setXYZ(x: Float, y: Float, z: Float): self.type = setXYZW(x, y, z, 1)

    def setXYZW(x: Float, y: Float, z: Float, w: Float): self.type = {
        xyzw(0) = x
        xyzw(1) = y
        xyzw(2) = z
        xyzw(3) = w
        this
    }
    def putXYZW(out: FloatBuffer) = out.put(xyzw)
    def getXYZW = xyzw.clone
}

trait VertexColorAttribute { self =>
    private val rgba = Array[Float](1, 1, 1, 1)
    def setRGB(r: Float, g: Float, b: Float): self.type = setRGBA(r, g, b, 1)

    def setRGBA(r: Float, g: Float, b: Float, a: Float): self.type = {
        rgba(0) = r
        rgba(1) = g
        rgba(2) = b
        rgba(3) = a
        this
    }
    def putRGBA(out: FloatBuffer) = out.put(rgba)
    def getRGBA = rgba.clone
}

trait VertexTextureAttribute { self =>
    private val st = Array[Float](0, 0)
    def setST(s: Float, t: Float): self.type = {
        st(0) = s
        st(1) = t
        this
    }
    def putST(out: FloatBuffer) = out.put(st)
    def getST = st.clone
}

So the Base traits are pretty simple, but I had to get creative to make the “Meta” traits. To make a “Meta” trait for a given “Base” trait, the implementation needs to extend the VertexMeta[BaseType] trait. To help make the compiler happy later on, I also had to define VertexMetaBase with an abstract type V, so that VertexMeta looks like this:

trait VertexMeta[V1] extends VertexMetaBase {
    type V = V1
}

And the meat of the implementation goes in VertexMetaBase.

trait VertexMetaBase {
    type V

    private var _numElements = 0
    private var currentByteOffset = 0
    private var putFunction: (V, FloatBuffer) => Unit = { (_, _) => () }

    protected def registerAttribute(numElements: Int, put: (V, FloatBuffer) => Unit): VertexAttributeInformation[V] = {
        this._numElements += numElements
        val byteCount = numElements * sizeOf[Float]
        val info = VertexAttributeInformation(numElements, currentByteOffset, put)
        val currentPutter = putFunction
        putFunction = { (v, b) => currentPutter(v, b); put(v, b) }
        currentByteOffset += byteCount
        info
    }
    def stride = currentByteOffset

    def numElements = _numElements

    def createVertexBuffer(verts: V*): FloatBuffer = {
        val buffer = BufferUtils.createFloatBuffer(verts.size * numElements)
        for (v <- verts) putFunction(v, buffer)
        buffer.flip
        buffer
    }
}

If you’re reading carefully, you’ll notice that I mentioned a VertexAttributeInformation class. It’s defined as follows:

case class VertexAttributeInformation[V](numElements: Int, byteOffset: Int, put: (V, FloatBuffer) => Unit)

It just gets used by the different Attribute Meta traits in order to associate a property with an attribute, i.e. for when you’re using shaders. Anyway, with that big thing out of the way, the Meta traits are fairly simple to define.

trait VertexPositionMeta { self: VertexMetaBase { type V <: VertexPositionAttribute } =>
    private val positionInfo = registerAttribute(4, { (v, b) => v.putXYZW(b) })
    def setPositionAttribPointer(index: Int) =
        GL20.glVertexAttribPointer(index, positionInfo.numElements, GL11.GL_FLOAT, false, stride, positionInfo.byteOffset)
}

trait VertexColorMeta { self: VertexMetaBase { type V <: VertexColorAttribute } =>
    private val colorInfo = registerAttribute(4, { (v, b) => v.putRGBA(b) })
    def setColorAttribPointer(index: Int) =
        GL20.glVertexAttribPointer(index, colorInfo.numElements, GL11.GL_FLOAT, false, stride, colorInfo.byteOffset)
}

trait VertexTextureMeta { self: VertexMetaBase { type V <: VertexTextureAttribute } =>
    private val textureInfo = registerAttribute(2, { (v, b) => v.putST(b) })
    def setTextureAttribPointer(index: Int) =
        GL20.glVertexAttribPointer(index, textureInfo.numElements, GL11.GL_FLOAT, false, stride, textureInfo.byteOffset)
}

Note the explicitly typed self on both of those trait definitions. Having declared that, you can only mix these traits into a VertexMetaBase whose V type is a subclass of their respective attribute. Finally, you can mix all of these things together very concisely:

class ColoredTexturedVertex
    extends VertexPositionAttribute
    with VertexColorAttribute
    with VertexTextureAttribute
object ColoredTexturedVertex extends VertexMeta[ColoredTexturedVertex]
    with VertexPositionMeta
    with VertexColorMeta
    with VertexTextureMeta

class ColoredVertex
    extends VertexPositionAttribute
    with VertexColorAttribute
object ColoredVertex extends VertexMeta[ColoredVertex]
    with VertexPositionMeta
    with VertexColorMeta

I split the class definitions onto multiple lines for readability, but really all that is needed to create a mix-and-match vertex attributes class is those two definitions; two lines of code.

Shorter Code With Scala!

I’d like to wrap this post up by pointing out just how much shorter the tutorial code became after I made a few helper functions and used the power of Scala as mentioned above. The Textured Quad Tutorial defines a setupQuad method, which I don’t want to post here, simply because it’s about 60 lines long. After my changes, it’s down to about 30, and here’s what it looks like.

def setupQuad = {
    // use the mix-and-match attribute vertex class to initialize the quad's vertices into a buffer
    val interleavedBuffer = ColoredVertex.createVertexBuffer(
        new ColoredVertex().setXYZ(-.5f, .5f, 0).setRGBA(1, 0, 0, 1),
        new ColoredVertex().setXYZ(-.5f, -.5f, 0).setRGBA(0, 1, 0, 1),
        new ColoredVertex().setXYZ(.5f, -.5f, 0).setRGBA(0, 0, 1, 1),
        new ColoredVertex().setXYZ(.5f, .5f, 0).setRGBA(1, 1, 1, 1))

    val indices = Array[Byte](
        0, 1, 2, //bottom-left triangle
        2, 3, 0) //top-right triangle
    indicesCount = indices.length
    val indexBuffer = BufferUtils.createByteBuffer(indices.length)
    indexBuffer.put(indices).flip

    //create a new VAO, which can have up to 16 attribute VBOs assigned to it
    vao = newVAO
    vao.bind {
        vbo = newVBO(GL15.GL_ARRAY_BUFFER)

        vbo.bind {
            GL15.glBufferData(GL15.GL_ARRAY_BUFFER, interleavedBuffer, GL15.GL_STATIC_DRAW)
            ColoredVertex.setPositionAttribPointer(0)
            ColoredVertex.setColorAttribPointer(1)
        }
    }

    //create a new VBO for the indeices
    vboi = newVBO(GL15.GL_ELEMENT_ARRAY_BUFFER)
    vboi.bind {
        GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indexBuffer, GL15.GL_STATIC_DRAW)
    }
}

Obviously this could be taken much further. I didn’t introduce the concept of a “Model”, which seems like the logical next step. But I’m not aiming to make a graphics library, this was just an exploration for now. Still, it seems like Scala can and will be a powerful tool in any further work with OpenGL.