mirror of
https://github.com/love2d/love.git
synced 2026-08-16 00:02:12 +02:00
Added instancing support to Meshes via Mesh:setInstanceCount. Added a new built-in variable to vertex shaders: int love_InstanceID.
love.graphics.draw(mesh) will draw the mesh instancecount times, using hardware instancing when available. The only way to draw individual instances differently from each other is to use the love_InstanceID variable in a vertex shader. Added love.graphics.isSupported(“instancing”). Hardware instancing is supported if true, otherwise a (slower) pseudo-instancing fallback is used internally when drawing instanced meshes.
This commit is contained in:
@@ -175,6 +175,7 @@ StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::suppor
|
||||
{ "mipmap", Graphics::SUPPORT_MIPMAP },
|
||||
{ "dxt", Graphics::SUPPORT_DXT },
|
||||
{ "bc5", Graphics::SUPPORT_BC5 },
|
||||
{ "instancing", Graphics::SUPPORT_INSTANCING },
|
||||
};
|
||||
|
||||
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
|
||||
|
||||
@@ -94,6 +94,7 @@ public:
|
||||
SUPPORT_MIPMAP,
|
||||
SUPPORT_DXT,
|
||||
SUPPORT_BC5,
|
||||
SUPPORT_INSTANCING,
|
||||
SUPPORT_MAX_ENUM
|
||||
};
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ Mesh::Mesh(const std::vector<Vertex> &verts, Mesh::DrawMode mode)
|
||||
, vertex_count(0)
|
||||
, ibo(nullptr)
|
||||
, element_count(0)
|
||||
, instance_count(1)
|
||||
, draw_mode(mode)
|
||||
, texture(nullptr)
|
||||
, colors_enabled(false)
|
||||
@@ -51,8 +52,8 @@ Mesh::~Mesh()
|
||||
|
||||
void Mesh::setVertices(const std::vector<Vertex> &verts)
|
||||
{
|
||||
if (verts.size() < 3)
|
||||
throw love::Exception("At least 3 vertices are required.");
|
||||
if (verts.size() == 0)
|
||||
throw love::Exception("At least one vertex is required.");
|
||||
|
||||
size_t size = sizeof(Vertex) * verts.size();
|
||||
|
||||
@@ -170,6 +171,19 @@ size_t Mesh::getVertexMapCount() const
|
||||
return element_count;
|
||||
}
|
||||
|
||||
void Mesh::setInstanceCount(int count)
|
||||
{
|
||||
if (count < 1)
|
||||
count = 1;
|
||||
|
||||
instance_count = count;
|
||||
}
|
||||
|
||||
int Mesh::getInstanceCount() const
|
||||
{
|
||||
return instance_count;
|
||||
}
|
||||
|
||||
void Mesh::setTexture(Texture *tex)
|
||||
{
|
||||
tex->retain();
|
||||
@@ -270,18 +284,27 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
|
||||
|
||||
if (ibo && element_count > 0)
|
||||
{
|
||||
// Use the custom vertex map (index buffer) to draw the vertices.
|
||||
VertexBuffer::Bind ibo_bind(*ibo);
|
||||
|
||||
// Make sure the index buffer isn't mapped (sends data to GPU if needed.)
|
||||
ibo->unmap();
|
||||
|
||||
// Use the custom vertex map to draw the vertices.
|
||||
glDrawElements(mode, element_count, GL_UNSIGNED_INT, ibo->getPointer(0));
|
||||
const void *indices = ibo->getPointer(0);
|
||||
const GLenum type = GL_UNSIGNED_INT;
|
||||
|
||||
if (instance_count > 1)
|
||||
gl.drawElementsInstanced(mode, element_count, type, indices, instance_count);
|
||||
else
|
||||
glDrawElements(mode, element_count, type, indices);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal non-indexed drawing (no custom vertex map.)
|
||||
glDrawArrays(mode, 0, vertex_count);
|
||||
if (instance_count > 1)
|
||||
gl.drawArraysInstanced(mode, 0, vertex_count, instance_count);
|
||||
else
|
||||
glDrawArrays(mode, 0, vertex_count);
|
||||
}
|
||||
|
||||
if (wireframe)
|
||||
|
||||
@@ -109,6 +109,15 @@ public:
|
||||
**/
|
||||
size_t getVertexMapCount() const;
|
||||
|
||||
/**
|
||||
* Sets the number of instances of this Mesh to draw (uses hardware
|
||||
* instancing when possible.)
|
||||
* A custom vertex shader is necessary in order to introduce differences
|
||||
* in each instance.
|
||||
**/
|
||||
void setInstanceCount(int count);
|
||||
int getInstanceCount() const;
|
||||
|
||||
/**
|
||||
* Sets the texture used when drawing the Mesh.
|
||||
**/
|
||||
@@ -165,6 +174,8 @@ private:
|
||||
VertexBuffer *ibo;
|
||||
size_t element_count;
|
||||
|
||||
int instance_count;
|
||||
|
||||
DrawMode draw_mode;
|
||||
|
||||
Texture *texture;
|
||||
|
||||
@@ -113,6 +113,8 @@ void OpenGL::initContext()
|
||||
initMaxValues();
|
||||
createDefaultTexture();
|
||||
|
||||
state.lastPseudoInstanceID = -1;
|
||||
|
||||
contextInitialized = true;
|
||||
}
|
||||
|
||||
@@ -214,10 +216,69 @@ void OpenGL::createDefaultTexture()
|
||||
|
||||
void OpenGL::prepareDraw()
|
||||
{
|
||||
// Make sure the active shader has the correct values for the built-in
|
||||
// screen params uniform.
|
||||
if (Shader::current)
|
||||
Shader::current->checkSetScreenParams();
|
||||
Shader *shader = Shader::current;
|
||||
if (shader != nullptr)
|
||||
{
|
||||
// Make sure the active shader has the correct values for its
|
||||
// love-provided uniforms.
|
||||
shader->checkSetScreenParams();
|
||||
|
||||
// Make sure the Instance ID variable is up-to-date when
|
||||
// pseudo-instancing is used.
|
||||
if (state.lastPseudoInstanceID != 0 && shader->hasVertexAttrib(ATTRIB_PSEUDO_INSTANCE_ID))
|
||||
{
|
||||
glVertexAttrib1f((GLuint) ATTRIB_PSEUDO_INSTANCE_ID, 0.0f);
|
||||
state.lastPseudoInstanceID = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGL::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
|
||||
{
|
||||
Shader *shader = Shader::current;
|
||||
|
||||
if (GLEE_ARB_draw_instanced)
|
||||
glDrawArraysInstancedARB(mode, first, count, primcount);
|
||||
else
|
||||
{
|
||||
bool shaderHasID = shader && shader->hasVertexAttrib(ATTRIB_PSEUDO_INSTANCE_ID);
|
||||
|
||||
// Pseudo-instancing fallback.
|
||||
for (int i = 0; i < primcount; i++)
|
||||
{
|
||||
if (shaderHasID)
|
||||
glVertexAttrib1f((GLuint) ATTRIB_PSEUDO_INSTANCE_ID, (GLfloat) i);
|
||||
|
||||
glDrawArrays(mode, first, count);
|
||||
}
|
||||
|
||||
if (shaderHasID)
|
||||
state.lastPseudoInstanceID = primcount - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGL::drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount)
|
||||
{
|
||||
Shader *shader = Shader::current;
|
||||
|
||||
if (GLEE_ARB_draw_instanced)
|
||||
glDrawElementsInstancedARB(mode, count, type, indices, primcount);
|
||||
else
|
||||
{
|
||||
bool shaderHasID = shader && shader->hasVertexAttrib(ATTRIB_PSEUDO_INSTANCE_ID);
|
||||
|
||||
// Pseudo-instancing fallback.
|
||||
for (int i = 0; i < primcount; i++)
|
||||
{
|
||||
if (shaderHasID)
|
||||
glVertexAttrib1f((GLuint) ATTRIB_PSEUDO_INSTANCE_ID, (GLfloat) i);
|
||||
|
||||
glDrawElements(mode, count, type, indices);
|
||||
}
|
||||
|
||||
if (shaderHasID)
|
||||
state.lastPseudoInstanceID = primcount - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGL::setColor(const Color &c)
|
||||
|
||||
@@ -63,6 +63,17 @@ public:
|
||||
VENDOR_UNKNOWN
|
||||
};
|
||||
|
||||
// Vertex attributes used in shaders by LOVE. The values map to OpenGL
|
||||
// generic vertex attribute indices, when applicable.
|
||||
// LOVE uses the old hard-coded attribute APIs for positions, colors, etc.
|
||||
// (for now.)
|
||||
enum VertexAttrib
|
||||
{
|
||||
// Instance ID when pseudo-instancing is used.
|
||||
ATTRIB_PSEUDO_INSTANCE_ID = 1,
|
||||
ATTRIB_MAX_ENUM
|
||||
};
|
||||
|
||||
// A rectangle representing an OpenGL viewport or a scissor box.
|
||||
struct Viewport
|
||||
{
|
||||
@@ -99,6 +110,16 @@ public:
|
||||
**/
|
||||
void prepareDraw();
|
||||
|
||||
/**
|
||||
* glDrawArraysInstanced with a pseudo-instancing fallback.
|
||||
**/
|
||||
void drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount);
|
||||
|
||||
/**
|
||||
* glDrawElementsInstanced with a pseudo-instancing fallback.
|
||||
**/
|
||||
void drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount);
|
||||
|
||||
/**
|
||||
* Sets the current constant color.
|
||||
**/
|
||||
@@ -230,6 +251,9 @@ private:
|
||||
Viewport viewport;
|
||||
Viewport scissor;
|
||||
|
||||
// The last ID value used for pseudo-instancing.
|
||||
int lastPseudoInstanceID;
|
||||
|
||||
} state;
|
||||
|
||||
}; // OpenGL
|
||||
|
||||
@@ -70,6 +70,7 @@ Shader::Shader(const ShaderSources &sources)
|
||||
: shaderSources(sources)
|
||||
, program(0)
|
||||
, builtinUniforms()
|
||||
, vertexAttributes()
|
||||
, lastCanvas((Canvas *) -1)
|
||||
{
|
||||
if (shaderSources.empty())
|
||||
@@ -181,6 +182,14 @@ void Shader::createProgram(const std::vector<GLuint> &shaderids)
|
||||
for (it = shaderids.begin(); it != shaderids.end(); ++it)
|
||||
glAttachShader(program, *it);
|
||||
|
||||
// Bind generic vertex attribute indices to names in the shader.
|
||||
for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
if (attribNames.find((OpenGL::VertexAttrib) i, name))
|
||||
glBindAttribLocation(program, i, (const GLchar *) name);
|
||||
}
|
||||
|
||||
glLinkProgram(program);
|
||||
|
||||
// flag shaders for auto-deletion when the program object is deleted.
|
||||
@@ -273,6 +282,15 @@ bool Shader::loadVolatile()
|
||||
// Retreive all active uniform variables in this shader from OpenGL.
|
||||
mapActiveUniforms();
|
||||
|
||||
for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
if (attribNames.find(OpenGL::VertexAttrib(i), name))
|
||||
vertexAttributes[i] = glGetAttribLocation(program, name);
|
||||
else
|
||||
vertexAttributes[i] = -1;
|
||||
}
|
||||
|
||||
if (current == this)
|
||||
{
|
||||
// make sure glUseProgram gets called.
|
||||
@@ -633,6 +651,11 @@ int Shader::getTextureUnit(const std::string &name)
|
||||
return texunit;
|
||||
}
|
||||
|
||||
bool Shader::hasVertexAttrib(OpenGL::VertexAttrib attrib) const
|
||||
{
|
||||
return vertexAttributes[int(attrib)] != -1;
|
||||
}
|
||||
|
||||
bool Shader::hasBuiltinExtern(BuiltinExtern builtin) const
|
||||
{
|
||||
return builtinUniforms[int(builtin)] != -1;
|
||||
@@ -730,6 +753,13 @@ StringMap<Shader::ShaderType, Shader::TYPE_MAX_ENUM>::Entry Shader::typeNameEntr
|
||||
|
||||
StringMap<Shader::ShaderType, Shader::TYPE_MAX_ENUM> Shader::typeNames(Shader::typeNameEntries, sizeof(Shader::typeNameEntries));
|
||||
|
||||
StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM>::Entry Shader::attribNameEntries[] =
|
||||
{
|
||||
{"love_PseudoInstanceID", OpenGL::ATTRIB_PSEUDO_INSTANCE_ID},
|
||||
};
|
||||
|
||||
StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM> Shader::attribNames(Shader::attribNameEntries, sizeof(Shader::attribNameEntries));
|
||||
|
||||
StringMap<Shader::BuiltinExtern, Shader::BUILTIN_MAX_ENUM>::Entry Shader::builtinNameEntries[] =
|
||||
{
|
||||
{"love_ScreenParams", Shader::BUILTIN_SCREEN_PARAMS},
|
||||
|
||||
@@ -138,6 +138,7 @@ public:
|
||||
/**
|
||||
* Internal use only.
|
||||
**/
|
||||
bool hasVertexAttrib(OpenGL::VertexAttrib attrib) const;
|
||||
bool hasBuiltinExtern(BuiltinExtern builtin) const;
|
||||
bool sendBuiltinFloat(BuiltinExtern builtin, int size, const GLfloat *m, int count);
|
||||
void checkSetScreenParams();
|
||||
@@ -198,6 +199,9 @@ private:
|
||||
// Location values for any built-in uniform variables.
|
||||
GLint builtinUniforms[BUILTIN_MAX_ENUM];
|
||||
|
||||
// Location values for any generic vertex attribute variables.
|
||||
GLint vertexAttributes[OpenGL::ATTRIB_MAX_ENUM];
|
||||
|
||||
// Uniform location buffer map
|
||||
std::map<std::string, Uniform> uniforms;
|
||||
|
||||
@@ -220,6 +224,10 @@ private:
|
||||
static StringMap<ShaderType, TYPE_MAX_ENUM>::Entry typeNameEntries[];
|
||||
static StringMap<ShaderType, TYPE_MAX_ENUM> typeNames;
|
||||
|
||||
// Names for the generic vertex attributes used by love.
|
||||
static StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM>::Entry attribNameEntries[];
|
||||
static StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM> attribNames;
|
||||
|
||||
// Names for the built-in uniform variables.
|
||||
static StringMap<BuiltinExtern, BUILTIN_MAX_ENUM>::Entry builtinNameEntries[];
|
||||
static StringMap<BuiltinExtern, BUILTIN_MAX_ENUM> builtinNames;
|
||||
|
||||
@@ -984,6 +984,10 @@ int w_isSupported(lua_State *L)
|
||||
if (!Image::hasCompressedTextureSupport(image::CompressedData::FORMAT_BC5))
|
||||
supported = false;
|
||||
break;
|
||||
case Graphics::SUPPORT_INSTANCING:
|
||||
if (!GLEE_ARB_draw_instanced)
|
||||
supported = false;
|
||||
break;
|
||||
default:
|
||||
supported = false;
|
||||
}
|
||||
|
||||
@@ -236,6 +236,20 @@ int w_Mesh_getVertexMap(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Mesh_setInstanceCount(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
t->setInstanceCount(luaL_checkint(L, 2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Mesh_getInstanceCount(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
lua_pushinteger(L, t->getInstanceCount());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Mesh_setTexture(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
@@ -338,6 +352,8 @@ static const luaL_Reg functions[] =
|
||||
{ "getVertexCount", w_Mesh_getVertexCount },
|
||||
{ "setVertexMap", w_Mesh_setVertexMap },
|
||||
{ "getVertexMap", w_Mesh_getVertexMap },
|
||||
{ "setInstanceCount", w_Mesh_setInstanceCount },
|
||||
{ "getInstanceCount", w_Mesh_getInstanceCount },
|
||||
{ "setTexture", w_Mesh_setTexture },
|
||||
{ "getTexture", w_Mesh_getTexture },
|
||||
{ "setDrawMode", w_Mesh_setDrawMode },
|
||||
|
||||
@@ -41,6 +41,8 @@ int w_Mesh_getVertices(lua_State *L);
|
||||
int w_Mesh_getVertexCount(lua_State *L);
|
||||
int w_Mesh_setVertexMap(lua_State *L);
|
||||
int w_Mesh_getVertexMap(lua_State *L);
|
||||
int w_Mesh_setInstanceCount(lua_State *L);
|
||||
int w_Mesh_getInstanceCount(lua_State *L);
|
||||
int w_Mesh_setTexture(lua_State *L);
|
||||
int w_Mesh_getTexture(lua_State *L);
|
||||
int w_Mesh_setDrawMode(lua_State *L);
|
||||
|
||||
Reference in New Issue
Block a user