Issues

Issue #768 resolved

Custom vertex attributes for Meshes

Alex Szpakowski
created an issue

Custom vertex attributes are something I feel would hugely benefit love.graphics' extensibility by lovers (keeping in line with the "mechanism over policy" philosophy), provided they aren't required knowledge for any simple uses of LÖVE.

For those who don't know, a vertex attribute is some variable associated with a vertex in an object. For example, all things drawn in LÖVE currently have some combination of position, color, and texture coordinate vertex attributes. Custom attributes would have a lover-defined number of components (between 1 and 4) per attribute, and would likely only be usable from inside a custom vertex shader, since LÖVE wouldn't have any use for the lover-defined attribute on its own.

I think the Mesh type is the best place to have them, since you already specify per-vertex attribute information.

The tricky part is coming up with an implementation that's as simple and as powerful as possible, while also not interfering with "standard" use of Meshes (no custom attributes.)

Unity3d and libgdx' Mesh objects both seem to have have several pre-defined attributes you can choose to use. libgdx' implementation looks a bit more customizable than Unity's.

This is pretty good, but maybe a bit brittle. We should see if we can do better.

One issue we will have to deal with is how to link the custom attributes to the attribute variables in vertex shaders. It could either be by string name or by index. There are some downsides to each, mostly relating to how OpenGL links them together in its own API.

Another is how to keep the out-facing API clean and simple. I like Mesh:setVertex(index, x, y, u, v, r, g, b, a) (etc.) but obviously it wouldn't work like that with custom attributes.

And, we should decide whether to have custom attributes as an "array of vertices" (as the current API is) or as individual arrays per attribute. The benefit to the former is it cleanly identifies each individual vertex and is also more performant normally. The benefit to the latter is the API might(?) be easier to figure out and it can give better performance if you only want to modify a single attribute of every vertex.

I don't consider this a blocker for 0.9.0, but it might be nice to squeeze it in before release. :)

Comments (6)

  1. Alex Szpakowski reporter

    It's also worth noting that (aside from the obvious position attribute), per-vertex texture coordinate and color information is very useful even when shaders aren't used, so the API still needs to be able to use those without shaders - but it would also be really nice if the API let you choose the number of components for those attributes (e.g. xyz position, or 4-component texture coordinates.)

    For the truly custom attributes, choosing the type of data (normalized byte or float) would also be useful. OpenGL's pre-shader API doesn't let position and texture coordinates be normalized bytes.

  2. Alex Szpakowski reporter

    This is what I've come up with so far. It's still a WIP and I'm not completely happy with the APIs though.


    There's a new object: VertexBuffer. It stores arrays of vertex attributes. It can either store a single attribute repeated N times, or a collection of attributes repeated N times.

    When you create a VertexBuffer, you give it the formats of the vertex attributes you want to store in the buffer. This is a table, and looks something like this: {"float",2, "byte",4}. That format table would mean the VertexBuffer has an array of 2-component floating point numbers plus 4-component byte numbers. That particular case could represent x,y, r,g,b,a, for example.

    You also give the VertexBuffer constructor one of these:

    • A number representing the total number of vertices to store in the buffer (a "vertex" in this case just means the collection of attributes defined above.)
    • Or a table containing tables with the vertex attribute values in them, matching the definition you gave. For the example above, {{0,0, 255,255,255,128}, {100,0, 255,0,255,255}, {100,100, 255,0,0,255}} would be valid and contains 3 vertices (3 sets of vertex attributes.)

    VertexBuffers can have their contents modified. either with VertexBuffer:setVertex(vertexindex, a1_1, a1_2, a2_1, a2_2, ...) or with VertexBuffer:setAttribute(vertexindex, attributeindex, a2, a2, ...). The former sets all the attributes for a particular vertex in the VertexBuffer, the latter sets a specific attribute in a specific vertex.

    You can attach an attribute from a VertexBuffer to a Mesh. When you do that, the attribute will be used when drawing the Mesh (provided you have a shader which makes use of the attribute.)

    The syntax for that is: Mesh:setVertexAttribute(attribname, vertexbuffer, attributeindex). For the example above, I might do: Mesh:setVertexAttribute("mycolor", buffer, 2), which would make the Mesh use the second attribute in my VertexBuffer (not the second vertex).

    When drawing a Mesh with attributes attached, a Vertex Shader needs to be active which makes use of those attributes. The shader's name for the attributes also needs to match the names given in setVertexAttribute. For the example above, the vertex shader should have attribute vec4 mycolor; declared near the top (and it should use it in its code.)

    A shader is needed because LÖVE wouldn't know what to do with the attribute otherwise. There are also some features of this which wouldn't work at all if I made this work without shaders.

    There is also an optional variant of Mesh:setVertexAttribute, where an additional number parameter specifies how many instances (via Mesh:setInstanceCount) should be drawn before the next attribute of the buffer is used. If it's 0, the attribute is per-vertex, otherwise it's used per-instance or per-10-instances or whatever you want.

  3. Alex Szpakowski reporter

    Added the ability to have custom vertex attributes in Meshes (resolves issue #768.)

    Added new love.graphics.newMesh variants: newMesh(vertexformat, vertices [, drawmode, meshusage]) and newMesh(vertexformat, numvertices [, drawmode, meshusage]).

    Replaced the regular love.graphics.newMesh variants with newMesh(vertices [, drawmode, meshusage]) and newMesh(numvertices [, drawmode, meshusage]). To use an image or canvas with a mesh, use Mesh:setTexture.

    vertexformat is a table with the following prototype: { {attributename, datatype, components}, {attributename, datatype, components}, ... }

    Where attributename is the name of the vertex attribute (can be the built-in names 'VertexPosition', 'VertexTexCoord', or 'VertexColor', or a custom name for use in a vertex shader), datatype is the type of values used for the attribute ('float' or 'byte'), and components is the number of components in the vertex attribute (between 1 and 4.)

    The vertex format is used to determine the layout of the vertices in the mesh, for example the 'regular' newMesh variants use this vertex format: format = { {"VertexPosition", "float", 2}, {"VertexTexCoord", "float", 2}, {"VertexColor", "byte", 4}, }

    The mesh usage parameter accepts the same constants as the spritebatch usage hint in love.graphics.newSpriteBatch - "dynamic", "static", and "stream".

    Mesh:setVertex now sets all vertex attributes for a specific vertex in the Mesh.

    Added Mesh:setVertexAttribute(vertexindex, attributeindex, attributevalue1, ...), which sets the values for a specific vertex attribute in a specific vertex in the Mesh (resolves issue #784.)

    Added Mesh:getVertexFormat and Mesh:flush.

    Added Mesh:setAttributeEnabled(attributename, enable) and Mesh:isAttributeEnabled(attributename), to enable or disable the use of a specific attribute when drawing the Mesh.

    Added Mesh:attachAttribute(attributename, mesh), which makes the Mesh use a vertex attribute from another mesh when drawing the Mesh. This can be used to separate out vertex attributes which are updated at different rates into different meshes, and to share vertex data between multiple meshes.

    Removed Mesh:setVertices, Mesh:getVertices, and Mesh:setVertexColors.

    → <<cset f02774821e49>>

  4. Log in to comment