mirror of
https://github.com/love2d/love.git
synced 2026-08-20 04:30:09 +02:00
Add low-level hardware instancing support to Meshes.
Add “instancing” Graphics Feature constant. Add love.graphics.drawInstanced(mesh, instancecount, x, y, …) which draws the mesh multiple times in a single draw call. Each instance of the mesh will appear in the exact same spot unless one of the following are used: - Add an optional vertex attribute step type argument to Mesh:attachAttribute. “pervertex” is the default. “perinstance” causes the attribute to be per-instance instead of per-vertex, when the mesh is drawn. - Add love_InstanceID as a built-in read only int variable in GLSL 3 vertex shaders. It is always 0 except when a Mesh is drawn with more than 1 instance specified in the draw call. It can be used to manually compute or index into per-instance values in a shader. --HG-- branch : minor
This commit is contained in:
@@ -1339,6 +1339,7 @@ StringMap<Graphics::Feature, Graphics::FEATURE_MAX_ENUM>::Entry Graphics::featur
|
|||||||
{ "fullnpot", FEATURE_FULL_NPOT },
|
{ "fullnpot", FEATURE_FULL_NPOT },
|
||||||
{ "pixelshaderhighp", FEATURE_PIXEL_SHADER_HIGHP },
|
{ "pixelshaderhighp", FEATURE_PIXEL_SHADER_HIGHP },
|
||||||
{ "glsl3", FEATURE_GLSL3 },
|
{ "glsl3", FEATURE_GLSL3 },
|
||||||
|
{ "instancing", FEATURE_INSTANCING },
|
||||||
};
|
};
|
||||||
|
|
||||||
StringMap<Graphics::Feature, Graphics::FEATURE_MAX_ENUM> Graphics::features(Graphics::featureEntries, sizeof(Graphics::featureEntries));
|
StringMap<Graphics::Feature, Graphics::FEATURE_MAX_ENUM> Graphics::features(Graphics::featureEntries, sizeof(Graphics::featureEntries));
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ public:
|
|||||||
FEATURE_FULL_NPOT,
|
FEATURE_FULL_NPOT,
|
||||||
FEATURE_PIXEL_SHADER_HIGHP,
|
FEATURE_PIXEL_SHADER_HIGHP,
|
||||||
FEATURE_GLSL3,
|
FEATURE_GLSL3,
|
||||||
|
FEATURE_INSTANCING,
|
||||||
FEATURE_MAX_ENUM
|
FEATURE_MAX_ENUM
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -490,6 +490,11 @@ void Graphics::flushStreamDraws()
|
|||||||
streamBufferState.indexCount = 0;
|
streamBufferState.indexCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Graphics::drawInstanced(Mesh *mesh, const love::Matrix4 &m, int instancecount)
|
||||||
|
{
|
||||||
|
mesh->drawInstanced(this, m, instancecount);
|
||||||
|
}
|
||||||
|
|
||||||
static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, const GLvoid* /*usr*/)
|
static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, const GLvoid* /*usr*/)
|
||||||
{
|
{
|
||||||
// Human-readable strings for the debug info.
|
// Human-readable strings for the debug info.
|
||||||
@@ -1487,6 +1492,8 @@ bool Graphics::isSupported(Feature feature) const
|
|||||||
return gl.isPixelShaderHighpSupported();
|
return gl.isPixelShaderHighpSupported();
|
||||||
case FEATURE_GLSL3:
|
case FEATURE_GLSL3:
|
||||||
return GLAD_ES_VERSION_3_0 || gl.isCoreProfile();
|
return GLAD_ES_VERSION_3_0 || gl.isCoreProfile();
|
||||||
|
case FEATURE_INSTANCING:
|
||||||
|
return gl.isInstancingSupported();
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ public:
|
|||||||
|
|
||||||
void flushStreamDraws() override;
|
void flushStreamDraws() override;
|
||||||
|
|
||||||
|
void drawInstanced(Mesh *mesh, const Matrix4 &m, int instancecount);
|
||||||
|
|
||||||
void clear(Colorf color) override;
|
void clear(Colorf color) override;
|
||||||
void clear(const std::vector<OptionalColorf> &colors) override;
|
void clear(const std::vector<OptionalColorf> &colors) override;
|
||||||
|
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ void Mesh::setupAttachedAttributes()
|
|||||||
if (attachedAttributes.find(name) != attachedAttributes.end())
|
if (attachedAttributes.find(name) != attachedAttributes.end())
|
||||||
throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
|
throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
|
||||||
|
|
||||||
attachedAttributes[name] = {this, (int) i, true};
|
attachedAttributes[name] = {this, (int) i, STEP_PER_VERTEX, true};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,8 +312,11 @@ bool Mesh::isAttributeEnabled(const std::string &name) const
|
|||||||
return it->second.enabled;
|
return it->second.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname)
|
void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step)
|
||||||
{
|
{
|
||||||
|
if (step == STEP_PER_INSTANCE && !gl.isInstancingSupported())
|
||||||
|
throw love::Exception("Vertex attribute instancing is not supported on this system.");
|
||||||
|
|
||||||
if (mesh != this)
|
if (mesh != this)
|
||||||
{
|
{
|
||||||
for (const auto &it : mesh->attachedAttributes)
|
for (const auto &it : mesh->attachedAttributes)
|
||||||
@@ -335,6 +338,7 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::strin
|
|||||||
newattrib.mesh = mesh;
|
newattrib.mesh = mesh;
|
||||||
newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
|
newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
|
||||||
newattrib.index = mesh->getAttributeIndex(attachname);
|
newattrib.index = mesh->getAttributeIndex(attachname);
|
||||||
|
newattrib.step = step;
|
||||||
|
|
||||||
if (newattrib.index < 0)
|
if (newattrib.index < 0)
|
||||||
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", attachname.c_str());
|
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", attachname.c_str());
|
||||||
@@ -598,16 +602,20 @@ int Mesh::bindAttributeToShaderInput(int attributeindex, const std::string &inpu
|
|||||||
return attriblocation;
|
return attriblocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::draw(Graphics *gfx, const Matrix4 &m)
|
void Mesh::drawInstanced(love::graphics::Graphics *gfx, const love::Matrix4 &m, int instancecount)
|
||||||
{
|
{
|
||||||
if (vertexCount <= 0)
|
if (vertexCount <= 0 || instancecount <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (instancecount > 1 && !gl.isInstancingSupported())
|
||||||
|
throw love::Exception("Instancing is not supported on this system.");
|
||||||
|
|
||||||
gfx->flushStreamDraws();
|
gfx->flushStreamDraws();
|
||||||
|
|
||||||
OpenGL::TempDebugGroup debuggroup("Mesh draw");
|
OpenGL::TempDebugGroup debuggroup("Mesh draw");
|
||||||
|
|
||||||
uint32 enabledattribs = 0;
|
uint32 enabledattribs = 0;
|
||||||
|
uint32 instancedattribs = 0;
|
||||||
|
|
||||||
for (const auto &attrib : attachedAttributes)
|
for (const auto &attrib : attachedAttributes)
|
||||||
{
|
{
|
||||||
@@ -618,14 +626,21 @@ void Mesh::draw(Graphics *gfx, const Matrix4 &m)
|
|||||||
int location = mesh->bindAttributeToShaderInput(attrib.second.index, attrib.first);
|
int location = mesh->bindAttributeToShaderInput(attrib.second.index, attrib.first);
|
||||||
|
|
||||||
if (location >= 0)
|
if (location >= 0)
|
||||||
enabledattribs |= 1u << (uint32) location;
|
{
|
||||||
|
uint32 bit = 1u << (uint32) location;
|
||||||
|
|
||||||
|
enabledattribs |= bit;
|
||||||
|
|
||||||
|
if (attrib.second.step == STEP_PER_INSTANCE)
|
||||||
|
instancedattribs |= bit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not supported on all platforms or GL versions, I believe.
|
// Not supported on all platforms or GL versions, I believe.
|
||||||
if (!(enabledattribs & ATTRIBFLAG_POS))
|
if (!(enabledattribs & ATTRIBFLAG_POS))
|
||||||
throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn.");
|
throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn.");
|
||||||
|
|
||||||
gl.useVertexAttribArrays(enabledattribs);
|
gl.useVertexAttribArrays(enabledattribs, instancedattribs);
|
||||||
|
|
||||||
gl.bindTextureToUnit(texture, 0, false);
|
gl.bindTextureToUnit(texture, 0, false);
|
||||||
|
|
||||||
@@ -654,7 +669,7 @@ void Mesh::draw(Graphics *gfx, const Matrix4 &m)
|
|||||||
GLenum type = OpenGL::getGLIndexDataType(elementDataType);
|
GLenum type = OpenGL::getGLIndexDataType(elementDataType);
|
||||||
|
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
gl.drawElements(getGLDrawMode(drawMode), count, type, indices);
|
gl.drawElements(getGLDrawMode(drawMode), count, type, indices, instancecount);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -668,10 +683,15 @@ void Mesh::draw(Graphics *gfx, const Matrix4 &m)
|
|||||||
|
|
||||||
// Normal non-indexed drawing (no custom vertex map.)
|
// Normal non-indexed drawing (no custom vertex map.)
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
gl.drawArrays(getGLDrawMode(drawMode), start, count);
|
gl.drawArrays(getGLDrawMode(drawMode), start, count, instancecount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mesh::draw(love::graphics::Graphics *gfx, const love::Matrix4 &m)
|
||||||
|
{
|
||||||
|
drawInstanced(gfx, m, 1);
|
||||||
|
}
|
||||||
|
|
||||||
size_t Mesh::getAttribFormatSize(const AttribFormat &format)
|
size_t Mesh::getAttribFormatSize(const AttribFormat &format)
|
||||||
{
|
{
|
||||||
switch (format.type)
|
switch (format.type)
|
||||||
@@ -742,24 +762,42 @@ bool Mesh::getConstant(DataType in, const char *&out)
|
|||||||
return dataTypes.find(in, out);
|
return dataTypes.find(in, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Mesh::getConstant(const char *in, AttributeStep &out)
|
||||||
|
{
|
||||||
|
return attributeSteps.find(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mesh::getConstant(AttributeStep in, const char *&out)
|
||||||
|
{
|
||||||
|
return attributeSteps.find(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
StringMap<Mesh::DrawMode, Mesh::DRAWMODE_MAX_ENUM>::Entry Mesh::drawModeEntries[] =
|
StringMap<Mesh::DrawMode, Mesh::DRAWMODE_MAX_ENUM>::Entry Mesh::drawModeEntries[] =
|
||||||
{
|
{
|
||||||
{"fan", DRAWMODE_FAN},
|
{ "fan", DRAWMODE_FAN },
|
||||||
{"strip", DRAWMODE_STRIP},
|
{ "strip", DRAWMODE_STRIP },
|
||||||
{"triangles", DRAWMODE_TRIANGLES},
|
{ "triangles", DRAWMODE_TRIANGLES },
|
||||||
{"points", DRAWMODE_POINTS},
|
{ "points", DRAWMODE_POINTS },
|
||||||
};
|
};
|
||||||
|
|
||||||
StringMap<Mesh::DrawMode, Mesh::DRAWMODE_MAX_ENUM> Mesh::drawModes(Mesh::drawModeEntries, sizeof(Mesh::drawModeEntries));
|
StringMap<Mesh::DrawMode, Mesh::DRAWMODE_MAX_ENUM> Mesh::drawModes(Mesh::drawModeEntries, sizeof(Mesh::drawModeEntries));
|
||||||
|
|
||||||
StringMap<Mesh::DataType, Mesh::DATA_MAX_ENUM>::Entry Mesh::dataTypeEntries[] =
|
StringMap<Mesh::DataType, Mesh::DATA_MAX_ENUM>::Entry Mesh::dataTypeEntries[] =
|
||||||
{
|
{
|
||||||
{"byte", DATA_BYTE},
|
{ "byte", DATA_BYTE },
|
||||||
{"float", DATA_FLOAT},
|
{ "float", DATA_FLOAT },
|
||||||
};
|
};
|
||||||
|
|
||||||
StringMap<Mesh::DataType, Mesh::DATA_MAX_ENUM> Mesh::dataTypes(Mesh::dataTypeEntries, sizeof(Mesh::dataTypeEntries));
|
StringMap<Mesh::DataType, Mesh::DATA_MAX_ENUM> Mesh::dataTypes(Mesh::dataTypeEntries, sizeof(Mesh::dataTypeEntries));
|
||||||
|
|
||||||
|
StringMap<Mesh::AttributeStep, Mesh::STEP_MAX_ENUM>::Entry Mesh::attributeStepEntries[] =
|
||||||
|
{
|
||||||
|
{ "pervertex", STEP_PER_VERTEX },
|
||||||
|
{ "perinstance", STEP_PER_INSTANCE },
|
||||||
|
};
|
||||||
|
|
||||||
|
StringMap<Mesh::AttributeStep, Mesh::STEP_MAX_ENUM> Mesh::attributeSteps(Mesh::attributeStepEntries, sizeof(Mesh::attributeStepEntries));
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
} // graphics
|
} // graphics
|
||||||
} // love
|
} // love
|
||||||
|
|||||||
@@ -72,6 +72,13 @@ public:
|
|||||||
DATA_MAX_ENUM
|
DATA_MAX_ENUM
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum AttributeStep
|
||||||
|
{
|
||||||
|
STEP_PER_VERTEX,
|
||||||
|
STEP_PER_INSTANCE,
|
||||||
|
STEP_MAX_ENUM
|
||||||
|
};
|
||||||
|
|
||||||
struct AttribFormat
|
struct AttribFormat
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -132,7 +139,7 @@ public:
|
|||||||
* Attaches a vertex attribute from another Mesh to this one. The attribute
|
* Attaches a vertex attribute from another Mesh to this one. The attribute
|
||||||
* will be used when drawing this Mesh.
|
* will be used when drawing this Mesh.
|
||||||
**/
|
**/
|
||||||
void attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname);
|
void attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX);
|
||||||
bool detachAttribute(const std::string &name);
|
bool detachAttribute(const std::string &name);
|
||||||
|
|
||||||
void *mapVertexData();
|
void *mapVertexData();
|
||||||
@@ -192,6 +199,8 @@ public:
|
|||||||
|
|
||||||
int bindAttributeToShaderInput(int attributeindex, const std::string &inputname);
|
int bindAttributeToShaderInput(int attributeindex, const std::string &inputname);
|
||||||
|
|
||||||
|
void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount);
|
||||||
|
|
||||||
// Implements Drawable.
|
// Implements Drawable.
|
||||||
void draw(Graphics *gfx, const Matrix4 &m) override;
|
void draw(Graphics *gfx, const Matrix4 &m) override;
|
||||||
|
|
||||||
@@ -201,12 +210,16 @@ public:
|
|||||||
static bool getConstant(const char *in, DataType &out);
|
static bool getConstant(const char *in, DataType &out);
|
||||||
static bool getConstant(DataType in, const char *&out);
|
static bool getConstant(DataType in, const char *&out);
|
||||||
|
|
||||||
|
static bool getConstant(const char *in, AttributeStep &out);
|
||||||
|
static bool getConstant(AttributeStep in, const char *&out);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
struct AttachedAttribute
|
struct AttachedAttribute
|
||||||
{
|
{
|
||||||
Mesh *mesh;
|
Mesh *mesh;
|
||||||
int index;
|
int index;
|
||||||
|
AttributeStep step;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -254,6 +267,9 @@ private:
|
|||||||
static StringMap<DataType, DATA_MAX_ENUM>::Entry dataTypeEntries[];
|
static StringMap<DataType, DATA_MAX_ENUM>::Entry dataTypeEntries[];
|
||||||
static StringMap<DataType, DATA_MAX_ENUM> dataTypes;
|
static StringMap<DataType, DATA_MAX_ENUM> dataTypes;
|
||||||
|
|
||||||
|
static StringMap<AttributeStep, STEP_MAX_ENUM>::Entry attributeStepEntries[];
|
||||||
|
static StringMap<AttributeStep, STEP_MAX_ENUM> attributeSteps;
|
||||||
|
|
||||||
}; // Mesh
|
}; // Mesh
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -134,8 +134,15 @@ void OpenGL::setupContext()
|
|||||||
|
|
||||||
GLint maxvertexattribs = 1;
|
GLint maxvertexattribs = 1;
|
||||||
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxvertexattribs);
|
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxvertexattribs);
|
||||||
|
|
||||||
state.enabledAttribArrays = (uint32) ((1ull << uint32(maxvertexattribs)) - 1);
|
state.enabledAttribArrays = (uint32) ((1ull << uint32(maxvertexattribs)) - 1);
|
||||||
useVertexAttribArrays(0);
|
|
||||||
|
if (GLAD_ES_VERSION_3_0 || isCoreProfile())
|
||||||
|
state.instancedAttribArrays = state.enabledAttribArrays;
|
||||||
|
else
|
||||||
|
state.instancedAttribArrays = 0;
|
||||||
|
|
||||||
|
useVertexAttribArrays(0, 0);
|
||||||
|
|
||||||
// Get the current viewport.
|
// Get the current viewport.
|
||||||
glGetIntegerv(GL_VIEWPORT, (GLint *) &state.viewport.x);
|
glGetIntegerv(GL_VIEWPORT, (GLint *) &state.viewport.x);
|
||||||
@@ -427,23 +434,32 @@ void OpenGL::deleteBuffer(GLuint buffer)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGL::drawArrays(GLenum mode, GLint first, GLsizei count)
|
void OpenGL::drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instancecount)
|
||||||
{
|
{
|
||||||
glDrawArrays(mode, first, count);
|
if (instancecount > 1)
|
||||||
|
glDrawArraysInstanced(mode, first, count, instancecount);
|
||||||
|
else
|
||||||
|
glDrawArrays(mode, first, count);
|
||||||
|
|
||||||
++stats.drawCalls;
|
++stats.drawCalls;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGL::drawElements(GLenum mode, GLsizei count, GLenum type, const void *indices)
|
void OpenGL::drawElements(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount)
|
||||||
{
|
{
|
||||||
glDrawElements(mode, count, type, indices);
|
if (count > 1)
|
||||||
|
glDrawElementsInstanced(mode, count, type, indices, instancecount);
|
||||||
|
else
|
||||||
|
glDrawElements(mode, count, type, indices);
|
||||||
|
|
||||||
++stats.drawCalls;
|
++stats.drawCalls;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGL::useVertexAttribArrays(uint32 arraybits)
|
void OpenGL::useVertexAttribArrays(uint32 arraybits, uint32 instancedbits)
|
||||||
{
|
{
|
||||||
uint32 diff = arraybits ^ state.enabledAttribArrays;
|
uint32 diff = arraybits ^ state.enabledAttribArrays;
|
||||||
|
uint32 instancediff = instancedbits ^ state.instancedAttribArrays;
|
||||||
|
|
||||||
if (diff == 0)
|
if (diff == 0 && instancediff == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Max 32 attributes. As of when this was written, no GL driver exposes more
|
// Max 32 attributes. As of when this was written, no GL driver exposes more
|
||||||
@@ -459,9 +475,13 @@ void OpenGL::useVertexAttribArrays(uint32 arraybits)
|
|||||||
else
|
else
|
||||||
glDisableVertexAttribArray(i);
|
glDisableVertexAttribArray(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (instancediff & bit)
|
||||||
|
glVertexAttribDivisor(i, (instancedbits & bit) != 0 ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.enabledAttribArrays = arraybits;
|
state.enabledAttribArrays = arraybits;
|
||||||
|
state.instancedAttribArrays = instancedbits;
|
||||||
|
|
||||||
// glDisableVertexAttribArray will make the constant value for a vertex
|
// glDisableVertexAttribArray will make the constant value for a vertex
|
||||||
// attribute undefined. We rely on the per-vertex color attribute being
|
// attribute undefined. We rely on the per-vertex color attribute being
|
||||||
@@ -733,6 +753,11 @@ bool OpenGL::isPixelShaderHighpSupported() const
|
|||||||
return pixelShaderHighpSupported;
|
return pixelShaderHighpSupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OpenGL::isInstancingSupported() const
|
||||||
|
{
|
||||||
|
return GLAD_ES_VERSION_3_0 || isCoreProfile();
|
||||||
|
}
|
||||||
|
|
||||||
int OpenGL::getMaxTextureSize() const
|
int OpenGL::getMaxTextureSize() const
|
||||||
{
|
{
|
||||||
return maxTextureSize;
|
return maxTextureSize;
|
||||||
|
|||||||
@@ -212,8 +212,8 @@ public:
|
|||||||
* glDrawArrays and glDrawElements which increment the draw-call counter by
|
* glDrawArrays and glDrawElements which increment the draw-call counter by
|
||||||
* themselves.
|
* themselves.
|
||||||
**/
|
**/
|
||||||
void drawArrays(GLenum mode, GLint first, GLsizei count);
|
void drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instancecount = 1);
|
||||||
void drawElements(GLenum mode, GLsizei count, GLenum type, const void *indices);
|
void drawElements(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount = 1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the enabled vertex attribute arrays based on the specified attribute
|
* Sets the enabled vertex attribute arrays based on the specified attribute
|
||||||
@@ -222,7 +222,7 @@ public:
|
|||||||
* See the VertexAttribFlags enum for the standard vertex attributes.
|
* See the VertexAttribFlags enum for the standard vertex attributes.
|
||||||
* This function *must* be used instead of glEnable/DisableVertexAttribArray.
|
* This function *must* be used instead of glEnable/DisableVertexAttribArray.
|
||||||
**/
|
**/
|
||||||
void useVertexAttribArrays(uint32 arraybits);
|
void useVertexAttribArrays(uint32 arraybits, uint32 instancedbits = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the OpenGL rendering viewport to the specified rectangle.
|
* Sets the OpenGL rendering viewport to the specified rectangle.
|
||||||
@@ -315,6 +315,7 @@ public:
|
|||||||
|
|
||||||
bool isClampZeroTextureWrapSupported() const;
|
bool isClampZeroTextureWrapSupported() const;
|
||||||
bool isPixelShaderHighpSupported() const;
|
bool isPixelShaderHighpSupported() const;
|
||||||
|
bool isInstancingSupported() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the maximum supported width or height of a texture.
|
* Returns the maximum supported width or height of a texture.
|
||||||
@@ -410,6 +411,7 @@ private:
|
|||||||
int curTextureUnit;
|
int curTextureUnit;
|
||||||
|
|
||||||
uint32 enabledAttribArrays;
|
uint32 enabledAttribArrays;
|
||||||
|
uint32 instancedAttribArrays;
|
||||||
|
|
||||||
Colorf constantColor;
|
Colorf constantColor;
|
||||||
Colorf lastConstantColor;
|
Colorf lastConstantColor;
|
||||||
|
|||||||
@@ -1670,6 +1670,36 @@ int w_draw(lua_State *L)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w_drawInstanced(lua_State *L)
|
||||||
|
{
|
||||||
|
Mesh *t = luax_checkmesh(L, 1);
|
||||||
|
int instancecount = (int) luaL_checkinteger(L, 2);
|
||||||
|
|
||||||
|
if (luax_istype(L, 3, math::Transform::type))
|
||||||
|
{
|
||||||
|
math::Transform *tf = luax_totype<math::Transform>(L, 3);
|
||||||
|
luax_catchexcept(L, [&]() { instance()->drawInstanced(t, tf->getMatrix(), instancecount); });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float x = (float) luaL_optnumber(L, 3, 0.0);
|
||||||
|
float y = (float) luaL_optnumber(L, 4, 0.0);
|
||||||
|
float a = (float) luaL_optnumber(L, 5, 0.0);
|
||||||
|
float sx = (float) luaL_optnumber(L, 6, 1.0);
|
||||||
|
float sy = (float) luaL_optnumber(L, 7, sx);
|
||||||
|
float ox = (float) luaL_optnumber(L, 8, 0.0);
|
||||||
|
float oy = (float) luaL_optnumber(L, 9, 0.0);
|
||||||
|
float kx = (float) luaL_optnumber(L, 10, 0.0);
|
||||||
|
float ky = (float) luaL_optnumber(L, 11, 0.0);
|
||||||
|
|
||||||
|
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
|
||||||
|
|
||||||
|
luax_catchexcept(L, [&]() { instance()->drawInstanced(t, m, instancecount); });
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int w_print(lua_State *L)
|
int w_print(lua_State *L)
|
||||||
{
|
{
|
||||||
std::vector<Font::ColoredString> str;
|
std::vector<Font::ColoredString> str;
|
||||||
@@ -2195,6 +2225,7 @@ static const luaL_Reg functions[] =
|
|||||||
{ "captureScreenshot", w_captureScreenshot },
|
{ "captureScreenshot", w_captureScreenshot },
|
||||||
|
|
||||||
{ "draw", w_draw },
|
{ "draw", w_draw },
|
||||||
|
{ "drawInstanced", w_drawInstanced },
|
||||||
|
|
||||||
{ "print", w_print },
|
{ "print", w_print },
|
||||||
{ "printf", w_printf },
|
{ "printf", w_printf },
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ GLSL.VERTEX = {
|
|||||||
#define varying out
|
#define varying out
|
||||||
#ifndef LOVE_GLSL1_ON_GLSL3
|
#ifndef LOVE_GLSL1_ON_GLSL3
|
||||||
#define love_VertexID gl_VertexID
|
#define love_VertexID gl_VertexID
|
||||||
|
#define love_InstanceID gl_InstanceID
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -221,15 +222,15 @@ vec4 VideoTexel(vec2 texcoords) {
|
|||||||
}]],
|
}]],
|
||||||
|
|
||||||
FOOTER = [[
|
FOOTER = [[
|
||||||
uniform sampler2D MainTexture;
|
uniform sampler2D MainTex;
|
||||||
void main() {
|
void main() {
|
||||||
love_PixelColor = effect(VaryingColor, MainTexture, VaryingTexCoord.st, love_PixelCoord);
|
love_PixelColor = effect(VaryingColor, MainTex, VaryingTexCoord.st, love_PixelCoord);
|
||||||
}]],
|
}]],
|
||||||
|
|
||||||
FOOTER_MULTI_CANVAS = [[
|
FOOTER_MULTI_CANVAS = [[
|
||||||
uniform sampler2D MainTexture;
|
uniform sampler2D MainTex;
|
||||||
void main() {
|
void main() {
|
||||||
effects(VaryingColor, MainTexture, VaryingTexCoord.st, love_PixelCoord);
|
effects(VaryingColor, MainTex, VaryingTexCoord.st, love_PixelCoord);
|
||||||
}]],
|
}]],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -338,8 +338,15 @@ int w_Mesh_attachAttribute(lua_State *L)
|
|||||||
Mesh *t = luax_checkmesh(L, 1);
|
Mesh *t = luax_checkmesh(L, 1);
|
||||||
const char *name = luaL_checkstring(L, 2);
|
const char *name = luaL_checkstring(L, 2);
|
||||||
Mesh *mesh = luax_checkmesh(L, 3);
|
Mesh *mesh = luax_checkmesh(L, 3);
|
||||||
const char *attachname = luaL_optstring(L, 4, name);
|
|
||||||
luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh, attachname); });
|
Mesh::AttributeStep step = Mesh::STEP_PER_VERTEX;
|
||||||
|
const char *stepstr = lua_isnoneornil(L, 4) ? nullptr : luaL_checkstring(L, 4);
|
||||||
|
if (stepstr != nullptr && !Mesh::getConstant(stepstr, step))
|
||||||
|
return luaL_error(L, "Invalid vertex attribute step: %s", stepstr);
|
||||||
|
|
||||||
|
const char *attachname = luaL_optstring(L, 5, name);
|
||||||
|
|
||||||
|
luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh, attachname, step); });
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user