diff --git a/doc/changelog.dox b/doc/changelog.dox index 151f27d93..60743d8bb 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -233,7 +233,7 @@ See also: @ref Shaders::Generic::TransformationMatrix, @ref Shaders::Generic::NormalMatrix and @ref Shaders::Generic::TextureOffset for instancing -- Instancing in @ref Shaders::Flat +- Instancing in @ref Shaders::Flat and @ref Shaders::Phong @subsubsection changelog-latest-new-trade Trade library diff --git a/doc/snippets/MagnumShaders.cpp b/doc/snippets/MagnumShaders.cpp index a871a4094..55b3e5a9f 100644 --- a/doc/snippets/MagnumShaders.cpp +++ b/doc/snippets/MagnumShaders.cpp @@ -269,6 +269,28 @@ mesh.setInstanceCount(Containers::arraySize(instanceData)) /* [Flat-usage-instancing] */ } +{ +GL::Mesh mesh; +/* [Phong-usage-instancing] */ +struct { + Matrix4 transformation; + Matrix3x3 normal; +} instanceData[] { + {Matrix4::translation({1.0f, 2.0f, 0.0f})*Matrix4::rotationX(90.0_degf), {}}, + {Matrix4::translation({2.0f, 1.0f, 0.0f})*Matrix4::rotationY(90.0_degf), {}}, + {Matrix4::translation({3.0f, 0.0f, 1.0f})*Matrix4::rotationZ(90.0_degf), {}}, + // ... +}; +for(auto& instance: instanceData) + instance.normal = instance.transformation.normalMatrix(); + +mesh.setInstanceCount(Containers::arraySize(instanceData)) + .addVertexBufferInstanced(GL::Buffer{instanceData}, 1, 0, + Shaders::Phong::TransformationMatrix{}, + Shaders::Phong::NormalMatrix{}); +/* [Phong-usage-instancing] */ +} + { /* [MeshVisualizer-usage-geom1] */ struct Vertex { diff --git a/src/Magnum/Shaders/Phong.cpp b/src/Magnum/Shaders/Phong.cpp index 5caeff18b..72238fb2c 100644 --- a/src/Magnum/Shaders/Phong.cpp +++ b/src/Magnum/Shaders/Phong.cpp @@ -104,6 +104,8 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l #ifndef MAGNUM_TARGET_GLES2 .addSource(flags >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") #endif + .addSource(flags & Flag::InstancedTransformation ? "#define INSTANCED_TRANSFORMATION\n" : "") + .addSource(flags >= Flag::InstancedTextureOffset ? "#define INSTANCED_TEXTURE_OFFSET\n" : "") .addSource(rs.get("generic.glsl")) .addSource(rs.get("Phong.vert")); frag.addSource(flags & Flag::AmbientTexture ? "#define AMBIENT_TEXTURE\n" : "") @@ -152,6 +154,10 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l } if(flags >= Flag::InstancedObjectId) bindAttributeLocation(ObjectId::Location, "instanceObjectId"); + if(flags & Flag::InstancedTransformation) + bindAttributeLocation(TransformationMatrix::Location, "instancedTransformationMatrix"); + if(flags >= Flag::InstancedTextureOffset) + bindAttributeLocation(TextureOffset::Location, "instancedTextureOffset"); #endif } #endif @@ -364,6 +370,8 @@ Debug& operator<<(Debug& debug, const Phong::Flag value) { _c(InstancedObjectId) _c(ObjectId) #endif + _c(InstancedTransformation) + _c(InstancedTextureOffset) #undef _c /* LCOV_EXCL_STOP */ } @@ -379,12 +387,13 @@ Debug& operator<<(Debug& debug, const Phong::Flags value) { Phong::Flag::NormalTexture, Phong::Flag::AlphaMask, Phong::Flag::VertexColor, + Phong::Flag::InstancedTextureOffset, /* Superset of TextureTransformation */ Phong::Flag::TextureTransformation, #ifndef MAGNUM_TARGET_GLES2 Phong::Flag::InstancedObjectId, /* Superset of ObjectId */ - Phong::Flag::ObjectId + Phong::Flag::ObjectId, #endif - }); + Phong::Flag::InstancedTransformation}); } }} diff --git a/src/Magnum/Shaders/Phong.h b/src/Magnum/Shaders/Phong.h index 33fc31df7..8b66448e4 100644 --- a/src/Magnum/Shaders/Phong.h +++ b/src/Magnum/Shaders/Phong.h @@ -106,6 +106,30 @@ example. @requires_gles30 Object ID output requires integer buffer attachments, which are not available in OpenGL ES 2.0 or WebGL 1.0. +@section Shaders-Phong-instancing Instanced rendering + +Enabling @ref Flag::InstancedTransformation will turn the shader into an +instanced one. It'll take per-instance transformation and normal matrix from +the @ref TransformationMatrix and @ref NormalMatrix attributes, applying those +before the matrix set by @ref setTransformationMatrix() and +@ref setNormalMatrix(). Besides that, @ref Flag::VertexColor (and the +@ref Color3 / @ref Color4) attributes can work as both per-vertex and +per-instance, and for texturing it's possible to have per-instance texture +offset taken from @ref TextureOffset when @ref Flag::InstancedTextureOffset is +enabled (similarly to transformation, applied before @ref setTextureMatrix()). +The snippet below shows adding a buffer with per-instance transformation to a +mesh --- note how a normal matrix attribute has to be populated and supplied as +well to ensure lighting works: + +@snippet MagnumShaders.cpp Phong-usage-instancing + +@requires_gl33 Extension @gl_extension{ARB,instanced_arrays} +@requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, + @gl_extension{EXT,instanced_arrays} or @gl_extension{NV,instanced_arrays} + in OpenGL ES 2.0. +@requires_webgl20 Extension @webgl_extension{ANGLE,instanced_arrays} in WebGL + 1.0. + @section Shaders-Phong-zero-lights Zero lights Creating this shader with zero lights makes its output equivalent to the @@ -113,7 +137,10 @@ Creating this shader with zero lights makes its output equivalent to the (if @ref Flag::AmbientTexture is enabled) are taken into account, which correspond to @ref Flat::setColor() and @ref Flat::bindTexture(). This is useful to reduce complexity in apps that render models with pre-baked lights. -In addition, enabling @ref Flag::VertexColor and using a default ambient color with no texturing makes this shader equivalent to @ref VertexColor. +For instanced workflows using zero lights means the @ref NormalMatrix instance +attribute doesn't need to be supplied either. In addition, enabling +@ref Flag::VertexColor and using a default ambient color with no texturing +makes this shader equivalent to @ref VertexColor. @see @ref shaders */ @@ -189,6 +216,51 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { typedef Generic3D::ObjectId ObjectId; #endif + /** + * @brief (Instanced) transformation matrix + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", @ref Magnum::Matrix4. + * Used only if @ref Flag::InstancedTransformation is set. + * @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} + * @requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, + * @gl_extension{EXT,instanced_arrays} or + * @gl_extension{NV,instanced_arrays} in OpenGL ES 2.0. + * @requires_webgl20 Extension @webgl_extension{ANGLE,instanced_arrays} + * in WebGL 1.0. + */ + typedef Generic3D::TransformationMatrix TransformationMatrix; + + /** + * @brief (Instanced) normal matrix + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", @ref Magnum::Matrix3x3. + * Used only if @ref Flag::InstancedTransformation is set. + * @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} + * @requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, + * @gl_extension{EXT,instanced_arrays} or + * @gl_extension{NV,instanced_arrays} in OpenGL ES 2.0. + * @requires_webgl20 Extension @webgl_extension{ANGLE,instanced_arrays} + * in WebGL 1.0. + */ + typedef Generic3D::NormalMatrix NormalMatrix; + + /** + * @brief (Instanced) texture offset + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", @ref Magnum::Vector2. Used + * only if @ref Flag::InstancedTextureOffset is set. + * @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} + * @requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, + * @gl_extension{EXT,instanced_arrays} or + * @gl_extension{NV,instanced_arrays} in OpenGL ES 2.0. + * @requires_webgl20 Extension @webgl_extension{ANGLE,instanced_arrays} + * in WebGL 1.0. + */ + typedef typename Generic3D::TextureOffset TextureOffset; + enum: UnsignedInt { /** * Color shader output. @ref shaders-generic "Generic output", @@ -298,8 +370,45 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { * WebGL 1.0. * @m_since_latest */ - InstancedObjectId = (1 << 8)|ObjectId + InstancedObjectId = (1 << 8)|ObjectId, #endif + + /** + * Instanced transformation. Retrieves a per-instance + * transformation and normal matrix from the + * @ref TransformationMatrix / @ref NormalMatrix attributes and + * uses them together with matrices coming from + * @ref setTransformationMatrix() and @ref setNormalMatrix() (first + * the per-instance, then the uniform matrix). See + * @ref Shaders-Phong-instancing for more information. + * @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} + * @requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, + * @gl_extension{EXT,instanced_arrays} or + * @gl_extension{NV,instanced_arrays} in OpenGL ES 2.0. + * @requires_webgl20 Extension @webgl_extension{ANGLE,instanced_arrays} + * in WebGL 1.0. + * @m_since_latest + */ + InstancedTransformation = 1 << 9, + + /** + * Instanced texture offset. Retrieves a per-instance offset vector + * from the @ref TextureOffset attribute and uses it together with + * the matrix coming from @ref setTextureMatrix() (first the + * per-instance vector, then the uniform matrix). Instanced texture + * scaling and rotation is not supported at the moment, you can + * specify that only via the uniform @ref setTextureMatrix(). + * Implicitly enables @ref Flag::TextureTransformation. See + * @ref Shaders-Phong-instancing for more information. + * @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} + * @requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, + * @gl_extension{EXT,instanced_arrays} or + * @gl_extension{NV,instanced_arrays} in OpenGL ES 2.0. + * @requires_webgl20 Extension @webgl_extension{ANGLE,instanced_arrays} + * in WebGL 1.0. + * @m_since_latest + */ + InstancedTextureOffset = (1 << 10)|TextureTransformation }; /** diff --git a/src/Magnum/Shaders/Phong.vert b/src/Magnum/Shaders/Phong.vert index 6e86a4513..2868f706d 100644 --- a/src/Magnum/Shaders/Phong.vert +++ b/src/Magnum/Shaders/Phong.vert @@ -122,6 +122,25 @@ in highp uint instanceObjectId; flat out highp uint interpolatedInstanceObjectId; #endif +#ifdef INSTANCED_TRANSFORMATION +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = TRANSFORMATION_MATRIX_ATTRIBUTE_LOCATION) +#endif +in highp mat4 instancedTransformationMatrix; + +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = NORMAL_MATRIX_ATTRIBUTE_LOCATION) +#endif +in highp mat3 instancedNormalMatrix; +#endif + +#ifdef INSTANCED_TEXTURE_OFFSET +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = TEXTURE_OFFSET_ATTRIBUTE_LOCATION) +#endif +in mediump vec2 instancedTextureOffset; +#endif + #if LIGHT_COUNT out mediump vec3 transformedNormal; #ifdef NORMAL_TEXTURE @@ -133,14 +152,26 @@ out highp vec3 cameraDirection; void main() { /* Transformed vertex position */ - highp vec4 transformedPosition4 = transformationMatrix*position; + highp vec4 transformedPosition4 = transformationMatrix* + #ifdef INSTANCED_TRANSFORMATION + instancedTransformationMatrix* + #endif + position; highp vec3 transformedPosition = transformedPosition4.xyz/transformedPosition4.w; #if LIGHT_COUNT /* Transformed normal and tangent vector */ - transformedNormal = normalMatrix*normal; + transformedNormal = normalMatrix* + #ifdef INSTANCED_TRANSFORMATION + instancedNormalMatrix* + #endif + normal; #ifdef NORMAL_TEXTURE - transformedTangent = normalMatrix*tangent; + transformedTangent = normalMatrix* + #ifdef INSTANCED_TRANSFORMATION + instancedNormalMatrix* + #endif + tangent; #endif /* Direction to the light */ @@ -158,7 +189,11 @@ void main() { /* Texture coordinates, if needed */ interpolatedTextureCoordinates = #ifdef TEXTURE_TRANSFORMATION - (textureMatrix*vec3(textureCoordinates, 1.0)).xy + (textureMatrix*vec3( + #ifdef INSTANCED_TEXTURE_OFFSET + instancedTextureOffset + + #endif + textureCoordinates, 1.0)).xy #else textureCoordinates #endif diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index 688a6c862..a86a7726c 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -211,6 +211,8 @@ if(BUILD_GL_TESTS) PhongTestFiles/colored.tga PhongTestFiles/defaults.tga + PhongTestFiles/instanced.tga + PhongTestFiles/instanced-normal.tga PhongTestFiles/shininess-black-specular.tga PhongTestFiles/shininess0-overflow.tga PhongTestFiles/shininess0.tga diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index b17082d06..59b20b029 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -35,6 +35,7 @@ #include "Magnum/PixelFormat.h" #include "Magnum/DebugTools/CompareImage.h" #include "Magnum/GL/Context.h" +#include "Magnum/GL/Extensions.h" #include "Magnum/GL/Framebuffer.h" #include "Magnum/GL/Mesh.h" #include "Magnum/GL/OpenGLTester.h" @@ -104,6 +105,8 @@ struct PhongGLTest: GL::OpenGLTester { void renderZeroLights(); + void renderInstanced(); + private: PluginManager::Manager _manager{"nonexistent"}; std::string _testDir; @@ -121,12 +124,12 @@ struct PhongGLTest: GL::OpenGLTester { - Mesa Intel - Mesa AMD - SwiftShader ES2/ES3 - - ARM Mali (Huawei P10) ES2/ES3 - - WebGL 1 / 2 (on Mesa Intel) - - NVidia Windows - - Intel Windows - - AMD on macOS - - iPhone 6 w/ iOS 12.4 + - ARM Mali (Huawei P10) ES2/ES3 (except instancing) + - WebGL 1 / 2 (on Mesa Intel) (except instancing) + - NVidia Windows (except instancing) + - Intel Windows (except instancing) + - AMD on macOS (except instancing) + - iPhone 6 w/ iOS 12.4 (except instancing) */ constexpr struct { @@ -155,7 +158,10 @@ constexpr struct { {"object ID + alpha mask + specular texture", Phong::Flag::ObjectId|Phong::Flag::AlphaMask|Phong::Flag::SpecularTexture, 1}, #endif {"five lights", {}, 5}, - {"zero lights", {}, 0} + {"zero lights", {}, 0}, + {"instanced transformation", Phong::Flag::InstancedTransformation, 3}, + {"instanced specular texture offset", Phong::Flag::SpecularTexture|Phong::Flag::InstancedTextureOffset, 3}, + {"instanced normal texture offset", Phong::Flag::NormalTexture|Phong::Flag::InstancedTextureOffset, 3} }; using namespace Math::Literals; @@ -284,6 +290,32 @@ constexpr struct { }; #endif +constexpr struct { + const char* name; + const char* file; + Phong::Flags flags; + Float maxThreshold, meanThreshold; +} RenderInstancedData[] { + {"diffuse", "instanced.tga", {}, + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* AMD has one off pixel; SwiftShader a bit more */ + 93.67f, 0.106f, + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage */ + 93.67f, 0.106f, + #endif + }, + {"diffuse + normal", "instanced-normal.tga", Phong::Flag::NormalTexture, + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* AMD has one off pixel */ + 94.0f, 0.132f, + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage */ + 94.0f, 0.132f, + #endif + } +}; + PhongGLTest::PhongGLTest() { addInstancedTests({&PhongGLTest::construct}, Containers::arraySize(ConstructData)); @@ -356,6 +388,11 @@ PhongGLTest::PhongGLTest() { #endif ); + addInstancedTests({&PhongGLTest::renderInstanced}, + Containers::arraySize(RenderInstancedData), + &PhongGLTest::renderSetup, + &PhongGLTest::renderTeardown); + /* Load the plugins directly from the build tree. Otherwise they're either static and already loaded or not present in the build tree */ #ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME @@ -1332,6 +1369,115 @@ void PhongGLTest::renderZeroLights() { #endif } +void PhongGLTest::renderInstanced() { + auto&& data = RenderInstancedData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::instanced_arrays::string() + std::string(" is not supported")); + #elif defined(MAGNUM_TARGET_GLES2) + #ifndef MAGNUM_TARGET_WEBGL + if(!GL::Context::current().isExtensionSupported() && + !GL::Context::current().isExtensionSupported() && + !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP("GL_{ANGLE,EXT,NV}_instanced_arrays is not supported"); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::instanced_arrays::string() + std::string(" is not supported")); + #endif + #endif + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, + Primitives::UVSphereFlag::TextureCoordinates| + Primitives::UVSphereFlag::Tangents)); + + /* Three spheres, each in a different location, differently rotated to + ensure the normal matrix is properly used as well. */ + struct { + Matrix4 transformation; + Matrix3x3 normal; + Color3 color; + Vector2 textureOffset; + } instanceData[] { + {Matrix4::translation({-1.25f, -1.25f, 0.0f})* + Matrix4::rotationX(90.0_degf), + {}, 0xff3333_rgbf, {0.0f, 0.0f}}, + {Matrix4::translation({ 1.25f, -1.25f, 0.0f})* + Matrix4::rotationY(90.0_degf), + {}, 0x33ff33_rgbf, {1.0f, 0.0f}}, + {Matrix4::translation({ 0.0f, 1.0f, 1.0f})* + Matrix4::rotationZ(90.0_degf), + {}, 0x9999ff_rgbf, {0.5f, 1.0f}} + }; + for(auto& instance: instanceData) + instance.normal = instance.transformation.normalMatrix(); + + sphere + .addVertexBufferInstanced(GL::Buffer{instanceData}, 1, 0, + Phong::TransformationMatrix{}, + Phong::NormalMatrix{}, + Phong::Color3{}, + Phong::TextureOffset{}) + .setInstanceCount(3); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + Containers::Optional image; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/diffuse-texture.tga")) && (image = importer->image2D(0))); + GL::Texture2D diffuse; + diffuse.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/normal-texture.tga")) && (image = importer->image2D(0))); + GL::Texture2D normal; + normal.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + + Phong shader{Phong::Flag::DiffuseTexture| + Phong::Flag::VertexColor| + Phong::Flag::InstancedTransformation| + Phong::Flag::InstancedTextureOffset|data.flags, 2}; + shader + .setLightPositions({{-3.0f, -3.0f, 0.0f}, + { 3.0f, -3.0f, 0.0f}}) + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-1.75f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)* + Matrix4::scaling(Vector3{0.4f})) + .setNormalMatrix((Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)).normalMatrix()) + .setProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) + .bindDiffuseTexture(diffuse) + .setDiffuseColor(0xffff99_rgbf); + + if(data.flags & Phong::Flag::NormalTexture) + shader.bindNormalTexture(normal); + + shader.draw(sphere); + + MAGNUM_VERIFY_NO_GL_ERROR(); + 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, "PhongTestFiles", data.file}), + (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); +} + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::PhongGLTest) diff --git a/src/Magnum/Shaders/Test/PhongTest.cpp b/src/Magnum/Shaders/Test/PhongTest.cpp index 4a8a7af54..29d28417e 100644 --- a/src/Magnum/Shaders/Test/PhongTest.cpp +++ b/src/Magnum/Shaders/Test/PhongTest.cpp @@ -39,9 +39,7 @@ struct PhongTest: TestSuite::Tester { void debugFlag(); void debugFlags(); - #ifndef MAGNUM_TARGET_GLES2 - void debugFlagsInstancedObjectId(); - #endif + void debugFlagsSupersets(); }; PhongTest::PhongTest() { @@ -50,10 +48,7 @@ PhongTest::PhongTest() { &PhongTest::debugFlag, &PhongTest::debugFlags, - #ifndef MAGNUM_TARGET_GLES2 - &PhongTest::debugFlagsInstancedObjectId - #endif - }); + &PhongTest::debugFlagsSupersets}); } void PhongTest::constructNoCreate() { @@ -84,16 +79,23 @@ void PhongTest::debugFlags() { CORRADE_COMPARE(out.str(), "Shaders::Phong::Flag::DiffuseTexture|Shaders::Phong::Flag::SpecularTexture Shaders::Phong::Flags{}\n"); } -#ifndef MAGNUM_TARGET_GLES2 -void PhongTest::debugFlagsInstancedObjectId() { - std::ostringstream out; - +void PhongTest::debugFlagsSupersets() { + #ifndef MAGNUM_TARGET_GLES2 /* InstancedObjectId is a superset of ObjectId so only one should be - *printed */ - Debug{&out} << (Phong::Flag::ObjectId|Phong::Flag::InstancedObjectId); - CORRADE_COMPARE(out.str(), "Shaders::Phong::Flag::InstancedObjectId\n"); + printed */ + { + std::ostringstream out; + Debug{&out} << (Phong::Flag::ObjectId|Phong::Flag::InstancedObjectId); + CORRADE_COMPARE(out.str(), "Shaders::Phong::Flag::InstancedObjectId\n"); + } + #endif + + /* InstancedTextureOffset is a superset of TextureTransformation so only + one should be printed */ + std::ostringstream out; + Debug{&out} << (Phong::Flag::InstancedTextureOffset|Phong::Flag::TextureTransformation); + CORRADE_COMPARE(out.str(), "Shaders::Phong::Flag::InstancedTextureOffset\n"); } -#endif }}}} diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/instanced-normal.tga b/src/Magnum/Shaders/Test/PhongTestFiles/instanced-normal.tga new file mode 100644 index 000000000..ae238e262 Binary files /dev/null and b/src/Magnum/Shaders/Test/PhongTestFiles/instanced-normal.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/instanced.tga b/src/Magnum/Shaders/Test/PhongTestFiles/instanced.tga new file mode 100644 index 000000000..c148e581a Binary files /dev/null and b/src/Magnum/Shaders/Test/PhongTestFiles/instanced.tga differ