Added SpriteBatch:attachAttribute(attributename, mesh), which lets SpriteBatches use per-vertex information from a vertex attribute in a Mesh when drawing.

Each sprite in a SpriteBatch has 4 vertices in the following order: top-left, bottom-left, top-right, bottom-right. The index returned by SpriteBatch:add (and used by SpriteBatch:set) can be multiplied by 4 to determine the first vertex in a specific sprite.
This commit is contained in:
Alex Szpakowski
2015-11-26 21:24:29 -04:00
parent f1d0187a58
commit 3c1c13793e
7 changed files with 140 additions and 55 deletions
+3
View File
@@ -90,6 +90,9 @@ void *GLBuffer::map()
void GLBuffer::unmapStatic(size_t offset, size_t size)
{
if (size == 0)
return;
// Upload the mapped data to the buffer.
glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
}
+50 -42
View File
@@ -149,7 +149,7 @@ void Mesh::setupAttachedAttributes()
if (attachedAttributes.find(name) != attachedAttributes.end())
throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
attachedAttributes[name] = {this, i, true};
attachedAttributes[name] = {this, (int) i, true};
}
}
@@ -287,6 +287,17 @@ Mesh::DataType Mesh::getAttributeInfo(int attribindex, int &components) const
return type;
}
int Mesh::getAttributeIndex(const std::string &name) const
{
for (int i = 0; i < (int) vertexFormat.size(); i++)
{
if (vertexFormat[i].name == name)
return i;
}
return -1;
}
void Mesh::setAttributeEnabled(const std::string &name, bool enable)
{
auto it = attachedAttributes.find(name);
@@ -328,20 +339,10 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh)
oldattrib = it->second;
newattrib.mesh = mesh;
newattrib.index = std::numeric_limits<size_t>::max();
newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
newattrib.index = mesh->getAttributeIndex(name);
// Find the index of the attribute in the mesh.
for (size_t i = 0; i < mesh->vertexFormat.size(); i++)
{
if (mesh->vertexFormat[i].name == name)
{
newattrib.index = i;
break;
}
}
if (newattrib.index == std::numeric_limits<size_t>::max())
if (newattrib.index < 0)
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
if (newattrib.mesh != this)
@@ -537,6 +538,39 @@ void Mesh::getDrawRange(int &min, int &max) const
max = rangeMax;
}
int Mesh::bindAttributeToShaderInput(int attributeindex, const std::string &inputname)
{
const AttribFormat &format = vertexFormat[attributeindex];
GLint attriblocation = -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.
VertexAttribID builtinattrib;
if (Shader::getConstant(inputname.c_str(), builtinattrib))
attriblocation = (GLint) builtinattrib;
else if (Shader::current)
attriblocation = Shader::current->getAttribLocation(inputname);
// The active shader might not use this vertex attribute name.
if (attriblocation < 0)
return attriblocation;
// Needed for unmap and glVertexAttribPointer.
GLBuffer::Bind vbobind(*vbo);
// Make sure the buffer isn't mapped (sends data to GPU if needed.)
vbo->unmap();
const void *gloffset = vbo->getPointer(getAttributeOffset(attributeindex));
GLenum datatype = getGLDataType(format.type);
GLboolean normalized = (datatype == GL_UNSIGNED_BYTE);
glVertexAttribPointer(attriblocation, format.components, datatype, normalized, vertexStride, gloffset);
return attriblocation;
}
void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
OpenGL::TempDebugGroup debuggroup("Mesh draw");
@@ -549,36 +583,10 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
continue;
Mesh *mesh = attrib.second.mesh;
const AttribFormat &format = mesh->vertexFormat[attrib.second.index];
int location = mesh->bindAttributeToShaderInput(attrib.second.index, attrib.first);
GLint attriblocation = -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.
VertexAttribID builtinattrib;
if (Shader::getConstant(format.name.c_str(), builtinattrib))
attriblocation = (GLint) builtinattrib;
else if (Shader::current)
attriblocation = Shader::current->getAttribLocation(format.name);
// The active shader might not use this vertex attribute name.
if (attriblocation < 0)
continue;
// Needed for unmap and glVertexAttribPointer.
GLBuffer::Bind vbobind(*mesh->vbo);
// Make sure the buffer isn't mapped (sends data to GPU if needed.)
mesh->vbo->unmap();
size_t offset = mesh->getAttributeOffset(attrib.second.index);
const void *gloffset = mesh->vbo->getPointer(offset);
GLenum datatype = getGLDataType(format.type);
GLboolean normalized = (datatype == GL_UNSIGNED_BYTE);
glVertexAttribPointer(attriblocation, format.components, datatype, normalized, mesh->vertexStride, gloffset);
enabledattribs |= 1 << uint32(attriblocation);
if (location >= 0)
enabledattribs |= 1u << (uint32) location;
}
// Not supported on all platforms or GL versions, I believe.
+4 -1
View File
@@ -126,6 +126,7 @@ public:
**/
const std::vector<AttribFormat> &getVertexFormat() const;
DataType getAttributeInfo(int attribindex, int &components) const;
int getAttributeIndex(const std::string &name) const;
/**
* Sets whether a specific vertex attribute is used when drawing the Mesh.
@@ -193,6 +194,8 @@ public:
void setDrawRange();
void getDrawRange(int &min, int &max) const;
int bindAttributeToShaderInput(int attributeindex, const std::string &inputname);
// Implements Drawable.
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) override;
@@ -212,7 +215,7 @@ private:
struct AttachedAttribute
{
Mesh *mesh;
size_t index;
int index;
bool enabled;
};
+54 -12
View File
@@ -193,6 +193,28 @@ int SpriteBatch::getBufferSize() const
return size;
}
void SpriteBatch::attachAttribute(const std::string &name, Mesh *mesh)
{
AttachedAttribute oldattrib = {};
AttachedAttribute newattrib = {};
if (mesh->getVertexCount() < (size_t) getBufferSize() * 4)
throw love::Exception("Mesh has too few vertices to be attached to this SpriteBatch (at least %d vertices are required)", getBufferSize()*4);
auto it = attached_attributes.find(name);
if (it != attached_attributes.end())
oldattrib = it->second;
newattrib.index = mesh->getAttributeIndex(name);
if (newattrib.index < 0)
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
newattrib.mesh = mesh;
attached_attributes[name] = newattrib;
}
void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
const size_t pos_offset = offsetof(Vertex, x);
@@ -209,27 +231,47 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
gl.bindTexture(*(GLuint *) texture->getHandle());
GLBuffer::Bind array_bind(*array_buf);
GLBuffer::Bind element_bind(*quad_indices.getBuffer());
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
array_buf->unmap();
uint32 enabledattribs = ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD;
// Apply per-sprite color, if a color is set.
if (color)
{
enabledattribs |= ATTRIBFLAG_COLOR;
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), array_buf->getPointer(color_offset));
// Scope this bind so it doesn't interfere with the
// Mesh::bindAttributeToShaderInput calls below.
GLBuffer::Bind array_bind(*array_buf);
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
array_buf->unmap();
// Apply per-sprite color, if a color is set.
if (color)
{
enabledattribs |= ATTRIBFLAG_COLOR;
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), array_buf->getPointer(color_offset));
}
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(pos_offset));
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(texel_offset));
}
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(pos_offset));
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(texel_offset));
for (const auto &it : attached_attributes)
{
Mesh *mesh = it.second.mesh.get();
// We have to do this check here as well because setBufferSize can be
// called after attachAttribute.
if (mesh->getVertexCount() < (size_t) getBufferSize() * 4)
throw love::Exception("Mesh with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str());
int location = mesh->bindAttributeToShaderInput(it.second.index, it.first);
if (location >= 0)
enabledattribs |= 1u << (uint32) location;
}
gl.useVertexAttribArrays(enabledattribs);
gl.prepareDraw();
GLBuffer::Bind element_bind(*quad_indices.getBuffer());
gl.drawElements(GL_TRIANGLES, (GLsizei) quad_indices.getIndexCount(next), quad_indices.getType(), quad_indices.getPointer(0));
}
+17
View File
@@ -24,6 +24,9 @@
// C
#include <cstring>
// C++
#include <unordered_map>
// LOVE
#include "common/math.h"
#include "common/Matrix.h"
@@ -98,11 +101,23 @@ public:
**/
int getBufferSize() const;
/**
* Attaches a specific vertex attribute from a Mesh to this SpriteBatch.
* The vertex attribute will be used when drawing the SpriteBatch.
**/
void attachAttribute(const std::string &name, Mesh *mesh);
// Implements Drawable.
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
private:
struct AttachedAttribute
{
StrongRef<Mesh> mesh;
int index;
};
void addv(const Vertex *v, const Matrix3 &m, int index);
/**
@@ -129,6 +144,8 @@ private:
GLBuffer *array_buf;
QuadIndices quad_indices;
std::unordered_map<std::string, AttachedAttribute> attached_attributes;
}; // SpriteBatch
} // opengl
@@ -203,6 +203,16 @@ int w_SpriteBatch_getBufferSize(lua_State *L)
return 1;
}
int w_SpriteBatch_attachAttribute(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
const char *name = luaL_checkstring(L, 2);
Mesh *m = luax_checktype<Mesh>(L, 3, GRAPHICS_MESH_ID);
luax_catchexcept(L, [&](){ t->attachAttribute(name, m); });
return 0;
}
static const luaL_Reg functions[] =
{
{ "add", w_SpriteBatch_add },
@@ -216,6 +226,7 @@ static const luaL_Reg functions[] =
{ "getCount", w_SpriteBatch_getCount },
{ "setBufferSize", w_SpriteBatch_setBufferSize },
{ "getBufferSize", w_SpriteBatch_getBufferSize },
{ "attachAttribute", w_SpriteBatch_attachAttribute },
{ 0, 0 }
};
@@ -43,6 +43,7 @@ int w_SpriteBatch_getColor(lua_State *L);
int w_SpriteBatch_getCount(lua_State *L);
int w_SpriteBatch_setBufferSize(lua_State *L);
int w_SpriteBatch_getBufferSize(lua_State *L);
int w_SpriteBatch_attachAttribute(lua_State *L);
extern "C" int luaopen_spritebatch(lua_State *L);