I worked further on this and implemented most of the needed functions. You can see the progress in the
feature/shader_uniforms branch.
At the moment, the current incomplete API looks as follows:
void setUniformFloat(const std::string& name, float x);
void setUniformVec2(const std::string& name, const Vec2& vector);
void setUniformVec3(const std::string& name, const Vec3& vector);
void setUniformVec4(const std::string& name, const Vec4& vector);
void setUniformVec4(const std::string& name, const Color& color);
void setUniformInt(const std::string& name, int x);
void setUniformIvec2(const std::string& name, const Ivec2& vector);
void setUniformIvec3(const std::string& name, const Ivec3& vector);
void setUniformIvec4(const std::string& name, const Ivec4& vector);
void setUniformBool(const std::string& name, bool x);
void setUniformBvec2(const std::string& name, const Bvec2& vector);
void setUniformBvec3(const std::string& name, const Bvec3& vector);
void setUniformBvec4(const std::string& name, const Bvec4& vector);
void setUniformMat3(const std::string& name, const float* pointer);
void setUniformMat3(const std::string& name, const Mat3& matrix);
void setUniformMat4(const std::string& name, const float* pointer);
void setUniformMat4(const std::string& name, const Mat4& matrix);
void setUniformMat4(const std::string& name, const sf::Transform& transform);
void setUniformFloatArray(const std::string& name, const float* valueArray, std::size_t length);
void setUniformVec2Array(const std::string& name, const Vector2f* vectorArray, std::size_t length);
void setUniformVec3Array(const std::string& name, const Vector3f* vectorArray, std::size_t length);
void setUniformVec4Array(const std::string& name, const Vec4* vectorArray, std::size_t length);
void setUniformMat3Array(const std::string& name, const Mat3* matrixArray, std::size_t length);
void setUniformMat4Array(const std::string& name, const Mat4* matrixArray, std::size_t length);
void setUniformSampler2D(const std::string& name, const Texture& texture);
void setUniformSampler2D(const std::string& name, CurrentTextureType);
New typesVec4,
Mat3 and
Mat4 are new types nested in the
sf::Shader class. I thought about providing a dedicated namespace
sf::Glsl for these types. This would not bloat the
sf::Shader scope too much, and it is immediately obvious what the types stand for. This would look as follows:
template <std::size_t Columns, std::size_t Rows>
struct Matrix
{
explicit Matrix(const float* pointer) :
pointer(pointer)
{
}
const float* pointer; // or value semantics?
};
template <typename T>
struct Vector4
{
Vector4() :
x(0),
y(0),
z(0),
w(0)
{
}
Vector4(T X, T Y, T Z, T W) :
x(X),
y(Y),
z(Z),
w(W)
{
}
T x, y, z, w;
};
// Here comes the interesting part
typedef Matrix<3, 3> Mat3;
typedef Matrix<4, 4> Mat4;
typedef Vector2<float> Vec2;
typedef Vector2<int> Ivec2;
typedef Vector2<bool> Bvec2;
typedef Vector3<float> Vec3;
typedef Vector3<int> Ivec3;
typedef Vector3<bool> Bvec3;
typedef Vector4<float> Vec4;
typedef Vector4<int> Ivec4;
typedef Vector4<bool> Bvec4;
Now, why do we even need those types? I really tried to find a way around them, but as soon as we want to provide support for GLSL arrays, it's impossible.
GLSL arraysAs you see above, arrays are currently passed with this signature:
void setUniformTArray(const std::string& name, const T* array, std::size_t length);
With a
Glsl namespace, it would also be possible to write
Glsl::Array<T> type and a single template
void setUniformArray(const std::string& name, const Glsl::Array<T>& array). But this would again raise questions for value/pointer semantics and unnecessary copies, as it is already the case for vectors and matrices.
I also considered a generic
Shader::setUniform() interface that doesn't contain the GLSL type in the method name. However, as the shader code needs to be adapted whenever the type changes, the use cases are limited. On the contrary, I see
quite some potential for type errors that cannot be caught at compile time.
There are indeed some opportunities for polymorphism on C++ side, for example a class that can write an entire GLSL struct. Without deeper thought, it also seems that the whole array functions could be easily implemented with templates, but the reality looks a bit more complicated. Since different OpenGL functions are called depending on the types, an internal dispatch is still necessary. And because OpenGL must remain contained in the .cpp file, but templates are implemented in the header, we still need an indirection layer.
Eventually, this boils down to the same as providing an additional generic
setUniformArray() method, which selects the concrete
setUniformTArray() method depending on the type. If this is really a desired feature, we could think about providing something like this in the future, also for basic GLSL types (not arrays), but I wouldn't add it now already.
Number of overloadsIn order to reduce the total number of functions, I omitted the overloads that accept every parameter separately. With C++11, people can use the
{x, y} syntax anyway. That is, instead of providing
setUniformVec3(float x, float y, float z);
we just have
setUniformVec3(const sf::Glsl::Vec3& v);
which is exactly the same as:
setUniformVec3(const sf::Vector3f& v);
I have used the typedefs in the signature for consistency with
Vec4. Maybe it's a better idea to use
sf::Vector2 and
sf::Vector3 for clarity, but we can also mention this in the documentation.