diff --git a/src/modules/graphics/Mesh.cpp b/src/modules/graphics/Mesh.cpp index 4db8cf316..f9c705e85 100644 --- a/src/modules/graphics/Mesh.cpp +++ b/src/modules/graphics/Mesh.cpp @@ -107,15 +107,18 @@ Mesh::Mesh(const std::vector &attributes, PrimitiveType d throw love::Exception("At least one buffer attribute must be specified in this constructor."); attachedAttributes = attributes; - vertexCount = attachedAttributes.size() > 0 ? LOVE_UINT32_MAX : 0; - for (const auto &attrib : attachedAttributes) + auto gfx = Module::getInstance(Module::M_GRAPHICS); + + for (int i = 0; i < (int) attachedAttributes.size(); i++) { - if ((attrib.buffer->getUsageFlags() & BUFFERUSAGEFLAG_VERTEX) == 0) - throw love::Exception("Buffer must be created with vertex buffer support to be used as a Mesh vertex attribute."); + auto &attrib = attachedAttributes[i]; - if (getAttachedAttributeIndex(attrib.name) != -1) + finalizeAttribute(gfx, attrib); + + int attributeIndex = getAttachedAttributeIndex(attrib.name); + if (attributeIndex != i && attributeIndex != -1) throw love::Exception("Duplicate vertex attribute name: %s", attrib.name.c_str()); vertexCount = std::min(vertexCount, attrib.buffer->getArrayLength()); @@ -140,7 +143,7 @@ void Mesh::setupAttachedAttributes() if (getAttachedAttributeIndex(name) != -1) throw love::Exception("Duplicate vertex attribute name: %s", name.c_str()); - attachedAttributes.push_back({name, vertexBuffer, nullptr, (int) i, 0, STEP_PER_VERTEX, true}); + attachedAttributes.push_back({name, vertexBuffer, nullptr, name, (int) i, 0, STEP_PER_VERTEX, true}); } } @@ -155,6 +158,24 @@ int Mesh::getAttachedAttributeIndex(const std::string &name) const return -1; } +void Mesh::finalizeAttribute(Graphics *gfx, BufferAttribute &attrib) const +{ + if ((attrib.buffer->getUsageFlags() & BUFFERUSAGEFLAG_VERTEX) == 0) + throw love::Exception("Buffer must be created with vertex buffer support to be used as a Mesh vertex attribute."); + + if (attrib.step == STEP_PER_INSTANCE && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING]) + throw love::Exception("Vertex attribute instancing is not supported on this system."); + + if (attrib.startArrayIndex < 0 || attrib.startArrayIndex >= (int)attrib.buffer->getArrayLength()) + throw love::Exception("Invalid start array index %d.", attrib.startArrayIndex + 1); + + int indexInBuffer = attrib.buffer->getDataMemberIndex(attrib.nameInBuffer); + if (indexInBuffer < 0) + throw love::Exception("Buffer does not have a vertex attribute with name '%s'.", attrib.nameInBuffer.c_str()); + + attrib.indexInBuffer = indexInBuffer; +} + void *Mesh::checkVertexDataOffset(size_t vertindex, size_t *byteoffset) { if (vertindex >= vertexCount) @@ -210,15 +231,7 @@ bool Mesh::isAttributeEnabled(const std::string &name) const void Mesh::attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh, const std::string &attachname, int startindex, AttributeStep step) { - if ((buffer->getUsageFlags() & BUFFERUSAGEFLAG_VERTEX) == 0) - throw love::Exception("Buffer must be created with vertex buffer support to be used as a Mesh vertex attribute."); - auto gfx = Module::getInstance(Module::M_GRAPHICS); - if (step == STEP_PER_INSTANCE && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING]) - throw love::Exception("Vertex attribute instancing is not supported on this system."); - - if (startindex < 0 || startindex >= (int) buffer->getArrayLength()) - throw love::Exception("Invalid start array index %d.", startindex + 1); BufferAttribute oldattrib = {}; BufferAttribute newattrib = {}; @@ -233,10 +246,13 @@ void Mesh::attachAttribute(const std::string &name, Buffer *buffer, Mesh *mesh, newattrib.buffer = buffer; newattrib.mesh = mesh; newattrib.enabled = oldattrib.buffer.get() ? oldattrib.enabled : true; - newattrib.indexInBuffer = buffer->getDataMemberIndex(attachname); + newattrib.nameInBuffer = attachname; + newattrib.indexInBuffer = -1; newattrib.startArrayIndex = startindex; newattrib.step = step; + finalizeAttribute(gfx, newattrib); + if (newattrib.indexInBuffer < 0) throw love::Exception("The specified vertex buffer does not have a vertex attribute named '%s'", attachname.c_str()); diff --git a/src/modules/graphics/Mesh.h b/src/modules/graphics/Mesh.h index b164b7cfb..6002971a6 100644 --- a/src/modules/graphics/Mesh.h +++ b/src/modules/graphics/Mesh.h @@ -56,6 +56,7 @@ class Mesh : public Drawable std::string name; StrongRef buffer; StrongRef mesh; + std::string nameInBuffer; int indexInBuffer; int startArrayIndex; AttributeStep step; @@ -186,6 +187,7 @@ class Mesh : public Drawable void setupAttachedAttributes(); int getAttachedAttributeIndex(const std::string &name) const; + void finalizeAttribute(Graphics *gfx, BufferAttribute &attrib) const; void drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buffer *indirectargs, int argsindex); diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index 32c26821a..3a2eb613a 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -1990,6 +1990,79 @@ static Mesh *newCustomMesh(lua_State *L) return t; } +static bool luax_isbufferattributetable(lua_State* L, int idx) +{ + if (lua_type(L, idx) != LUA_TTABLE) + return false; + + lua_rawgeti(L, idx, 1); + if (lua_type(L, -1) != LUA_TTABLE) + { + lua_pop(L, 1); + return false; + } + + lua_getfield(L, -1, "buffer"); + bool isbuffer = luax_istype(L, -1, Buffer::type); + lua_pop(L, 2); + return isbuffer; +} + +static Mesh::BufferAttribute luax_checkbufferattributetable(lua_State *L, int idx) +{ + Mesh::BufferAttribute attrib = {}; + + attrib.step = STEP_PER_VERTEX; + attrib.enabled = true; + + lua_getfield(L, idx, "buffer"); + attrib.buffer = luax_checkbuffer(L, -1); + lua_pop(L, 1); + + lua_getfield(L, idx, "name"); + attrib.name = luax_checkstring(L, -1); + lua_pop(L, 1); + + lua_getfield(L, idx, "step"); + if (!lua_isnoneornil(L, -1)) + { + const char *stepstr = luaL_checkstring(L, -1); + if (!getConstant(stepstr, attrib.step)) + luax_enumerror(L, "vertex attribute step", getConstants(attrib.step), stepstr); + } + lua_pop(L, 1); + + lua_getfield(L, idx, "nameinbuffer"); + if (!lua_isnoneornil(L, -1)) + attrib.nameInBuffer = luax_checkstring(L, -1); + else + attrib.nameInBuffer = attrib.name; + lua_pop(L, 1); + + lua_getfield(L, idx, "startindex"); + attrib.startArrayIndex = (int) luaL_optinteger(L, -1, 1) - 1; + lua_pop(L, 1); + + return attrib; +} + +static Mesh* newMeshFromBuffers(lua_State *L) +{ + std::vector attributes; + for (size_t i = 1; i <= luax_objlen(L, 1); i++) + { + lua_rawgeti(L, 1, i); + attributes.push_back(luax_checkbufferattributetable(L, -1)); + lua_pop(L, 1); + } + + PrimitiveType drawmode = luax_checkmeshdrawmode(L, 2); + + Mesh *t = nullptr; + luax_catchexcept(L, [&]() { t = instance()->newMesh(attributes, drawmode); }); + return t; +} + int w_newMesh(lua_State *L) { luax_checkgraphicscreated(L); @@ -2002,7 +2075,9 @@ int w_newMesh(lua_State *L) Mesh *t = nullptr; int arg2type = lua_type(L, 2); - if (arg1type == LUA_TTABLE && (arg2type == LUA_TTABLE || arg2type == LUA_TNUMBER || arg2type == LUA_TUSERDATA)) + if (luax_isbufferattributetable(L, 1)) + t = newMeshFromBuffers(L); + else if (arg1type == LUA_TTABLE && (arg2type == LUA_TTABLE || arg2type == LUA_TNUMBER || arg2type == LUA_TUSERDATA)) t = newCustomMesh(L); else t = newStandardMesh(L);