mirror of
https://github.com/love2d/love.git
synced 2026-08-16 16:20:42 +02:00
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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user