Browse Source

Meshes now use vertex buffers for more efficient drawing.

--HG--
branch : Mesh
Alex Szpakowski 12 years ago
parent
commit
b59e3d9c35

+ 129 - 28
src/modules/graphics/opengl/Mesh.cpp

@@ -31,64 +31,131 @@ namespace opengl
 {
 {
 
 
 Mesh::Mesh(const std::vector<Vertex> &verts, Mesh::DrawMode mode)
 Mesh::Mesh(const std::vector<Vertex> &verts, Mesh::DrawMode mode)
-	: draw_mode(mode)
+	: vbo(0)
+	, vertex_count(0)
+	, ibo(0)
+	, element_count(0)
+	, draw_mode(mode)
 	, image(0)
 	, image(0)
+	, colors_enabled(false)
 {
 {
 	setVertices(verts);
 	setVertices(verts);
 }
 }
 
 
+Mesh::~Mesh()
+{
+	delete vbo;
+	delete ibo;
+}
+
 void Mesh::setVertices(const std::vector<Vertex> &verts)
 void Mesh::setVertices(const std::vector<Vertex> &verts)
 {
 {
 	if (verts.size() < 3)
 	if (verts.size() < 3)
 		throw love::Exception("At least 3 vertices are required.");
 		throw love::Exception("At least 3 vertices are required.");
 
 
-	vertices = verts;
-}
+	size_t size = sizeof(Vertex) * verts.size();
 
 
-const std::vector<Vertex> &Mesh::getVertices() const
-{
-	return vertices;
+	if (vbo && size > vbo->getSize())
+	{
+		delete vbo;
+		vbo = 0;
+	}
+
+	if (!vbo)
+	{
+		// Full memory backing because we might access the data at any time.
+		vbo = VertexBuffer::Create(size, GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, VertexBuffer::BACKING_FULL);
+	}
+
+	vertex_count = verts.size();
+
+	VertexBuffer::Bind vbo_bind(*vbo);
+	VertexBuffer::Mapper vbo_mapper(*vbo);
+
+	// Fill the buffer with the vertices.
+	memcpy(vbo_mapper.get(), &verts[0], size);
 }
 }
 
 
-void Mesh::setVertex(size_t i, Vertex v)
+void Mesh::setVertex(size_t index, const Vertex &v)
 {
 {
-	if (i >= vertices.size())
-		throw love::Exception("Invalid index.");
+	if (index >= vertex_count)
+		throw love::Exception("Invalid vertex index: %ld", index);
+
+	VertexBuffer::Bind vbo_bind(*vbo);
 
 
-	vertices[i] = v;
+	// We unmap the vertex buffer in Mesh::draw. This lets us coalesce the
+	// buffer transfer calls.
+	Vertex *vertices = (Vertex *) vbo->map();
+	vertices[index] = v;
 }
 }
 
 
-Vertex Mesh::getVertex(size_t i) const
+Vertex Mesh::getVertex(size_t index) const
 {
 {
-	if (i >= vertices.size())
-		throw love::Exception("Invalid index.");
+	if (index >= vertex_count)
+		throw love::Exception("Invalid vertex index: %ld", index);
 
 
-	return vertices[i];
+	VertexBuffer::Bind vbo_bind(*vbo);
+
+	// We unmap the vertex buffer in Mesh::draw.
+	Vertex *vertices = (Vertex *) vbo->map();
+	return vertices[index];
 }
 }
 
 
 size_t Mesh::getVertexCount() const
 size_t Mesh::getVertexCount() const
 {
 {
-	return vertices.size();
+	return vertex_count;
 }
 }
 
 
 void Mesh::setVertexMap(const std::vector<uint16> &map)
 void Mesh::setVertexMap(const std::vector<uint16> &map)
 {
 {
 	for (size_t i = 0; i < map.size(); i++)
 	for (size_t i = 0; i < map.size(); i++)
 	{
 	{
-		if (map[i] >= vertices.size())
+		if (map[i] >= vertex_count)
 			throw love::Exception("Invalid vertex map value: %d", map[i]);
 			throw love::Exception("Invalid vertex map value: %d", map[i]);
 	}
 	}
 
 
-	vertex_map = map;
+	size_t size = sizeof(uint16) * map.size();
+
+	if (ibo && size > ibo->getSize())
+	{
+		delete ibo;
+		ibo = 0;
+	}
+
+	if (!ibo)
+	{
+		// Full memory backing because we might access the data at any time.
+		ibo = VertexBuffer::Create(size, GL_ELEMENT_ARRAY_BUFFER, GL_DYNAMIC_DRAW, VertexBuffer::BACKING_FULL);
+	}
+
+	element_count = map.size();
+
+	if (element_count > 0)
+	{
+		VertexBuffer::Bind ibo_bind(*ibo);
+		VertexBuffer::Mapper ibo_map(*ibo);
+
+		// Fill the buffer.
+		memcpy(ibo_map.get(), &map[0], size);
+	}
 }
 }
 
 
-const std::vector<uint16> &Mesh::getVertexMap() const
+const uint16 *Mesh::getVertexMap() const
 {
 {
-	return vertex_map;
+	if (ibo && element_count > 0)
+	{
+		VertexBuffer::Bind ibo_bind(*ibo);
+
+		// We unmap the buffer in Mesh::draw and Mesh::setVertexMap.
+		return (uint16 *) ibo->map();
+	}
+
+	return 0;
 }
 }
 
 
-Mesh::~Mesh()
+size_t Mesh::getVertexMapCount() const
 {
 {
+	return element_count;
 }
 }
 
 
 void Mesh::setImage(Image *img)
 void Mesh::setImage(Image *img)
@@ -124,9 +191,23 @@ Mesh::DrawMode Mesh::getDrawMode() const
 	return draw_mode;
 	return draw_mode;
 }
 }
 
 
+void Mesh::setVertexColors(bool enable)
+{
+	colors_enabled = enable;
+}
+
+bool Mesh::hasVertexColors() const
+{
+	return colors_enabled;
+}
+
 void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
 void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
 {
 {
-	if (vertices.size() == 0)
+	const size_t pos_offset   = offsetof(Vertex, x);
+	const size_t tex_offset   = offsetof(Vertex, s);
+	const size_t color_offset = offsetof(Vertex, r);
+
+	if (vertex_count == 0)
 		return;
 		return;
 
 
 	if (image)
 	if (image)
@@ -140,29 +221,49 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 	glPushMatrix();
 	glPushMatrix();
 	glMultMatrixf(m.getElements());
 	glMultMatrixf(m.getElements());
 
 
+	VertexBuffer::Bind vbo_bind(*vbo);
+
+	// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
+	vbo->unmap();
+
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 
 
-	glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &(vertices[0].x));
-	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &(vertices[0].s));
+	glVertexPointer(2, GL_FLOAT, sizeof(Vertex), vbo->getPointer(pos_offset));
+	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), vbo->getPointer(tex_offset));
 
 
+	if (hasVertexColors())
 	{
 	{
+		// Per-vertex colors.
 		glEnableClientState(GL_COLOR_ARRAY);
 		glEnableClientState(GL_COLOR_ARRAY);
-		glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &(vertices[0].r));
+		glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), vbo->getPointer(color_offset));
 	}
 	}
 
 
-	GLenum gl_draw_mode = getGLDrawMode(draw_mode);
+	GLenum mode = getGLDrawMode(draw_mode);
 
 
-	if (vertex_map.size() > 0)
-		glDrawElements(gl_draw_mode, vertex_map.size(), GL_UNSIGNED_SHORT, &vertex_map[0]);
+	if (element_count > 0)
+	{
+		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_SHORT, ibo->getPointer(0));
+	}
 	else
 	else
-		glDrawArrays(gl_draw_mode, 0, vertices.size());
+	{
+		// Normal non-indexed drawing (no custom vertex map.)
+		glDrawArrays(mode, 0, vertex_count);
+	}
 
 
 	glDisableClientState(GL_VERTEX_ARRAY);
 	glDisableClientState(GL_VERTEX_ARRAY);
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 
 
+	if (hasVertexColors())
 	{
 	{
 		glDisableClientState(GL_COLOR_ARRAY);
 		glDisableClientState(GL_COLOR_ARRAY);
+		// Using the color array leaves the GL constant color undefined.
 		gl.setColor(gl.getColor());
 		gl.setColor(gl.getColor());
 	}
 	}
 
 

+ 35 - 14
src/modules/graphics/opengl/Mesh.h

@@ -25,9 +25,9 @@
 #include "common/int.h"
 #include "common/int.h"
 #include "common/math.h"
 #include "common/math.h"
 #include "common/StringMap.h"
 #include "common/StringMap.h"
-#include "graphics/Volatile.h"
 #include "graphics/Drawable.h"
 #include "graphics/Drawable.h"
-#include "graphics/opengl/Image.h"
+#include "Image.h"
+#include "VertexBuffer.h"
 
 
 // C++
 // C++
 #include <vector>
 #include <vector>
@@ -41,7 +41,7 @@ namespace opengl
 
 
 /**
 /**
  * Holds and draws arbitrary vertex geometry.
  * Holds and draws arbitrary vertex geometry.
- * Each vertex in a Mesh has a position, texture coordinate, and color.
+ * Each vertex in the Mesh has a position, texture coordinate, and color.
  **/
  **/
 class Mesh : public Drawable
 class Mesh : public Drawable
 {
 {
@@ -71,18 +71,13 @@ public:
 	 **/
 	 **/
 	void setVertices(const std::vector<Vertex> &verts);
 	void setVertices(const std::vector<Vertex> &verts);
 
 
-	/**
-	 * Gets a reference to the vertices used in the Mesh.
-	 **/
-	const std::vector<Vertex> &getVertices() const;
-
 	/**
 	/**
 	 * Sets an individual vertex in the Mesh.
 	 * Sets an individual vertex in the Mesh.
-	 * @param i The index into the list of vertices to use.
+	 * @param index The index into the list of vertices to use.
 	 * @param v The new vertex.
 	 * @param v The new vertex.
 	 **/
 	 **/
-	void setVertex(size_t i, Vertex v);
-	Vertex getVertex(size_t i) const;
+	void setVertex(size_t index, const Vertex &v);
+	Vertex getVertex(size_t index) const;
 
 
 	/**
 	/**
 	 * Gets the total number of vertices in the Mesh.
 	 * Gets the total number of vertices in the Mesh.
@@ -96,7 +91,18 @@ public:
 	 * {0, 1, 2, 3, 4, ...}
 	 * {0, 1, 2, 3, 4, ...}
 	 **/
 	 **/
 	void setVertexMap(const std::vector<uint16> &map);
 	void setVertexMap(const std::vector<uint16> &map);
-	const std::vector<uint16> &getVertexMap() const;
+
+	/**
+	 * Gets a pointer to the vertex map array. The pointer is only valid until
+	 * the next function call in the graphics module.
+	 * May return null if the vertex map is empty.
+	 **/
+	const uint16 *getVertexMap() const;
+
+	/**
+	 * Gets the total number of elements in the vertex map array.
+	 **/
+	size_t getVertexMapCount() const;
 
 
 	/**
 	/**
 	 * Sets the Image used when drawing the Mesh.
 	 * Sets the Image used when drawing the Mesh.
@@ -120,6 +126,13 @@ public:
 	void setDrawMode(DrawMode mode);
 	void setDrawMode(DrawMode mode);
 	DrawMode getDrawMode() const;
 	DrawMode getDrawMode() 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.
+	 **/
+	void setVertexColors(bool enable);
+	bool hasVertexColors() const;
+
 	// Implements Drawable.
 	// Implements Drawable.
 	void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
 	void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
 
 
@@ -130,13 +143,21 @@ private:
 
 
 	GLenum getGLDrawMode(DrawMode mode) const;
 	GLenum getGLDrawMode(DrawMode mode) const;
 
 
-	std::vector<Vertex> vertices;
-	std::vector<uint16> vertex_map;
+	// Vertex buffer.
+	VertexBuffer *vbo;
+	size_t vertex_count;
+
+	// Element (vertex index) buffer, for the vertex map.
+	VertexBuffer *ibo;
+	size_t element_count;
 
 
 	DrawMode draw_mode;
 	DrawMode draw_mode;
 
 
 	Image *image;
 	Image *image;
 
 
+	// Whether the per-vertex colors are used when drawing.
+	bool colors_enabled;
+
 	static StringMap<DrawMode, DRAW_MODE_MAX_ENUM>::Entry drawModeEntries[];
 	static StringMap<DrawMode, DRAW_MODE_MAX_ENUM>::Entry drawModeEntries[];
 	static StringMap<DrawMode, DRAW_MODE_MAX_ENUM> drawModes;
 	static StringMap<DrawMode, DRAW_MODE_MAX_ENUM> drawModes;
 
 

+ 32 - 21
src/modules/graphics/opengl/VertexBuffer.cpp

@@ -42,25 +42,27 @@ namespace opengl
 
 
 // VertexBuffer
 // VertexBuffer
 
 
-VertexBuffer *VertexBuffer::Create(size_t size, GLenum target, GLenum usage)
+VertexBuffer *VertexBuffer::Create(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
 {
 {
 	try
 	try
 	{
 	{
 		// Try to create a VBO.
 		// Try to create a VBO.
-		return new VBO(size, target, usage);
+		return new VBO(size, target, usage, backing);
 	}
 	}
 	catch(const love::Exception &)
 	catch(const love::Exception &)
 	{
 	{
 		// VBO not supported ... create regular array.
 		// VBO not supported ... create regular array.
-		return new VertexArray(size, target, usage);
+		return new VertexArray(size, target, usage, backing);
 	}
 	}
 }
 }
 
 
-VertexBuffer::VertexBuffer(size_t size, GLenum target, GLenum usage)
+VertexBuffer::VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
 	: is_bound(false)
 	: is_bound(false)
+	, is_mapped(false)
 	, size(size)
 	, size(size)
 	, target(target)
 	, target(target)
 	, usage(usage)
 	, usage(usage)
+	, backing(backing)
 {
 {
 }
 }
 
 
@@ -70,8 +72,8 @@ VertexBuffer::~VertexBuffer()
 
 
 // VertexArray
 // VertexArray
 
 
-VertexArray::VertexArray(size_t size, GLenum target, GLenum usage)
-	: VertexBuffer(size, target, usage)
+VertexArray::VertexArray(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
+	: VertexBuffer(size, target, usage, backing)
 	, buf(new char[size])
 	, buf(new char[size])
 {
 {
 }
 }
@@ -83,11 +85,13 @@ VertexArray::~VertexArray()
 
 
 void *VertexArray::map()
 void *VertexArray::map()
 {
 {
+	is_mapped = true;
 	return buf;
 	return buf;
 }
 }
 
 
 void VertexArray::unmap()
 void VertexArray::unmap()
 {
 {
+	is_mapped = false;
 }
 }
 
 
 void VertexArray::bind()
 void VertexArray::bind()
@@ -112,20 +116,25 @@ const void *VertexArray::getPointer(size_t offset) const
 
 
 // VBO
 // VBO
 
 
-VBO::VBO(size_t size, GLenum target, GLenum usage)
-	: VertexBuffer(size, target, usage)
+VBO::VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
+	: VertexBuffer(size, target, usage, backing)
 	, vbo(0)
 	, vbo(0)
 	, memory_map(0)
 	, memory_map(0)
-	, is_mapped(false)
 	, is_dirty(true)
 	, is_dirty(true)
 {
 {
 	if (!(GLEE_ARB_vertex_buffer_object || GLEE_VERSION_1_5))
 	if (!(GLEE_ARB_vertex_buffer_object || GLEE_VERSION_1_5))
 		throw love::Exception("Not supported");
 		throw love::Exception("Not supported");
 
 
+	if (getMemoryBacking() == BACKING_FULL)
+		memory_map = malloc(getSize());
+
 	bool ok = load(false);
 	bool ok = load(false);
 
 
 	if (!ok)
 	if (!ok)
+	{
+		free(memory_map);
 		throw love::Exception("Could not load VBO.");
 		throw love::Exception("Could not load VBO.");
+	}
 }
 }
 
 
 VBO::~VBO()
 VBO::~VBO()
@@ -150,10 +159,12 @@ void *VBO::map()
 	}
 	}
 
 
 	if (is_dirty)
 	if (is_dirty)
+	{
 		glGetBufferSubDataARB(getTarget(), 0, getSize(), memory_map);
 		glGetBufferSubDataARB(getTarget(), 0, getSize(), memory_map);
+		is_dirty = false;
+	}
 
 
 	is_mapped = true;
 	is_mapped = true;
-	is_dirty = false;
 
 
 	return memory_map;
 	return memory_map;
 }
 }
@@ -198,11 +209,10 @@ void VBO::unbind()
 
 
 void VBO::fill(size_t offset, size_t size, const void *data)
 void VBO::fill(size_t offset, size_t size, const void *data)
 {
 {
-	if (is_mapped)
-	{
+	if (is_mapped || getMemoryBacking() == BACKING_FULL)
 		memcpy(static_cast<char *>(memory_map) + offset, data, size);
 		memcpy(static_cast<char *>(memory_map) + offset, data, size);
-	}
-	else
+
+	if (!is_mapped)
 	{
 	{
 		// Not all systems have access to some faster paths...
 		// Not all systems have access to some faster paths...
 		if (GLEE_APPLE_flush_buffer_range)
 		if (GLEE_APPLE_flush_buffer_range)
@@ -226,7 +236,8 @@ void VBO::fill(size_t offset, size_t size, const void *data)
 			glBufferSubDataARB(getTarget(), offset, size, data);
 			glBufferSubDataARB(getTarget(), offset, size, data);
 		}
 		}
 
 
-		is_dirty = true;
+		if (getMemoryBacking() != BACKING_FULL)
+			is_dirty = true;
 	}
 	}
 }
 }
 
 
@@ -257,23 +268,23 @@ bool VBO::load(bool restore)
 	while (GL_NO_ERROR != glGetError())
 	while (GL_NO_ERROR != glGetError())
 		/* clear error messages */;
 		/* clear error messages */;
 
 
-	// Note that if 'src' is '0', no data will be copied.
-	glBufferDataARB(getTarget(), getSize(), src, getUsage());
-	GLenum err = glGetError();
-
 	// We don't want to flush the entire buffer when we just modify a small
 	// We don't want to flush the entire buffer when we just modify a small
 	// portion of it (VBO::fill without VBO::map), so we'll handle the flushing
 	// portion of it (VBO::fill without VBO::map), so we'll handle the flushing
 	// ourselves when we can.
 	// ourselves when we can.
 	if (GLEE_APPLE_flush_buffer_range)
 	if (GLEE_APPLE_flush_buffer_range)
 		glBufferParameteriAPPLE(getTarget(), GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
 		glBufferParameteriAPPLE(getTarget(), GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
 
 
+	// Note that if 'src' is '0', no data will be copied.
+	glBufferDataARB(getTarget(), getSize(), src, getUsage());
+	GLenum err = glGetError();
+
 	return (GL_NO_ERROR == err);
 	return (GL_NO_ERROR == err);
 }
 }
 
 
 void VBO::unload(bool save)
 void VBO::unload(bool save)
 {
 {
-	// Save data before unloading.
-	if (save)
+	// Save data before unloading, if we need to.
+	if (save && getMemoryBacking() == BACKING_PARTIAL)
 	{
 	{
 		VertexBuffer::Bind bind(*this);
 		VertexBuffer::Bind bind(*this);
 
 

+ 39 - 13
src/modules/graphics/opengl/VertexBuffer.h

@@ -48,6 +48,18 @@ class VertexBuffer
 {
 {
 public:
 public:
 
 
+	// Different guarantees for VertexBuffer data storage.
+	enum MemoryBacking
+	{
+		// The VertexBuffer is will have a valid copy of its data in main memory
+		// at all times.
+		BACKING_FULL,
+
+		// The VertexBuffer will have a valid copy of its data in main memory
+		// when it needs to be reloaded and when it's mapped.
+		BACKING_PARTIAL
+	};
+
 	/**
 	/**
 	 * Create a new VertexBuffer (either a plain vertex array, or a VBO),
 	 * Create a new VertexBuffer (either a plain vertex array, or a VBO),
 	 * based on what's supported on the system.
 	 * based on what's supported on the system.
@@ -58,9 +70,10 @@ public:
 	 * @param size The size of the VertexBuffer (in bytes).
 	 * @param size The size of the VertexBuffer (in bytes).
 	 * @param target GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER.
 	 * @param target GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER.
 	 * @param usage GL_DYNAMIC_DRAW, etc.
 	 * @param usage GL_DYNAMIC_DRAW, etc.
+	 * @param backing Determines what guarantees are placed on the data.
 	 * @return A new VertexBuffer.
 	 * @return A new VertexBuffer.
 	 */
 	 */
-	static VertexBuffer *Create(size_t size, GLenum target, GLenum usage);
+	static VertexBuffer *Create(size_t size, GLenum target, GLenum usage, MemoryBacking backing = BACKING_PARTIAL);
 
 
 	/**
 	/**
 	 * Constructor.
 	 * Constructor.
@@ -68,8 +81,9 @@ public:
 	 * @param size The size of the VertexBuffer in bytes.
 	 * @param size The size of the VertexBuffer in bytes.
 	 * @param target The target VertexBuffer object, e.g. GL_ARRAY_BUFFER.
 	 * @param target The target VertexBuffer object, e.g. GL_ARRAY_BUFFER.
 	 * @param usage Usage hint, e.g. GL_DYNAMIC_DRAW.
 	 * @param usage Usage hint, e.g. GL_DYNAMIC_DRAW.
+	 * @param backing Determines what guarantees are placed on the data.
 	 */
 	 */
-	VertexBuffer(size_t size, GLenum target, GLenum usage);
+	VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacking backing = BACKING_PARTIAL);
 
 
 	/**
 	/**
 	 * Destructor. Does nothing, but must be declared virtual.
 	 * Destructor. Does nothing, but must be declared virtual.
@@ -111,6 +125,16 @@ public:
 		return is_bound;
 		return is_bound;
 	}
 	}
 
 
+	bool isMapped() const
+	{
+		return is_mapped;
+	}
+
+	MemoryBacking getMemoryBacking() const
+	{
+		return backing;
+	}
+
 	/**
 	/**
 	 * Map the VertexBuffer to client memory.
 	 * Map the VertexBuffer to client memory.
 	 *
 	 *
@@ -230,6 +254,9 @@ protected:
 	// Whether the buffer is currently bound.
 	// Whether the buffer is currently bound.
 	bool is_bound;
 	bool is_bound;
 
 
+	// Whether the buffer is currently mapped to main memory.
+	bool is_mapped;
+
 private:
 private:
 
 
 	// The size of the buffer, in bytes.
 	// The size of the buffer, in bytes.
@@ -240,6 +267,9 @@ private:
 
 
 	// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
 	// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
 	GLenum usage;
 	GLenum usage;
+
+	//
+	MemoryBacking backing;
 };
 };
 
 
 /**
 /**
@@ -253,9 +283,9 @@ class VertexArray : public VertexBuffer
 public:
 public:
 
 
 	/**
 	/**
-	 * @copydoc VertexBuffer(int, GLenum, GLenum)
+	 * @copydoc VertexBuffer(int, GLenum, GLenum, Backing)
 	 */
 	 */
-	VertexArray(size_t size, GLenum target, GLenum usage);
+	VertexArray(size_t size, GLenum target, GLenum usage, MemoryBacking backing);
 
 
 	/**
 	/**
 	 * Frees the data we've allocated.
 	 * Frees the data we've allocated.
@@ -281,19 +311,19 @@ private:
  * This will be used on all systems that support it. It's in general
  * This will be used on all systems that support it. It's in general
  * faster than vertex arrays, but especially in use-cases where there
  * faster than vertex arrays, but especially in use-cases where there
  * is no need to update the data every frame.
  * is no need to update the data every frame.
- */
+ **/
 class VBO : public VertexBuffer, public Volatile
 class VBO : public VertexBuffer, public Volatile
 {
 {
 public:
 public:
 
 
 	/**
 	/**
-	 * @copydoc VertexBuffer(size_t, GLenum, GLenum)
-	 */
-	VBO(size_t size, GLenum target, GLenum usage);
+	 * @copydoc VertexBuffer(size_t, GLenum, GLenum, Backing)
+	 **/
+	VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing);
 
 
 	/**
 	/**
 	 * Deletes the VBOs from OpenGL.
 	 * Deletes the VBOs from OpenGL.
-	 */
+	 **/
 	virtual ~VBO();
 	virtual ~VBO();
 
 
 	// Implements VertexBuffer.
 	// Implements VertexBuffer.
@@ -332,10 +362,6 @@ private:
 	// call to map().
 	// call to map().
 	void *memory_map;
 	void *memory_map;
 
 
-	// Set if the vbo currently operates on main instead of gpu
-	// memory.
-	bool is_mapped;
-
 	// Set if the buffer was modified while operating on gpu memory
 	// Set if the buffer was modified while operating on gpu memory
 	// and needs to be synchronized.
 	// and needs to be synchronized.
 	bool is_dirty;
 	bool is_dirty;

+ 8 - 0
src/modules/graphics/opengl/wrap_Graphics.cpp

@@ -453,6 +453,8 @@ int w_newMesh(lua_State *L)
 	std::vector<Vertex> vertices;
 	std::vector<Vertex> vertices;
 	vertices.reserve(vertex_count);
 	vertices.reserve(vertex_count);
 
 
+	bool use_colors = false;
+
 	// Get the vertices from the table.
 	// Get the vertices from the table.
 	for (size_t i = 1; i <= vertex_count; i++)
 	for (size_t i = 1; i <= vertex_count; i++)
 	{
 	{
@@ -477,6 +479,10 @@ int w_newMesh(lua_State *L)
 		v.b = (unsigned char) luaL_optinteger(L, -2, 255);
 		v.b = (unsigned char) luaL_optinteger(L, -2, 255);
 		v.a = (unsigned char) luaL_optinteger(L, -1, 255);
 		v.a = (unsigned char) luaL_optinteger(L, -1, 255);
 
 
+		// 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);
 		lua_pop(L, 9);
 		vertices.push_back(v);
 		vertices.push_back(v);
 	}
 	}
@@ -487,6 +493,8 @@ int w_newMesh(lua_State *L)
 	if (img)
 	if (img)
 		t->setImage(img);
 		t->setImage(img);
 
 
+	t->setVertexColors(use_colors);
+
 	luax_pushtype(L, "Mesh", GRAPHICS_MESH_T, t);
 	luax_pushtype(L, "Mesh", GRAPHICS_MESH_T, t);
 	return 1;
 	return 1;
 }
 }

+ 22 - 47
src/modules/graphics/opengl/wrap_Mesh.cpp

@@ -93,48 +93,6 @@ int w_Mesh_getVertex(lua_State *L)
 	return 8;
 	return 8;
 }
 }
 
 
-int w_Mesh_getVertices(lua_State *L)
-{
-	Mesh *t = luax_checkmesh(L, 1);
-	const std::vector<Vertex> &vertices = t->getVertices();
-
-	lua_createtable(L, vertices.size(), 0);
-
-	for (size_t i = 0; i < vertices.size(); i++)
-	{
-		lua_createtable(L, 8, 0);
-		const Vertex &v = vertices[i];
-
-		lua_pushnumber(L, v.x);
-		lua_rawseti(L, -2, 1);
-
-		lua_pushnumber(L, v.y);
-		lua_rawseti(L, -2, 2);
-
-		lua_pushnumber(L, v.s);
-		lua_rawseti(L, -2, 3);
-
-		lua_pushnumber(L, v.t);
-		lua_rawseti(L, -2, 4);
-
-		lua_pushnumber(L, v.r);
-		lua_rawseti(L, -2, 5);
-
-		lua_pushnumber(L, v.g);
-		lua_rawseti(L, -2, 6);
-
-		lua_pushnumber(L, v.b);
-		lua_rawseti(L, -2, 7);
-
-		lua_pushnumber(L, v.a);
-		lua_rawseti(L, -2, 8);
-
-		lua_rawseti(L, -2, i + 1);
-	}
-
-	return 1;
-}
-
 int w_Mesh_getVertexCount(lua_State *L)
 int w_Mesh_getVertexCount(lua_State *L)
 {
 {
 	Mesh *t = luax_checkmesh(L, 1);
 	Mesh *t = luax_checkmesh(L, 1);
@@ -172,12 +130,14 @@ int w_Mesh_getVertexMap(lua_State *L)
 {
 {
 	Mesh *t = luax_checkmesh(L, 1);
 	Mesh *t = luax_checkmesh(L, 1);
 
 
-	const std::vector<uint16> &map = t->getVertexMap();
-	lua_createtable(L, map.size(), 0);
+	const uint16 *vertex_map = t->getVertexMap();
+	size_t elements = t->getVertexMapCount();
 
 
-	for (size_t i = 0; i < map.size(); i++)
+	lua_createtable(L, elements, 0);
+
+	for (size_t i = 0; i < elements; i++)
 	{
 	{
-		lua_pushinteger(L, map[i] + 1);
+		lua_pushinteger(L, lua_Integer(vertex_map[i]) + 1);
 		lua_rawseti(L, -2, i + 1);
 		lua_rawseti(L, -2, i + 1);
 	}
 	}
 
 
@@ -238,11 +198,24 @@ int w_Mesh_getDrawMode(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
+int w_Mesh_setVertexColors(lua_State *L)
+{
+	Mesh *t = luax_checkmesh(L, 1);
+	t->setVertexColors(luax_toboolean(L, 2));
+	return 0;
+}
+
+int w_Mesh_hasVertexColors(lua_State *L)
+{
+	Mesh *t = luax_checkmesh(L, 1);
+	luax_pushboolean(L, t->hasVertexColors());
+	return 1;
+}
+
 static const luaL_Reg functions[] =
 static const luaL_Reg functions[] =
 {
 {
 	{ "setVertex", w_Mesh_setVertex },
 	{ "setVertex", w_Mesh_setVertex },
 	{ "getVertex", w_Mesh_getVertex },
 	{ "getVertex", w_Mesh_getVertex },
-	{ "getVertices", w_Mesh_getVertices },
 	{ "getVertexCount", w_Mesh_getVertexCount },
 	{ "getVertexCount", w_Mesh_getVertexCount },
 	{ "setVertexMap", w_Mesh_setVertexMap },
 	{ "setVertexMap", w_Mesh_setVertexMap },
 	{ "getVertexMap", w_Mesh_getVertexMap },
 	{ "getVertexMap", w_Mesh_getVertexMap },
@@ -250,6 +223,8 @@ static const luaL_Reg functions[] =
 	{ "getImage", w_Mesh_getImage },
 	{ "getImage", w_Mesh_getImage },
 	{ "setDrawMode", w_Mesh_setDrawMode },
 	{ "setDrawMode", w_Mesh_setDrawMode },
 	{ "getDrawMode", w_Mesh_getDrawMode },
 	{ "getDrawMode", w_Mesh_getDrawMode },
+	{ "setVertexColors", w_Mesh_setVertexColors },
+	{ "hasVertexColors", w_Mesh_hasVertexColors },
 	{ 0, 0 }
 	{ 0, 0 }
 };
 };
 
 

+ 2 - 1
src/modules/graphics/opengl/wrap_Mesh.h

@@ -36,7 +36,6 @@ Mesh *luax_checkmesh(lua_State *L, int idx);
 
 
 int w_Mesh_setVertex(lua_State *L);
 int w_Mesh_setVertex(lua_State *L);
 int w_Mesh_getVertex(lua_State *L);
 int w_Mesh_getVertex(lua_State *L);
-int w_Mesh_getVertices(lua_State *L);
 int w_Mesh_getVertexCount(lua_State *L);
 int w_Mesh_getVertexCount(lua_State *L);
 int w_Mesh_setVertexMap(lua_State *L);
 int w_Mesh_setVertexMap(lua_State *L);
 int w_Mesh_getVertexMap(lua_State *L);
 int w_Mesh_getVertexMap(lua_State *L);
@@ -44,6 +43,8 @@ int w_Mesh_setImage(lua_State *L);
 int w_Mesh_getImage(lua_State *L);
 int w_Mesh_getImage(lua_State *L);
 int w_Mesh_setDrawMode(lua_State *L);
 int w_Mesh_setDrawMode(lua_State *L);
 int w_Mesh_getDrawMode(lua_State *L);
 int w_Mesh_getDrawMode(lua_State *L);
+int w_Mesh_setVertexColors(lua_State *L);
+int w_Mesh_hasVertexColors(lua_State *L);
 
 
 extern "C" int luaopen_mesh(lua_State *L);
 extern "C" int luaopen_mesh(lua_State *L);