From 4197a364fd43bc06e9f4e0f65d5ba511739ac783 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sun, 23 Feb 2020 22:39:18 -0400 Subject: [PATCH] Internal restructuring of some Mesh code --- src/modules/graphics/Graphics.cpp | 19 ++- src/modules/graphics/Graphics.h | 3 +- src/modules/graphics/Mesh.cpp | 154 ++++++++++++++++--------- src/modules/graphics/Mesh.h | 22 ++-- src/modules/graphics/vertex.cpp | 8 -- src/modules/graphics/vertex.h | 9 -- src/modules/graphics/wrap_Graphics.cpp | 55 ++++++--- 7 files changed, 158 insertions(+), 112 deletions(-) diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index db1135840..2cb470ed3 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -268,26 +268,21 @@ Buffer *Graphics::newBuffer(const Buffer::Settings &settings, DataFormat format, return newBuffer(settings, dataformat, data, size, arraylength); } -Mesh *Graphics::newMesh(const std::vector &vertices, PrimitiveType drawmode, BufferUsage usage) -{ - return newMesh(Mesh::getDefaultVertexFormat(), &vertices[0], vertices.size() * sizeof(Vertex), drawmode, usage); -} - -Mesh *Graphics::newMesh(int vertexcount, PrimitiveType drawmode, BufferUsage usage) -{ - return newMesh(Mesh::getDefaultVertexFormat(), vertexcount, drawmode, usage); -} - -love::graphics::Mesh *Graphics::newMesh(const std::vector &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage) +Mesh *Graphics::newMesh(const std::vector &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage) { return new Mesh(this, vertexformat, vertexcount, drawmode, usage); } -love::graphics::Mesh *Graphics::newMesh(const std::vector &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage) +Mesh *Graphics::newMesh(const std::vector &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage) { return new Mesh(this, vertexformat, data, datasize, drawmode, usage); } +Mesh *Graphics::newMesh(const std::vector &attributes, PrimitiveType drawmode) +{ + return new Mesh(attributes, drawmode); +} + love::graphics::Text *Graphics::newText(graphics::Font *font, const std::vector &text) { return new Text(font, text); diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index d598ff198..83ee68e01 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -442,10 +442,9 @@ public: virtual Buffer *newBuffer(const Buffer::Settings &settings, const std::vector &format, const void *data, size_t size, size_t arraylength) = 0; virtual Buffer *newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength); - Mesh *newMesh(const std::vector &vertices, PrimitiveType drawmode, BufferUsage usage); - Mesh *newMesh(int vertexcount, PrimitiveType drawmode, BufferUsage usage); Mesh *newMesh(const std::vector &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage); Mesh *newMesh(const std::vector &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage); + Mesh *newMesh(const std::vector &attributes, PrimitiveType drawmode); Text *newText(Font *font, const std::vector &text = {}); diff --git a/src/modules/graphics/Mesh.cpp b/src/modules/graphics/Mesh.cpp index 107c11e49..98fff062d 100644 --- a/src/modules/graphics/Mesh.cpp +++ b/src/modules/graphics/Mesh.cpp @@ -34,27 +34,13 @@ namespace love namespace graphics { -static const char *getBuiltinAttribName(BuiltinVertexAttribute attribid) -{ - const char *name = ""; - getConstant(attribid, name); - return name; -} - static_assert(offsetof(Vertex, x) == sizeof(float) * 0, "Incorrect position offset in Vertex struct"); static_assert(offsetof(Vertex, s) == sizeof(float) * 2, "Incorrect texture coordinate offset in Vertex struct"); static_assert(offsetof(Vertex, color.r) == sizeof(float) * 4, "Incorrect color offset in Vertex struct"); std::vector Mesh::getDefaultVertexFormat() { - // Corresponds to the love::Vertex struct. - std::vector vertexformat = { - { getBuiltinAttribName(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2, 0 }, - { getBuiltinAttribName(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2, 0 }, - { getBuiltinAttribName(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4, 0 }, - }; - - return vertexformat; + return Buffer::getCommonFormatDeclaration(CommonFormat::XYf_STf_RGBAub); } love::Type Mesh::type("Mesh", &Drawable::type); @@ -63,6 +49,7 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector & : vertexBuffer(nullptr) , vertexCount(0) , vertexStride(0) + , vertexScratchBuffer(nullptr) , indexBuffer(nullptr) , useIndexBuffer(false) , indexCount(0) @@ -89,6 +76,7 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector & : vertexBuffer(nullptr) , vertexCount((size_t) vertexcount) , vertexStride(0) + , vertexScratchBuffer(nullptr) , indexBuffer(nullptr) , useIndexBuffer(false) , indexCount(0) @@ -115,6 +103,37 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector & vertexScratchBuffer = new char[vertexStride]; } +Mesh::Mesh(const std::vector &attributes, PrimitiveType drawmode) + : vertexBuffer(nullptr) + , vertexCount(0) + , vertexStride(0) + , vertexScratchBuffer(nullptr) + , indexBuffer(nullptr) + , useIndexBuffer(false) + , indexCount(0) + , indexDataType(INDEX_UINT16) + , primitiveType(drawmode) + , rangeStart(-1) + , rangeCount(-1) +{ + if (attributes.size() == 0) + throw love::Exception("At least one buffer attribute must be specified in this constructor."); + + attachedAttributes = attributes; + + vertexCount = LOVE_UINT32_MAX; + + for (const auto &attrib : attachedAttributes) + { + if (getAttachedAttributeIndex(attrib.name) != -1) + throw love::Exception("Duplicate vertex attribute name: %s", attrib.name.c_str()); + + vertexCount = std::min(vertexCount, attrib.buffer->getArrayLength()); + } + + indexDataType = getIndexDataTypeFromMax(vertexCount); +} + Mesh::~Mesh() { delete vertexScratchBuffer; @@ -126,18 +145,32 @@ void Mesh::setupAttachedAttributes() { const std::string &name = vertexFormat[i].decl.name; - if (attachedAttributes.find(name) != attachedAttributes.end()) + if (getAttachedAttributeIndex(name) != -1) throw love::Exception("Duplicate vertex attribute name: %s", name.c_str()); - attachedAttributes[name] = {vertexBuffer, (int) i, STEP_PER_VERTEX, true}; + attachedAttributes.push_back({name, vertexBuffer, (int) i, STEP_PER_VERTEX, true}); } } +int Mesh::getAttachedAttributeIndex(const std::string &name) const +{ + for (int i = 0; i < (int) attachedAttributes.size(); i++) + { + if (attachedAttributes[i].name == name) + return i; + } + + return -1; +} + void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize) { if (vertindex >= vertexCount) throw love::Exception("Invalid vertex index: %ld", vertindex + 1); + if (vertexBuffer.get() == nullptr) + throw love::Exception("setVertex can only be called on a Mesh which owns its own vertex buffer."); + size_t offset = vertindex * vertexStride; size_t size = std::min(datasize, vertexStride); @@ -152,6 +185,9 @@ size_t Mesh::getVertex(size_t vertindex, void *data, size_t datasize) if (vertindex >= vertexCount) throw love::Exception("Invalid vertex index: %ld", vertindex + 1); + if (vertexBuffer.get() == nullptr) + throw love::Exception("getVertex can only be called on a Mesh which owns its own vertex buffer."); + size_t offset = vertindex * vertexStride; size_t size = std::min(datasize, vertexStride); @@ -175,6 +211,9 @@ void Mesh::setVertexAttribute(size_t vertindex, int attribindex, const void *dat if (attribindex >= (int) vertexFormat.size()) throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1); + if (vertexBuffer.get() == nullptr) + throw love::Exception("setVertexAttribute can only be called on a Mesh which owns its own vertex buffer."); + const auto &member = vertexFormat[attribindex]; size_t offset = vertindex * vertexStride + member.offset; @@ -194,6 +233,9 @@ size_t Mesh::getVertexAttribute(size_t vertindex, int attribindex, void *data, s if (attribindex >= (int) vertexFormat.size()) throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1); + if (vertexBuffer.get() == nullptr) + throw love::Exception("getVertexAttribute can only be called on a Mesh which owns its own vertex buffer."); + const auto &member = vertexFormat[attribindex]; size_t offset = vertindex * vertexStride + member.offset; @@ -239,39 +281,37 @@ int Mesh::getAttributeIndex(const std::string &name) const void Mesh::setAttributeEnabled(const std::string &name, bool enable) { - auto it = attachedAttributes.find(name); - - if (it == attachedAttributes.end()) + int index = getAttachedAttributeIndex(name); + if (index == -1) throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str()); - it->second.enabled = enable; + attachedAttributes[index].enabled = enable; } bool Mesh::isAttributeEnabled(const std::string &name) const { - const auto it = attachedAttributes.find(name); - - if (it == attachedAttributes.end()) + int index = getAttachedAttributeIndex(name); + if (index == -1) throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str()); - return it->second.enabled; + return attachedAttributes[index].enabled; } void Mesh::attachAttribute(const std::string &name, Buffer *buffer, const std::string &attachname, AttributeStep step) { if ((buffer->getTypeFlags() & Buffer::TYPEFLAG_VERTEX) == 0) - throw love::Exception("GraphicsBuffer must be created with vertex buffer support to be used as a Mesh vertex attribute."); + 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."); - AttachedAttribute oldattrib = {}; - AttachedAttribute newattrib = {}; + BufferAttribute oldattrib = {}; + BufferAttribute newattrib = {}; - auto it = attachedAttributes.find(name); - if (it != attachedAttributes.end()) - oldattrib = it->second; + int oldindex = getAttachedAttributeIndex(name); + if (oldindex != -1) + oldattrib = attachedAttributes[oldindex]; else if (attachedAttributes.size() + 1 > VertexAttributes::MAX) throw love::Exception("A maximum of %d attributes can be attached at once.", VertexAttributes::MAX); @@ -283,42 +323,46 @@ void Mesh::attachAttribute(const std::string &name, Buffer *buffer, const std::s if (newattrib.index < 0) throw love::Exception("The specified vertex buffer does not have a vertex attribute named '%s'", attachname.c_str()); - attachedAttributes[name] = newattrib; + if (oldindex != -1) + attachedAttributes[oldindex] = newattrib; + else + attachedAttributes.push_back(newattrib); } bool Mesh::detachAttribute(const std::string &name) { - auto it = attachedAttributes.find(name); + int index = getAttachedAttributeIndex(name); + if (index == -1) + return false; - if (it != attachedAttributes.end()) - { - attachedAttributes.erase(it); + attachedAttributes.erase(attachedAttributes.begin() + index); - if (getAttributeIndex(name) != -1) - attachAttribute(name, vertexBuffer, name); + if (getAttributeIndex(name) != -1) + attachAttribute(name, vertexBuffer, name); - return true; - } - - return false; + return true; } void *Mesh::mapVertexData() { - return vertexBuffer->map(); + return vertexBuffer.get() != nullptr ? vertexBuffer->map() : nullptr; } void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize) { + if (!vertexBuffer.get()) + return; + vertexBuffer->setMappedRangeModified(modifiedoffset, modifiedsize); vertexBuffer->unmap(); } void Mesh::flush() { - vertexBuffer->unmap(); + if (vertexBuffer.get()) + vertexBuffer->unmap(); - if (indexBuffer != nullptr) + if (indexBuffer.get()) indexBuffer->unmap(); } @@ -352,7 +396,8 @@ void Mesh::setVertexMap(const std::vector &map) if (indexBuffer.get() == nullptr || size > indexBuffer->getSize() || indexBuffer->getDataMember(0).decl.format != dataformat) { auto gfx = Module::getInstance(Module::M_GRAPHICS); - Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, vertexBuffer->getUsage()); + auto usage = vertexBuffer.get() ? vertexBuffer->getUsage() : BUFFERUSAGE_DYNAMIC; + Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, usage); indexBuffer.set(gfx->newBuffer(settings, dataformat, nullptr, size, 0), Acquire::NORETAIN); } @@ -386,7 +431,8 @@ void Mesh::setVertexMap(IndexDataType datatype, const void *data, size_t datasiz if (indexBuffer.get() == nullptr || datasize > indexBuffer->getSize() || indexBuffer->getDataMember(0).decl.format != dataformat) { auto gfx = Module::getInstance(Module::M_GRAPHICS); - Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, vertexBuffer->getUsage()); + auto usage = vertexBuffer.get() ? vertexBuffer->getUsage() : BUFFERUSAGE_DYNAMIC; + Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, usage); indexBuffer.set(gfx->newBuffer(settings, dataformat, nullptr, datasize, 0), Acquire::NORETAIN); } @@ -529,32 +575,32 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) for (const auto &attrib : attachedAttributes) { - if (!attrib.second.enabled) + if (!attrib.enabled) continue; - Buffer *buffer = attrib.second.buffer.get(); + Buffer *buffer = attrib.buffer.get(); int attributeindex = -1; // If the attribute is one of the LOVE-defined ones, use the constant // attribute index for it, otherwise query the index from the shader. BuiltinVertexAttribute builtinattrib; - if (getConstant(attrib.first.c_str(), builtinattrib)) + if (getConstant(attrib.name.c_str(), builtinattrib)) attributeindex = (int) builtinattrib; else if (Shader::current) - attributeindex = Shader::current->getVertexAttributeIndex(attrib.first); + attributeindex = Shader::current->getVertexAttributeIndex(attrib.name); if (attributeindex >= 0) { // Make sure the buffer isn't mapped (sends data to GPU if needed.) buffer->unmap(); - const auto &member = buffer->getDataMember(attrib.second.index); + const auto &member = buffer->getDataMember(attrib.index); - uint16 offset = (uint16) buffer->getMemberOffset(attrib.second.index); + uint16 offset = (uint16) buffer->getMemberOffset(attrib.index); uint16 stride = (uint16) buffer->getArrayStride(); attributes.set(attributeindex, member.decl.format, offset, activebuffers); - attributes.setBufferLayout(activebuffers, stride, attrib.second.step); + attributes.setBufferLayout(activebuffers, stride, attrib.step); // TODO: Ideally we want to reuse buffers with the same stride+step. buffers.set(activebuffers, buffer, 0); diff --git a/src/modules/graphics/Mesh.h b/src/modules/graphics/Mesh.h index 7e099f3dc..0c81642fc 100644 --- a/src/modules/graphics/Mesh.h +++ b/src/modules/graphics/Mesh.h @@ -50,10 +50,20 @@ class Mesh : public Drawable { public: + struct BufferAttribute + { + std::string name; + StrongRef buffer; + int index; + AttributeStep step; + bool enabled; + }; + static love::Type type; Mesh(Graphics *gfx, const std::vector &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage); Mesh(Graphics *gfx, const std::vector &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage); + Mesh(const std::vector &attributes, PrimitiveType drawmode); virtual ~Mesh(); @@ -105,6 +115,7 @@ public: **/ void attachAttribute(const std::string &name, Buffer *buffer, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX); bool detachAttribute(const std::string &name); + const std::vector &getAttachedAttributes(); void *mapVertexData(); void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1); @@ -172,19 +183,12 @@ private: friend class SpriteBatch; - struct AttachedAttribute - { - StrongRef buffer; - int index; - AttributeStep step; - bool enabled; - }; - void setupAttachedAttributes(); + int getAttachedAttributeIndex(const std::string &name) const; std::vector vertexFormat; - std::unordered_map attachedAttributes; + std::vector attachedAttributes; // Vertex buffer, for the vertex data. StrongRef vertexBuffer; diff --git a/src/modules/graphics/vertex.cpp b/src/modules/graphics/vertex.cpp index c64496b98..0ecfb7d26 100644 --- a/src/modules/graphics/vertex.cpp +++ b/src/modules/graphics/vertex.cpp @@ -358,14 +358,6 @@ DEFINE_STRINGMAP_BEGIN(AttributeStep, STEP_MAX_ENUM, attributeStep) } DEFINE_STRINGMAP_END(AttributeStep, STEP_MAX_ENUM, attributeStep) -DEFINE_STRINGMAP_BEGIN(DataTypeDeprecated, DATADEPRECATED_MAX_ENUM, dataType) -{ - { "unorm8", DATADEPRECATED_UNORM8 }, - { "unorm16", DATADEPRECATED_UNORM16 }, - { "float", DATADEPRECATED_FLOAT }, -} -DEFINE_STRINGMAP_END(DataTypeDeprecated, DATADEPRECATED_MAX_ENUM, dataType) - DEFINE_STRINGMAP_BEGIN(DataFormat, DATAFORMAT_MAX_ENUM, dataFormat) { { "float", DATAFORMAT_FLOAT }, diff --git a/src/modules/graphics/vertex.h b/src/modules/graphics/vertex.h index 33072939e..0c7477579 100644 --- a/src/modules/graphics/vertex.h +++ b/src/modules/graphics/vertex.h @@ -102,14 +102,6 @@ enum BufferUsage BUFFERUSAGE_MAX_ENUM }; -enum DataTypeDeprecated -{ - DATADEPRECATED_UNORM8, - DATADEPRECATED_UNORM16, - DATADEPRECATED_FLOAT, - DATADEPRECATED_MAX_ENUM -}; - // Value types used when interfacing with the GPU (vertex and shader data). // The order of this enum affects the dataFormatInfo array. enum DataFormat @@ -387,7 +379,6 @@ DECLARE_STRINGMAP(IndexDataType); DECLARE_STRINGMAP(BufferUsage); DECLARE_STRINGMAP(PrimitiveType); DECLARE_STRINGMAP(AttributeStep); -DECLARE_STRINGMAP(DataTypeDeprecated); DECLARE_STRINGMAP(DataFormat); DECLARE_STRINGMAP(DataBaseType); DECLARE_STRINGMAP(CullMode); diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index dc24f7eaf..418a89e23 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -1557,6 +1557,8 @@ static Mesh *newStandardMesh(lua_State *L) PrimitiveType drawmode = luax_optmeshdrawmode(L, 2, PRIMITIVE_TRIANGLE_FAN); BufferUsage usage = luax_optmeshusage(L, 3, BUFFERUSAGE_DYNAMIC); + auto format = Mesh::getDefaultVertexFormat(); + // First argument is a table of standard vertices, or the number of // standard vertices. if (lua_istable(L, 1)) @@ -1595,12 +1597,12 @@ static Mesh *newStandardMesh(lua_State *L) vertices.push_back(v); } - luax_catchexcept(L, [&](){ t = instance()->newMesh(vertices, drawmode, usage); }); + luax_catchexcept(L, [&](){ t = instance()->newMesh(format, vertices.data(), vertices.size() * sizeof(Vertex), drawmode, usage); }); } else { int count = (int) luaL_checkinteger(L, 1); - luax_catchexcept(L, [&](){ t = instance()->newMesh(count, drawmode, usage); }); + luax_catchexcept(L, [&](){ t = instance()->newMesh(format, count, drawmode, usage); }); } return t; @@ -1641,24 +1643,41 @@ static Mesh *newCustomMesh(lua_State *L) if (!getConstant(tname, format)) { - DataTypeDeprecated legacyType = DATADEPRECATED_FLOAT; - - if (strcmp(tname, "byte") == 0) // Legacy name. - legacyType = DATADEPRECATED_UNORM8; - else if (!getConstant(tname, legacyType)) - { - luax_enumerror(L, "Mesh vertex data format name", getConstants(format), tname); - return nullptr; - } - int components = (int) luaL_checkinteger(L, -1); - if (components <= 0 || components > 4) - { - luaL_error(L, "Number of vertex attribute components must be between 1 and 4 (got %d)", components); - return nullptr; - } - // TODO: convert legacy type+components to new format enum. + // Check deprecated format names. + if (strcmp(tname, "byte") == 0 || strcmp(tname, "unorm8") == 0) + { + if (components == 4) + format = DATAFORMAT_UNORM8_VEC4; + else + luaL_error(L, "Invalid component count (%d) for vertex data type %s", components, tname); + } + else if (strcmp(tname, "unorm16") == 0) + { + if (components == 2) + format = DATAFORMAT_UNORM16_VEC2; + else if (components == 4) + format = DATAFORMAT_UNORM16_VEC4; + else + luaL_error(L, "Invalid component count (%d) for vertex data type %s", components, tname); + + } + else if (strcmp(tname, "float") == 0) + { + if (components == 1) + format = DATAFORMAT_FLOAT; + else if (components == 2) + format = DATAFORMAT_FLOAT_VEC2; + else if (components == 3) + format = DATAFORMAT_FLOAT_VEC3; + else if (components == 4) + format = DATAFORMAT_FLOAT_VEC4; + else + luaL_error(L, "Invalid component count (%d) for vertex data type %s", components, tname); + } + else + luax_enumerror(L, "vertex data format", getConstants(format), tname); } lua_pop(L, 4);