Browse Source

love.graphics.newGeometry now takes an optional draw mode argument ("fan", "strip", or "triangles".) "fan" is the default.

This allows for more diverse and complex Geometries, provided the lover understands how to use each mode. See http://escience.anu.edu.au/lecture/cg/surfaceModeling/image/surfaceModeling015.png

Changed love.graphics.newGeometry to optionally accept vertices as individual arguments instead of just a table of vertices (resolves issue #651).
Changed Geometry:setVertex to optionally accept a table containing x,y,u,v,r,g,b,a instead of just individual arguments.

Added Geometry:set/getDrawMode.

Removed the convex-check in love.graphics.newGeometry (all geometry modes have at least some form of concavity support, lovers can do their own check if needed with love.math.isConvex.)

Added an optional "vertex map" table argument to love.graphics.newGeometry (AKA a vertex index array / element array in graphics programming lingo.)
This allows lovers to change the order in which the vertices are used when drawing, or re-use a single vertex multiple times for different parts of the Geometry.
This is especially useful in the "triangles" Geometry draw mode. The vertex map lets you draw very complex (and concave) shapes / meshes without duplicating vertex data.

Added Geometry:set/getVertexMap.
Alex Szpakowski 12 years ago
parent
commit
454ba9d968

+ 83 - 14
src/modules/graphics/Geometry.cpp

@@ -35,18 +35,25 @@ namespace love
 namespace graphics
 {
 
-Geometry::Geometry(const std::vector<vertex> &polygon)
+Geometry::Geometry(const std::vector<vertex> &polygon, const std::vector<uint16> &elements, Geometry::DrawMode mode)
 	: vertexArray(NULL)
+	, vertexCount(polygon.size())
+	, elementArray(NULL)
+	, elementCount(elements.size())
 	, x_min(std::numeric_limits<float>::max())
 	, x_max(std::numeric_limits<float>::min())
 	, y_min(std::numeric_limits<float>::max())
 	, y_max(std::numeric_limits<float>::min())
 	, vertexColors(false)
+	, drawMode(mode)
 {
-	if (!Math::instance.isConvex(polygon))
-		throw love::Exception("Geometry must be convex");
+	for (size_t i = 0; i < elementCount; i++)
+	{
+		// All values in the element array need to be a valid vertex index.
+		if (elements[i] >= vertexCount)
+			throw love::Exception("Invalid vertex map value");
+	}
 
-	vertexCount = polygon.size();
 	vertexArray = new vertex[vertexCount];
 	for (size_t i = 0; i < vertexCount; ++i)
 	{
@@ -57,16 +64,25 @@ Geometry::Geometry(const std::vector<vertex> &polygon)
 		y_min = v.y < y_min ? v.y : y_min;
 		y_max = v.y > y_max ? v.y : y_max;
 	}
+
+	if (elementCount > 0)
+	{
+		elementArray = new uint16[elementCount];
+		memcpy(elementArray, &elements[0], elementCount * sizeof(uint16));
+	}
 }
 
 Geometry::Geometry(float x, float y, float w, float h, float sw, float sh)
 	: vertexArray(NULL)
 	, vertexCount(4)
+	, elementArray(NULL)
+	, elementCount(0)
 	, x_min(x)
 	, x_max(x+w)
 	, y_min(y)
 	, y_max(y+h)
 	, vertexColors(false)
+	, drawMode(DRAW_MODE_FAN)
 {
 	vertexArray = new vertex[4];
 	float s0 = x/sw, s1 = (x+w)/sw, t0 = y/sh, t1 = (y+h)/sh;
@@ -78,14 +94,22 @@ Geometry::Geometry(float x, float y, float w, float h, float sw, float sh)
 
 Geometry::Geometry(const Geometry &other)
 	: vertexCount(other.vertexCount)
+	, elementCount(other.elementCount)
 	, x_min(other.x_min)
 	, x_max(other.x_max)
 	, y_min(other.y_min)
 	, y_max(other.y_max)
 	, vertexColors(other.vertexColors)
+	, drawMode(other.drawMode)
 {
 	vertexArray = new vertex[vertexCount];
 	memcpy(vertexArray, other.vertexArray, vertexCount * sizeof(vertex));
+
+	if (elementCount > 0)
+	{
+		elementArray = new uint16[elementCount];
+		memcpy(elementArray, other.elementArray, elementCount * sizeof(uint16));
+	}
 }
 
 Geometry &Geometry::operator=(const Geometry &other)
@@ -94,25 +118,25 @@ Geometry &Geometry::operator=(const Geometry &other)
 	{
 		Geometry temp(other);
 		std::swap(vertexArray, temp.vertexArray);
+		std::swap(elementArray, temp.elementArray);
 
-		vertexCount = temp.vertexCount;
-		x_min       = temp.x_min;
-		x_max       = temp.x_max;
-		y_min       = temp.y_min;
-		y_max       = temp.y_max;
+		vertexCount  = temp.vertexCount;
+		elementCount = temp.elementCount;
+		x_min        = temp.x_min;
+		x_max        = temp.x_max;
+		y_min        = temp.y_min;
+		y_max        = temp.y_max;
 
 		vertexColors = other.vertexColors;
+		drawMode     = other.drawMode;
 	}
 	return *this;
 }
 
 Geometry::~Geometry()
 {
-	if (vertexArray)
-	{
-		delete[] vertexArray;
-		vertexArray = NULL;
-	}
+	delete[] vertexArray;
+	delete[] elementArray;
 }
 
 const vertex &Geometry::getVertex(size_t i) const
@@ -148,10 +172,55 @@ void Geometry::flip(bool x, bool y)
 	}
 }
 
+void Geometry::setElementArray(const uint16 *elements, size_t count)
+{
+	if (count == 0 || elements == NULL)
+	{
+		delete[] elementArray;
+		elementArray = NULL;
+		elementCount = 0;
+		return;
+	}
+
+	for (size_t i = 0; i < count; i++)
+	{
+		if (elements[i] >= vertexCount)
+			throw love::Exception("Invalid vertex map value");
+	}
+
+	if (count > elementCount)
+	{
+		delete[] elementArray;
+		elementArray = new uint16[count];
+	}
+
+	elementCount = count;
+	memcpy(elementArray, elements, elementCount * sizeof(uint16));
+}
+
 void Geometry::setVertexColors(bool on)
 {
 	vertexColors = on;
 }
 
+bool Geometry::getConstant(const char *in, Geometry::DrawMode &out)
+{
+	return drawModes.find(in, out);
+}
+
+bool Geometry::getConstant(Geometry::DrawMode in, const char *&out)
+{
+	return drawModes.find(in, out);
+}
+
+StringMap<Geometry::DrawMode, Geometry::DRAW_MODE_MAX_ENUM>::Entry Geometry::drawModeEntries[] =
+{
+	{"fan", Geometry::DRAW_MODE_FAN},
+	{"strip", Geometry::DRAW_MODE_STRIP},
+	{"triangles", Geometry::DRAW_MODE_TRIANGLES}
+};
+
+StringMap<Geometry::DrawMode, Geometry::DRAW_MODE_MAX_ENUM> Geometry::drawModes(Geometry::drawModeEntries, sizeof(Geometry::drawModeEntries));
+
 } // graphics
 } // love

+ 50 - 6
src/modules/graphics/Geometry.h

@@ -24,8 +24,10 @@
 // LOVE
 #include "common/Object.h"
 #include "common/math.h"
+#include "common/StringMap.h"
+#include "common/int.h"
 
-// std
+// stdlib
 #include <vector>
 
 namespace love
@@ -36,11 +38,21 @@ namespace graphics
 class Geometry : public Object
 {
 public:
+
+	// How the Geometry's vertices are used when drawing.
+	// http://escience.anu.edu.au/lecture/cg/surfaceModeling/image/surfaceModeling015.png
+	enum DrawMode
+	{
+		DRAW_MODE_FAN,
+		DRAW_MODE_STRIP,
+		DRAW_MODE_TRIANGLES,
+		DRAW_MODE_MAX_ENUM
+	};
+
 	/**
 	 * Creates a new geometry object from a std::vector<vertex>.
-	 * @param v
 	 **/
-	Geometry(const std::vector<vertex> &polygon);
+	Geometry(const std::vector<vertex> &polygon, const std::vector<uint16> &elements, DrawMode mode = DRAW_MODE_FAN);
 
 	/**
 	 * Creates a new geometry from (texture) quad information.
@@ -65,7 +77,7 @@ public:
 	/**
 	 * Returns a pointer to the vertex array.
 	 **/
-	const vertex *getVertexArray() const
+	inline const vertex *getVertexArray() const
 	{
 		return vertexArray;
 	}
@@ -73,11 +85,23 @@ public:
 	/**
 	 * Returns the size of the vertex array.
 	 **/
-	size_t getVertexCount() const
+	inline size_t getVertexCount() const
 	{
 		return vertexCount;
 	}
 
+	inline const uint16 *getElementArray() const
+	{
+		return elementArray;
+	}
+
+	inline size_t getElementCount() const
+	{
+		return elementCount;
+	}
+
+	void setElementArray(const uint16 *elements, size_t count);
+
 	/**
 	 * Sets whether this Geometry will use custom per-vertex colors.
 	 **/
@@ -86,15 +110,30 @@ public:
 	/**
 	 * Returns whether this Geometry is using custom per-vertex colors.
 	 **/
-	bool hasVertexColors() const
+	inline bool hasVertexColors() const
 	{
 		return vertexColors;
 	};
 
+	/**
+	 * Returns the mode used when drawing this Geometry.
+	 **/
+	inline DrawMode getDrawMode() const
+	{
+		return drawMode;
+	}
+
+	static bool getConstant(const char *in, DrawMode &out);
+	static bool getConstant(DrawMode in, const char *&out);
+
 private:
+
 	vertex *vertexArray;
 	size_t vertexCount;
 
+	uint16 *elementArray;
+	size_t elementCount;
+
 	float x_min;
 	float x_max;
 
@@ -102,6 +141,11 @@ private:
 	float y_max;
 
 	bool vertexColors;
+
+	DrawMode drawMode;
+
+	static StringMap<DrawMode, DRAW_MODE_MAX_ENUM>::Entry drawModeEntries[];
+	static StringMap<DrawMode, DRAW_MODE_MAX_ENUM> drawModes;
 };
 
 } // graphics

+ 22 - 3
src/modules/graphics/opengl/Canvas.cpp

@@ -626,7 +626,22 @@ void Canvas::drawg(love::graphics::Geometry *geom, float x, float y, float angle
 		glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex), (GLvoid *)&v->r);
 	}
 
-	drawv(t, v, vcount, GL_TRIANGLE_FAN);
+	GLenum glmode;
+	switch (geom->getDrawMode())
+	{
+	case Geometry::DRAW_MODE_FAN:
+	default:
+		glmode = GL_TRIANGLE_FAN;
+		break;
+	case Geometry::DRAW_MODE_STRIP:
+		glmode = GL_TRIANGLE_STRIP;
+		break;
+	case Geometry::DRAW_MODE_TRIANGLES:
+		glmode = GL_TRIANGLES;
+		break;
+	}
+
+	drawv(t, v, vcount, glmode, geom->getElementArray(), geom->getElementCount());
 
 	if (geom->hasVertexColors())
 	{
@@ -763,7 +778,7 @@ int Canvas::getHeight()
 	return height;
 }
 
-void Canvas::drawv(const Matrix &t, const vertex *v, GLsizei count, GLenum mode) const
+void Canvas::drawv(const Matrix &t, const vertex *v, GLsizei count, GLenum mode, const uint16 *e, GLsizei ecount) const
 {
 	glPushMatrix();
 
@@ -780,7 +795,11 @@ void Canvas::drawv(const Matrix &t, const vertex *v, GLsizei count, GLenum mode)
 	//      glDrawArrays(), drawg() needs to be updated accordingly!
 	glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&v[0].x);
 	glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&v[0].s);
-	glDrawArrays(mode, 0, count);
+
+	if (e != 0 && ecount > 0)
+		glDrawElements(mode, ecount, GL_UNSIGNED_SHORT, (GLvoid *) e);
+	else
+		glDrawArrays(mode, 0, count);
 
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_VERTEX_ARRAY);

+ 1 - 1
src/modules/graphics/opengl/Canvas.h

@@ -142,7 +142,7 @@ private:
 	std::vector<Canvas *> attachedCanvases;
 
 	void setupGrab();
-	void drawv(const Matrix &t, const vertex *v, GLsizei count = 4, GLenum mode = GL_QUADS) const;
+	void drawv(const Matrix &t, const vertex *v, GLsizei count = 4, GLenum mode = GL_QUADS, const uint16 *e = 0, GLsizei ecount = 0) const;
 
 	static StringMap<TextureType, TYPE_MAX_ENUM>::Entry textureTypeEntries[];
 	static StringMap<TextureType, TYPE_MAX_ENUM> textureTypes;

+ 2 - 2
src/modules/graphics/opengl/Graphics.cpp

@@ -440,9 +440,9 @@ Image *Graphics::newImage(love::image::CompressedData *cdata)
 	return image;
 }
 
-Geometry *Graphics::newGeometry(const std::vector<vertex> &vertices)
+Geometry *Graphics::newGeometry(const std::vector<vertex> &vertices, const std::vector<uint16> &vertexmap, Geometry::DrawMode mode)
 {
-	return new Geometry(vertices);
+	return new Geometry(vertices, vertexmap, mode);
 }
 
 Geometry *Graphics::newQuad(float x, float y, float w, float h, float sw, float sh)

+ 1 - 1
src/modules/graphics/opengl/Graphics.h

@@ -225,7 +225,7 @@ public:
 	/**
 	 * Creates a Geometry object.
 	 **/
-	Geometry *newGeometry(const std::vector<vertex> &vertices);
+	Geometry *newGeometry(const std::vector<vertex> &vertices, const std::vector<uint16> &vertexmap, Geometry::DrawMode mode);
 
 	/**
 	 * Creates a quadliteral Geometry object.

+ 22 - 3
src/modules/graphics/opengl/Image.cpp

@@ -120,7 +120,22 @@ void Image::drawg(love::graphics::Geometry *geom, float x, float y, float angle,
 		glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex), (GLvoid *) &v[0].r);
 	}
 
-	drawv(t, v, geom->getVertexCount(), GL_TRIANGLE_FAN);
+	GLenum glmode;
+	switch (geom->getDrawMode())
+	{
+	case Geometry::DRAW_MODE_FAN:
+	default:
+		glmode = GL_TRIANGLE_FAN;
+		break;
+	case Geometry::DRAW_MODE_STRIP:
+		glmode = GL_TRIANGLE_STRIP;
+		break;
+	case Geometry::DRAW_MODE_TRIANGLES:
+		glmode = GL_TRIANGLES;
+		break;
+	}
+
+	drawv(t, v, geom->getVertexCount(), glmode, geom->getElementArray(), geom->getElementCount());
 
 	if (geom->hasVertexColors())
 	{
@@ -536,7 +551,7 @@ bool Image::refresh()
 	return true;
 }
 
-void Image::drawv(const Matrix &t, const vertex *v, GLsizei count, GLenum mode) const
+void Image::drawv(const Matrix &t, const vertex *v, GLsizei count, GLenum mode, const uint16 *e, GLsizei ecount) const
 {
 	bind();
 
@@ -553,7 +568,11 @@ void Image::drawv(const Matrix &t, const vertex *v, GLsizei count, GLenum mode)
 	//      glDrawArrays(), drawg() needs to be updated accordingly!
 	glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&v[0].x);
 	glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&v[0].s);
-	glDrawArrays(mode, 0, count);
+
+	if (e != 0 && ecount > 0)
+		glDrawElements(mode, ecount, GL_UNSIGNED_SHORT, (GLvoid *) e);
+	else
+		glDrawArrays(mode, 0, count);
 
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_VERTEX_ARRAY);

+ 1 - 1
src/modules/graphics/opengl/Image.h

@@ -138,7 +138,7 @@ public:
 
 private:
 
-	void drawv(const Matrix &t, const vertex *v, GLsizei count = 4, GLenum mode = GL_QUADS) const;
+	void drawv(const Matrix &t, const vertex *v, GLsizei count = 4, GLenum mode = GL_QUADS, const uint16 *e = 0, GLsizei ecount = 0) const;
 
 	friend class Shader;
 	GLuint getTextureName() const

+ 18 - 4
src/modules/graphics/opengl/SpriteBatch.cpp

@@ -133,17 +133,28 @@ int SpriteBatch::addg(Geometry *geom, float x, float y, float a, float sx, float
 	if (vertexcount > 4)
 		throw love::Exception("Cannot add Geometries with more than 4 vertices to SpriteBatch");
 
-	// If the Geometry has 3 vertices, then 2 triangles will be rendered in the
+	// Which vertices to add to the SpriteBatch.
+	size_t vertex_indices[4] = {0, 1, 2, 3};
+
+	if (geom->getDrawMode() == Geometry::DRAW_MODE_STRIP)
+	{
+		// We have to do some vertex reordering shenanigans to get 4-vertex
+		// triangle strip Geometries to render properly.
+		std::swap(vertex_indices[0], vertex_indices[1]);
+	}
+
+	// If the Geometry has 3 vertices, then 2 triangles will be added to the
 	// SpriteBatch: 0-1-2 and 0-2-0. 0-2-0 will get ignored during rasterization.
+	for (size_t i = geom->getVertexCount(); i < 4; i++)
+		vertex_indices[i] = vertex_indices[0];
+
 	for (size_t i = 0; i < 4; i++)
-		sprite[i] = geom->getVertex(i % vertexcount);
+		sprite[i] = geom->getVertex(vertex_indices[i]);
 
-	// Transform.
 	static Matrix t;
 	t.setTransformation(x, y, a, sx, sy, ox, oy, kx, ky);
 	t.transform(sprite, sprite, 4);
 
-	// Set vertex colors to the constant color, if Geometry has no custom colors.
 	if (color && !geom->hasVertexColors())
 		setColorv(sprite, *color);
 
@@ -279,6 +290,9 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
 	const int vertex_offset = sizeof(unsigned char) * 4;
 	const int texel_offset = sizeof(unsigned char) * 4 + sizeof(float) * 2;
 
+	if (next == 0)
+		return;
+
 	static Matrix t;
 
 	glPushMatrix();

+ 108 - 10
src/modules/graphics/opengl/wrap_Geometry.cpp

@@ -34,7 +34,6 @@ Geometry *luax_checkgeometry(lua_State *L, int idx)
 	return luax_checktype<Geometry>(L, idx, "Geometry", GRAPHICS_GEOMETRY_T);
 }
 
-// different name than in Geometry.cpp to make the triangulation transparent
 int w_Geometry_getVertexCount(lua_State *L)
 {
 	Geometry *geom = luax_checkgeometry(L, 1);
@@ -69,17 +68,37 @@ int w_Geometry_getVertex(lua_State *L)
 int w_Geometry_setVertex(lua_State *L)
 {
 	Geometry *geom = luax_checkgeometry(L, 1);
-	size_t i = size_t(luaL_checkint(L, 2));
+	size_t i = size_t(luaL_checkinteger(L, 2));
 
 	vertex v;
-	v.x = luaL_checknumber(L, 3);
-	v.y = luaL_checknumber(L, 4);
-	v.s = luaL_checknumber(L, 5);
-	v.t = luaL_checknumber(L, 6);
-	v.r = luaL_optint(L,  7, 255);
-	v.g = luaL_optint(L,  8, 255);
-	v.b = luaL_optint(L,  9, 255);
-	v.a = luaL_optint(L, 10, 255);
+
+	if (lua_istable(L, 3))
+	{
+		for (int i = 1; i <= 8; i++)
+			lua_rawgeti(L, 3, i);
+
+		v.x = luaL_checknumber(L, -8);
+		v.y = luaL_checknumber(L, -7);
+		v.s = luaL_checknumber(L, -6);
+		v.t = luaL_checknumber(L, -5);
+		v.r = luaL_optinteger(L, -4, 255);
+		v.g = luaL_optinteger(L, -3, 255);
+		v.b = luaL_optinteger(L, -2, 255);
+		v.a = luaL_optinteger(L, -1, 255);
+
+		lua_pop(L, 8);
+	}
+	else
+	{
+		v.x = luaL_checknumber(L, 3);
+		v.y = luaL_checknumber(L, 4);
+		v.s = luaL_checknumber(L, 5);
+		v.t = luaL_checknumber(L, 6);
+		v.r = luaL_optinteger(L,  7, 255);
+		v.g = luaL_optinteger(L,  8, 255);
+		v.b = luaL_optinteger(L,  9, 255);
+		v.a = luaL_optinteger(L, 10, 255);
+	}
 
 	try
 	{
@@ -117,6 +136,82 @@ int w_Geometry_hasVertexColors(lua_State *L)
 	return 1;
 }
 
+int w_Geometry_getDrawMode(lua_State *L)
+{
+	Geometry *geom = luax_checkgeometry(L, 1);
+
+	Geometry::DrawMode mode = geom->getDrawMode();
+	const char *str;
+
+	if (!Geometry::getConstant(mode, str))
+		return luaL_error(L, "Unknown Geometry draw mode");
+
+	lua_pushstring(L, str);
+
+	return 1;
+}
+
+int w_Geometry_getVertexMap(lua_State *L)
+{
+	Geometry *g = luax_checkgeometry(L, 1);
+
+	size_t elementcount = g->getElementCount();
+	const uint16 *elements = g->getElementArray();
+
+	if (elementcount == 0 || elements == 0)
+		return 0;
+
+	lua_createtable(L, elementcount, 0);
+	for (size_t i = 0; i < elementcount; i++)
+	{
+		lua_pushinteger(L, elements[i]);
+		lua_rawseti(L, -2, i + 1);
+	}
+
+	return 1;
+}
+
+int w_Geometry_setVertexMap(lua_State *L)
+{
+	Geometry *g = luax_checkgeometry(L, 1);
+
+	for (int i = lua_gettop(L); i >= 2; i--)
+	{
+		if (lua_isnil(L, i))
+			lua_pop(L, 1);
+		else
+			break;
+	}
+
+	bool is_table = lua_istable(L, 2);
+	int nargs = is_table ? lua_objlen(L, 2) : lua_gettop(L) - 1;
+
+	std::vector<uint16> vertexmap;
+	vertexmap.reserve(nargs);
+
+	for (int i = 0; i < nargs; i++)
+	{
+		if (is_table)
+		{
+			lua_rawgeti(L, 2, i + 1);
+			vertexmap.push_back(luaL_checkinteger(L, -1) - 1);
+			lua_pop(L, 1);
+		}
+		else
+			vertexmap.push_back(luaL_checkinteger(L, i + 2) - 1);
+	}
+
+	try
+	{
+		g->setElementArray(&vertexmap[0], vertexmap.size());
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
+	return 0;
+}
+
 static const luaL_Reg w_Geometry_functions[] =
 {
 	{ "getVertexCount", w_Geometry_getVertexCount },
@@ -125,6 +220,9 @@ static const luaL_Reg w_Geometry_functions[] =
 	{ "flip", w_Geometry_flip },
 	{ "setVertexColors", w_Geometry_setVertexColors },
 	{ "hasVertexColors", w_Geometry_hasVertexColors },
+	{ "getDrawMode", w_Geometry_getDrawMode },
+	{ "getVertexMap", w_Geometry_getVertexMap },
+	{ "setVertexMap", w_Geometry_setVertexMap },
 	{ 0, 0 }
 };
 

+ 3 - 0
src/modules/graphics/opengl/wrap_Geometry.h

@@ -39,6 +39,9 @@ int w_Geometry_setVertex(lua_State *L);
 int w_Geometry_flip(lua_State *L);
 int w_Geometry_setVertexColors(lua_State *L);
 int w_Geometry_hasVertexColors(lua_State *L);
+int w_Geometry_getDrawMode(lua_State *L);
+int w_Geometry_getVertexMap(lua_State *L);
+int w_Geometry_setVertexMap(lua_State *L);
 extern "C" int luaopen_geometry(lua_State *L);
 
 } // opengl

+ 94 - 15
src/modules/graphics/opengl/wrap_Graphics.cpp

@@ -234,22 +234,99 @@ int w_newImage(lua_State *L)
 
 int w_newGeometry(lua_State *L)
 {
-	std::vector<vertex> vertices;
 	luaL_checktype(L, 1, LUA_TTABLE);
 
-	size_t n = lua_objlen(L, 1);
-	if (n < 3)
-		return luaL_error(L, "Need at least three points to construct a geometry.");
+	// Determine whether a table of vertices is being given. We assume it is if
+	// type(args[1][1]) == "table", otherwise assume the args are the verts.
+	lua_rawgeti(L, 1, 1);
+	bool is_table = lua_istable(L, -1);
+	lua_pop(L, 1);
+
+	// Get rid of any trailing nils in the arg list (it messes with our style.)
+	for (int i = lua_gettop(L); i >= 2; i--)
+	{
+		if (lua_isnil(L, i))
+			lua_pop(L, 1);
+		else
+			break;
+	}
+
+	Geometry::DrawMode mode = Geometry::DRAW_MODE_FAN;
+	std::string txt;
+	bool has_vertexmap = false;
+
+	// The last argument (or second-last) may be an optional draw mode.
+	if (lua_type(L, -1) == LUA_TSTRING)
+	{
+		txt = luaL_checkstring(L, -1);
+		lua_remove(L, -1);
+	}
+	else if (lua_type(L, -2) == LUA_TSTRING && lua_istable(L, -1))
+	{
+		txt = luaL_checkstring(L, -2);
+		lua_remove(L, -2);
+
+		// If the draw mode is the second-last argument, the last argument will
+		// be the vertex map.
+		has_vertexmap = true;
+	}
+
+	if (txt.length() > 0 && !Geometry::getConstant(txt.c_str(), mode))
+		return luaL_error(L, "Invalid Geometry draw mode: %s", txt.c_str());
+
+	std::vector<uint16> vertexmap;
+
+	// Get the vertex map table, if it exists.
+	if (has_vertexmap)
+	{
+		// It will always be the last argument.
+		int tableidx = lua_gettop(L);
+
+		size_t elementcount = lua_objlen(L, -1);
+		vertexmap.reserve(elementcount);
+
+		for (size_t i = 0; i < elementcount; i++)
+		{
+			lua_rawgeti(L, tableidx, i + 1);
+			if (!lua_isnumber(L, -1))
+				return luaL_argerror(L, tableidx + 1, "vertex index expected");
+
+			vertexmap.push_back(lua_tointeger(L, -1) - 1);
+			lua_pop(L, 1);
+		}
+
+		// We don't want to read the vertex map as a vertex table later.
+		lua_remove(L, -1);
+	}
+
+	size_t vertexcount = is_table ? lua_objlen(L, 1) : lua_gettop(L);
+	if (vertexcount < 3)
+		return luaL_error(L, "At least three points are needed to construct a Geometry.");
+
+	if (!lua_checkstack(L, 9))
+		return luaL_error(L, "Too many arguments!");
 
 	bool hasvertexcolors = false;
 
-	vertices.reserve(n);
-	for (size_t i = 0; i < n; ++i)
+	std::vector<vertex> vertices;
+	vertices.reserve(vertexcount);
+
+	for (size_t i = 0; i < vertexcount; ++i)
 	{
 		vertex v;
-		lua_rawgeti(L, 1, i+1);
-		if (!lua_istable(L, -1))
-			return luax_typerror(L, 1, "table of tables");
+
+		if (is_table)
+		{
+			lua_rawgeti(L, 1, i+1);
+			if (!lua_istable(L, -1))
+				return luaL_typerror(L, 1, "table of tables");
+		}
+		else
+		{
+			// Push the vertex table at this arg index to the top of the stack.
+			luaL_checktype(L, i + 1, LUA_TTABLE);
+			lua_pushvalue(L, i + 1);
+		}
 
 		for (int j = 1; j <= 8; j++)
 			lua_rawgeti(L, -j, j);
@@ -260,13 +337,15 @@ int w_newGeometry(lua_State *L)
 		v.s = luaL_checknumber(L, -6);
 		v.t = luaL_checknumber(L, -5);
 
-		v.r = luaL_optint(L, -4, 255);
-		v.g = luaL_optint(L, -3, 255);
-		v.b = luaL_optint(L, -2, 255);
-		v.a = luaL_optint(L, -1, 255);
+		v.r = luaL_optinteger(L, -4, 255);
+		v.g = luaL_optinteger(L, -3, 255);
+		v.b = luaL_optinteger(L, -2, 255);
+		v.a = luaL_optinteger(L, -1, 255);
 
 		lua_pop(L, 9);
 
+		// Custom vertex colors are disabled by default unless a unique color
+		// is used for at least one vertex.
 		if (v.r != 255 || v.g != 255 || v.b != 255 || v.a != 255)
 			hasvertexcolors = true;
 
@@ -276,7 +355,7 @@ int w_newGeometry(lua_State *L)
 	Geometry *geom = 0;
 	try
 	{
-		geom = instance->newGeometry(vertices);
+		geom = instance->newGeometry(vertices, vertexmap, mode);
 	}
 	catch (love::Exception &e)
 	{
@@ -288,7 +367,7 @@ int w_newGeometry(lua_State *L)
 
 	geom->setVertexColors(hasvertexcolors);
 
-	luax_newtype(L, "Geometry", GRAPHICS_GEOMETRY_T, (void *)geom);
+	luax_newtype(L, "Geometry", GRAPHICS_GEOMETRY_T, (void *) geom);
 	return 1;
 }