diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index b5dee7847..e4312e5ed 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -209,6 +209,7 @@ void Shader::mapActiveUniforms() u.name = std::string(cname, (size_t) namelength); u.location = glGetUniformLocation(program, u.name.c_str()); + u.baseType = getUniformBaseType(u.type); delete[] cname; @@ -432,15 +433,38 @@ void Shader::checkSetUniformError(const Uniform &u, int size, int count, Uniform if ((u.count == 1 && count > 1) || count < 0) throw love::Exception("Invalid number of values (expected %d, got %d).", u.count, count); - UniformType basetype = getUniformBaseType(u.type); - - if (basetype == UNIFORM_SAMPLER && sendtype != UNIFORM_SAMPLER) + if (u.baseType == UNIFORM_SAMPLER && sendtype != u.baseType) throw love::Exception("Cannot send a value of this type to an Image variable."); - if (sendtype == UNIFORM_FLOAT && basetype == UNIFORM_INT) + if ((sendtype == UNIFORM_FLOAT && u.baseType == UNIFORM_INT) || (sendtype == UNIFORM_INT && u.baseType == UNIFORM_FLOAT)) throw love::Exception("Cannot convert between float and int."); } +void Shader::sendInt(const std::string &name, int size, const GLint *vec, int count) +{ + TemporaryAttacher attacher(this); + + const Uniform &u = getUniform(name); + checkSetUniformError(u, size, count, UNIFORM_INT); + + switch (size) + { + case 4: + glUniform4iv(u.location, count, vec); + break; + case 3: + glUniform3iv(u.location, count, vec); + break; + case 2: + glUniform2iv(u.location, count, vec); + break; + case 1: + default: + glUniform1iv(u.location, count, vec); + break; + } +} + void Shader::sendFloat(const std::string &name, int size, const GLfloat *vec, int count) { TemporaryAttacher attacher(this); diff --git a/src/modules/graphics/opengl/Shader.h b/src/modules/graphics/opengl/Shader.h index cc7a31556..0e9a3994d 100644 --- a/src/modules/graphics/opengl/Shader.h +++ b/src/modules/graphics/opengl/Shader.h @@ -87,14 +87,25 @@ public: **/ std::string getWarnings() const; + /** + * Send at least one integer or int-vector value to this Shader as a uniform. + * + * @param name The name of the uniform variable in the source code. + * @param size Number of elements in each vector to send. + * A value of 1 indicates a single-component vector (an int). + * @param vec Pointer to the integer or int-vector values. + * @param count Number of integer or int-vector values. + **/ + void sendInt(const std::string &name, int size, const GLint *vec, int count); + /** * Send at least one float or vector value to this Shader as a uniform. * * @param name The name of the uniform variable in the source code. * @param size Number of elements in each vector to send. * A value of 1 indicates a single-component vector (a float). - * @param vec Pointer to the float or vector values. - * @param count Number of float or vector values. + * @param vec Pointer to the float or float-vector values. + * @param count Number of float or float-vector values. **/ void sendFloat(const std::string &name, int size, const GLfloat *vec, int count); @@ -127,15 +138,6 @@ public: private: - // Represents a single uniform/extern shader variable. - struct Uniform - { - GLint location; - GLint count; - GLenum type; - std::string name; - }; - // Types of potential uniform variables used in love's shaders. enum UniformType { @@ -146,6 +148,16 @@ private: UNIFORM_UNKNOWN }; + // Represents a single uniform/extern shader variable. + struct Uniform + { + GLint location; + GLint count; + GLenum type; + UniformType baseType; + std::string name; + }; + // Map active uniform names to their locations. void mapActiveUniforms(); diff --git a/src/modules/graphics/opengl/wrap_Shader.cpp b/src/modules/graphics/opengl/wrap_Shader.cpp index a2e424fb7..a9f0331f0 100644 --- a/src/modules/graphics/opengl/wrap_Shader.cpp +++ b/src/modules/graphics/opengl/wrap_Shader.cpp @@ -43,78 +43,105 @@ int w_Shader_getWarnings(lua_State *L) return 1; } -static int _sendScalars(lua_State *L, Shader *shader, const char *name, int count) +template +static T *_getScalars(lua_State *L, int count, size_t &dimension) { - float *values = new float[count]; + dimension = 1; + + T *values = new T[count]; for (int i = 0; i < count; ++i) { if (lua_isnumber(L, 3 + i)) - values[i] = (float)lua_tonumber(L, 3 + i); + values[i] = static_cast(lua_tonumber(L, 3 + i)); else if (lua_isboolean(L, 3 + i)) - values[i] = (float)lua_toboolean(L, 3 + i); + values[i] = static_cast(lua_toboolean(L, 3 + i)); else { delete[] values; - return luaL_typerror(L, 3 + i, "number or boolean"); + luaL_typerror(L, 3 + i, "number or boolean"); + return 0; } - values[i] = (float)lua_tonumber(L, 3 + i); } - try - { - shader->sendFloat(name, 1, values, count); - } - catch(love::Exception &e) - { - delete[] values; - return luaL_error(L, "%s", e.what()); - } - - delete[] values; - return 0; + return values; } -static int _sendVectors(lua_State *L, Shader *shader, const char *name, int count) +template +static T *_getVectors(lua_State *L, int count, size_t &dimension) { - size_t dimension = lua_objlen(L, 3); - float *values = new float[count * dimension]; + dimension = lua_objlen(L, 3); + T *values = new T[count * dimension]; for (int i = 0; i < count; ++i) { if (!lua_istable(L, 3 + i)) { delete[] values; - return luaL_typerror(L, 3 + i, "table"); + luaL_typerror(L, 3 + i, "table"); + return 0; } if (lua_objlen(L, 3 + i) != dimension) { delete[] values; - return luaL_error(L, "Error in argument %d: Expected table size %d, got %d.", - 3+i, dimension, lua_objlen(L, 3+i)); + luaL_error(L, "Error in argument %d: Expected table size %d, got %d.", + 3+i, dimension, lua_objlen(L, 3+i)); + return 0; } for (size_t k = 1; k <= dimension; ++k) { lua_rawgeti(L, 3 + i, k); - if (lua_isboolean(L, -1)) - values[i * dimension + k - 1] = (float)lua_toboolean(L, -1); + if (lua_isnumber(L, -1)) + values[i * dimension + k - 1] = static_cast(lua_tonumber(L, -1)); + else if (lua_isboolean(L, -1)) + values[i * dimension + k - 1] = static_cast(lua_toboolean(L, -1)); else - values[i * dimension + k - 1] = (float)lua_tonumber(L, -1); + { + delete[] values; + luaL_typerror(L, -1, "number or boolean"); + return 0; + } } lua_pop(L, int(dimension)); } + return values; +} + +int w_Shader_sendInt(lua_State *L) +{ + Shader *shader = luax_checkshader(L, 1); + const char *name = luaL_checkstring(L, 2); + int count = lua_gettop(L) - 2; + + if (count < 1) + return luaL_error(L, "No variable to send."); + + int *values = 0; + size_t dimension = 1; + + if (lua_isnumber(L, 3) || lua_isboolean(L, 3)) + values = _getScalars(L, count, dimension); + else if (lua_istable(L, 3)) + values = _getVectors(L, count, dimension); + else + return luaL_typerror(L, 3, "number, boolean, or table"); + + if (!values) + return luaL_error(L, "Error in arguments."); + try { - shader->sendFloat(name, dimension, values, count); + shader->sendInt(name, dimension, values, count); } - catch(love::Exception &e) + catch (love::Exception &e) { delete[] values; return luaL_error(L, "%s", e.what()); } - + delete[] values; + return 0; } @@ -127,12 +154,32 @@ int w_Shader_sendFloat(lua_State *L) if (count < 1) return luaL_error(L, "No variable to send."); - if (lua_isnumber(L, 3) || lua_isboolean(L, 3)) - return _sendScalars(L, shader, name, count); - else if (lua_istable(L, 3)) - return _sendVectors(L, shader, name, count); + float *values = 0; + size_t dimension = 1; - return luaL_typerror(L, 3, "number, boolean, or table"); + if (lua_isnumber(L, 3) || lua_isboolean(L, 3)) + values = _getScalars(L, count, dimension); + else if (lua_istable(L, 3)) + values = _getVectors(L, count, dimension); + else + return luaL_typerror(L, 3, "number, boolean, or table"); + + if (!values) + return luaL_error(L, "Error in arguments."); + + try + { + shader->sendFloat(name, dimension, values, count); + } + catch (love::Exception &e) + { + delete[] values; + return luaL_error(L, "%s", e.what()); + } + + delete[] values; + + return 0; } int w_Shader_sendMatrix(lua_State *L) @@ -232,6 +279,7 @@ int w_Shader_sendCanvas(lua_State *L) static const luaL_Reg functions[] = { { "getWarnings", w_Shader_getWarnings }, + { "sendInt", w_Shader_sendInt }, { "sendFloat", w_Shader_sendFloat }, { "sendMatrix", w_Shader_sendMatrix }, { "sendImage", w_Shader_sendImage }, diff --git a/src/modules/graphics/opengl/wrap_Shader.h b/src/modules/graphics/opengl/wrap_Shader.h index b3a92f887..6d58bc0a8 100644 --- a/src/modules/graphics/opengl/wrap_Shader.h +++ b/src/modules/graphics/opengl/wrap_Shader.h @@ -33,6 +33,7 @@ namespace opengl Shader *luax_checkshader(lua_State *L, int idx); int w_Shader_getWarnings(lua_State *L); +int w_Shader_sendInt(lua_State *L); int w_Shader_sendFloat(lua_State *L); int w_Shader_sendMatrix(lua_State *L); int w_Shader_sendImage(lua_State *L);