Skip to content

Commit

Permalink
Add love.graphics.newMesh variant to create a Mesh from existing Buff…
Browse files Browse the repository at this point in the history
…ers.

This allows a Mesh to be created that doesn't have its own internal vertex buffer and purely references other vertex buffers instead.

The prototype looks like this:
mesh = love.graphics.newMesh(attributelist, drawmode)

where attributelist is an array of tables each with the following fields, similar to Mesh:attachAttribute:
{
    buffer = vertexbuffer,
    name = "VertexPosition", -- the name this vertex attribute will use in a shader
    nameinbuffer = nil, -- the name of the attribute in the vertex buffer. Defaults to the name field.
    step = nil, -- vertex attribute step ("pervertex" or "perinstance"), defaults to "pervertex".
    startindex = nil, -- 1-based array index within the given vertex buffer where the attribute data will start being pulled from during rendering. Defaults to 1.
}
  • Loading branch information
slime73 committed Sep 30, 2023
1 parent 691d910 commit dbc7d7f
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 16 deletions.
46 changes: 31 additions & 15 deletions src/modules/graphics/Mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,18 @@ Mesh::Mesh(const std::vector<Mesh::BufferAttribute> &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<Graphics>(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());
Expand All @@ -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});
}
}

Expand All @@ -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)
Expand Down Expand Up @@ -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<Graphics>(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 = {};
Expand All @@ -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());

Expand Down
2 changes: 2 additions & 0 deletions src/modules/graphics/Mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Mesh : public Drawable
std::string name;
StrongRef<Buffer> buffer;
StrongRef<Mesh> mesh;
std::string nameInBuffer;
int indexInBuffer;
int startArrayIndex;
AttributeStep step;
Expand Down Expand Up @@ -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);

Expand Down
77 changes: 76 additions & 1 deletion src/modules/graphics/wrap_Graphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Mesh::BufferAttribute> 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);
Expand All @@ -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);
Expand Down

0 comments on commit dbc7d7f

Please sign in to comment.