diff --git a/src/graphics/buffer.c b/src/graphics/buffer.c index fd172052..80686d9d 100644 --- a/src/graphics/buffer.c +++ b/src/graphics/buffer.c @@ -5,6 +5,7 @@ void lovrBufferDestroy(Buffer* buffer) { glDeleteBuffers(1, &buffer->vbo); glDeleteVertexArrays(1, &buffer->vao); + vec_deinit(&buffer->map); free(buffer->data); free(buffer); } @@ -12,7 +13,23 @@ void lovrBufferDestroy(Buffer* buffer) { void lovrBufferDraw(Buffer* buffer) { glBindVertexArray(buffer->vao); glEnableVertexAttribArray(0); - glDrawArrays(buffer->drawMode, buffer->rangeStart, buffer->rangeCount); + int usingIbo = buffer->map.length > 0; + + int start, count; + if (buffer->isRangeEnabled) { + start = buffer->rangeStart; + count = buffer->rangeCount; + } else { + start = 0; + count = usingIbo ? buffer->map.length : buffer->size; + } + + if (usingIbo) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->ibo); + glDrawElements(buffer->drawMode, count, GL_UNSIGNED_INT, (GLvoid*) NULL + start); + } else { + glDrawArrays(buffer->drawMode, start, count); + } glDisableVertexAttribArray(0); } @@ -46,17 +63,47 @@ void lovrBufferSetVertex(Buffer* buffer, int index, float x, float y, float z) { glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL); } +unsigned int* lovrBufferGetVertexMap(Buffer* buffer, int* count) { + *count = buffer->map.length; + return buffer->map.data; +} + +void lovrBufferSetVertexMap(Buffer* buffer, unsigned int* map, int count) { + if (count == 0 || !map) { + vec_clear(&buffer->map); + glDeleteBuffers(1, &buffer->ibo); + buffer->ibo = 0; + } else if (!buffer->ibo) { + glGenBuffers(1, &buffer->ibo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->ibo); + } + + vec_clear(&buffer->map); + vec_reserve(&buffer->map, count); + vec_pusharr(&buffer->map, map, count); + + glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(unsigned int), buffer->map.data, GL_STATIC_DRAW); +} + +char lovrBufferIsRangeEnabled(Buffer* buffer) { + return buffer->isRangeEnabled; +} + +void lovrBufferSetRangeEnabled(Buffer* buffer, char isEnabled) { + buffer->isRangeEnabled = isEnabled; +} + void lovrBufferGetDrawRange(Buffer* buffer, int* start, int* end) { - *start = buffer->rangeStart + 1; - *end = buffer->rangeStart + 1 + buffer->rangeCount; + *start = buffer->rangeStart; + *end = buffer->rangeStart + buffer->rangeCount - 1; } int lovrBufferSetDrawRange(Buffer* buffer, int rangeStart, int rangeEnd) { - if (rangeStart <= 0 || rangeEnd <= 0 || rangeStart > rangeEnd) { + if (rangeStart < 0 || rangeEnd < 0 || rangeStart > rangeEnd) { return 1; } - buffer->rangeStart = rangeStart - 1; + buffer->rangeStart = rangeStart; buffer->rangeCount = rangeEnd - rangeStart + 1; return 0; } diff --git a/src/graphics/buffer.h b/src/graphics/buffer.h index 935c999f..92998177 100644 --- a/src/graphics/buffer.h +++ b/src/graphics/buffer.h @@ -1,4 +1,5 @@ #include "../glfw.h" +#include "../vendor/vec/vec.h" #ifndef LOVR_BUFFER_TYPES #define LOVR_BUFFER_TYPES @@ -15,6 +16,8 @@ typedef enum { BUFFER_STREAM = GL_STREAM_DRAW } BufferUsage; +typedef vec_t(unsigned int) vec_uint_t; + typedef struct { int size; GLfloat* data; @@ -22,6 +25,9 @@ typedef struct { BufferUsage usage; GLuint vao; GLuint vbo; + GLuint ibo; + vec_uint_t map; + char isRangeEnabled; int rangeStart; int rangeCount; } Buffer; @@ -34,5 +40,7 @@ int lovrBufferSetDrawMode(Buffer* buffer, BufferDrawMode drawMode); int lovrBufferGetVertexCount(Buffer* buffer); void lovrBufferGetVertex(Buffer* buffer, int index, float* x, float* y, float* z); void lovrBufferSetVertex(Buffer* buffer, int index, float x, float y, float z); +unsigned int* lovrBufferGetVertexMap(Buffer* buffer, int* count); +void lovrBufferSetVertexMap(Buffer* buffer, unsigned int* map, int count); void lovrBufferGetDrawRange(Buffer* buffer, int* start, int* count); int lovrBufferSetDrawRange(Buffer* buffer, int start, int count); diff --git a/src/graphics/graphics.c b/src/graphics/graphics.c index aec52799..64f735ce 100644 --- a/src/graphics/graphics.c +++ b/src/graphics/graphics.c @@ -68,6 +68,10 @@ Buffer* lovrGraphicsNewBuffer(int size, BufferDrawMode drawMode, BufferUsage usa buffer->usage = usage; buffer->size = size; buffer->data = malloc(buffer->size * 3 * sizeof(GLfloat)); + buffer->vao = 0; + buffer->vbo = 0; + buffer->ibo = 0; + buffer->isRangeEnabled = 0; buffer->rangeStart = 0; buffer->rangeCount = buffer->size; @@ -77,6 +81,8 @@ Buffer* lovrGraphicsNewBuffer(int size, BufferDrawMode drawMode, BufferUsage usa glGenVertexArrays(1, &buffer->vao); + vec_init(&buffer->map); + return buffer; } diff --git a/src/lovr/buffer.c b/src/lovr/buffer.c index c618abda..45e43033 100644 --- a/src/lovr/buffer.c +++ b/src/lovr/buffer.c @@ -28,6 +28,8 @@ const luaL_Reg lovrBuffer[] = { { "getVertexCount", l_lovrBufferGetVertexCount }, { "getVertex", l_lovrBufferGetVertex }, { "setVertex", l_lovrBufferSetVertex }, + { "getVertexMap", l_lovrBufferGetVertexMap }, + { "setVertexMap", l_lovrBufferSetVertexMap }, { "getDrawMode", l_lovrBufferGetDrawMode }, { "setDrawMode", l_lovrBufferSetDrawMode }, { "getDrawRange", l_lovrBufferGetDrawRange }, @@ -111,19 +113,75 @@ int l_lovrBufferSetVertex(lua_State* L) { return 0; } +int l_lovrBufferGetVertexMap(lua_State* L) { + Buffer* buffer = luax_checkbuffer(L, 1); + int count; + unsigned int* indices = lovrBufferGetVertexMap(buffer, &count); + + if (count == 0) { + lua_pushnil(L); + return 1; + } + + lua_newtable(L); + for (int i = 0; i < count; i++) { + lua_pushinteger(L, indices[i]); + lua_rawseti(L, -2, i + 1); + } + + return 1; +} + +int l_lovrBufferSetVertexMap(lua_State* L) { + Buffer* buffer = luax_checkbuffer(L, 1); + + if (lua_isnoneornil(L, 2)) { + lovrBufferSetVertexMap(buffer, NULL, 0); + return 0; + } + + luaL_checktype(L, 2, LUA_TTABLE); + int count = lua_objlen(L, 2); + unsigned int* indices = malloc(count * sizeof(int)); + + for (int i = 0; i < count; i++) { + lua_rawgeti(L, 2, i + 1); + if (!lua_isnumber(L, -1)) { + return luaL_error(L, "Buffer vertex map index #%d must be numeric", i); + } + indices[i] = lua_tonumber(L, -1) - 1; + lua_pop(L, 1); + } + + lovrBufferSetVertexMap(buffer, indices, count); + free(indices); + return 0; +} + int l_lovrBufferGetDrawRange(lua_State* L) { Buffer* buffer = luax_checkbuffer(L, 1); + if (lovrBufferIsRangeEnabled(buffer)) { + lua_pushnil(L); + return 1; + } + int start, end; lovrBufferGetDrawRange(buffer, &start, &end); - lua_pushinteger(L, start); - lua_pushinteger(L, end); + lua_pushinteger(L, start + 1); + lua_pushinteger(L, end + 1); return 2; } int l_lovrBufferSetDrawRange(lua_State* L) { Buffer* buffer = luax_checkbuffer(L, 1); - int rangeStart = luaL_checkinteger(L, 2); - int rangeEnd = luaL_checkinteger(L, 3); + if (lua_isnoneornil(L, 2)) { + lovrBufferSetRangeEnabled(buffer, 0); + return 0; + } + + lovrBufferSetRangeEnabled(buffer, 1); + int rangeStart = luaL_checkinteger(L, 2) - 1; + int rangeEnd = luaL_checkinteger(L, 3) - 1; if (lovrBufferSetDrawRange(buffer, rangeStart, rangeEnd)) { return luaL_error(L, "Invalid buffer draw range (%d, %d)", rangeStart, rangeEnd); } diff --git a/src/lovr/buffer.h b/src/lovr/buffer.h index 61e69595..2e71fd88 100644 --- a/src/lovr/buffer.h +++ b/src/lovr/buffer.h @@ -12,6 +12,8 @@ int l_lovrBufferDraw(lua_State* L); int l_lovrBufferGetVertexCount(lua_State* L); int l_lovrBufferGetVertex(lua_State* L); int l_lovrBufferSetVertex(lua_State* L); +int l_lovrBufferGetVertexMap(lua_State* L); +int l_lovrBufferSetVertexMap(lua_State* L); int l_lovrBufferGetDrawMode(lua_State* L); int l_lovrBufferSetDrawMode(lua_State* L); int l_lovrBufferGetDrawRange(lua_State* L);