From 1732384262903769b257cec62c4d90f6c642c267 Mon Sep 17 00:00:00 2001 From: mcc Date: Sun, 4 Mar 2018 17:07:54 -0500 Subject: [PATCH] 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. --- src/api/types/mesh.c | 15 ++++++++ src/graphics/mesh.c | 82 ++++++++++++++++++++++++++++++++------------ src/graphics/mesh.h | 15 +++++++- 3 files changed, 90 insertions(+), 22 deletions(-) diff --git a/src/api/types/mesh.c b/src/api/types/mesh.c index 68624813..573261fb 100644 --- a/src/api/types/mesh.c +++ b/src/api/types/mesh.c @@ -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 } }; diff --git a/src/graphics/mesh.c b/src/graphics/mesh.c index cb657bd4..8a74bfb8 100644 --- a/src/graphics/mesh.c +++ b/src/graphics/mesh.c @@ -4,6 +4,36 @@ #include #include +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); +} diff --git a/src/graphics/mesh.h b/src/graphics/mesh.h index d79750f8..15c3b66e 100644 --- a/src/graphics/mesh.h +++ b/src/graphics/mesh.h @@ -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);