Browse Source

Shader:send now optionally accepts a Data object (with optional byte offset and size parameters). Resolves issue #1343.

Also changed the optional 'columnMajor' argument of the matrix variant of Shader:send and Transform:setMatrix to accept enum constants instead. Valid values are "row" and "column".

--HG--
branch : minor
Alex Szpakowski 8 years ago
parent
commit
5f64737956

+ 2 - 0
src/modules/graphics/Shader.h

@@ -139,6 +139,8 @@ public:
 			unsigned int *uints;
 			unsigned int *uints;
 		};
 		};
 
 
+		size_t dataSize;
+
 		Texture **textures;
 		Texture **textures;
 	};
 	};
 
 

+ 11 - 11
src/modules/graphics/opengl/Shader.cpp

@@ -219,35 +219,35 @@ void Shader::mapActiveUniforms()
 		}
 		}
 		else
 		else
 		{
 		{
-			size_t datasize = 0;
+			u.dataSize = 0;
 
 
 			switch (u.baseType)
 			switch (u.baseType)
 			{
 			{
 			case UNIFORM_FLOAT:
 			case UNIFORM_FLOAT:
-				datasize = sizeof(float) * u.components * u.count;
-				u.data = malloc(datasize);
+				u.dataSize = sizeof(float) * u.components * u.count;
+				u.data = malloc(u.dataSize);
 				break;
 				break;
 			case UNIFORM_INT:
 			case UNIFORM_INT:
 			case UNIFORM_BOOL:
 			case UNIFORM_BOOL:
 			case UNIFORM_SAMPLER:
 			case UNIFORM_SAMPLER:
-				datasize = sizeof(int) * u.components * u.count;
-				u.data = malloc(datasize);
+				u.dataSize = sizeof(int) * u.components * u.count;
+				u.data = malloc(u.dataSize);
 				break;
 				break;
 			case UNIFORM_UINT:
 			case UNIFORM_UINT:
-				datasize = sizeof(unsigned int) * u.components * u.count;
-				u.data = malloc(datasize);
+				u.dataSize = sizeof(unsigned int) * u.components * u.count;
+				u.data = malloc(u.dataSize);
 				break;
 				break;
 			case UNIFORM_MATRIX:
 			case UNIFORM_MATRIX:
-				datasize = sizeof(float) * (u.matrix.rows * u.matrix.columns) * u.count;
-				u.data = malloc(datasize);
+				u.dataSize = sizeof(float) * (u.matrix.rows * u.matrix.columns) * u.count;
+				u.data = malloc(u.dataSize);
 				break;
 				break;
 			default:
 			default:
 				break;
 				break;
 			}
 			}
 
 
-			if (datasize > 0)
+			if (u.dataSize > 0)
 			{
 			{
-				memset(u.data, 0, datasize);
+				memset(u.data, 0, u.dataSize);
 
 
 				if (u.baseType == UNIFORM_SAMPLER)
 				if (u.baseType == UNIFORM_SAMPLER)
 				{
 				{

+ 126 - 13
src/modules/graphics/wrap_Shader.cpp

@@ -27,6 +27,7 @@
 #include <string>
 #include <string>
 #include <algorithm>
 #include <algorithm>
 #include <cmath>
 #include <cmath>
+#include <limits>
 
 
 namespace love
 namespace love
 {
 {
@@ -157,9 +158,14 @@ int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shad
 {
 {
 	bool columnmajor = false;
 	bool columnmajor = false;
 
 
-	if (lua_isboolean(L, startidx))
+	if (lua_type(L, startidx) == LUA_TSTRING)
 	{
 	{
-		columnmajor = luax_toboolean(L, startidx);
+		const char *layoutstr = lua_tostring(L, startidx);
+		math::Transform::MatrixLayout layout;
+		if (!math::Transform::getConstant(layoutstr, layout))
+			return luaL_error(L, "Invalid matrix layout: %s", layoutstr);
+
+		columnmajor = (layout == math::Transform::MATRIX_COLUMN_MAJOR);
 		startidx++;
 		startidx++;
 	}
 	}
 
 
@@ -278,17 +284,8 @@ int w_Shader_sendTextures(lua_State *L, int startidx, Shader *shader, const Shad
 	return 0;
 	return 0;
 }
 }
 
 
-int w_Shader_send(lua_State *L)
+static int w_Shader_sendLuaValues(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info, const char *name)
 {
 {
-	Shader *shader = luax_checkshader(L, 1);
-	const char *name = luaL_checkstring(L, 2);
-
-	const Shader::UniformInfo *info = shader->getUniformInfo(name);
-	if (info == nullptr)
-		return luaL_error(L, "Shader uniform '%s' does not exist.\nA common error is to define but not use the variable.", name);
-
-	int startidx = 3;
-
 	switch (info->baseType)
 	switch (info->baseType)
 	{
 	{
 	case Shader::UNIFORM_FLOAT:
 	case Shader::UNIFORM_FLOAT:
@@ -308,6 +305,119 @@ int w_Shader_send(lua_State *L)
 	}
 	}
 }
 }
 
 
+static int w_Shader_sendData(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info, bool colors)
+{
+	if (info->baseType == Shader::UNIFORM_SAMPLER)
+		return luaL_error(L, "Uniform sampler values (textures) cannot be sent to Shaders via Data objects.");
+
+	bool columnmajor = false;
+	if (info->baseType == Shader::UNIFORM_MATRIX && lua_type(L, startidx + 1) == LUA_TSTRING)
+	{
+		const char *layoutstr = lua_tostring(L, startidx + 1);
+		math::Transform::MatrixLayout layout;
+		if (!math::Transform::getConstant(layoutstr, layout))
+			return luaL_error(L, "Invalid matrix layout: %s", layoutstr);
+
+		columnmajor = (layout == math::Transform::MATRIX_COLUMN_MAJOR);
+		startidx++;
+	}
+
+	Data *data = luax_checktype<Data>(L, startidx);
+
+	size_t size = data->getSize();
+
+	ptrdiff_t offset = (ptrdiff_t) luaL_optinteger(L, startidx + 1, 0);
+	if (offset < 0)
+		return luaL_error(L, "Offset cannot be negative.");
+	else if ((size_t) offset >= size)
+		return luaL_error(L, "Offset must be less than the size of the Data.");
+
+	size_t uniformstride = info->dataSize / info->count;
+
+	if (!lua_isnoneornil(L, startidx + 2))
+	{
+		lua_Integer datasize = luaL_checkinteger(L, startidx + 2);
+		if (datasize <= 0)
+			return luaL_error(L, "Size must be greater than 0.");
+		else if ((size_t) datasize > size - offset)
+			return luaL_error(L, "Size and offset must fit within the Data's bounds.");
+		else if (size % uniformstride != 0)
+			return luaL_error(L, "Size must be a multiple of the uniform's size in bytes.");
+		else if (size > info->dataSize)
+			return luaL_error(L, "Size must not be greater than the uniform's total size in bytes.");
+
+		size = (size_t) datasize;
+	}
+	else
+	{
+		size -= offset;
+		size = std::min((size / uniformstride) * uniformstride, info->dataSize);
+	}
+
+	if (size == 0)
+		return luaL_error(L, "Size to copy must be greater than 0.");
+
+	int count = (int) (size / uniformstride);
+	const char *mem = (const char *) data->getData() + offset;
+
+	if (info->baseType != Shader::UNIFORM_MATRIX || columnmajor)
+		memcpy(info->data, mem, size);
+	else
+	{
+		int columns = info->matrix.columns;
+		int rows = info->matrix.rows;
+
+		const float *src = (const float *) mem;
+		float *dst = info->floats;
+
+		for (int i = 0; i < count; i++)
+		{
+			for (int row = 0; row < rows; row++)
+			{
+				for (int column = 0; column < columns; column++)
+					dst[column * rows + row] = src[row * columns + column];
+			}
+
+			src += columns * rows;
+			dst += columns * rows;
+		}
+	}
+
+	if (colors && graphics::isGammaCorrect())
+	{
+		// alpha is always linear (when present).
+		int components = info->components;
+		int gammacomponents = std::min(components, 3);
+		float *values = info->floats;
+
+		for (int i = 0; i < count; i++)
+		{
+			for (int j = 0; j < gammacomponents; j++)
+				values[i * components + j] = math::gammaToLinear(values[i * components + j]);
+		}
+	}
+
+	shader->updateUniform(info, count);
+	return 0;
+}
+
+int w_Shader_send(lua_State *L)
+{
+	Shader *shader = luax_checkshader(L, 1);
+	const char *name = luaL_checkstring(L, 2);
+
+	const Shader::UniformInfo *info = shader->getUniformInfo(name);
+	if (info == nullptr)
+		return luaL_error(L, "Shader uniform '%s' does not exist.\nA common error is to define but not use the variable.", name);
+
+	int startidx = 3;
+
+	if (luax_istype(L, startidx, Data::type))
+		return w_Shader_sendData(L, startidx, shader, info, false);
+	else
+		return w_Shader_sendLuaValues(L, startidx, shader, info, name);
+}
+
 int w_Shader_sendColors(lua_State *L)
 int w_Shader_sendColors(lua_State *L)
 {
 {
 	Shader *shader = luax_checkshader(L, 1);
 	Shader *shader = luax_checkshader(L, 1);
@@ -320,7 +430,10 @@ int w_Shader_sendColors(lua_State *L)
 	if (info->baseType != Shader::UNIFORM_FLOAT || info->components < 3)
 	if (info->baseType != Shader::UNIFORM_FLOAT || info->components < 3)
 		return luaL_error(L, "sendColor can only be used on vec3 or vec4 uniforms.");
 		return luaL_error(L, "sendColor can only be used on vec3 or vec4 uniforms.");
 
 
-	return w_Shader_sendFloats(L, 3, shader, info, true);
+	if (luax_istype(L, 3, Data::type))
+		return w_Shader_sendData(L, 3, shader, info, true);
+	else
+		return w_Shader_sendFloats(L, 3, shader, info, true);
 }
 }
 
 
 int w_Shader_hasUniform(lua_State *L)
 int w_Shader_hasUniform(lua_State *L)

+ 18 - 0
src/modules/math/Transform.cpp

@@ -129,5 +129,23 @@ void Transform::setMatrix(const Matrix4 &m)
 	inverseDirty = true;
 	inverseDirty = true;
 }
 }
 
 
+bool Transform::getConstant(const char *in, MatrixLayout &out)
+{
+	return matrixLayouts.find(in, out);
+}
+
+bool Transform::getConstant(MatrixLayout in, const char *&out)
+{
+	return matrixLayouts.find(in, out);
+}
+
+StringMap<Transform::MatrixLayout, Transform::MATRIX_MAX_ENUM>::Entry Transform::matrixLayoutEntries[] =
+{
+	{ "row",    MATRIX_ROW_MAJOR    },
+	{ "column", MATRIX_COLUMN_MAJOR },
+};
+
+StringMap<Transform::MatrixLayout, Transform::MATRIX_MAX_ENUM> Transform::matrixLayouts(Transform::matrixLayoutEntries, sizeof(Transform::matrixLayoutEntries));
+
 } // math
 } // math
 } // love
 } // love

+ 14 - 0
src/modules/math/Transform.h

@@ -24,6 +24,7 @@
 #include "common/Object.h"
 #include "common/Object.h"
 #include "common/Matrix.h"
 #include "common/Matrix.h"
 #include "common/Vector.h"
 #include "common/Vector.h"
+#include "common/StringMap.h"
 
 
 namespace love
 namespace love
 {
 {
@@ -34,6 +35,13 @@ class Transform : public Object
 {
 {
 public:
 public:
 
 
+	enum MatrixLayout
+	{
+		MATRIX_ROW_MAJOR,
+		MATRIX_COLUMN_MAJOR,
+		MATRIX_MAX_ENUM
+	};
+
 	static love::Type type;
 	static love::Type type;
 
 
 	Transform();
 	Transform();
@@ -61,6 +69,9 @@ public:
 	const Matrix4 &getMatrix() const;
 	const Matrix4 &getMatrix() const;
 	void setMatrix(const Matrix4 &m);
 	void setMatrix(const Matrix4 &m);
 
 
+	static bool getConstant(const char *in, MatrixLayout &out);
+	static bool getConstant(MatrixLayout in, const char *&out);
+
 private:
 private:
 
 
 	inline const Matrix4 &getInverseMatrix()
 	inline const Matrix4 &getInverseMatrix()
@@ -78,6 +89,9 @@ private:
 	bool inverseDirty;
 	bool inverseDirty;
 	Matrix4 inverseMatrix;
 	Matrix4 inverseMatrix;
 
 
+	static StringMap<MatrixLayout, MATRIX_MAX_ENUM>::Entry matrixLayoutEntries[];
+	static StringMap<MatrixLayout, MATRIX_MAX_ENUM> matrixLayouts;
+
 }; // Transform
 }; // Transform
 
 
 
 

+ 7 - 2
src/modules/math/wrap_Transform.cpp

@@ -135,9 +135,14 @@ int w_Transform_setMatrix(lua_State *L)
 	bool columnmajor = false;
 	bool columnmajor = false;
 
 
 	int idx = 2;
 	int idx = 2;
-	if (lua_isboolean(L, idx))
+	if (lua_type(L, idx) == LUA_TSTRING)
 	{
 	{
-		columnmajor = luax_toboolean(L, idx);
+		const char *layoutstr = lua_tostring(L, idx);
+		Transform::MatrixLayout layout;
+		if (!Transform::getConstant(layoutstr, layout))
+			return luaL_error(L, "Invalid matrix layout: %s", layoutstr);
+
+		columnmajor = (layout == Transform::MATRIX_COLUMN_MAJOR);
 		idx++;
 		idx++;
 	}
 	}