Browse Source

Meshes will now use the smallest integer data type possible for index buffer (vertex map) values, instead of always using 32-bit integer numbers.

Alex Szpakowski 11 years ago
parent
commit
6259e38d84

+ 7 - 0
src/modules/graphics/opengl/Graphics.cpp

@@ -53,6 +53,7 @@ Graphics::Graphics()
 	, width(0)
 	, height(0)
 	, created(false)
+	, activeStencil(false)
 	, savedState()
 {
 	currentWindow = love::window::sdl::Window::createSingleton();
@@ -383,6 +384,8 @@ void Graphics::defineStencil()
 	glEnable(GL_STENCIL_TEST);
 	glStencilFunc(GL_ALWAYS, 1, 1);
 	glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+
+	activeStencil = true;
 }
 
 void Graphics::useStencil(bool invert)
@@ -394,8 +397,12 @@ void Graphics::useStencil(bool invert)
 
 void Graphics::discardStencil()
 {
+	if (!activeStencil)
+		return;
+
 	setColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]);
 	glDisable(GL_STENCIL_TEST);
+	activeStencil = false;
 }
 
 Image *Graphics::newImage(love::image::ImageData *data, Texture::Format format)

+ 2 - 0
src/modules/graphics/opengl/Graphics.h

@@ -482,6 +482,8 @@ private:
 	int height;
 	bool created;
 
+	bool activeStencil;
+
 	DisplayState savedState;
 
 }; // Graphics

+ 101 - 21
src/modules/graphics/opengl/Mesh.cpp

@@ -38,6 +38,7 @@ Mesh::Mesh(const std::vector<Vertex> &verts, Mesh::DrawMode mode)
 	, vertex_count(0)
 	, ibo(nullptr)
 	, element_count(0)
+	, element_data_type(getGLDataTypeFromMax(verts.size()))
 	, instance_count(1)
 	, draw_mode(mode)
 	, range_min(-1)
@@ -53,6 +54,7 @@ Mesh::Mesh(int vertexcount, Mesh::DrawMode mode)
 	, vertex_count(0)
 	, ibo(nullptr)
 	, element_count(0)
+	, element_data_type(getGLDataTypeFromMax(vertexcount))
 	, draw_mode(mode)
 	, range_min(-1)
 	, range_max(-1)
@@ -151,15 +153,42 @@ size_t Mesh::getVertexCount() const
 	return vertex_count;
 }
 
-void Mesh::setVertexMap(const std::vector<uint32> &map)
+/**
+ * Copies index data from a vector to a mapped index buffer.
+ **/
+template <typename T>
+static void copyToIndexBuffer(const std::vector<uint32> &indices, VertexBuffer::Mapper &buffermap, size_t maxval)
 {
-	for (size_t i = 0; i < map.size(); i++)
+	T *elems = (T *) buffermap.get();
+
+	for (size_t i = 0; i < indices.size(); i++)
 	{
-		if (map[i] >= vertex_count)
-			throw love::Exception("Invalid vertex map value: %d", map[i] + 1);
+		if (indices[i] >= maxval)
+			throw love::Exception("Invalid vertex map value: %d", indices[i] + 1);
+
+		elems[i] = (T) indices[i];
 	}
+}
 
-	size_t size = sizeof(uint32) * map.size();
+void Mesh::setVertexMap(const std::vector<uint32> &map)
+{
+	GLenum datatype = getGLDataTypeFromMax(vertex_count);
+
+	// Calculate the size in bytes of the index buffer data.
+	size_t size = map.size();
+	switch (datatype)
+	{
+	case GL_UNSIGNED_BYTE:
+		size *= sizeof(uint8);
+		break;
+	case GL_UNSIGNED_SHORT:
+		size *= sizeof(uint16);
+		break;
+	case GL_UNSIGNED_INT:
+	default:
+		size *= sizeof(uint32);
+		break;
+	}
 
 	if (ibo && size > ibo->getSize())
 	{
@@ -175,27 +204,68 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
 
 	element_count = map.size();
 
-	if (ibo && element_count > 0)
-	{
-		VertexBuffer::Bind ibo_bind(*ibo);
-		VertexBuffer::Mapper ibo_map(*ibo);
+	if (!ibo || element_count == 0)
+		return;
 
-		// Fill the buffer.
-		memcpy(ibo_map.get(), &map[0], size);
+	VertexBuffer::Bind ibo_bind(*ibo);
+	VertexBuffer::Mapper ibo_map(*ibo);
+
+	// Fill the buffer with the index values from the vector.
+	switch (datatype)
+	{
+	case GL_UNSIGNED_BYTE:
+		copyToIndexBuffer<uint8>(map, ibo_map, vertex_count);
+		break;
+	case GL_UNSIGNED_SHORT:
+		copyToIndexBuffer<uint16>(map, ibo_map, vertex_count);
+		break;
+	case GL_UNSIGNED_INT:
+	default:
+		copyToIndexBuffer<uint32>(map, ibo_map, vertex_count);
+		break;
 	}
+
+	element_data_type = datatype;
 }
 
-const uint32 *Mesh::getVertexMap() const
+/**
+ * Copies index data from a mapped buffer to a vector.
+ **/
+template <typename T>
+static void copyFromIndexBuffer(void *buffer, std::vector<uint32> &indices, size_t maxval)
 {
-	if (ibo && element_count > 0)
-	{
-		VertexBuffer::Bind ibo_bind(*ibo);
+	T *elems = (T *) buffer;
+	for (size_t i = 0; i < maxval; i++)
+		indices.push_back((uint32) elems[i]);
+}
 
-		// We unmap the buffer in Mesh::draw and Mesh::setVertexMap.
-		return (uint32 *) ibo->map();
-	}
+void Mesh::getVertexMap(std::vector<uint32> &map) const
+{
+	if (!ibo || element_count == 0)
+		return;
 
-	return nullptr;
+	map.clear();
+	map.reserve(element_count);
+
+	VertexBuffer::Bind ibo_bind(*ibo);
+
+	// We unmap the buffer in Mesh::draw and Mesh::setVertexMap.
+	void *buffer = ibo->map();
+
+	// Fill the vector from the buffer.
+	switch (element_data_type)
+	{
+	case GL_UNSIGNED_BYTE:
+		copyFromIndexBuffer<uint8>(buffer, map, vertex_count);
+		break;
+	case GL_UNSIGNED_SHORT:
+		copyFromIndexBuffer<uint16>(buffer, map, vertex_count);
+		break;
+	case GL_UNSIGNED_INT:
+	default:
+		copyFromIndexBuffer<uint32>(buffer, map, vertex_count);
+		break;
+	}
 }
 
 size_t Mesh::getVertexMapCount() const
@@ -335,7 +405,7 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 			min = std::min(std::max(range_min, 0), max);
 
 		const void *indices = ibo->getPointer(min * sizeof(uint32));
-		GLenum type = GL_UNSIGNED_INT;
+		GLenum type = element_data_type;
 
 		if (instance_count > 1)
 			gl.drawElementsInstanced(mode, max - min + 1, type, indices, instance_count);
@@ -375,7 +445,7 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 		texture->postdraw();
 }
 
-GLenum Mesh::getGLDrawMode(Mesh::DrawMode mode) const
+GLenum Mesh::getGLDrawMode(DrawMode mode) const
 {
 	switch (mode)
 	{
@@ -394,6 +464,16 @@ GLenum Mesh::getGLDrawMode(Mesh::DrawMode mode) const
 	return GL_TRIANGLES;
 }
 
+GLenum Mesh::getGLDataTypeFromMax(size_t maxvalue) const
+{
+	if (maxvalue > LOVE_UINT16_MAX)
+		return GL_UNSIGNED_INT;
+	else if (maxvalue > LOVE_UINT8_MAX)
+		return GL_UNSIGNED_SHORT;
+	else
+		return GL_UNSIGNED_BYTE;
+}
+
 bool Mesh::getConstant(const char *in, Mesh::DrawMode &out)
 {
 	return drawModes.find(in, out);

+ 5 - 4
src/modules/graphics/opengl/Mesh.h

@@ -109,11 +109,10 @@ public:
 	void setVertexMap(const std::vector<uint32> &map);
 
 	/**
-	 * 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.
+	 * Fills the uint32 vector passed into the method with the previously set
+	 * vertex map (index buffer) values.
 	 **/
-	const uint32 *getVertexMap() const;
+	void getVertexMap(std::vector<uint32> &map) const;
 
 	/**
 	 * Gets the total number of elements in the vertex map array.
@@ -171,6 +170,7 @@ public:
 private:
 
 	GLenum getGLDrawMode(DrawMode mode) const;
+	GLenum getGLDataTypeFromMax(size_t maxvalue) const;
 
 	// Vertex buffer.
 	VertexBuffer *vbo;
@@ -179,6 +179,7 @@ private:
 	// Element (vertex index) buffer, for the vertex map.
 	VertexBuffer *ibo;
 	size_t element_count;
+	GLenum element_data_type;
 
 	int instance_count;
 

+ 6 - 5
src/modules/graphics/opengl/wrap_Mesh.cpp

@@ -220,14 +220,15 @@ int w_Mesh_setVertexMap(lua_State *L)
 int w_Mesh_getVertexMap(lua_State *L)
 {
 	Mesh *t = luax_checkmesh(L, 1);
-	const uint32 *vertex_map = 0;
 
-	EXCEPT_GUARD(vertex_map = t->getVertexMap();)
-	size_t elements = t->getVertexMapCount();
+	std::vector<uint32> vertex_map;
+	EXCEPT_GUARD(t->getVertexMap(vertex_map);)
 
-	lua_createtable(L, elements, 0);
+	size_t element_count = vertex_map.size();
 
-	for (size_t i = 0; i < elements; i++)
+	lua_createtable(L, element_count, 0);
+
+	for (size_t i = 0; i < element_count; i++)
 	{
 		lua_pushinteger(L, lua_Integer(vertex_map[i]) + 1);
 		lua_rawseti(L, -2, i + 1);