Просмотр исходного кода

updated to latest mobile-common

fysx 12 лет назад
Родитель
Сommit
c8c7bc5853

+ 1 - 0
jni/love/src/modules/graphics/Graphics.cpp

@@ -165,6 +165,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));

+ 1 - 0
jni/love/src/modules/graphics/Graphics.h

@@ -94,6 +94,7 @@ public:
 		SUPPORT_MIPMAP,
 		SUPPORT_DXT,
 		SUPPORT_BC5,
+		SUPPORT_INSTANCING,
 		SUPPORT_MAX_ENUM
 	};
 

+ 1 - 1
jni/love/src/modules/graphics/opengl/Font.cpp

@@ -154,7 +154,7 @@ void Font::createTexture()
 	{
 		// Clean up before throwing.
 		gl.deleteTexture(t);
-		gl.bindTexture(0);
+		gl.bindTexture(gl.getDefaultTexture());
 		textures.pop_back();
 
 		throw love::Exception("Could not create font texture!");

+ 8 - 3
jni/love/src/modules/graphics/opengl/Graphics.cpp

@@ -508,6 +508,11 @@ Mesh *Graphics::newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode mode
 	return new Mesh(vertices, mode);
 }
 
+Mesh *Graphics::newMesh(int vertexcount, Mesh::DrawMode mode)
+{
+	return new Mesh(vertexcount, mode);
+}
+
 void Graphics::setColor(const Color &c)
 {
 	gl.setColor(c);
@@ -856,7 +861,7 @@ void Graphics::point(float x, float y)
 {
 	gl.prepareDraw();
 
-	gl.bindTexture(0);
+	gl.bindTexture(gl.getDefaultTexture());
 
 	GLfloat coord[] = {x, y};
 
@@ -957,7 +962,7 @@ void Graphics::arc(DrawMode mode, float x, float y, float radius, float angle1,
 	{
 		gl.prepareDraw();
 
-		gl.bindTexture(0);
+		gl.bindTexture(gl.getDefaultTexture());
 
 		gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
 		gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, 0, (GLvoid *) coords);
@@ -985,7 +990,7 @@ void Graphics::polygon(DrawMode mode, const float *coords, size_t count)
 	{
 		gl.prepareDraw();
 
-		gl.bindTexture(0);
+		gl.bindTexture(gl.getDefaultTexture());
 
 		gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
 		gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, 0, (GLvoid *) coords);

+ 1 - 0
jni/love/src/modules/graphics/opengl/Graphics.h

@@ -213,6 +213,7 @@ public:
 	Shader *newShader(const Shader::ShaderSources &sources);
 
 	Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode mode = Mesh::DRAW_MODE_FAN);
+	Mesh *newMesh(int vertexcount, Mesh::DrawMode mode = Mesh::DRAW_MODE_FAN);
 
 	/**
 	 * Sets the foreground color.

+ 97 - 7
jni/love/src/modules/graphics/opengl/Mesh.cpp

@@ -23,6 +23,9 @@
 #include "common/Matrix.h"
 #include "common/Exception.h"
 
+// C++
+#include <algorithm>
+
 namespace love
 {
 namespace graphics
@@ -31,15 +34,47 @@ namespace opengl
 {
 
 Mesh::Mesh(const std::vector<Vertex> &verts, Mesh::DrawMode mode)
+	: vbo(nullptr)
+	, vertex_count(0)
+	, ibo(nullptr)
+	, element_count(0)
+	, instance_count(1)
+	, draw_mode(mode)
+	, range_min(-1)
+	, range_max(-1)
+	, texture(nullptr)
+	, colors_enabled(false)
+	, wireframe(false)
+{
+	setVertices(verts);
+}
+
+Mesh::Mesh(int vertexcount, Mesh::DrawMode mode)
 	: vbo(nullptr)
 	, vertex_count(0)
 	, ibo(nullptr)
 	, element_count(0)
 	, draw_mode(mode)
+	, range_min(-1)
+	, range_max(-1)
 	, texture(nullptr)
 	, colors_enabled(false)
 	, wireframe(false)
 {
+	if (vertexcount < 1)
+		throw love::Exception("Invalid number of vertices.");
+
+	std::vector<Vertex> verts(vertexcount);
+
+	// Default-initialized vertices should have a white opaque color.
+	for (size_t i = 0; i < verts.size(); i++)
+	{
+		verts[i].r = 255;
+		verts[i].g = 255;
+		verts[i].b = 255;
+		verts[i].a = 255;
+	}
+
 	setVertices(verts);
 }
 
@@ -51,8 +86,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();
 
@@ -162,7 +197,7 @@ const uint32 *Mesh::getVertexMap() const
 		return (uint32 *) ibo->map();
 	}
 
-	return 0;
+	return nullptr;
 }
 
 size_t Mesh::getVertexMapCount() const
@@ -170,6 +205,16 @@ size_t Mesh::getVertexMapCount() const
 	return element_count;
 }
 
+void Mesh::setInstanceCount(int count)
+{
+	instance_count = std::max(count, 1);
+}
+
+int Mesh::getInstanceCount() const
+{
+	return instance_count;
+}
+
 void Mesh::setTexture(Texture *tex)
 {
 	tex->retain();
@@ -203,6 +248,26 @@ Mesh::DrawMode Mesh::getDrawMode() const
 	return draw_mode;
 }
 
+void Mesh::setDrawRange(int min, int max)
+{
+	if (min < 0 || max < 0 || min > max)
+		throw love::Exception("Invalid draw range.");
+
+	range_min = min;
+	range_max = max;
+}
+
+void Mesh::setDrawRange()
+{
+	range_min = range_max = -1;
+}
+
+void Mesh::getDrawRange(int &min, int &max) const
+{
+	min = range_min;
+	max = range_max;
+}
+
 void Mesh::setVertexColors(bool enable)
 {
 	colors_enabled = enable;
@@ -235,7 +300,7 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 	if (texture)
 		texture->predraw();
 	else
-		gl.bindTexture(0);
+		gl.bindTexture(gl.getDefaultTexture());
 
 	Matrix m;
 	m.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
@@ -270,18 +335,43 @@ 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));
+		int max = element_count - 1;
+		if (range_max >= 0)
+			max = std::min(std::max(range_max, 0), (int) element_count - 1);
+
+		int min = 0;
+		if (range_min >= 0)
+			min = std::min(std::max(range_min, 0), max);
+
+		const void *indices = ibo->getPointer(min * sizeof(uint32));
+		GLenum type = GL_UNSIGNED_INT;
+
+		if (instance_count > 1)
+			gl.drawElementsInstanced(mode, max - min + 1, type, indices, instance_count);
+		else
+			glDrawElements(mode, max - min + 1, type, indices);
 	}
 	else
 	{
+		int max = vertex_count - 1;
+		if (range_max >= 0)
+			max = std::min(std::max(range_max, 0), (int) vertex_count - 1);
+
+		int min = 0;
+		if (range_min >= 0)
+			min = std::min(std::max(range_min, 0), max);
+
 		// Normal non-indexed drawing (no custom vertex map.)
-		glDrawArrays(mode, 0, vertex_count);
+		if (instance_count > 1)
+			gl.drawArraysInstanced(mode, min, max - min + 1, instance_count);
+		else
+			glDrawArrays(mode, min, max - min + 1);
 	}
 
 	gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);

+ 29 - 0
jni/love/src/modules/graphics/opengl/Mesh.h

@@ -22,6 +22,7 @@
 #define LOVE_GRAPHICS_OPENGL_MESH_H
 
 // LOVE
+#include "common/config.h"
 #include "common/int.h"
 #include "common/math.h"
 #include "common/StringMap.h"
@@ -64,6 +65,16 @@ public:
 	 * @param mode The draw mode to use when drawing the Mesh.
 	 **/
 	Mesh(const std::vector<Vertex> &verts, DrawMode mode = DRAW_MODE_FAN);
+
+	/**
+	 * Constructor.
+	 * Creates a Mesh with a certain number of default-initialized (hidden)
+	 * vertices.
+	 * @param vertexcount The number of vertices to use in the Mesh.
+	 * @param mode The draw mode to use when drawing the Mesh.
+	 **/
+	Mesh(int vertexcount, DrawMode mode = DRAW_MODE_FAN);
+
 	virtual ~Mesh();
 
 	/**
@@ -109,6 +120,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.
 	 **/
@@ -131,6 +151,10 @@ public:
 	void setDrawMode(DrawMode mode);
 	DrawMode getDrawMode() const;
 
+	void setDrawRange(int min, int max);
+	void setDrawRange();
+	void getDrawRange(int &min, int &max) const;
+
 	/**
 	 * Sets whether per-vertex colors are enabled. If this is disabled, the
 	 * global color (love.graphics.setColor) will be used for the entire Mesh.
@@ -165,8 +189,13 @@ private:
 	VertexBuffer *ibo;
 	size_t element_count;
 
+	int instance_count;
+
 	DrawMode draw_mode;
 
+	int range_min;
+	int range_max;
+
 	Texture *texture;
 
 	// Whether the per-vertex colors are used when drawing.

+ 79 - 5
jni/love/src/modules/graphics/opengl/OpenGL.cpp

@@ -129,6 +129,8 @@ bool OpenGL::initContext()
 	initMaxValues();
 	createDefaultTexture();
 
+	state.lastPseudoInstanceID = -1;
+
 	contextInitialized = true;
 	return true;
 }
@@ -138,6 +140,9 @@ void OpenGL::deInitContext()
 	if (!contextInitialized)
 		return;
 
+	glDeleteTextures(1, &state.defaultTexture);
+	state.defaultTexture = 0;
+
 	contextInitialized = false;
 }
 
@@ -213,13 +218,15 @@ void OpenGL::initMatrices()
 
 void OpenGL::createDefaultTexture()
 {
-	// Set the 'default' texture (id 0) as a repeating white pixel. Otherwise,
+	// Set the 'default' texture as a repeating white pixel. Otherwise,
 	// texture2D calls inside a shader would return black when drawing graphics
 	// primitives, which would create the need to use different "passthrough"
 	// shaders for untextured primitives vs images.
 
 	GLuint curtexture = state.textureUnits[state.curTextureUnit];
-	bindTexture(0);
+
+	glGenTextures(1, &state.defaultTexture);
+	bindTexture(state.defaultTexture);
 
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -240,16 +247,26 @@ void OpenGL::prepareDraw()
 
 	Shader *shader = Shader::current;
 
-	// Make sure the active shader has the correct values for the built-in
-	// screen params uniform.
+	// Make sure the active shader has the correct values for its love-provided
+	// uniforms.
 	if (shader)
+	{
 		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;
+		}
+	}
+
 	if (GLAD_ES_VERSION_2_0 && shader)
 	{
 		// Send built-in uniforms to the current shader.
 		shader->sendBuiltinMatrix(Shader::BUILTIN_TRANSFORM_MATRIX, 4, transform.getElements(), 1);
-		shader->sendBuiltinMatrix(Shader::BUILTIN_TRANSFORM_MATRIX, 4, proj.getElements(), 1);
+		shader->sendBuiltinMatrix(Shader::BUILTIN_PROJECTION_MATRIX, 4, proj.getElements(), 1);
 
 		Matrix tp_matrix(proj * transform);
 		shader->sendBuiltinMatrix(Shader::BUILTIN_TRANSFORM_PROJECTION_MATRIX, 4, tp_matrix.getElements(), 1);
@@ -265,6 +282,58 @@ void OpenGL::prepareDraw()
 	}
 }
 
+void OpenGL::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
+{
+	Shader *shader = Shader::current;
+
+	if (GLAD_ARB_draw_instanced)
+		glDrawArraysInstancedARB(mode, first, count, primcount);
+	else if (GLAD_EXT_draw_instanced && GLAD_ES_VERSION_2_0)
+		glDrawArraysInstancedEXT(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 (GLAD_ARB_draw_instanced)
+		glDrawElementsInstancedARB(mode, count, type, indices, primcount);
+	else if (GLAD_EXT_draw_instanced && GLAD_ES_VERSION_2_0)
+		glDrawElementsInstancedEXT(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)
 {
 	if (GLAD_ES_VERSION_2_0)
@@ -415,6 +484,11 @@ GLuint OpenGL::getDefaultFBO() const
 	return state.defaultFBO;
 }
 
+GLuint OpenGL::getDefaultTexture() const
+{
+	return state.defaultTexture;
+}
+
 void OpenGL::setTextureUnit(int textureunit)
 {
 	if (textureunit < 0 || (size_t) textureunit >= state.textureUnits.size())

+ 24 - 2
jni/love/src/modules/graphics/opengl/OpenGL.h

@@ -71,13 +71,15 @@ public:
 		VENDOR_UNKNOWN
 	};
 
-	// Vertex attributes. The values map to OpenGL generic vertex attribute
-	// indices, when applicable (GLES2.)
+	// 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 on desktop GL (for now.)
 	enum VertexAttrib
 	{
 		ATTRIB_POS      = 0,
 		ATTRIB_TEXCOORD = 1,
 		ATTRIB_COLOR    = 2,
+		ATTRIB_PSEUDO_INSTANCE_ID = 3, // Shader pseudo-instance ID.
 		ATTRIB_MAX_ENUM
 	};
 
@@ -125,6 +127,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.
 	 **/
@@ -201,6 +213,11 @@ public:
 	 **/
 	GLuint getDefaultFBO() const;
 
+	/**
+	 * Gets the ID for love's default texture (used for "untextured" draws.)
+	 **/
+	GLuint getDefaultTexture() const;
+
 	/**
 	 * Helper for setting the active texture unit.
 	 *
@@ -295,8 +312,13 @@ private:
 
 		float pointSize;
 
+		GLuint defaultTexture;
+
 		GLuint defaultFBO;
 
+		// The last ID value used for pseudo-instancing.
+		int lastPseudoInstanceID;
+
 	} state;
 
 }; // OpenGL

+ 1 - 1
jni/love/src/modules/graphics/opengl/Polyline.cpp

@@ -330,7 +330,7 @@ void Polyline::draw()
 	gl.prepareDraw();
 
 	// draw the core line
-	gl.bindTexture(0);
+	gl.bindTexture(gl.getDefaultTexture());
 	gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
 	gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, 0, (GLvoid *) vertices);
 	glDrawArrays(draw_mode, 0, vertex_count);

+ 24 - 13
jni/love/src/modules/graphics/opengl/Shader.cpp

@@ -73,6 +73,7 @@ Shader::Shader(const ShaderSources &sources)
 	: shaderSources(sources)
 	, program(0)
 	, builtinUniforms()
+	, vertexAttributes()
 	, lastCanvas((Canvas *) -1)
 {
 	if (shaderSources.empty())
@@ -184,17 +185,16 @@ void Shader::createProgram(const std::vector<GLuint> &shaderids)
 	for (it = shaderids.begin(); it != shaderids.end(); ++it)
 		glAttachShader(program, *it);
 
-	// We use generic vertex attributes in OpenGL ES 2, so we have to bind the
-	// attribute indices to names in the shader.
-	if (GLAD_ES_VERSION_2_0)
+	// 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;
-		for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++)
-		{
-			if (attribNames.find(OpenGL::VertexAttrib(i), name))
-				glBindAttribLocation(program, i, (const GLchar *) name);
-		}
+		// Desktop OpenGL should ignore some of these.
+		if (!GLAD_ES_VERSION_2_0 && i <= int(OpenGL::ATTRIB_COLOR))
+			continue;
 
+		const char *name = nullptr;
+		if (attribNames.find((OpenGL::VertexAttrib) i, name))
+			glBindAttribLocation(program, i, (const GLchar *) name);
 	}
 
 	glLinkProgram(program);
@@ -268,10 +268,6 @@ bool Shader::loadVolatile()
 	activeTexUnits.clear();
 	activeTexUnits.insert(activeTexUnits.begin(), maxTexUnits, 0);
 
-	// Built-in uniform locations default to -1 (nonexistant.)
-	for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
-		builtinUniforms[i] = -1;
-
 	// Built-in uniform locations default to -1 (nonexistant.)
 	for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
 		builtinUniforms[i] = -1;
@@ -307,6 +303,15 @@ bool Shader::loadVolatile()
 	// Retrieve 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.
@@ -679,6 +684,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;
@@ -808,6 +818,7 @@ StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM>::Entry Shader::attribNa
 	{"VertexPosition", OpenGL::ATTRIB_POS},
 	{"VertexTexCoord", OpenGL::ATTRIB_TEXCOORD},
 	{"VertexColor", OpenGL::ATTRIB_COLOR},
+	{"love_PseudoInstanceID", OpenGL::ATTRIB_PSEUDO_INSTANCE_ID},
 };
 
 StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM> Shader::attribNames(Shader::attribNameEntries, sizeof(Shader::attribNameEntries));

+ 6 - 2
jni/love/src/modules/graphics/opengl/Shader.h

@@ -146,6 +146,7 @@ public:
 	/**
 	 * Internal use only.
 	 **/
+	bool hasVertexAttrib(OpenGL::VertexAttrib attrib) const;
 	bool hasBuiltinExtern(BuiltinExtern builtin) const;
 	bool sendBuiltinMatrix(BuiltinExtern builtin, int size, const GLfloat *m, int count);
 	bool sendBuiltinFloat(BuiltinExtern builtin, int size, const GLfloat *m, int count);
@@ -211,6 +212,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;
 
@@ -234,11 +238,11 @@ private:
 	static StringMap<ShaderType, TYPE_MAX_ENUM>::Entry typeNameEntries[];
 	static StringMap<ShaderType, TYPE_MAX_ENUM> typeNames;
 
-	// Names for the generic vertex attributes used in OpenGL ES 2.
+	// 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 LOVE-defined built-in uniform variables.
+	// Names for the built-in uniform variables.
 	static StringMap<BuiltinExtern, BUILTIN_MAX_ENUM>::Entry builtinNameEntries[];
 	static StringMap<BuiltinExtern, BUILTIN_MAX_ENUM> builtinNames;
 

+ 47 - 33
jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp

@@ -440,8 +440,10 @@ int w_newShader(lua_State *L)
 
 int w_newMesh(lua_State *L)
 {
-	// Check first argument: mandatory table of vertices.
-	luaL_checktype(L, 1, LUA_TTABLE);
+	// Check first argument: table of vertices or number of vertices.
+	int ttype = lua_type(L, 1);
+	if (ttype != LUA_TTABLE && ttype != LUA_TNUMBER)
+		luaL_argerror(L, 1, "table or number expected");
 
 	// Second argument: optional texture.
 	Texture *tex = nullptr;
@@ -456,52 +458,60 @@ int w_newMesh(lua_State *L)
 	if (str && !Mesh::getConstant(str, mode))
 		return luaL_error(L, "Invalid mesh draw mode: %s", str);
 
-	size_t vertex_count = lua_objlen(L, 1);
-	std::vector<Vertex> vertices;
-	vertices.reserve(vertex_count);
-
-	bool use_colors = false;
+	Mesh *t = nullptr;
 
-	// Get the vertices from the table.
-	for (size_t i = 1; i <= vertex_count; i++)
+	if (ttype == LUA_TTABLE)
 	{
-		lua_rawgeti(L, 1, i);
+		size_t vertex_count = lua_objlen(L, 1);
+		std::vector<Vertex> vertices;
+		vertices.reserve(vertex_count);
 
-		if (lua_type(L, -1) != LUA_TTABLE)
-			return luax_typerror(L, 1, "table of tables");
+		bool use_colors = false;
 
-		for (int j = 1; j <= 8; j++)
-			lua_rawgeti(L, -j, j);
+		// Get the vertices from the table.
+		for (size_t i = 1; i <= vertex_count; i++)
+		{
+			lua_rawgeti(L, 1, i);
 
-		Vertex v;
+			if (lua_type(L, -1) != LUA_TTABLE)
+				return luax_typerror(L, 1, "table of tables");
 
-		v.x = (float) luaL_checknumber(L, -8);
-		v.y = (float) luaL_checknumber(L, -7);
+			for (int j = 1; j <= 8; j++)
+				lua_rawgeti(L, -j, j);
 
-		v.s = (float) luaL_checknumber(L, -6);
-		v.t = (float) luaL_checknumber(L, -5);
+			Vertex v;
 
-		v.r = (unsigned char) luaL_optinteger(L, -4, 255);
-		v.g = (unsigned char) luaL_optinteger(L, -3, 255);
-		v.b = (unsigned char) luaL_optinteger(L, -2, 255);
-		v.a = (unsigned char) luaL_optinteger(L, -1, 255);
+			v.x = (float) luaL_checknumber(L, -8);
+			v.y = (float) luaL_checknumber(L, -7);
 
-		// Enable per-vertex coloring if any color is not the default.
-		if (!use_colors && (v.r != 255 || v.g != 255 || v.b != 255 || v.a != 255))
-			use_colors = true;
+			v.s = (float) luaL_checknumber(L, -6);
+			v.t = (float) luaL_checknumber(L, -5);
 
-		lua_pop(L, 9);
-		vertices.push_back(v);
-	}
+			v.r = (unsigned char) luaL_optinteger(L, -4, 255);
+			v.g = (unsigned char) luaL_optinteger(L, -3, 255);
+			v.b = (unsigned char) luaL_optinteger(L, -2, 255);
+			v.a = (unsigned char) luaL_optinteger(L, -1, 255);
 
-	Mesh *t = nullptr;
-	EXCEPT_GUARD(t = instance->newMesh(vertices, mode);)
+			// Enable per-vertex coloring if any color is not the default.
+			if (!use_colors && (v.r != 255 || v.g != 255 || v.b != 255 || v.a != 255))
+				use_colors = true;
+
+			lua_pop(L, 9);
+			vertices.push_back(v);
+		}
+
+		EXCEPT_GUARD(t = instance->newMesh(vertices, mode);)
+		t->setVertexColors(use_colors);
+	}
+	else
+	{
+		int count = luaL_checkint(L, 1);
+		EXCEPT_GUARD(t = instance->newMesh(count, mode);)
+	}
 
 	if (tex)
 		t->setTexture(tex);
 
-	t->setVertexColors(use_colors);
-
 	luax_pushtype(L, "Mesh", GRAPHICS_MESH_T, t);
 	return 1;
 }
@@ -1014,6 +1024,10 @@ int w_isSupported(lua_State *L)
 			if (!Image::hasCompressedTextureSupport(image::CompressedData::FORMAT_BC5))
 				supported = false;
 			break;
+		case Graphics::SUPPORT_INSTANCING:
+			if (!GLAD_ARB_draw_instanced)
+				supported = false;
+			break;
 		default:
 			supported = false;
 		}

+ 50 - 0
jni/love/src/modules/graphics/opengl/wrap_Mesh.cpp

@@ -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);
@@ -301,6 +315,38 @@ int w_Mesh_getDrawMode(lua_State *L)
 	return 1;
 }
 
+int w_Mesh_setDrawRange(lua_State *L)
+{
+	Mesh *t = luax_checkmesh(L, 1);
+
+	if (lua_isnoneornil(L, 2))
+		t->setDrawRange();
+	else
+	{
+		int rangemin = luaL_checkint(L, 2) - 1;
+		int rangemax = luaL_checkint(L, 3) - 1;
+		EXCEPT_GUARD(t->setDrawRange(rangemin, rangemax);)
+	}
+
+	return 0;
+}
+
+int w_Mesh_getDrawRange(lua_State *L)
+{
+	Mesh *t = luax_checkmesh(L, 1);
+
+	int rangemin = -1;
+	int rangemax = -1;
+	t->getDrawRange(rangemin, rangemax);
+
+	if (rangemin < 0 || rangemax < 0)
+		return 0;
+
+	lua_pushinteger(L, rangemin + 1);
+	lua_pushinteger(L, rangemax + 1);
+	return 2;
+}
+
 int w_Mesh_setVertexColors(lua_State *L)
 {
 	Mesh *t = luax_checkmesh(L, 1);
@@ -338,10 +384,14 @@ 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 },
 	{ "getDrawMode", w_Mesh_getDrawMode },
+	{ "setDrawRange", w_Mesh_setDrawRange },
+	{ "getDrawRange", w_Mesh_getDrawRange },
 	{ "setVertexColors", w_Mesh_setVertexColors },
 	{ "hasVertexColors", w_Mesh_hasVertexColors },
 	{ "setWireframe", w_Mesh_setWireframe },

+ 4 - 0
jni/love/src/modules/graphics/opengl/wrap_Mesh.h

@@ -41,10 +41,14 @@ 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);
 int w_Mesh_getDrawMode(lua_State *L);
+int w_Mesh_setDrawRange(lua_State *L);
+int w_Mesh_getDrawRange(lua_State *L);
 int w_Mesh_setVertexColors(lua_State *L);
 int w_Mesh_hasVertexColors(lua_State *L);
 int w_Mesh_setWireframe(lua_State *L);

+ 21 - 0
jni/love/src/scripts/boot.lua.h

@@ -319,6 +319,27 @@ const unsigned char boot_lua[] =
 	0x76, 0x65, 0x2e, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x28, 0x69, 0x64, 0x2c, 0x78, 
 	0x2c, 0x79, 0x2c, 0x70, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
 	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
+	0x09, 0x09, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x3d, 0x20, 
+	0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x78, 0x2c, 0x79, 0x2c, 0x62, 0x29, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 
+	0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 
+	0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 
+	0x64, 0x28, 0x78, 0x2c, 0x79, 0x2c, 0x62, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
+	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
+	0x09, 0x09, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x20, 0x3d, 
+	0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x78, 0x2c, 0x79, 0x2c, 0x62, 0x29, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x72, 
+	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 
+	0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x72, 0x65, 0x6c, 0x65, 0x61, 
+	0x73, 0x65, 0x64, 0x28, 0x78, 0x2c, 0x79, 0x2c, 0x62, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
+	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
+	0x09, 0x09, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x66, 
+	0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x78, 0x2c, 0x79, 0x2c, 0x62, 0x29, 0x0a,
+	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x6d, 
+	0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 
+	0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x28, 
+	0x78, 0x2c, 0x79, 0x2c, 0x62, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a,
+	0x09, 0x09, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
 	0x09, 0x09, 0x6a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 
 	0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x6a, 0x2c, 0x62, 0x29, 0x0a,
 	0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, 

+ 19 - 2
jni/love/src/scripts/graphics.lua

@@ -1330,7 +1330,15 @@ uniform vec2 love_ScreenParams;]]
 #define VertexColor gl_Color
 
 #define VaryingTexCoord gl_TexCoord[0]
-#define VaryingColor gl_FrontColor]],
+#define VaryingColor gl_FrontColor
+
+#if defined(GL_ARB_draw_instanced)
+	#extension GL_ARB_draw_instanced : enable
+	#define love_InstanceID gl_InstanceIDARB
+#else
+	attribute float love_PseudoInstanceID;
+	int love_InstanceID = int(love_PseudoInstanceID);
+#endif]],
 
 		FOOTER = [[
 void main() {
@@ -1349,7 +1357,16 @@ attribute vec4 VertexTexCoord;
 attribute vec4 VertexColor;
 
 varying vec4 VaryingTexCoord;
-varying lowp vec4 VaryingColor;]],
+varying lowp vec4 VaryingColor;
+
+#if defined(GL_EXT_draw_instanced)
+	#extension GL_EXT_draw_instanced : enable
+	#define love_InstanceID gl_InstanceIDEXT
+#else
+	attribute float love_PseudoInstanceID;
+	int love_InstanceID = int(love_PseudoInstanceID);
+#endif
+]],
 
 		FOOTER = [[
 void main() {

+ 35 - 3
jni/love/src/scripts/graphics.lua.h

@@ -6332,8 +6332,23 @@ const unsigned char graphics_lua[] =
 	0x43, 0x6f, 0x6f, 0x72, 0x64, 0x20, 0x67, 0x6c, 0x5f, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x5b, 
 	0x30, 0x5d, 0x0a,
 	0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6c, 
-	0x6f, 0x72, 0x20, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x6f, 0x6e, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x5d, 0x5d, 
-	0x2c, 0x0a,
+	0x6f, 0x72, 0x20, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x6f, 0x6e, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x0a,
+	0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x47, 0x4c, 0x5f, 0x41, 0x52, 0x42, 
+	0x5f, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x29, 0x0a,
+	0x09, 0x23, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x47, 0x4c, 0x5f, 0x41, 0x52, 0x42, 
+	0x5f, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x20, 0x3a, 0x20, 
+	0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x0a,
+	0x09, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x49, 0x6e, 0x73, 0x74, 
+	0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x20, 0x67, 0x6c, 0x5f, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 
+	0x49, 0x44, 0x41, 0x52, 0x42, 0x0a,
+	0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a,
+	0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6c, 
+	0x6f, 0x76, 0x65, 0x5f, 0x50, 0x73, 0x65, 0x75, 0x64, 0x6f, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 
+	0x49, 0x44, 0x3b, 0x0a,
+	0x09, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 
+	0x49, 0x44, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x74, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x50, 0x73, 0x65, 0x75, 
+	0x64, 0x6f, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x29, 0x3b, 0x0a,
+	0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x5d, 0x5d, 0x2c, 0x0a,
 	0x09, 0x09, 0x46, 0x4f, 0x4f, 0x54, 0x45, 0x52, 0x20, 0x3d, 0x20, 0x5b, 0x5b, 0x0a,
 	0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a,
 	0x09, 0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x20, 0x3d, 
@@ -6359,7 +6374,24 @@ const unsigned char graphics_lua[] =
 	0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x56, 0x61, 0x72, 0x79, 0x69, 
 	0x6e, 0x67, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x3b, 0x0a,
 	0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x6c, 0x6f, 0x77, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 
-	0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x5d, 0x5d, 0x2c, 0x0a,
+	0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0a,
+	0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x47, 0x4c, 0x5f, 0x45, 0x58, 0x54, 
+	0x5f, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x29, 0x0a,
+	0x09, 0x23, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x47, 0x4c, 0x5f, 0x45, 0x58, 0x54, 
+	0x5f, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x20, 0x3a, 0x20, 
+	0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x0a,
+	0x09, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x49, 0x6e, 0x73, 0x74, 
+	0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x20, 0x67, 0x6c, 0x5f, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 
+	0x49, 0x44, 0x45, 0x58, 0x54, 0x0a,
+	0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a,
+	0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6c, 
+	0x6f, 0x76, 0x65, 0x5f, 0x50, 0x73, 0x65, 0x75, 0x64, 0x6f, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 
+	0x49, 0x44, 0x3b, 0x0a,
+	0x09, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 
+	0x49, 0x44, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x74, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x50, 0x73, 0x65, 0x75, 
+	0x64, 0x6f, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x29, 0x3b, 0x0a,
+	0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a,
+	0x5d, 0x5d, 0x2c, 0x0a,
 	0x09, 0x09, 0x46, 0x4f, 0x4f, 0x54, 0x45, 0x52, 0x20, 0x3d, 0x20, 0x5b, 0x5b, 0x0a,
 	0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a,
 	0x09, 0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x20, 0x3d,