From 9a06b3515bc5a0960343a86acd289aa4d62b6c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 13 Mar 2020 19:20:30 +0100 Subject: [PATCH] Shaders: implement texture coordinate transformation for all shaders. Except MeshVisualizer and VertexColor, which don't have any texturing, so there it's not needed. In most cases the tests are reusing existing ground truth files and only modifying transformations / flipping images. --- doc/changelog.dox | 5 + src/Magnum/Shaders/AbstractVector.h | 5 + src/Magnum/Shaders/AbstractVector2D.vert | 19 ++- src/Magnum/Shaders/AbstractVector3D.vert | 19 ++- src/Magnum/Shaders/CMakeLists.txt | 6 +- src/Magnum/Shaders/DistanceFieldVector.cpp | 40 ++++- src/Magnum/Shaders/DistanceFieldVector.frag | 8 +- src/Magnum/Shaders/DistanceFieldVector.h | 84 +++++++++- src/Magnum/Shaders/Flat.cpp | 16 ++ src/Magnum/Shaders/Flat.frag | 6 +- src/Magnum/Shaders/Flat.h | 34 +++- src/Magnum/Shaders/Flat2D.vert | 19 ++- src/Magnum/Shaders/Flat3D.vert | 19 ++- src/Magnum/Shaders/Phong.cpp | 17 ++ src/Magnum/Shaders/Phong.h | 25 ++- src/Magnum/Shaders/Phong.vert | 19 ++- .../Test/DistanceFieldVectorGLTest.cpp | 127 +++++++++++---- .../Shaders/Test/DistanceFieldVectorTest.cpp | 24 ++- src/Magnum/Shaders/Test/FlatGLTest.cpp | 113 ++++++++++--- src/Magnum/Shaders/Test/PhongGLTest.cpp | 44 +++++- .../textured-diffuse-transformed.tga | Bin 0 -> 19218 bytes src/Magnum/Shaders/Test/VectorGLTest.cpp | 148 ++++++++++++++---- src/Magnum/Shaders/Test/VectorTest.cpp | 24 ++- src/Magnum/Shaders/Vector.cpp | 40 ++++- src/Magnum/Shaders/Vector.frag | 4 +- src/Magnum/Shaders/Vector.h | 80 +++++++++- 26 files changed, 820 insertions(+), 125 deletions(-) create mode 100644 src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-transformed.tga 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 0000000000000000000000000000000000000000..201778990f0e193bc4edcc500b788459d72811c9 GIT binary patch literal 19218 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 literal 0 HcmV?d00001 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