Implementation of mesh:attachAttributes(), with optional instance divisor

Some features remain to be implemented, these are marked with TODOs.

Currently it is not allowed to attach a mesh which itself has attachments.
This commit is contained in:
mcc 2018-03-04 17:07:54 -05:00
parent 58e59d9772
commit 1732384262
3 changed files with 90 additions and 22 deletions

View File

@ -229,6 +229,20 @@ int l_lovrMeshSetMaterial(lua_State* L) {
return 0;
}
int l_lovrMeshAttachAttributes(lua_State* L) {
Mesh* attachTo = luax_checktype(L, 1, Mesh);
Mesh* attachThis = luax_checktype(L, 2, Mesh);
int instanceDivisor = luaL_optnumber(L, 3, 0);
// TODO: Check attribute name(s) in 4th argument and if present only attach those
VertexFormat* format = &attachThis->vertexData->format;
for(int c = 0; c < format->count; c++) {
lovrMeshAttach(attachTo, attachThis, c, instanceDivisor);
}
return 0;
}
const luaL_Reg lovrMesh[] = {
{ "drawInstanced", l_lovrMeshDrawInstanced },
{ "draw", l_lovrMeshDraw },
@ -249,5 +263,6 @@ const luaL_Reg lovrMesh[] = {
{ "setDrawRange", l_lovrMeshSetDrawRange },
{ "getMaterial", l_lovrMeshGetMaterial },
{ "setMaterial", l_lovrMeshSetMaterial },
{ "attachAttributes", l_lovrMeshAttachAttributes },
{ NULL, NULL }
};

View File

@ -4,6 +4,36 @@
#include <stdlib.h>
#include <stdio.h>
static void lovrMeshBindAttribute(Shader* shader, Mesh *mesh, VertexFormat *format, int i, bool enabled, int divisor) {
Attribute attribute = format->attributes[i];
int location = lovrShaderGetAttributeId(shader, attribute.name);
if (location >= 0) {
if (enabled) {
glEnableVertexAttribArray(location);
GLenum glType;
switch (attribute.type) {
case ATTR_FLOAT: glType = GL_FLOAT; break;
case ATTR_BYTE: glType = GL_UNSIGNED_BYTE; break;
case ATTR_INT: glType = GL_UNSIGNED_INT; break;
}
// Divisor lives in the VAO and the VAO is per-mesh, so the only reason we would need to set a zero divisor for an attribute is if a nonzero divisor attribute is attached then disabled, and a zero divisor attribute is enabled afterward in its place. Nonzero attachments length is used to determine there is a risk this has happened.
if (divisor || mesh->attachments.length)
glVertexAttribDivisor(location, divisor);
if (attribute.type == ATTR_INT) {
glVertexAttribIPointer(location, attribute.count, glType, format->stride, (void*) attribute.offset);
} else {
glVertexAttribPointer(location, attribute.count, glType, GL_TRUE, format->stride, (void*) attribute.offset);
}
} else {
glDisableVertexAttribArray(location);
}
}
}
static void lovrMeshBindAttributes(Mesh* mesh) {
Shader* shader = lovrGraphicsGetActiveShader();
if (shader == mesh->lastShader && !mesh->attributesDirty) {
@ -14,28 +44,15 @@ static void lovrMeshBindAttributes(Mesh* mesh) {
VertexFormat* format = &mesh->vertexData->format;
for (int i = 0; i < format->count; i++) {
Attribute attribute = format->attributes[i];
int location = lovrShaderGetAttributeId(shader, attribute.name);
lovrMeshBindAttribute(shader, mesh, format, i, mesh->enabledAttributes & (1 << i), 0);
}
if (location >= 0) {
if (mesh->enabledAttributes & (1 << i)) {
glEnableVertexAttribArray(location);
GLenum glType;
switch (attribute.type) {
case ATTR_FLOAT: glType = GL_FLOAT; break;
case ATTR_BYTE: glType = GL_UNSIGNED_BYTE; break;
case ATTR_INT: glType = GL_UNSIGNED_INT; break;
}
if (attribute.type == ATTR_INT) {
glVertexAttribIPointer(location, attribute.count, glType, format->stride, (void*) attribute.offset);
} else {
glVertexAttribPointer(location, attribute.count, glType, GL_TRUE, format->stride, (void*) attribute.offset);
}
} else {
glDisableVertexAttribArray(location);
}
{
int i; MeshAttachment attachment;
vec_foreach(&mesh->attachments, attachment, i) {
lovrGraphicsBindVertexBuffer(attachment.mesh->vbo);
// TODO: Allow disabling of attached attributes?
lovrMeshBindAttribute(shader, attachment.mesh, &attachment.mesh->vertexData->format, attachment.attribute, true, attachment.instanceDivisor);
}
}
@ -70,8 +87,11 @@ Mesh* lovrMeshCreate(uint32_t count, VertexFormat* format, MeshDrawMode drawMode
mesh->vbo = 0;
mesh->ibo = 0;
mesh->material = NULL;
vec_init(&mesh->attachments);
mesh->lastShader = NULL;
mesh->isAnAttachment = false;
glGenBuffers(1, &mesh->vbo);
glGenBuffers(1, &mesh->ibo);
lovrGraphicsBindVertexBuffer(mesh->vbo);
@ -88,6 +108,15 @@ void lovrMeshDestroy(void* ref) {
glDeleteBuffers(1, &mesh->vbo);
glDeleteBuffers(1, &mesh->ibo);
glDeleteVertexArrays(1, &mesh->vao);
{
int i; MeshAttachment attachment;
vec_foreach(&mesh->attachments, attachment, i) {
lovrRelease(attachment.mesh);
}
}
vec_deinit(&mesh->attachments);
free(mesh->indices.raw);
free(mesh);
}
@ -268,3 +297,14 @@ void lovrMeshUnmap(Mesh* mesh) {
glUnmapBuffer(GL_ARRAY_BUFFER);
#endif
}
void lovrMeshAttach(Mesh *attachTo, Mesh* attachThis, int attribute, int instanceDivisor)
{
lovrAssert(!attachTo->isAnAttachment, "Attempted to attach to a mesh which is an attachment itself");
lovrAssert(!attachThis->attachments.length, "Attempted to attach a mesh which has attachments itself");
MeshAttachment attachment = {attachThis, attribute, instanceDivisor};
attachThis->isAnAttachment = true;
lovrRetain(attachThis);
vec_push(&attachTo->attachments, attachment);
}

View File

@ -22,7 +22,17 @@ typedef enum {
MESH_STREAM = GL_STREAM_DRAW
} MeshUsage;
typedef struct Mesh Mesh;
typedef struct {
Mesh *mesh;
int attribute;
int instanceDivisor;
} MeshAttachment;
typedef vec_t(MeshAttachment) vec_meshattachment_t;
struct Mesh {
Ref ref;
VertexData* vertexData;
IndexPointer indices;
@ -43,7 +53,9 @@ typedef struct {
GLuint ibo;
Material* material;
Shader* lastShader;
} Mesh;
vec_meshattachment_t attachments;
bool isAnAttachment;
};
Mesh* lovrMeshCreate(uint32_t count, VertexFormat* format, MeshDrawMode drawMode, MeshUsage usage);
void lovrMeshDestroy(void* ref);
@ -64,3 +76,4 @@ Material* lovrMeshGetMaterial(Mesh* mesh);
void lovrMeshSetMaterial(Mesh* mesh, Material* material);
VertexPointer lovrMeshMap(Mesh* mesh, int start, size_t count, bool read, bool write);
void lovrMeshUnmap(Mesh* mesh);
void lovrMeshAttach(Mesh *attachTo, Mesh* attachThis, int attribute, int instanceDivisor);