Browse Source

Add support for non-square matrix uniforms in desktop GLSL. The notation is columns x rows, e.g. a mat4x2 has 4 columns and 2 rows.

Fixed the memory layout of matrices sent to shaders via Shader:send. Note that this is a breaking fix and will probably cause matrices designed to work with older versions of LÖVE to be transposed in shaders compared to what they were previously, if Shader:send or sendMatrix was used to put them in the shader.

--HG--
branch : minor
Alex Szpakowski 9 years ago
parent
commit
593f96a08c

+ 70 - 14
src/modules/graphics/opengl/Shader.cpp

@@ -200,7 +200,11 @@ void Shader::mapActiveUniforms()
 		u.name = std::string(cname, (size_t) namelen);
 		u.location = glGetUniformLocation(program, u.name.c_str());
 		u.baseType = getUniformBaseType(gltype);
-		u.components = getUniformTypeSize(gltype);
+
+		if (u.baseType == UNIFORM_MATRIX)
+			u.matrix = getMatrixSize(gltype);
+		else
+			u.components = getUniformTypeComponents(gltype);
 
 		// Initialize all samplers to 0. Both GLSL and GLSL ES are supposed to
 		// do this themselves, but some Android devices (galaxy tab 3 and 4)
@@ -517,19 +521,27 @@ void Shader::sendMatrices(const UniformInfo *info, const float *m, int count)
 
 	int location = info->location;
 
-	switch (info->components)
-	{
-	case 4:
-		glUniformMatrix4fv(location, count, GL_FALSE, m);
-		break;
-	case 3:
-		glUniformMatrix3fv(location, count, GL_FALSE, m);
-		break;
-	case 2:
-	default:
+	int columns = info->matrix.columns;
+	int rows = info->matrix.rows;
+
+	if (columns == 2 && rows == 2)
 		glUniformMatrix2fv(location, count, GL_FALSE, m);
-		break;
-	}
+	else if (columns == 3 && rows == 3)
+		glUniformMatrix3fv(location, count, GL_FALSE, m);
+	else if (columns == 4 && rows == 4)
+		glUniformMatrix4fv(location, count, GL_FALSE, m);
+	else if (columns == 2 && rows == 3)
+		glUniformMatrix2x3fv(location, count, GL_FALSE, m);
+	else if (columns == 2 && rows == 4)
+		glUniformMatrix2x4fv(location, count, GL_FALSE, m);
+	else if (columns == 3 && rows == 2)
+		glUniformMatrix3x2fv(location, count, GL_FALSE, m);
+	else if (columns == 3 && rows == 4)
+		glUniformMatrix3x4fv(location, count, GL_FALSE, m);
+	else if (columns == 4 && rows == 2)
+		glUniformMatrix4x2fv(location, count, GL_FALSE, m);
+	else if (columns == 4 && rows == 3)
+		glUniformMatrix4x3fv(location, count, GL_FALSE, m);
 }
 
 void Shader::sendTexture(const UniformInfo *info, Texture *texture)
@@ -819,7 +831,7 @@ bool Shader::isSupported()
 	return GLAD_ES_VERSION_2_0 || (getGLSLVersion() >= "1.2");
 }
 
-int Shader::getUniformTypeSize(GLenum type) const
+int Shader::getUniformTypeComponents(GLenum type) const
 {
 	switch (type)
 	{
@@ -850,6 +862,50 @@ int Shader::getUniformTypeSize(GLenum type) const
 	}
 }
 
+Shader::MatrixSize Shader::getMatrixSize(GLenum type) const
+{
+	MatrixSize m;
+
+	switch (type)
+	{
+	case GL_FLOAT_MAT2:
+		m.columns = m.rows = 2;
+		break;
+	case GL_FLOAT_MAT3:
+		m.columns = m.rows = 3;
+		break;
+	case GL_FLOAT_MAT4:
+		m.columns = m.rows = 4;
+		break;
+	case GL_FLOAT_MAT2x3:
+		m.columns = 2;
+		m.rows = 3;
+		break;
+	case GL_FLOAT_MAT2x4:
+		m.columns = 2;
+		m.rows = 4;
+		break;
+	case GL_FLOAT_MAT3x2:
+		m.columns = 3;
+		m.rows = 2;
+		break;
+	case GL_FLOAT_MAT3x4:
+		m.columns = 3;
+		m.rows = 4;
+		break;
+	case GL_FLOAT_MAT4x2:
+		m.columns = 4;
+		m.rows = 2;
+		break;
+	case GL_FLOAT_MAT4x3:
+		m.columns = 4;
+		m.rows = 3;
+		break;
+	}
+
+	return m;
+}
+
 Shader::UniformType Shader::getUniformBaseType(GLenum type) const
 {
 	switch (type)

+ 13 - 2
src/modules/graphics/opengl/Shader.h

@@ -88,11 +88,21 @@ public:
 		std::string pixel;
 	};
 
+	struct MatrixSize
+	{
+		short columns;
+		short rows;
+	};
+
 	struct UniformInfo
 	{
 		int location;
 		int count;
-		int components;
+		union
+		{
+			int components;
+			MatrixSize matrix;
+		};
 		UniformType baseType;
 		std::string name;
 	};
@@ -195,7 +205,8 @@ private:
 	// Map active uniform names to their locations.
 	void mapActiveUniforms();
 
-	int getUniformTypeSize(GLenum type) const;
+	int getUniformTypeComponents(GLenum type) const;
+	MatrixSize getMatrixSize(GLenum type) const;
 	UniformType getUniformBaseType(GLenum type) const;
 
 	GLuint compileCode(ShaderStage stage, const std::string &code);

+ 22 - 13
src/modules/graphics/opengl/wrap_Shader.cpp

@@ -151,8 +151,9 @@ int w_Shader_sendBooleans(lua_State *L, int startidx, Shader *shader, const Shad
 int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info)
 {
 	int count = _getCount(L, startidx, info);
-	int dimension = info->components;
-	int elements = dimension * dimension;
+	int columns = info->matrix.columns;
+	int rows = info->matrix.rows;
+	int elements = columns * rows;
 
 	float *values = shader->getScratchBuffer<float>(elements * count);
 
@@ -166,28 +167,36 @@ int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shad
 
 		if (table_of_tables)
 		{
-			int n = 0;
+			int n = i * elements;
 
-			for (int j = 1; j <= dimension; j++)
+			for (int row = 0; row < rows; row++)
 			{
-				lua_rawgeti(L, startidx + i, j);
+				lua_rawgeti(L, startidx + i, row + 1);
 
-				for (int k = 1; k <= dimension; k++)
+				for (int column = 0; column < columns; column++)
 				{
-					lua_rawgeti(L, -k, k);
-					values[i * elements + n] = (float) luaL_checknumber(L, -1);
-					n++;
+					// The table has the matrix elements laid out in row-major
+					// order, but we need to store them column-major in memory.
+					lua_rawgeti(L, -(column + 1), column + 1);
+					values[n + (column * rows + row)] = (float) luaL_checknumber(L, -1);
 				}
 
-				lua_pop(L, dimension + 1);
+				lua_pop(L, columns + 1);
 			}
 		}
 		else
 		{
-			for (int k = 1; k <= elements; k++)
+			int n = i * elements;
+
+			for (int column = 0; column < columns; column++)
 			{
-				lua_rawgeti(L, startidx + i, k);
-				values[i * elements + (k - 1)] = (float) luaL_checknumber(L, -1);
+				for (int row = 0; row < rows; row++)
+				{
+					// The table has the matrix elements laid out in row-major
+					// order, but we need to store them column-major in memory.
+					lua_rawgeti(L, startidx + i, row * columns + column + 1);
+					values[n + (column * rows + row)] = (float) luaL_checknumber(L, -1);
+				}
 			}
 
 			lua_pop(L, elements);