diff --git a/doc/changelog.dox b/doc/changelog.dox index 71aa7b52b..5c2801572 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -180,6 +180,11 @@ See also: and @ref SceneGraph::AbstractBasicTranslationRotation3D::rotateLocal(const Math::Quaternion&) "rotateLocal()" overloads taking a @ref Math::Quaternion +@subsubsection changelog-latest-new-shaders Shaders library + +- Texture coordinate transformation in @ref Shaders::DistanceFieldVector, + @ref Shaders::Flat, @ref Shaders::Phong and @ref Shaders::Vector + @subsubsection changelog-latest-new-trade Trade library - A new, redesigned @ref Trade::MeshData class that allows much more flexible diff --git a/src/Magnum/Shaders/AbstractVector.h b/src/Magnum/Shaders/AbstractVector.h index f465841fc..626e113db 100644 --- a/src/Magnum/Shaders/AbstractVector.h +++ b/src/Magnum/Shaders/AbstractVector.h @@ -83,6 +83,11 @@ template class AbstractVector: public GL::AbstractShader /** * @brief Bind vector texture * @return Reference to self (for method chaining) + * + * @see @ref DistanceFieldVector::Flag::TextureTransformation, + * @ref Vector::Flag::TextureTransformation, + * @ref DistanceFieldVector::setTextureMatrix(), + * @ref Vector::setTextureMatrix() */ AbstractVector& bindVectorTexture(GL::Texture2D& texture); diff --git a/src/Magnum/Shaders/AbstractVector2D.vert b/src/Magnum/Shaders/AbstractVector2D.vert index 6e97e6973..9b6613050 100644 --- a/src/Magnum/Shaders/AbstractVector2D.vert +++ b/src/Magnum/Shaders/AbstractVector2D.vert @@ -37,6 +37,17 @@ uniform highp mat3 transformationProjectionMatrix #endif ; +#ifdef TEXTURE_TRANSFORMATION +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 1) +#endif +uniform mediump mat3 textureMatrix + #ifndef GL_ES + = mat3(1.0) + #endif + ; +#endif + #ifdef EXPLICIT_ATTRIB_LOCATION layout(location = POSITION_ATTRIBUTE_LOCATION) #endif @@ -51,5 +62,11 @@ out mediump vec2 interpolatedTextureCoordinates; void main() { gl_Position.xywz = vec4(transformationProjectionMatrix*vec3(position, 1.0), 0.0); - interpolatedTextureCoordinates = textureCoordinates; + interpolatedTextureCoordinates = + #ifdef TEXTURE_TRANSFORMATION + (textureMatrix*vec3(textureCoordinates, 1.0)).xy + #else + textureCoordinates + #endif + ; } diff --git a/src/Magnum/Shaders/AbstractVector3D.vert b/src/Magnum/Shaders/AbstractVector3D.vert index 8f876f2de..6cc562032 100644 --- a/src/Magnum/Shaders/AbstractVector3D.vert +++ b/src/Magnum/Shaders/AbstractVector3D.vert @@ -37,6 +37,17 @@ uniform highp mat4 transformationProjectionMatrix #endif ; +#ifdef TEXTURE_TRANSFORMATION +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 1) +#endif +uniform mediump mat3 textureMatrix + #ifndef GL_ES + = mat3(1.0) + #endif + ; +#endif + #ifdef EXPLICIT_ATTRIB_LOCATION layout(location = POSITION_ATTRIBUTE_LOCATION) #endif @@ -51,5 +62,11 @@ out mediump vec2 interpolatedTextureCoordinates; void main() { gl_Position = transformationProjectionMatrix*position; - interpolatedTextureCoordinates = textureCoordinates; + interpolatedTextureCoordinates = + #ifdef TEXTURE_TRANSFORMATION + (textureMatrix*vec3(textureCoordinates, 1.0)).xy + #else + textureCoordinates + #endif + ; } diff --git a/src/Magnum/Shaders/CMakeLists.txt b/src/Magnum/Shaders/CMakeLists.txt index 7f4ce43b4..f53244858 100644 --- a/src/Magnum/Shaders/CMakeLists.txt +++ b/src/Magnum/Shaders/CMakeLists.txt @@ -32,16 +32,16 @@ set_target_properties(MagnumShaders_RCS-dependencies PROPERTIES FOLDER "Magnum/S set(MagnumShaders_SRCS AbstractVector.cpp - DistanceFieldVector.cpp - Vector.cpp VertexColor.cpp ${MagnumShaders_RCS}) set(MagnumShaders_GracefulAssert_SRCS + DistanceFieldVector.cpp Flat.cpp MeshVisualizer.cpp - Phong.cpp) + Phong.cpp + Vector.cpp) set(MagnumShaders_HEADERS DistanceFieldVector.h diff --git a/src/Magnum/Shaders/DistanceFieldVector.cpp b/src/Magnum/Shaders/DistanceFieldVector.cpp index 2ee306ef3..7707e6456 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.cpp +++ b/src/Magnum/Shaders/DistanceFieldVector.cpp @@ -25,6 +25,7 @@ #include "DistanceFieldVector.h" +#include #include #include @@ -45,7 +46,7 @@ namespace { template<> constexpr const char* vertexShaderName<3>() { return "AbstractVector3D.vert"; } } -template DistanceFieldVector::DistanceFieldVector() { +template DistanceFieldVector::DistanceFieldVector(const Flags flags): _flags{flags} { #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ if(!Utility::Resource::hasGroup("MagnumShaders")) @@ -62,7 +63,8 @@ template DistanceFieldVector::DistanceFieldV GL::Shader vert = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Vertex); GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment); - vert.addSource(rs.get("generic.glsl")) + vert.addSource(flags & Flag::TextureTransformation ? "#define TEXTURE_TRANSFORMATION\n" : "") + .addSource(rs.get("generic.glsl")) .addSource(rs.get(vertexShaderName())); frag.addSource(rs.get("generic.glsl")) .addSource(rs.get("DistanceFieldVector.frag")); @@ -89,6 +91,8 @@ template DistanceFieldVector::DistanceFieldV #endif { _transformationProjectionMatrixUniform = GL::AbstractShaderProgram::uniformLocation("transformationProjectionMatrix"); + if(flags & Flag::TextureTransformation) + _textureMatrixUniform = GL::AbstractShaderProgram::uniformLocation("textureMatrix"); _colorUniform = GL::AbstractShaderProgram::uniformLocation("color"); _outlineColorUniform = GL::AbstractShaderProgram::uniformLocation("outlineColor"); _outlineRangeUniform = GL::AbstractShaderProgram::uniformLocation("outlineRange"); @@ -106,6 +110,7 @@ template DistanceFieldVector::DistanceFieldV /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ #ifdef MAGNUM_TARGET_GLES setTransformationProjectionMatrix({}); + if(flags & Flag::TextureTransformation) setTextureMatrix({}); setColor(Color4{1.0f}); /* Outline color is zero by default */ setOutlineRange(0.5f, 1.0f); setSmoothness(0.04f); @@ -117,6 +122,13 @@ template DistanceFieldVector& DistanceFieldV return *this; } +template DistanceFieldVector& DistanceFieldVector::setTextureMatrix(const Matrix3& matrix) { + CORRADE_ASSERT(_flags & Flag::TextureTransformation, + "Shaders::DistanceFieldVector::setTextureMatrix(): the shader was not created with texture transformation enabled", *this); + GL::AbstractShaderProgram::setUniform(_textureMatrixUniform, matrix); + return *this; +} + template DistanceFieldVector& DistanceFieldVector::setColor(const Color4& color) { GL::AbstractShaderProgram::setUniform(_colorUniform, color); return *this; @@ -140,4 +152,28 @@ template DistanceFieldVector& DistanceFieldV template class DistanceFieldVector<2>; template class DistanceFieldVector<3>; +namespace Implementation { + +Debug& operator<<(Debug& debug, const DistanceFieldVectorFlag value) { + debug << "Shaders::DistanceFieldVector::Flag" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(v) case DistanceFieldVectorFlag::v: return debug << "::" #v; + _c(TextureTransformation) + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, const DistanceFieldVectorFlags value) { + return Containers::enumSetDebugOutput(debug, value, "Shaders::DistanceFieldVector::Flags{}", { + DistanceFieldVectorFlag::TextureTransformation + }); +} + +} + }} diff --git a/src/Magnum/Shaders/DistanceFieldVector.frag b/src/Magnum/Shaders/DistanceFieldVector.frag index 91ac21b5f..0deeddf54 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.frag +++ b/src/Magnum/Shaders/DistanceFieldVector.frag @@ -30,7 +30,7 @@ #endif #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 1) +layout(location = 2) #endif uniform lowp vec4 color #ifndef GL_ES @@ -39,12 +39,12 @@ uniform lowp vec4 color ; #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 2) +layout(location = 3) #endif uniform lowp vec4 outlineColor; /* defaults to zero */ #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 3) +layout(location = 4) #endif uniform lowp vec2 outlineRange #ifndef GL_ES @@ -53,7 +53,7 @@ uniform lowp vec2 outlineRange ; #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 4) +layout(location = 5) #endif uniform lowp float smoothness #ifndef GL_ES diff --git a/src/Magnum/Shaders/DistanceFieldVector.h b/src/Magnum/Shaders/DistanceFieldVector.h index 2c32dd793..742872733 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.h +++ b/src/Magnum/Shaders/DistanceFieldVector.h @@ -35,6 +35,13 @@ namespace Magnum { namespace Shaders { +namespace Implementation { + enum class DistanceFieldVectorFlag: UnsignedByte { + TextureTransformation = 1 << 0 + }; + typedef Containers::EnumSet DistanceFieldVectorFlags; +} + /** @brief Distance field vector shader @@ -67,7 +74,41 @@ Common rendering setup: */ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector: public AbstractVector { public: - explicit DistanceFieldVector(); + #ifdef DOXYGEN_GENERATING_OUTPUT + /** + * @brief Flag + * @m_since_latest + * + * @see @ref Flags, @ref flags() + */ + enum class Flag: UnsignedByte { + /** + * Enable texture coordinate transformation. + * @see @ref setTextureMatrix() + * @m_since_latest + */ + TextureTransformation = 1 << 0 + }; + + /** + * @brief Flags + * @m_since_latest + * + * @see @ref flags() + */ + typedef Containers::EnumSet Flags; + #else + /* Done this way to be prepared for possible future diversion of 2D + and 3D flags (e.g. introducing 3D-specific features) */ + typedef Implementation::DistanceFieldVectorFlag Flag; + typedef Implementation::DistanceFieldVectorFlags Flags; + #endif + + /** + * @brief Constructor + * @param flags Flags + */ + explicit DistanceFieldVector(Flags flags = {}); /** * @brief Construct without creating the underlying OpenGL object @@ -100,6 +141,12 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector /** @brief Move assignment */ DistanceFieldVector& operator=(DistanceFieldVector&&) noexcept = default; + /** + * @brief Flags + * @m_since_latest + */ + Flags flags() const { return _flags; } + /** * @brief Set transformation and projection matrix * @return Reference to self (for method chaining) @@ -108,6 +155,17 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector */ DistanceFieldVector& setTransformationProjectionMatrix(const MatrixTypeFor& matrix); + /** + * @brief Set texture coordinate transformation matrix + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with + * @ref Flag::TextureTransformation enabled. Initial value is an + * identity matrix. + */ + DistanceFieldVector& setTextureMatrix(const Matrix3& matrix); + /** * @brief Set fill color * @return Reference to self (for method chaining) @@ -170,11 +228,13 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector using GL::AbstractShaderProgram::dispatchCompute; #endif + Flags _flags; Int _transformationProjectionMatrixUniform{0}, - _colorUniform{1}, - _outlineColorUniform{2}, - _outlineRangeUniform{3}, - _smoothnessUniform{4}; + _textureMatrixUniform{1}, + _colorUniform{2}, + _outlineColorUniform{3}, + _outlineRangeUniform{4}, + _smoothnessUniform{5}; }; /** @brief Two-dimensional distance field vector shader */ @@ -183,6 +243,20 @@ typedef DistanceFieldVector<2> DistanceFieldVector2D; /** @brief Three-dimensional distance field vector shader */ typedef DistanceFieldVector<3> DistanceFieldVector3D; +#ifdef DOXYGEN_GENERATING_OUTPUT +/** @debugoperatorclassenum{DistanceFieldVector,DistanceFieldVector::Flag} */ +template Debug& operator<<(Debug& debug, DistanceFieldVector::Flag value); + +/** @debugoperatorclassenum{DistanceFieldVector,DistanceFieldVector::Flags} */ +template Debug& operator<<(Debug& debug, DistanceFieldVector::Flags value); +#else +namespace Implementation { + MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, DistanceFieldVectorFlag value); + MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, DistanceFieldVectorFlags value); + CORRADE_ENUMSET_OPERATORS(DistanceFieldVectorFlags) +} +#endif + }} #endif diff --git a/src/Magnum/Shaders/Flat.cpp b/src/Magnum/Shaders/Flat.cpp index 3880c2d5a..ac7dfdb05 100644 --- a/src/Magnum/Shaders/Flat.cpp +++ b/src/Magnum/Shaders/Flat.cpp @@ -50,6 +50,9 @@ namespace { } template Flat::Flat(const Flags flags): _flags(flags) { + CORRADE_ASSERT(!(flags & Flag::TextureTransformation) || (flags & Flag::Textured), + "Shaders::Flat: texture transformation enabled but the shader is not textured", ); + #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ if(!Utility::Resource::hasGroup("MagnumShaders")) @@ -68,6 +71,7 @@ template Flat::Flat(const Flags flags): _fla vert.addSource(flags & Flag::Textured ? "#define TEXTURED\n" : "") .addSource(flags & Flag::VertexColor ? "#define VERTEX_COLOR\n" : "") + .addSource(flags & Flag::TextureTransformation ? "#define TEXTURE_TRANSFORMATION\n" : "") .addSource(rs.get("generic.glsl")) .addSource(rs.get(vertexShaderName())); frag.addSource(flags & Flag::Textured ? "#define TEXTURED\n" : "") @@ -111,6 +115,8 @@ template Flat::Flat(const Flags flags): _fla #endif { _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); + if(flags & Flag::TextureTransformation) + _textureMatrixUniform = uniformLocation("textureMatrix"); _colorUniform = uniformLocation("color"); if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask"); #ifndef MAGNUM_TARGET_GLES2 @@ -128,6 +134,7 @@ template Flat::Flat(const Flags flags): _fla /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ #ifdef MAGNUM_TARGET_GLES setTransformationProjectionMatrix({}); + if(flags & Flag::TextureTransformation) setTextureMatrix({}); setColor(Magnum::Color4{1.0f}); if(flags & Flag::AlphaMask) setAlphaMask(0.5f); /* Object ID is zero by default */ @@ -139,6 +146,13 @@ template Flat& Flat::setTransfor return *this; } +template Flat& Flat::setTextureMatrix(const Matrix3& matrix) { + CORRADE_ASSERT(_flags & Flag::TextureTransformation, + "Shaders::Flat::setTextureMatrix(): the shader was not created with texture transformation enabled", *this); + setUniform(_textureMatrixUniform, matrix); + return *this; +} + template Flat& Flat::setColor(const Magnum::Color4& color) { setUniform(_colorUniform, color); return *this; @@ -181,6 +195,7 @@ Debug& operator<<(Debug& debug, const FlatFlag value) { _c(Textured) _c(AlphaMask) _c(VertexColor) + _c(TextureTransformation) #ifndef MAGNUM_TARGET_GLES2 _c(ObjectId) #endif @@ -196,6 +211,7 @@ Debug& operator<<(Debug& debug, const FlatFlags value) { FlatFlag::Textured, FlatFlag::AlphaMask, FlatFlag::VertexColor, + FlatFlag::TextureTransformation, #ifndef MAGNUM_TARGET_GLES2 FlatFlag::ObjectId #endif diff --git a/src/Magnum/Shaders/Flat.frag b/src/Magnum/Shaders/Flat.frag index ae2f759e9..71579c511 100644 --- a/src/Magnum/Shaders/Flat.frag +++ b/src/Magnum/Shaders/Flat.frag @@ -37,7 +37,7 @@ uniform lowp sampler2D textureData; #endif #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 1) +layout(location = 2) #endif uniform lowp vec4 color #ifndef GL_ES @@ -47,7 +47,7 @@ uniform lowp vec4 color #ifdef ALPHA_MASK #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 2) +layout(location = 3) #endif uniform lowp float alphaMask #ifndef GL_ES @@ -58,7 +58,7 @@ uniform lowp float alphaMask #ifdef OBJECT_ID #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 3) +layout(location = 4) #endif /* mediump is just 2^10, which might not be enough, this is 2^16 */ uniform highp uint objectId; /* defaults to zero */ diff --git a/src/Magnum/Shaders/Flat.h b/src/Magnum/Shaders/Flat.h index 38a1a87f0..4f3bb24d1 100644 --- a/src/Magnum/Shaders/Flat.h +++ b/src/Magnum/Shaders/Flat.h @@ -41,8 +41,9 @@ namespace Implementation { Textured = 1 << 0, AlphaMask = 1 << 1, VertexColor = 1 << 2, + TextureTransformation = 1 << 3, #ifndef MAGNUM_TARGET_GLES2 - ObjectId = 1 << 3 + ObjectId = 1 << 4 #endif }; typedef Containers::EnumSet FlatFlags; @@ -216,6 +217,14 @@ template class MAGNUM_SHADERS_EXPORT Flat: public GL::Ab */ VertexColor = 1 << 2, + /** + * Enable texture coordinate transformation. If this flag is set, + * the shader expects that @ref Flag::Textured is enabled as well. + * @see @ref setTextureMatrix() + * @m_since_latest + */ + TextureTransformation = 1 << 3, + #ifndef MAGNUM_TARGET_GLES2 /** * Enable object ID output. See @ref Shaders-Flat-usage-object-id @@ -225,7 +234,7 @@ template class MAGNUM_SHADERS_EXPORT Flat: public GL::Ab * WebGL 1.0. * @m_since{2019,10} */ - ObjectId = 1 << 3 + ObjectId = 1 << 4 #endif }; @@ -285,6 +294,17 @@ template class MAGNUM_SHADERS_EXPORT Flat: public GL::Ab */ Flat& setTransformationProjectionMatrix(const MatrixTypeFor& matrix); + /** + * @brief Set texture coordinate transformation matrix + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with + * @ref Flag::TextureTransformation enabled. Initial value is an + * identity matrix. + */ + Flat& setTextureMatrix(const Matrix3& matrix); + /** * @brief Set color * @return Reference to self (for method chaining) @@ -302,7 +322,8 @@ template class MAGNUM_SHADERS_EXPORT Flat: public GL::Ab * * Expects that the shader was created with @ref Flag::Textured * enabled. - * @see @ref setColor() + * @see @ref setColor(), @ref Flag::TextureTransformation, + * @ref setTextureMatrix() */ Flat& bindTexture(GL::Texture2D& texture); @@ -344,10 +365,11 @@ template class MAGNUM_SHADERS_EXPORT Flat: public GL::Ab Flags _flags; Int _transformationProjectionMatrixUniform{0}, - _colorUniform{1}, - _alphaMaskUniform{2}; + _textureMatrixUniform{1}, + _colorUniform{2}, + _alphaMaskUniform{3}; #ifndef MAGNUM_TARGET_GLES2 - Int _objectIdUniform{3}; + Int _objectIdUniform{4}; #endif }; diff --git a/src/Magnum/Shaders/Flat2D.vert b/src/Magnum/Shaders/Flat2D.vert index f8c8a1bc1..1f772f395 100644 --- a/src/Magnum/Shaders/Flat2D.vert +++ b/src/Magnum/Shaders/Flat2D.vert @@ -37,6 +37,17 @@ uniform highp mat3 transformationProjectionMatrix #endif ; +#ifdef TEXTURE_TRANSFORMATION +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 1) +#endif +uniform mediump mat3 textureMatrix + #ifndef GL_ES + = mat3(1.0) + #endif + ; +#endif + #ifdef EXPLICIT_ATTRIB_LOCATION layout(location = POSITION_ATTRIBUTE_LOCATION) #endif @@ -65,7 +76,13 @@ void main() { #ifdef TEXTURED /* Texture coordinates, if needed */ - interpolatedTextureCoordinates = textureCoordinates; + interpolatedTextureCoordinates = + #ifdef TEXTURE_TRANSFORMATION + (textureMatrix*vec3(textureCoordinates, 1.0)).xy + #else + textureCoordinates + #endif + ; #endif #ifdef VERTEX_COLOR diff --git a/src/Magnum/Shaders/Flat3D.vert b/src/Magnum/Shaders/Flat3D.vert index eda843c6b..0b8167293 100644 --- a/src/Magnum/Shaders/Flat3D.vert +++ b/src/Magnum/Shaders/Flat3D.vert @@ -37,6 +37,17 @@ uniform highp mat4 transformationProjectionMatrix #endif ; +#ifdef TEXTURE_TRANSFORMATION +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 1) +#endif +uniform mediump mat3 textureMatrix + #ifndef GL_ES + = mat3(1.0) + #endif + ; +#endif + #ifdef EXPLICIT_ATTRIB_LOCATION layout(location = POSITION_ATTRIBUTE_LOCATION) #endif @@ -65,7 +76,13 @@ void main() { #ifdef TEXTURED /* Texture coordinates, if needed */ - interpolatedTextureCoordinates = textureCoordinates; + interpolatedTextureCoordinates = + #ifdef TEXTURE_TRANSFORMATION + (textureMatrix*vec3(textureCoordinates, 1.0)).xy + #else + textureCoordinates + #endif + ; #endif #ifdef VERTEX_COLOR diff --git a/src/Magnum/Shaders/Phong.cpp b/src/Magnum/Shaders/Phong.cpp index 2414dbb7a..3c55782bb 100644 --- a/src/Magnum/Shaders/Phong.cpp +++ b/src/Magnum/Shaders/Phong.cpp @@ -38,6 +38,7 @@ #include "Magnum/GL/Shader.h" #include "Magnum/GL/Texture.h" #include "Magnum/Math/Color.h" +#include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Matrix4.h" #include "Magnum/Shaders/Implementation/CreateCompatibilityShader.h" @@ -54,6 +55,9 @@ namespace { } Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _lightCount{lightCount}, _lightColorsUniform{_lightPositionsUniform + Int(lightCount)} { + CORRADE_ASSERT(!(flags & Flag::TextureTransformation) || (flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture)), + "Shaders::Phong: texture transformation enabled but the shader is not textured", ); + #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ if(!Utility::Resource::hasGroup("MagnumShaders")) @@ -95,6 +99,7 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l vert.addSource(flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture) ? "#define TEXTURED\n" : "") .addSource(flags & Flag::NormalTexture ? "#define NORMAL_TEXTURE\n" : "") .addSource(flags & Flag::VertexColor ? "#define VERTEX_COLOR\n" : "") + .addSource(flags & Flag::TextureTransformation ? "#define TEXTURE_TRANSFORMATION\n" : "") .addSource(Utility::formatString("#define LIGHT_COUNT {}\n", lightCount)) .addSource(rs.get("generic.glsl")) .addSource(rs.get("Phong.vert")); @@ -152,6 +157,8 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l #endif { _transformationMatrixUniform = uniformLocation("transformationMatrix"); + if(flags & Flag::TextureTransformation) + _textureMatrixUniform = uniformLocation("textureMatrix"); _projectionMatrixUniform = uniformLocation("projectionMatrix"); _ambientColorUniform = uniformLocation("ambientColor"); if(lightCount) { @@ -195,6 +202,7 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l /* Light position is zero by default */ setNormalMatrix({}); } + if(flags & Flag::TextureTransformation) setTextureMatrix({}); if(flags & Flag::AlphaMask) setAlphaMask(0.5f); /* Object ID is zero by default */ #endif @@ -286,6 +294,13 @@ Phong& Phong::setProjectionMatrix(const Matrix4& matrix) { return *this; } +Phong& Phong::setTextureMatrix(const Matrix3& matrix) { + CORRADE_ASSERT(_flags & Flag::TextureTransformation, + "Shaders::Phong::setTextureMatrix(): the shader was not created with texture transformation enabled", *this); + setUniform(_textureMatrixUniform, matrix); + return *this; +} + Phong& Phong::setLightPositions(const Containers::ArrayView positions) { CORRADE_ASSERT(_lightCount == positions.size(), "Shaders::Phong::setLightPositions(): expected" << _lightCount << "items but got" << positions.size(), *this); @@ -338,6 +353,7 @@ Debug& operator<<(Debug& debug, const Phong::Flag value) { _c(NormalTexture) _c(AlphaMask) _c(VertexColor) + _c(TextureTransformation) #ifndef MAGNUM_TARGET_GLES2 _c(ObjectId) #endif @@ -356,6 +372,7 @@ Debug& operator<<(Debug& debug, const Phong::Flags value) { Phong::Flag::NormalTexture, Phong::Flag::AlphaMask, Phong::Flag::VertexColor, + Phong::Flag::TextureTransformation, #ifndef MAGNUM_TARGET_GLES2 Phong::Flag::ObjectId #endif diff --git a/src/Magnum/Shaders/Phong.h b/src/Magnum/Shaders/Phong.h index 5ee92ef8a..b40b3bd23 100644 --- a/src/Magnum/Shaders/Phong.h +++ b/src/Magnum/Shaders/Phong.h @@ -251,6 +251,17 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { */ VertexColor = 1 << 5, + /** + * Enable texture coordinate transformation. If this flag is set, + * the shader expects that at least one of + * @ref Flag::AmbientTexture, @ref Flag::DiffuseTexture, + * @ref Flag::SpecularTexture or @ref Flag::NormalTexture is + * enabled as well. + * @see @ref setTextureMatrix() + * @m_since_latest + */ + TextureTransformation = 1 << 6, + #ifndef MAGNUM_TARGET_GLES2 /** * Enable object ID output. See @ref Shaders-Phong-usage-object-id @@ -260,7 +271,7 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { * WebGL 1.0. * @m_since{2019,10} */ - ObjectId = 1 << 6 + ObjectId = 1 << 7 #endif }; @@ -481,6 +492,17 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { */ Phong& setProjectionMatrix(const Matrix4& matrix); + /** + * @brief Set texture coordinate transformation matrix + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with + * @ref Flag::TextureTransformation enabled. Initial value is an + * identity matrix. + */ + Phong& setTextureMatrix(const Matrix3& matrix); + /** * @brief Set light positions * @return Reference to self (for method chaining) @@ -567,6 +589,7 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { Int _transformationMatrixUniform{0}, _projectionMatrixUniform{1}, _normalMatrixUniform{2}, + _textureMatrixUniform{3}, _ambientColorUniform{4}, _diffuseColorUniform{5}, _specularColorUniform{6}, diff --git a/src/Magnum/Shaders/Phong.vert b/src/Magnum/Shaders/Phong.vert index 5e6d405d5..269402458 100644 --- a/src/Magnum/Shaders/Phong.vert +++ b/src/Magnum/Shaders/Phong.vert @@ -57,6 +57,17 @@ uniform mediump mat3 normalMatrix ; #endif +#ifdef TEXTURE_TRANSFORMATION +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 3) +#endif +uniform mediump mat3 textureMatrix + #ifndef GL_ES + = mat3(1.0) + #endif + ; +#endif + #if LIGHT_COUNT /* Needs to be last because it uses locations 10 to 10 + LIGHT_COUNT - 1 */ #ifdef EXPLICIT_UNIFORM_LOCATION @@ -136,7 +147,13 @@ void main() { #ifdef TEXTURED /* Texture coordinates, if needed */ - interpolatedTextureCoordinates = textureCoordinates; + interpolatedTextureCoordinates = + #ifdef TEXTURE_TRANSFORMATION + (textureMatrix*vec3(textureCoordinates, 1.0)).xy + #else + textureCoordinates + #endif + ; #endif #ifdef VERTEX_COLOR diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp index 1c45bf2fd..44f1e672b 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp @@ -23,10 +23,12 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include #include +#include #include "Magnum/Image.h" #include "Magnum/ImageView.h" @@ -60,6 +62,8 @@ struct DistanceFieldVectorGLTest: GL::OpenGLTester { template void construct(); template void constructMove(); + template void setTextureMatrixNotEnabled(); + void renderSetup(); void renderTeardown(); @@ -94,21 +98,46 @@ using namespace Math::Literals; constexpr struct { const char* name; + DistanceFieldVector2D::Flags flags; +} ConstructData[]{ + {"", {}}, + {"texture transformation", DistanceFieldVector2D::Flag::TextureTransformation} +}; + +const struct { + const char* name; + DistanceFieldVector2D::Flags flags; + Matrix3 textureTransformation; + Color4 color, outlineColor; Float outlineRangeStart, outlineRangeEnd, smoothness; const char* file2D; const char* file3D; + bool flip; } RenderData[] { - {"smooth0.1", 0.5f, 1.0f, 0.1f, "smooth0.1-2D.tga", "smooth0.1-3D.tga"}, - {"smooth0.2", 0.5f, 1.0f, 0.2f, "smooth0.2-2D.tga", "smooth0.2-3D.tga"}, - {"outline", 0.6f, 0.45f, 0.05f, "outline2D.tga", "outline3D.tga"} + {"texture transformation", DistanceFieldVector2D::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), + 0xffffff_rgbf, 0x00000000_rgbaf, 0.5f, 1.0f, 0.04f, + "defaults-distancefield.tga", "defaults-distancefield.tga", true}, + {"smooth0.1", {}, {}, 0xffff99_rgbf, 0x9999ff_rgbf, 0.5f, 1.0f, 0.1f, + "smooth0.1-2D.tga", "smooth0.1-3D.tga", false}, + {"smooth0.2", {}, {}, 0xffff99_rgbf, 0x9999ff_rgbf, 0.5f, 1.0f, 0.2f, + "smooth0.2-2D.tga", "smooth0.2-3D.tga", false}, + {"outline", {}, {}, 0xffff99_rgbf, 0x9999ff_rgbf, 0.6f, 0.45f, 0.05f, + "outline2D.tga", "outline3D.tga", false} }; DistanceFieldVectorGLTest::DistanceFieldVectorGLTest() { - addTests({ + addInstancedTests({ &DistanceFieldVectorGLTest::construct<2>, - &DistanceFieldVectorGLTest::construct<3>, + &DistanceFieldVectorGLTest::construct<3>}, + Containers::arraySize(ConstructData)); + + addTests({ &DistanceFieldVectorGLTest::constructMove<2>, - &DistanceFieldVectorGLTest::constructMove<3>}); + &DistanceFieldVectorGLTest::constructMove<3>, + + &DistanceFieldVectorGLTest::setTextureMatrixNotEnabled<2>, + &DistanceFieldVectorGLTest::setTextureMatrixNotEnabled<3>}); addTests({&DistanceFieldVectorGLTest::renderDefaults2D, &DistanceFieldVectorGLTest::renderDefaults3D}, @@ -148,7 +177,11 @@ DistanceFieldVectorGLTest::DistanceFieldVectorGLTest() { template void DistanceFieldVectorGLTest::construct() { setTestCaseTemplateName(std::to_string(dimensions)); - DistanceFieldVector shader; + auto&& data = ConstructData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + DistanceFieldVector shader{data.flags}; + CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_VERIFY(shader.id()); { #ifdef CORRADE_TARGET_APPLE @@ -163,7 +196,7 @@ template void DistanceFieldVectorGLTest::construct() { template void DistanceFieldVectorGLTest::constructMove() { setTestCaseTemplateName(std::to_string(dimensions)); - DistanceFieldVector a; + DistanceFieldVector a{DistanceFieldVector::Flag::TextureTransformation}; const GLuint id = a.id(); CORRADE_VERIFY(id); @@ -171,14 +204,29 @@ template void DistanceFieldVectorGLTest::constructMove() DistanceFieldVector b{std::move(a)}; CORRADE_COMPARE(b.id(), id); + CORRADE_COMPARE(b.flags(), DistanceFieldVector::Flag::TextureTransformation); CORRADE_VERIFY(!a.id()); DistanceFieldVector c{NoCreate}; c = std::move(b); CORRADE_COMPARE(c.id(), id); + CORRADE_COMPARE(c.flags(), DistanceFieldVector::Flag::TextureTransformation); CORRADE_VERIFY(!b.id()); } +template void DistanceFieldVectorGLTest::setTextureMatrixNotEnabled() { + setTestCaseTemplateName(std::to_string(dimensions)); + + std::ostringstream out; + Error redirectError{&out}; + + DistanceFieldVector shader; + shader.setTextureMatrix({}); + + CORRADE_COMPARE(out.str(), + "Shaders::DistanceFieldVector::setTextureMatrix(): the shader was not created with texture transformation enabled\n"); +} + constexpr Vector2i RenderSize{80, 80}; void DistanceFieldVectorGLTest::renderSetup() { @@ -355,18 +403,30 @@ void DistanceFieldVectorGLTest::render2D() { .setSubImage(0, {}, *image); #endif - DistanceFieldVector2D{} + DistanceFieldVector2D shader{data.flags}; + shader /** @todo implement background color */ - .setColor(0xffff99_rgbf) - .setOutlineColor(0x9999ff_rgbf) + .setColor(data.color) + .setOutlineColor(data.outlineColor) .setOutlineRange(data.outlineRangeStart, data.outlineRangeEnd) .setSmoothness(data.smoothness) - .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) - .bindVectorTexture(texture) - .draw(square); + .bindVectorTexture(texture); + + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); + else shader.setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})); + + shader.draw(square); MAGNUM_VERIFY_NO_GL_ERROR(); + Image2D rendered = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}); + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::StridedArrayView2D pixels = + Containers::arrayCast(rendered.pixels()); + if(data.flip) pixels = pixels.flipped<0>().flipped<1>(); + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) /* SwiftShader has off-by-one differences when smoothing, Apple A8 a bit more. */ @@ -375,9 +435,7 @@ void DistanceFieldVectorGLTest::render2D() { /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ const Float maxThreshold = 17.0f, meanThreshold = 2.386f; #endif - CORRADE_COMPARE_WITH( - /* Dropping the alpha channel, as it's always 1.0 */ - Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + CORRADE_COMPARE_WITH(pixels, Utility::Directory::join({_testDir, "VectorTestFiles", data.file2D}), (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } @@ -411,22 +469,33 @@ void DistanceFieldVectorGLTest::render3D() { .setSubImage(0, {}, *image); #endif - DistanceFieldVector3D{} + DistanceFieldVector3D shader{data.flags}; + shader /** @todo implement background color */ - .setColor(0xffff99_rgbf) - .setOutlineColor(0x9999ff_rgbf) + .setColor(data.color) + .setOutlineColor(data.outlineColor) .setOutlineRange(data.outlineRangeStart, data.outlineRangeEnd) .setSmoothness(data.smoothness) - .setTransformationProjectionMatrix( - Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationZ(15.0_degf)) - .bindVectorTexture(texture) - .draw(plane); + .bindVectorTexture(texture); + + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); + else shader.setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationZ(15.0_degf)); + + shader.draw(plane); MAGNUM_VERIFY_NO_GL_ERROR(); + Image2D rendered = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}); + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::StridedArrayView2D pixels = + Containers::arrayCast(rendered.pixels()); + if(data.flip) pixels = pixels.flipped<0>().flipped<1>(); + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) /* SwiftShader has off-by-one differences when smoothing plus a bunch of different pixels on primitive edges, Apple A8 a bit more. */ @@ -435,9 +504,7 @@ void DistanceFieldVectorGLTest::render3D() { /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ const Float maxThreshold = 17.0f, meanThreshold = 1.613f; #endif - CORRADE_COMPARE_WITH( - /* Dropping the alpha channel, as it's always 1.0 */ - Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + CORRADE_COMPARE_WITH(pixels, Utility::Directory::join({_testDir, "VectorTestFiles", data.file3D}), (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp index 7d0e071e2..80ea945b8 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp @@ -23,7 +23,9 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include "Magnum/Shaders/DistanceFieldVector.h" @@ -37,6 +39,9 @@ struct DistanceFieldVectorTest: TestSuite::Tester { void constructCopy2D(); void constructCopy3D(); + + void debugFlag(); + void debugFlags(); }; DistanceFieldVectorTest::DistanceFieldVectorTest() { @@ -44,7 +49,10 @@ DistanceFieldVectorTest::DistanceFieldVectorTest() { &DistanceFieldVectorTest::constructNoCreate3D, &DistanceFieldVectorTest::constructCopy2D, - &DistanceFieldVectorTest::constructCopy3D}); + &DistanceFieldVectorTest::constructCopy3D, + + &DistanceFieldVectorTest::debugFlag, + &DistanceFieldVectorTest::debugFlags}); } void DistanceFieldVectorTest::constructNoCreate2D() { @@ -75,6 +83,20 @@ void DistanceFieldVectorTest::constructCopy3D() { CORRADE_VERIFY(!(std::is_assignable{})); } +void DistanceFieldVectorTest::debugFlag() { + std::ostringstream out; + + Debug{&out} << DistanceFieldVector2D::Flag::TextureTransformation << DistanceFieldVector2D::Flag(0xf0); + CORRADE_COMPARE(out.str(), "Shaders::DistanceFieldVector::Flag::TextureTransformation Shaders::DistanceFieldVector::Flag(0xf0)\n"); +} + +void DistanceFieldVectorTest::debugFlags() { + std::ostringstream out; + + Debug{&out} << DistanceFieldVector3D::Flags{DistanceFieldVector3D::Flag::TextureTransformation|DistanceFieldVector3D::Flag(0xf0)} << DistanceFieldVector3D::Flags{}; + CORRADE_COMPARE(out.str(), "Shaders::DistanceFieldVector::Flag::TextureTransformation|Shaders::DistanceFieldVector::Flag(0xf0) Shaders::DistanceFieldVector::Flags{}\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::DistanceFieldVectorTest) diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index 1fc4effff..cf7485134 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -62,10 +62,14 @@ struct FlatGLTest: GL::OpenGLTester { explicit FlatGLTest(); template void construct(); + template void constructMove(); + template void constructTextureTransformationNotTextured(); + template void bindTextureNotEnabled(); template void setAlphaMaskNotEnabled(); + template void setTextureMatrixNotEnabled(); #ifndef MAGNUM_TARGET_GLES2 template void setObjectIdNotEnabled(); #endif @@ -132,6 +136,7 @@ constexpr struct { } ConstructData[]{ {"", {}}, {"textured", Flat2D::Flag::Textured}, + {"textured + texture transformation", Flat2D::Flag::Textured|Flat2D::Flag::TextureTransformation}, {"alpha mask", Flat2D::Flag::AlphaMask}, {"alpha mask + textured", Flat2D::Flag::AlphaMask|Flat2D::Flag::Textured}, {"vertex colors", Flat2D::Flag::VertexColor}, @@ -142,6 +147,19 @@ constexpr struct { #endif }; +const struct { + const char* name; + Flat2D::Flags flags; + Matrix3 textureTransformation; + bool flip; +} RenderTexturedData[]{ + {"", Flat2D::Flag::Textured, {}, false}, + {"texture transformeation", + Flat2D::Flag::Textured|Flat2D::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), + true}, +}; + const struct { const char* name; const char* expected2D; @@ -174,10 +192,15 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::constructMove<2>, &FlatGLTest::constructMove<3>, + &FlatGLTest::constructTextureTransformationNotTextured<2>, + &FlatGLTest::constructTextureTransformationNotTextured<3>, + &FlatGLTest::bindTextureNotEnabled<2>, &FlatGLTest::bindTextureNotEnabled<3>, &FlatGLTest::setAlphaMaskNotEnabled<2>, &FlatGLTest::setAlphaMaskNotEnabled<3>, + &FlatGLTest::setTextureMatrixNotEnabled<2>, + &FlatGLTest::setTextureMatrixNotEnabled<3>, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::setObjectIdNotEnabled<2>, &FlatGLTest::setObjectIdNotEnabled<3> @@ -189,10 +212,17 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderColored2D, &FlatGLTest::renderColored3D, &FlatGLTest::renderSinglePixelTextured2D, - &FlatGLTest::renderSinglePixelTextured3D, - &FlatGLTest::renderTextured2D, - &FlatGLTest::renderTextured3D, - &FlatGLTest::renderVertexColor2D, + &FlatGLTest::renderSinglePixelTextured3D}, + &FlatGLTest::renderSetup, + &FlatGLTest::renderTeardown); + + addInstancedTests({&FlatGLTest::renderTextured2D, + &FlatGLTest::renderTextured3D}, + Containers::arraySize(RenderTexturedData), + &FlatGLTest::renderSetup, + &FlatGLTest::renderTeardown); + + addTests({&FlatGLTest::renderVertexColor2D, &FlatGLTest::renderVertexColor2D, &FlatGLTest::renderVertexColor3D, &FlatGLTest::renderVertexColor3D}, @@ -276,6 +306,16 @@ template void FlatGLTest::constructMove() { CORRADE_VERIFY(!b.id()); } +template void FlatGLTest::constructTextureTransformationNotTextured() { + setTestCaseTemplateName(std::to_string(dimensions)); + + std::ostringstream out; + Error redirectError{&out}; + Flat{Flat::Flag::TextureTransformation}; + CORRADE_COMPARE(out.str(), + "Shaders::Flat: texture transformation enabled but the shader is not textured\n"); +} + template void FlatGLTest::bindTextureNotEnabled() { setTestCaseTemplateName(std::to_string(dimensions)); @@ -302,6 +342,19 @@ template void FlatGLTest::setAlphaMaskNotEnabled() { "Shaders::Flat::setAlphaMask(): the shader was not created with alpha mask enabled\n"); } +template void FlatGLTest::setTextureMatrixNotEnabled() { + setTestCaseTemplateName(std::to_string(dimensions)); + + std::ostringstream out; + Error redirectError{&out}; + + Flat shader; + shader.setTextureMatrix({}); + + CORRADE_COMPARE(out.str(), + "Shaders::Flat::setTextureMatrix(): the shader was not created with texture transformation enabled\n"); +} + #ifndef MAGNUM_TARGET_GLES2 template void FlatGLTest::setObjectIdNotEnabled() { setTestCaseTemplateName(std::to_string(dimensions)); @@ -539,6 +592,9 @@ void FlatGLTest::renderSinglePixelTextured3D() { } void FlatGLTest::renderTextured2D() { + auto&& data = RenderTexturedData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); @@ -558,16 +614,27 @@ void FlatGLTest::renderTextured2D() { .setStorage(1, TextureFormatRGB, image->size()) .setSubImage(0, {}, *image); - Flat2D{Flat2D::Flag::Textured} + Flat2D shader{data.flags}; + shader .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) /* Colorized. Case without a color (where it should be white) is tested in renderSinglePixelTextured() */ .setColor(0x9999ff_rgbf) - .bindTexture(texture) - .draw(circle); + .bindTexture(texture); + + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); + + shader.draw(circle); MAGNUM_VERIFY_NO_GL_ERROR(); + Image2D rendered = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}); + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::StridedArrayView2D pixels = + Containers::arrayCast(rendered.pixels()); + if(data.flip) pixels = pixels.flipped<0>().flipped<1>(); + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) /* SwiftShader has minor rounding errors, Apple A8 slightly more */ const Float maxThreshold = 2.334f, meanThreshold = 0.023f; @@ -575,14 +642,15 @@ void FlatGLTest::renderTextured2D() { /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ const Float maxThreshold = 15.667f, meanThreshold = 3.254f; #endif - CORRADE_COMPARE_WITH( - /* Dropping the alpha channel, as it's always 1.0 */ - Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + CORRADE_COMPARE_WITH(pixels, Utility::Directory::join(_testDir, "FlatTestFiles/textured2D.tga"), (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } void FlatGLTest::renderTextured3D() { + auto&& data = RenderTexturedData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); @@ -602,20 +670,31 @@ void FlatGLTest::renderTextured3D() { .setStorage(1, TextureFormatRGB, image->size()) .setSubImage(0, {}, *image); - Flat3D{Flat3D::Flag::Textured} + Flat3D shader{data.flags}; + shader .setTransformationProjectionMatrix( Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)) + Matrix4::rotationY(data.flip ? 15.0_degf : -15.0_degf)* + Matrix4::rotationX(data.flip ? -15.0_degf : 15.0_degf)) /* Colorized. Case without a color (where it should be white) is tested in renderSinglePixelTextured() */ .setColor(0x9999ff_rgbf) - .bindTexture(texture) - .draw(sphere); + .bindTexture(texture); + + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); + + shader.draw(sphere); MAGNUM_VERIFY_NO_GL_ERROR(); + Image2D rendered = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}); + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::StridedArrayView2D pixels = + Containers::arrayCast(rendered.pixels()); + if(data.flip) pixels = pixels.flipped<0>().flipped<1>(); + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) /* SwiftShader has 5 different pixels on the edges */ const Float maxThreshold = 139.0f, meanThreshold = 0.087f; @@ -623,9 +702,7 @@ void FlatGLTest::renderTextured3D() { /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ const Float maxThreshold = 139.0f, meanThreshold = 2.896f; #endif - CORRADE_COMPARE_WITH( - /* Dropping the alpha channel, as it's always 1.0 */ - Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + CORRADE_COMPARE_WITH(pixels, Utility::Directory::join(_testDir, "FlatTestFiles/textured3D.tga"), (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index b1f5da774..9cdac683b 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -65,8 +65,11 @@ struct PhongGLTest: GL::OpenGLTester { void constructMove(); + void constructTextureTransformationNotTextured(); + void bindTexturesNotEnabled(); void setAlphaMaskNotEnabled(); + void setTextureMatrixNotEnabled(); #ifndef MAGNUM_TARGET_GLES2 void setObjectIdNotEnabled(); #endif @@ -134,6 +137,7 @@ constexpr struct { {"", {}, 1}, {"ambient texture", Phong::Flag::AmbientTexture, 1}, {"diffuse texture", Phong::Flag::DiffuseTexture, 1}, + {"diffuse texture + texture transform", Phong::Flag::DiffuseTexture|Phong::Flag::TextureTransformation, 1}, {"specular texture", Phong::Flag::SpecularTexture, 1}, {"normal texture", Phong::Flag::NormalTexture, 1}, {"ambient + diffuse texture", Phong::Flag::AmbientTexture|Phong::Flag::DiffuseTexture, 1}, @@ -174,15 +178,20 @@ constexpr struct { {"multi bind", true} }; -constexpr struct { +const struct { const char* name; const char* expected; Phong::Flags flags; + Matrix3 textureTransformation; } RenderTexturedData[]{ - {"all", "textured.tga", Phong::Flag::AmbientTexture|Phong::Flag::DiffuseTexture|Phong::Flag::SpecularTexture}, - {"ambient", "textured-ambient.tga", Phong::Flag::AmbientTexture}, - {"diffuse", "textured-diffuse.tga", Phong::Flag::DiffuseTexture}, - {"specular", "textured-specular.tga", Phong::Flag::SpecularTexture} + {"all", "textured.tga", Phong::Flag::AmbientTexture|Phong::Flag::DiffuseTexture|Phong::Flag::SpecularTexture, {}}, + {"ambient", "textured-ambient.tga", Phong::Flag::AmbientTexture, {}}, + {"diffuse", "textured-diffuse.tga", Phong::Flag::DiffuseTexture, {}}, + {"diffuse transformed", "textured-diffuse-transformed.tga", + Phong::Flag::DiffuseTexture|Phong::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}) + }, + {"specular", "textured-specular.tga", Phong::Flag::SpecularTexture, {}} }; /* MSVC 2015 doesn't like constexpr here due to the angles */ @@ -262,8 +271,11 @@ PhongGLTest::PhongGLTest() { addTests({&PhongGLTest::constructMove, + &PhongGLTest::constructTextureTransformationNotTextured, + &PhongGLTest::bindTexturesNotEnabled, &PhongGLTest::setAlphaMaskNotEnabled, + &PhongGLTest::setTextureMatrixNotEnabled, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::setObjectIdNotEnabled, #endif @@ -388,6 +400,14 @@ void PhongGLTest::constructMove() { CORRADE_VERIFY(!b.id()); } +void PhongGLTest::constructTextureTransformationNotTextured() { + std::ostringstream out; + Error redirectError{&out}; + Phong{Phong::Flag::TextureTransformation}; + CORRADE_COMPARE(out.str(), + "Shaders::Phong: texture transformation enabled but the shader is not textured\n"); +} + void PhongGLTest::bindTexturesNotEnabled() { std::ostringstream out; Error redirectError{&out}; @@ -419,6 +439,17 @@ void PhongGLTest::setAlphaMaskNotEnabled() { "Shaders::Phong::setAlphaMask(): the shader was not created with alpha mask enabled\n"); } +void PhongGLTest::setTextureMatrixNotEnabled() { + std::ostringstream out; + Error redirectError{&out}; + + Phong shader; + shader.setTextureMatrix({}); + + CORRADE_COMPARE(out.str(), + "Shaders::Phong::setTextureMatrix(): the shader was not created with texture transformation enabled\n"); +} + #ifndef MAGNUM_TARGET_GLES2 void PhongGLTest::setObjectIdNotEnabled() { std::ostringstream out; @@ -677,6 +708,9 @@ void PhongGLTest::renderTextured() { Phong shader{data.flags, 2}; + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); CORRADE_VERIFY(importer); diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-transformed.tga b/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-transformed.tga new file mode 100644 index 000000000..201778990 Binary files /dev/null and b/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-transformed.tga differ diff --git a/src/Magnum/Shaders/Test/VectorGLTest.cpp b/src/Magnum/Shaders/Test/VectorGLTest.cpp index e231e0933..646e09b06 100644 --- a/src/Magnum/Shaders/Test/VectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VectorGLTest.cpp @@ -23,10 +23,12 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include #include +#include #include "Magnum/Image.h" #include "Magnum/ImageView.h" @@ -60,6 +62,8 @@ struct VectorGLTest: GL::OpenGLTester { template void construct(); template void constructMove(); + template void setTextureMatrixNotEnabled(); + void renderSetup(); void renderTeardown(); @@ -92,17 +96,52 @@ struct VectorGLTest: GL::OpenGLTester { using namespace Math::Literals; +constexpr struct { + const char* name; + Vector2D::Flags flags; +} ConstructData[]{ + {"", {}}, + {"texture transformation", Vector2D::Flag::TextureTransformation} +}; + +const struct { + const char* name; + Vector2D::Flags flags; + Matrix3 textureTransformation; + Color4 backgroundColor, color; + const char* file2D; + const char* file3D; + bool flip; +} RenderData[] { + {"texture transformation", Vector2D::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), + 0x00000000_rgbaf, 0xffffff_rgbf, + "defaults.tga", "defaults.tga", true}, + {"", {}, {}, 0x9999ff_rgbf, 0xffff99_rgbf, + "vector2D.tga", "vector3D.tga", false} +}; + VectorGLTest::VectorGLTest() { - addTests({ + addInstancedTests({ &VectorGLTest::construct<2>, - &VectorGLTest::construct<3>, + &VectorGLTest::construct<3>}, + Containers::arraySize(ConstructData)); + + addTests({ &VectorGLTest::constructMove<2>, - &VectorGLTest::constructMove<3>}); + &VectorGLTest::constructMove<3>, + + &VectorGLTest::setTextureMatrixNotEnabled<2>, + &VectorGLTest::setTextureMatrixNotEnabled<3>}); addTests({&VectorGLTest::renderDefaults2D, - &VectorGLTest::renderDefaults3D, - &VectorGLTest::render2D, - &VectorGLTest::render3D}, + &VectorGLTest::renderDefaults3D}, + &VectorGLTest::renderSetup, + &VectorGLTest::renderTeardown); + + addInstancedTests({&VectorGLTest::render2D, + &VectorGLTest::render3D}, + Containers::arraySize(RenderData), &VectorGLTest::renderSetup, &VectorGLTest::renderTeardown); @@ -133,7 +172,11 @@ VectorGLTest::VectorGLTest() { template void VectorGLTest::construct() { setTestCaseTemplateName(std::to_string(dimensions)); - Vector shader; + auto&& data = ConstructData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Vector shader{data.flags}; + CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_VERIFY(shader.id()); { #ifdef CORRADE_TARGET_APPLE @@ -148,7 +191,7 @@ template void VectorGLTest::construct() { template void VectorGLTest::constructMove() { setTestCaseTemplateName(std::to_string(dimensions)); - Vector a; + Vector a{Vector::Flag::TextureTransformation}; const GLuint id = a.id(); CORRADE_VERIFY(id); @@ -156,14 +199,29 @@ template void VectorGLTest::constructMove() { Vector b{std::move(a)}; CORRADE_COMPARE(b.id(), id); + CORRADE_COMPARE(b.flags(), Vector::Flag::TextureTransformation); CORRADE_VERIFY(!a.id()); Vector c{NoCreate}; c = std::move(b); CORRADE_COMPARE(c.id(), id); + CORRADE_COMPARE(c.flags(), Vector::Flag::TextureTransformation); CORRADE_VERIFY(!b.id()); } +template void VectorGLTest::setTextureMatrixNotEnabled() { + setTestCaseTemplateName(std::to_string(dimensions)); + + std::ostringstream out; + Error redirectError{&out}; + + Vector shader; + shader.setTextureMatrix({}); + + CORRADE_COMPARE(out.str(), + "Shaders::Vector::setTextureMatrix(): the shader was not created with texture transformation enabled\n"); +} + constexpr Vector2i RenderSize{80, 80}; void VectorGLTest::renderSetup() { @@ -294,6 +352,9 @@ void VectorGLTest::renderDefaults3D() { } void VectorGLTest::render2D() { + auto&& data = RenderData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); @@ -319,17 +380,27 @@ void VectorGLTest::render2D() { .setSubImage(0, {}, *image); #endif - Vector2D{} - .setBackgroundColor(0x9999ff_rgbf) - .setColor(0xffff99_rgbf) - .setTransformationProjectionMatrix( - Matrix3::projection({2.1f, 2.1f})* - Matrix3::rotation(5.0_degf)) - .bindVectorTexture(texture) - .draw(square); + Vector2D shader{data.flags}; + shader.setBackgroundColor(data.backgroundColor) + .setColor(data.color) + .bindVectorTexture(texture); + + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); + else shader.setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::rotation(5.0_degf)); + + shader.draw(square); MAGNUM_VERIFY_NO_GL_ERROR(); + Image2D rendered = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}); + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::StridedArrayView2D pixels = + Containers::arrayCast(rendered.pixels()); + if(data.flip) pixels = pixels.flipped<0>().flipped<1>(); + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) /* SwiftShader has differently rasterized edges on four pixels */ const Float maxThreshold = 170.0f, meanThreshold = 0.146f; @@ -337,14 +408,15 @@ void VectorGLTest::render2D() { /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ const Float maxThreshold = 170.0f, meanThreshold = 0.962f; #endif - CORRADE_COMPARE_WITH( - /* Dropping the alpha channel, as it's always 1.0 */ - Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), - Utility::Directory::join(_testDir, "VectorTestFiles/vector2D.tga"), + CORRADE_COMPARE_WITH(pixels, + Utility::Directory::join({_testDir, "VectorTestFiles", data.file2D}), (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } void VectorGLTest::render3D() { + auto&& data = RenderData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); @@ -370,19 +442,29 @@ void VectorGLTest::render3D() { .setSubImage(0, {}, *image); #endif - Vector3D{} - .setBackgroundColor(0x9999ff_rgbf) - .setColor(0xffff99_rgbf) - .setTransformationProjectionMatrix( - Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationZ(15.0_degf)) - .bindVectorTexture(texture) - .draw(plane); + Vector3D shader{data.flags}; + shader.setBackgroundColor(data.backgroundColor) + .setColor(data.color) + .bindVectorTexture(texture); + + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); + else shader.setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationZ(15.0_degf)); + + shader.draw(plane); MAGNUM_VERIFY_NO_GL_ERROR(); + Image2D rendered = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}); + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::StridedArrayView2D pixels = + Containers::arrayCast(rendered.pixels()); + if(data.flip) pixels = pixels.flipped<0>().flipped<1>(); + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) /* SwiftShader has differently rasterized edges on four pixels */ const Float maxThreshold = 170.0f, meanThreshold = 0.171f; @@ -390,10 +472,8 @@ void VectorGLTest::render3D() { /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ const Float maxThreshold = 170.0f, meanThreshold = 0.660f; #endif - CORRADE_COMPARE_WITH( - /* Dropping the alpha channel, as it's always 1.0 */ - Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), - Utility::Directory::join(_testDir, "VectorTestFiles/vector3D.tga"), + CORRADE_COMPARE_WITH(pixels, + Utility::Directory::join({_testDir, "VectorTestFiles", data.file3D}), (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } diff --git a/src/Magnum/Shaders/Test/VectorTest.cpp b/src/Magnum/Shaders/Test/VectorTest.cpp index 98d40bb62..ed110ddd4 100644 --- a/src/Magnum/Shaders/Test/VectorTest.cpp +++ b/src/Magnum/Shaders/Test/VectorTest.cpp @@ -23,7 +23,9 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include "Magnum/Shaders/Vector.h" @@ -37,6 +39,9 @@ struct VectorTest: TestSuite::Tester { void constructCopy2D(); void constructCopy3D(); + + void debugFlag(); + void debugFlags(); }; VectorTest::VectorTest() { @@ -44,7 +49,10 @@ VectorTest::VectorTest() { &VectorTest::constructNoCreate3D, &VectorTest::constructCopy2D, - &VectorTest::constructCopy3D}); + &VectorTest::constructCopy3D, + + &VectorTest::debugFlag, + &VectorTest::debugFlags}); } void VectorTest::constructNoCreate2D() { @@ -75,6 +83,20 @@ void VectorTest::constructCopy3D() { CORRADE_VERIFY(!(std::is_assignable{})); } +void VectorTest::debugFlag() { + std::ostringstream out; + + Debug{&out} << Vector2D::Flag::TextureTransformation << Vector2D::Flag(0xf0); + CORRADE_COMPARE(out.str(), "Shaders::Vector::Flag::TextureTransformation Shaders::Vector::Flag(0xf0)\n"); +} + +void VectorTest::debugFlags() { + std::ostringstream out; + + Debug{&out} << Vector3D::Flags{Vector3D::Flag::TextureTransformation|Vector3D::Flag(0xf0)} << Vector3D::Flags{}; + CORRADE_COMPARE(out.str(), "Shaders::Vector::Flag::TextureTransformation|Shaders::Vector::Flag(0xf0) Shaders::Vector::Flags{}\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::VectorTest) diff --git a/src/Magnum/Shaders/Vector.cpp b/src/Magnum/Shaders/Vector.cpp index b14498237..f84c71ca4 100644 --- a/src/Magnum/Shaders/Vector.cpp +++ b/src/Magnum/Shaders/Vector.cpp @@ -25,6 +25,7 @@ #include "Vector.h" +#include #include #include @@ -45,7 +46,7 @@ namespace { template<> constexpr const char* vertexShaderName<3>() { return "AbstractVector3D.vert"; } } -template Vector::Vector() { +template Vector::Vector(const Flags flags): _flags{flags} { #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ if(!Utility::Resource::hasGroup("MagnumShaders")) @@ -62,7 +63,8 @@ template Vector::Vector() { GL::Shader vert = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Vertex); GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment); - vert.addSource(rs.get("generic.glsl")) + vert.addSource(flags & Flag::TextureTransformation ? "#define TEXTURE_TRANSFORMATION\n" : "") + .addSource(rs.get("generic.glsl")) .addSource(rs.get(vertexShaderName())); frag.addSource(rs.get("generic.glsl")) .addSource(rs.get("Vector.frag")); @@ -89,6 +91,8 @@ template Vector::Vector() { #endif { _transformationProjectionMatrixUniform = GL::AbstractShaderProgram::uniformLocation("transformationProjectionMatrix"); + if(flags & Flag::TextureTransformation) + _textureMatrixUniform = GL::AbstractShaderProgram::uniformLocation("textureMatrix"); _backgroundColorUniform = GL::AbstractShaderProgram::uniformLocation("backgroundColor"); _colorUniform = GL::AbstractShaderProgram::uniformLocation("color"); } @@ -103,6 +107,7 @@ template Vector::Vector() { /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ #ifdef MAGNUM_TARGET_GLES setTransformationProjectionMatrix({}); + if(flags & Flag::TextureTransformation) setTextureMatrix({}); setColor(Color4{1.0f}); /* Background color is zero by default */ #endif } @@ -112,6 +117,13 @@ template Vector& Vector::setTran return *this; } +template Vector& Vector::setTextureMatrix(const Matrix3& matrix) { + CORRADE_ASSERT(_flags & Flag::TextureTransformation, + "Shaders::Vector::setTextureMatrix(): the shader was not created with texture transformation enabled", *this); + GL::AbstractShaderProgram::setUniform(_textureMatrixUniform, matrix); + return *this; +} + template Vector& Vector::setBackgroundColor(const Color4& color) { GL::AbstractShaderProgram::setUniform(_backgroundColorUniform, color); return *this; @@ -125,4 +137,28 @@ template Vector& Vector::setColo template class Vector<2>; template class Vector<3>; +namespace Implementation { + +Debug& operator<<(Debug& debug, const VectorFlag value) { + debug << "Shaders::Vector::Flag" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(v) case VectorFlag::v: return debug << "::" #v; + _c(TextureTransformation) + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, const VectorFlags value) { + return Containers::enumSetDebugOutput(debug, value, "Shaders::Vector::Flags{}", { + VectorFlag::TextureTransformation + }); +} + +} + }} diff --git a/src/Magnum/Shaders/Vector.frag b/src/Magnum/Shaders/Vector.frag index 34f6cd5af..e5f50bb3f 100644 --- a/src/Magnum/Shaders/Vector.frag +++ b/src/Magnum/Shaders/Vector.frag @@ -30,12 +30,12 @@ #endif #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 1) +layout(location = 2) #endif uniform lowp vec4 backgroundColor; /* defaults to zero */ #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 2) +layout(location = 3) #endif uniform lowp vec4 color #ifndef GL_ES diff --git a/src/Magnum/Shaders/Vector.h b/src/Magnum/Shaders/Vector.h index c3a8e979a..942de64a1 100644 --- a/src/Magnum/Shaders/Vector.h +++ b/src/Magnum/Shaders/Vector.h @@ -35,6 +35,13 @@ namespace Magnum { namespace Shaders { +namespace Implementation { + enum class VectorFlag: UnsignedByte { + TextureTransformation = 1 << 0 + }; + typedef Containers::EnumSet VectorFlags; +} + /** @brief Vector shader @@ -63,7 +70,41 @@ Common rendering setup: */ template class MAGNUM_SHADERS_EXPORT Vector: public AbstractVector { public: - explicit Vector(); + #ifdef DOXYGEN_GENERATING_OUTPUT + /** + * @brief Flag + * @m_since_latest + * + * @see @ref Flags, @ref flags() + */ + enum class Flag: UnsignedByte { + /** + * Enable texture coordinate transformation. + * @see @ref setTextureMatrix() + * @m_since_latest + */ + TextureTransformation = 1 << 0 + }; + + /** + * @brief Flags + * @m_since_latest + * + * @see @ref flags() + */ + typedef Containers::EnumSet Flags; + #else + /* Done this way to be prepared for possible future diversion of 2D + and 3D flags (e.g. introducing 3D-specific features) */ + typedef Implementation::VectorFlag Flag; + typedef Implementation::VectorFlags Flags; + #endif + + /** + * @brief Constructor + * @param flags Flags + */ + explicit Vector(Flags flags = {}); /** * @brief Construct without creating the underlying OpenGL object @@ -96,6 +137,12 @@ template class MAGNUM_SHADERS_EXPORT Vector: public Abst /** @brief Move assignment */ Vector& operator=(Vector&&) noexcept = default; + /** + * @brief Flags + * @m_since_latest + */ + Flags flags() const { return _flags; } + /** * @brief Set transformation and projection matrix * @return Reference to self (for method chaining) @@ -104,6 +151,17 @@ template class MAGNUM_SHADERS_EXPORT Vector: public Abst */ Vector& setTransformationProjectionMatrix(const MatrixTypeFor& matrix); + /** + * @brief Set texture coordinate transformation matrix + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with + * @ref Flag::TextureTransformation enabled. Initial value is an + * identity matrix. + */ + Vector& setTextureMatrix(const Matrix3& matrix); + /** * @brief Set background color * @return Reference to self (for method chaining) @@ -139,9 +197,11 @@ template class MAGNUM_SHADERS_EXPORT Vector: public Abst using GL::AbstractShaderProgram::dispatchCompute; #endif + Flags _flags; Int _transformationProjectionMatrixUniform{0}, - _backgroundColorUniform{1}, - _colorUniform{2}; + _textureMatrixUniform{1}, + _backgroundColorUniform{2}, + _colorUniform{3}; }; /** @brief Two-dimensional vector shader */ @@ -150,6 +210,20 @@ typedef Vector<2> Vector2D; /** @brief Three-dimensional vector shader */ typedef Vector<3> Vector3D; +#ifdef DOXYGEN_GENERATING_OUTPUT +/** @debugoperatorclassenum{Vector,Vector::Flag} */ +template Debug& operator<<(Debug& debug, Vector::Flag value); + +/** @debugoperatorclassenum{Vector,Vector::Flags} */ +template Debug& operator<<(Debug& debug, Vector::Flags value); +#else +namespace Implementation { + MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, VectorFlag value); + MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, VectorFlags value); + CORRADE_ENUMSET_OPERATORS(VectorFlags) +} +#endif + }} #endif