From 8276405ce3e8a64108958a6fd0a65ae459fa511e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 8 Aug 2019 01:04:06 +0200 Subject: [PATCH] Shaders: rendering tests for Flat shader. --- src/Magnum/Shaders/Test/CMakeLists.txt | 35 +- src/Magnum/Shaders/Test/FlatGLTest.cpp | 478 ++++++++++++++++++ .../Shaders/Test/FlatTestFiles/colored2D.tga | Bin 0 -> 19218 bytes .../Shaders/Test/FlatTestFiles/colored3D.tga | Bin 0 -> 19218 bytes .../Shaders/Test/FlatTestFiles/defaults.tga | Bin 0 -> 19218 bytes .../textured2D-alpha-mask0.5.tga | Bin 0 -> 19218 bytes .../Test/FlatTestFiles/textured2D-alpha.tga | Bin 0 -> 19218 bytes .../Shaders/Test/FlatTestFiles/textured2D.tga | Bin 0 -> 19218 bytes .../textured3D-alpha-mask0.5.tga | Bin 0 -> 19218 bytes .../Test/FlatTestFiles/textured3D-alpha.tga | Bin 0 -> 19218 bytes .../Shaders/Test/FlatTestFiles/textured3D.tga | Bin 0 -> 19218 bytes 11 files changed, 512 insertions(+), 1 deletion(-) create mode 100644 src/Magnum/Shaders/Test/FlatTestFiles/colored2D.tga create mode 100644 src/Magnum/Shaders/Test/FlatTestFiles/colored3D.tga create mode 100644 src/Magnum/Shaders/Test/FlatTestFiles/defaults.tga create mode 100644 src/Magnum/Shaders/Test/FlatTestFiles/textured2D-alpha-mask0.5.tga create mode 100644 src/Magnum/Shaders/Test/FlatTestFiles/textured2D-alpha.tga create mode 100644 src/Magnum/Shaders/Test/FlatTestFiles/textured2D.tga create mode 100644 src/Magnum/Shaders/Test/FlatTestFiles/textured3D-alpha-mask0.5.tga create mode 100644 src/Magnum/Shaders/Test/FlatTestFiles/textured3D-alpha.tga create mode 100644 src/Magnum/Shaders/Test/FlatTestFiles/textured3D.tga diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index 3e468f9a2..5cd989b31 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -73,7 +73,40 @@ if(BUILD_GL_TESTS) endif() corrade_add_test(ShadersDistanceFieldVectorGLTest DistanceFieldVectorGLTest.cpp LIBRARIES MagnumShaders MagnumOpenGLTester) - corrade_add_test(ShadersFlatGLTest FlatGLTest.cpp LIBRARIES MagnumShadersTestLib MagnumOpenGLTester) + + corrade_add_test(ShadersFlatGLTest FlatGLTest.cpp + LIBRARIES + MagnumDebugTools + MagnumMeshTools + MagnumPrimitives + MagnumShadersTestLib + MagnumOpenGLTester + FILES + TestFiles/diffuse-alpha-texture.tga + TestFiles/diffuse-texture.tga + TestFiles/alpha-mask1.0.tga + + FlatTestFiles/colored2D.tga + FlatTestFiles/colored3D.tga + FlatTestFiles/defaults.tga + FlatTestFiles/textured2D.tga + FlatTestFiles/textured3D.tga + FlatTestFiles/textured2D-alpha.tga + FlatTestFiles/textured3D-alpha.tga + FlatTestFiles/textured2D-alpha-mask0.5.tga + FlatTestFiles/textured3D-alpha-mask0.5.tga) + if(NOT BUILD_PLUGINS_STATIC) + target_include_directories(ShadersFlatGLTest PRIVATE $) + else() + target_include_directories(ShadersFlatGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + if(WITH_ANYIMAGEIMPORTER) + target_link_libraries(ShadersFlatGLTest PRIVATE AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + target_link_libraries(ShadersFlatGLTest PRIVATE TgaImporter) + endif() + endif() + corrade_add_test(ShadersMeshVisualizerGLTest MeshVisualizerGLTest.cpp LIBRARIES MagnumShaders MagnumOpenGLTester) corrade_add_test(ShadersPhongGLTest PhongGLTest.cpp diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index 42342cfc9..85cda5f0b 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -24,15 +24,35 @@ */ #include +#include +#include +#include #include +#include #include +#include "Magnum/Image.h" #include "Magnum/ImageView.h" #include "Magnum/PixelFormat.h" +#include "Magnum/DebugTools/CompareImage.h" +#include "Magnum/GL/Mesh.h" +#include "Magnum/GL/Framebuffer.h" +#include "Magnum/GL/Renderer.h" +#include "Magnum/GL/Renderbuffer.h" +#include "Magnum/GL/RenderbufferFormat.h" #include "Magnum/GL/Texture.h" #include "Magnum/GL/TextureFormat.h" #include "Magnum/GL/OpenGLTester.h" +#include "Magnum/MeshTools/Compile.h" +#include "Magnum/Primitives/Circle.h" +#include "Magnum/Primitives/UVSphere.h" #include "Magnum/Shaders/Flat.h" +#include "Magnum/Trade/AbstractImporter.h" +#include "Magnum/Trade/ImageData.h" +#include "Magnum/Trade/MeshData2D.h" +#include "Magnum/Trade/MeshData3D.h" + +#include "configure.h" namespace Magnum { namespace Shaders { namespace Test { namespace { @@ -48,8 +68,34 @@ struct FlatGLTest: GL::OpenGLTester { template void setAlphaMask(); template void setAlphaMaskNotEnabled(); + + void renderSetup(); + void renderTeardown(); + + void renderDefaults2D(); + void renderDefaults3D(); + void renderColored2D(); + void renderColored3D(); + void renderSinglePixelTextured2D(); + void renderSinglePixelTextured3D(); + void renderTextured2D(); + void renderTextured3D(); + + void renderAlphaSetup(); + void renderAlphaTeardown(); + + void renderAlpha2D(); + void renderAlpha3D(); + + private: + PluginManager::Manager _manager{"nonexistent"}; + + GL::Renderbuffer _color{NoCreate}, _depth{NoCreate}; + GL::Framebuffer _framebuffer{NoCreate}; }; +using namespace Math::Literals; + constexpr struct { const char* name; Flat2D::Flags flags; @@ -58,6 +104,28 @@ constexpr struct { {"textured", Flat2D::Flag::Textured} }; +const struct { + const char* name; + const char* expected2D; + const char* expected3D; + bool blending; + Flat2D::Flags flags; + Float threshold; +} RenderAlphaData[] { + /* All those deliberately have a non-white diffuse in order to match the + expected data from textured() */ + {"none", "FlatTestFiles/textured2D.tga", "FlatTestFiles/textured3D.tga", false, + Flat2D::Flag::Textured, 0.0f}, + {"blending", "FlatTestFiles/textured2D-alpha.tga", "FlatTestFiles/textured3D-alpha.tga", true, + Flat2D::Flag::Textured, 0.0f}, + {"masking 0.0", "FlatTestFiles/textured2D.tga", "FlatTestFiles/textured3D.tga", false, + Flat2D::Flag::Textured, 0.0f}, + {"masking 0.5", "FlatTestFiles/textured2D-alpha-mask0.5.tga", "FlatTestFiles/textured3D-alpha-mask0.5.tga", false, + Flat2D::Flag::Textured|Flat2D::Flag::AlphaMask, 0.5f}, + {"masking 1.0", "TestFiles/alpha-mask1.0.tga", "TestFiles/alpha-mask1.0.tga", false, + Flat2D::Flag::Textured|Flat2D::Flag::AlphaMask, 1.0f} +}; + FlatGLTest::FlatGLTest() { addInstancedTests({ &FlatGLTest::construct<2>, @@ -77,6 +145,32 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::setAlphaMask<3>, &FlatGLTest::setAlphaMaskNotEnabled<2>, &FlatGLTest::setAlphaMaskNotEnabled<3>}); + + addTests({&FlatGLTest::renderDefaults2D, + &FlatGLTest::renderDefaults3D, + &FlatGLTest::renderColored2D, + &FlatGLTest::renderColored3D, + &FlatGLTest::renderSinglePixelTextured2D, + &FlatGLTest::renderSinglePixelTextured3D, + &FlatGLTest::renderTextured2D, + &FlatGLTest::renderTextured3D}, + &FlatGLTest::renderSetup, + &FlatGLTest::renderTeardown); + + addInstancedTests({&FlatGLTest::renderAlpha2D, + &FlatGLTest::renderAlpha3D}, + Containers::arraySize(RenderAlphaData), + &FlatGLTest::renderAlphaSetup, + &FlatGLTest::renderAlphaTeardown); + + /* 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 + CORRADE_INTERNAL_ASSERT(_manager.load(ANYIMAGEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + #ifdef TGAIMPORTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT(_manager.load(TGAIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif } template void FlatGLTest::construct() { @@ -174,6 +268,390 @@ template void FlatGLTest::setAlphaMaskNotEnabled() { "Shaders::Flat::setAlphaMask(): the shader was not created with alpha mask enabled\n"); } +constexpr Vector2i RenderSize{80, 80}; + +void FlatGLTest::renderSetup() { + /* Pick a color that's directly representable on RGBA4 as well to reduce + artifacts */ + GL::Renderer::setClearColor(0x111111_rgbf); + GL::Renderer::enable(GL::Renderer::Feature::FaceCulling); + + _color = GL::Renderbuffer{}; + _color.setStorage( + #if !defined(MAGNUM_TARGET_GLES2) || !defined(MAGNUM_TARGET_WEBGL) + GL::RenderbufferFormat::RGBA8, + #else + GL::RenderbufferFormat::RGBA4, + #endif + RenderSize); + _framebuffer = GL::Framebuffer{{{}, RenderSize}}; + _framebuffer.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _color) + .clear(GL::FramebufferClear::Color) + .bind(); +} + +void FlatGLTest::renderTeardown() { + _color = GL::Renderbuffer{NoCreate}; + _framebuffer = GL::Framebuffer{NoCreate}; +} + +void FlatGLTest::renderDefaults2D() { + GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32)); + + Flat2D shader; + circle.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + 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(SHADERS_TEST_DIR, "FlatTestFiles/defaults.tga"), + (DebugTools::CompareImageToFile{_manager, 0.0f, 0.0f})); +} + +void FlatGLTest::renderDefaults3D() { + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); + + Flat3D shader; + sphere.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + 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(SHADERS_TEST_DIR, "FlatTestFiles/defaults.tga"), + (DebugTools::CompareImageToFile{_manager, 0.0f, 0.0f})); +} + +void FlatGLTest::renderColored2D() { + GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32)); + + Flat2D shader; + shader.setColor(0x9999ff_rgbf) + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})); + + circle.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + 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(SHADERS_TEST_DIR, "FlatTestFiles/colored2D.tga"), + (DebugTools::CompareImageToFile{_manager, 0.0f, 0.0f})); +} + +void FlatGLTest::renderColored3D() { + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); + + Flat3D shader; + shader.setColor(0x9999ff_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::rotationX(15.0_degf)); + + sphere.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + 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(SHADERS_TEST_DIR, "FlatTestFiles/colored3D.tga"), + (DebugTools::CompareImageToFile{_manager, 0.0f, 0.0f})); +} + +constexpr GL::TextureFormat TextureFormatRGB = + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + GL::TextureFormat::RGB8 + #else + GL::TextureFormat::RGB + #endif + ; +constexpr GL::TextureFormat TextureFormatRGBA = + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + GL::TextureFormat::RGBA8 + #else + GL::TextureFormat::RGBA + #endif + ; + +void FlatGLTest::renderSinglePixelTextured2D() { + GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32, + Primitives::CircleTextureCoords::Generate)); + + const Color4ub diffuseData[]{ 0x9999ff_rgb }; + ImageView2D diffuseImage{PixelFormat::RGBA8Unorm, Vector2i{1}, diffuseData}; + GL::Texture2D texture; + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGBA, Vector2i{1}) + .setSubImage(0, {}, diffuseImage); + + Flat2D shader{Flat3D::Flag::Textured}; + shader.setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + .bindTexture(texture); + circle.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + 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(SHADERS_TEST_DIR, "FlatTestFiles/colored2D.tga"), + (DebugTools::CompareImageToFile{_manager, 0.0f, 0.0f})); +} + +void FlatGLTest::renderSinglePixelTextured3D() { + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, + Primitives::UVSphereTextureCoords::Generate)); + + const Color4ub diffuseData[]{ 0x9999ff_rgb }; + ImageView2D diffuseImage{PixelFormat::RGBA8Unorm, Vector2i{1}, diffuseData}; + GL::Texture2D texture; + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGBA, Vector2i{1}) + .setSubImage(0, {}, diffuseImage); + + Flat3D shader{Flat3D::Flag::Textured}; + 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)) + .bindTexture(texture); + sphere.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + 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(SHADERS_TEST_DIR, "FlatTestFiles/colored3D.tga"), + (DebugTools::CompareImageToFile{_manager, 0.0f, 0.0f})); +} + +void FlatGLTest::renderTextured2D() { + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32, + Primitives::CircleTextureCoords::Generate)); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + GL::Texture2D texture; + Containers::Optional image; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(SHADERS_TEST_DIR, "TestFiles/diffuse-texture.tga")) && (image = importer->image2D(0))); + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + + Flat2D shader{Flat2D::Flag::Textured}; + 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); + circle.draw(shader); + + 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(SHADERS_TEST_DIR, "FlatTestFiles/textured2D.tga"), + (DebugTools::CompareImageToFile{_manager, 0.0f, 0.0f})); +} + +void FlatGLTest::renderTextured3D() { + 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::UVSphereTextureCoords::Generate)); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + GL::Texture2D texture; + Containers::Optional image; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(SHADERS_TEST_DIR, "TestFiles/diffuse-texture.tga")) && (image = importer->image2D(0))); + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + + Flat3D shader{Flat3D::Flag::Textured}; + 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)) + /* Colorized. Case without a color (where it should be white) is tested + in renderSinglePixelTextured() */ + .setColor(0x9999ff_rgbf) + .bindTexture(texture); + sphere.draw(shader); + + 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(SHADERS_TEST_DIR, "FlatTestFiles/textured3D.tga"), + (DebugTools::CompareImageToFile{_manager, 0.0f, 0.0f})); +} + +void FlatGLTest::renderAlphaSetup() { + renderSetup(); + if(RenderAlphaData[testCaseInstanceId()].blending) + GL::Renderer::enable(GL::Renderer::Feature::Blending); + GL::Renderer::setBlendFunction(GL::Renderer::BlendFunction::SourceAlpha, GL::Renderer::BlendFunction::OneMinusSourceAlpha); + GL::Renderer::setBlendEquation(GL::Renderer::BlendEquation::Add); +} + +void FlatGLTest::renderAlphaTeardown() { + if(RenderAlphaData[testCaseInstanceId()].blending) + GL::Renderer::disable(GL::Renderer::Feature::Blending); + renderTeardown(); +} + +void FlatGLTest::renderAlpha2D() { + auto&& data = RenderAlphaData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + Containers::Optional image; + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + GL::Texture2D texture; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join({SHADERS_TEST_DIR, "TestFiles", "diffuse-alpha-texture.tga"})) && (image = importer->image2D(0))); + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGBA, image->size()) + .setSubImage(0, {}, *image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32, + Primitives::CircleTextureCoords::Generate)); + + Flat2D shader{data.flags}; + shader.setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + .setColor(0x9999ff_rgbf) + .bindTexture(texture); + + if(data.flags & Flat3D::Flag::AlphaMask) + shader.setAlphaMask(data.threshold); + + circle.draw(shader); + + 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(SHADERS_TEST_DIR, data.expected2D), + /* Minor differences between opaque and diffuse, not sure why */ + (DebugTools::CompareImageToFile{_manager, 24.34f, 0.290f})); +} + +void FlatGLTest::renderAlpha3D() { + auto&& data = RenderAlphaData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + Containers::Optional image; + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + GL::Texture2D texture; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join({SHADERS_TEST_DIR, "TestFiles", "diffuse-alpha-texture.tga"})) && (image = importer->image2D(0))); + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGBA, image->size()) + .setSubImage(0, {}, *image); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, + Primitives::UVSphereTextureCoords::Generate)); + + 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)) + .setColor(0x9999ff_rgbf) + .bindTexture(texture); + + if(data.flags & Flat3D::Flag::AlphaMask) + shader.setAlphaMask(data.threshold); + + /* For proper Z order draw back faces first and then front faces */ + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Front); + sphere.draw(shader); + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Back); + sphere.draw(shader); + + 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({SHADERS_TEST_DIR, data.expected3D}), + /* Minor differences between opaque and diffuse, not sure why */ + (DebugTools::CompareImageToFile{_manager, 2.0f, 0.204f})); +} + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::FlatGLTest) diff --git a/src/Magnum/Shaders/Test/FlatTestFiles/colored2D.tga b/src/Magnum/Shaders/Test/FlatTestFiles/colored2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..a28c76de6b8e42e6d0308ece7582ba8742a46a66 GIT binary patch literal 19218 zcmeH|QI3N!5JRKhVE^`5yn{RLzWmA=|1!=no+X4iP%ont>xH z@W@&s)*3K&qZEs9rrVehetZ@Ue+y3w5yW%;D?+l7MBvDzoIqJ1GB6cY3W7{c2p4Ze zhUXCox{(FhNESY1QW$}(gIAD>YH_-7YWHJw&{Y z91@9YTH&So28wSX2S%b=C+{=?>W~A)SNjdjBd}f~3rqtok~eVp0_(4S1&-ksbL8+a zf&j@Y7;>OR_8T~SfpzvP7;>OR_8T~SfpzvP7;>P+yx-S-U=o4#7i{thX0Eizeoy>L zFUy@)lrNalD|xTk?~7XbnrXbYUgon_T<1Mm#k;T<7g4J(?9Sa!(I|?!owtr+eY|qFf*glb|u)QG*U_Tbv|B<6= zpBT!UqajiIm5fU-6oo}+=^nLWN!zj~imUG1LbP?Z?%2=Mt^AhtekgoNuE6MtJ9$#HhLy5!?ABe(3=w6+*Y?$)uE(6Y|tTC*&ca(%Vx4<;p2|^irWOy*DvXqMV3Fuin1!0`wP7 z4+KFN)A8s4AP6J9EBwh zuHQ+-qgVHK!yS5WR^ZUC-^{|}_V!-09d2({+RSdap9RV49ZFqnvU;;}F4Sf}Wue-8 zXGS0Q?bRs>AH8M=QfQ5(GZ*2$u|4>@Q0n?vFtfVu+Cu)T8X)qbTt{*;l3dp%u~<5z z!-%=HBqBc?1HalL=U1Fuh4DVGP*}a0HOEX(<=4*|@(_ pfGHtduRe_eNe}5c?K^ZxkHDK~4xdi$KD@p+_Y3S7*e~$+0>7*D6|MjP literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/FlatTestFiles/defaults.tga b/src/Magnum/Shaders/Test/FlatTestFiles/defaults.tga new file mode 100644 index 0000000000000000000000000000000000000000..d901c0fabf48fa78d09786ca93e1fd6decf31c51 GIT binary patch literal 19218 zcmeH}Np8e23XrJwYz_54;J zEec?rs$XLgh$R}`&>&5V0*Zx^R0cMQfwF@JpxH~=u&e+!J~4m=P1;NZ72QSxr)(0X zW`$Ac2lue}0GkK#2bKe1#UckZ`j(23!Giicx>ms$g@e0fT8o64z;P{`#ypq~65miN zPeW)_Ld#CI85J~0k(yOu4#J@rTF}Z5D{=u#<=rl$KmzsXi(Fluh%EkAz^Kmidxp8s#aN@k+>%4VQ| zAmIx^64gTpR-=+P4N0MpYqjl zKjO`Iai2+f-^$XvH{WSmTxw``{OMiJO4SFSt6cXg&3f})&f$s5rS^4>KTEk&s=n+g z%5{sOS^LUEIz5XmEBElz)>yNqSq$y6qdbJoS!_X>!%y3Lmo<%6YL}+96f-{uZOtb{ VBp|F2oQO=0JAy|!v#zfO^9Sxqcz^%^ literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/FlatTestFiles/textured2D-alpha-mask0.5.tga b/src/Magnum/Shaders/Test/FlatTestFiles/textured2D-alpha-mask0.5.tga new file mode 100644 index 0000000000000000000000000000000000000000..179345aac3b0c9c4ac20e82b41a1df45c25895b1 GIT binary patch literal 19218 zcmeI1TW?!M5XU3F0dG9G-NnuWLTVk`iS0Nf&81FDQpZlyq?FJ$jhzc^At_GlOPnCo zs60SHfIvu433x#C1*AUnfDjTw37-HEAR$D{mtYc?(`9FNy?aWvCvBopwELeqXZCky z&U)jtwLRAM?@ytv(Dp>z<9k1QPvE|vz@1uAvirVe;BFuRO4GnC*Vlq1D9zN@?E`d? z290zDXz7+qA#qxYmPrB;{`1BL$#r9VXsNrSX)q>Z0s&yN2!K`bHUU4#^^>Gc(I%DS z*e2;C!Yjf)^!5E^V?#1c$L{uimMZ&j_w&Wx%%j75>-^%)(yTe}Z{@R}PGnylNNKv= z@t&(r|3+c#qv=yWUA=0~d%IfPdhz7VlSg+QFUcs18X~<*{eYc$L@w1>52f)sqf#f9 zK6=QhhDh&Hr*-2|6;#soP#UirDn(WaN;B0E>0OGNJJGN3S zk{@QdL#%Lw&8-pva0%)x2LgU=;UxUU)Df(E3a;;bBa$0r#g#C)X3OExKDMw%?BTxg zVr&&#C%=&9$6~C8U7)CP-L4g}eKwKBqNgmZw@X&M92q;zJaII$zf=WYIZU;NmU@h@ zlGe#fDV`%;s(X@e?)$lZQg4dH=DZfk4M7`k@7puWk-||%#N5Z{QH(WI*2T^w`6=ur zdA(Os8oc#v@1K=(lITrwKZ61#KAz(fc|O{^f38L}GsKWnuSaqN@Gk$KyyT7~h}AU93Ddiq8|L*|6c6CbijNEQ zzX=xCBiN=(;WLY2(9d27KR5j(4gdu&z)-;~prrX|f?bTUdXk@pw;0DrH^+>>md{+v zW<>kr$_x&}8!ssHYwaf{IP6p>3LM5{jI;prDQ4MG>M52_^Pv=*H2G8_d(}iFd(}iV z2b3c5yJ@J1_wn{0+ri>`yFU}NTa6XwZZ*cAHHM0qs*b8)Wj|L}5_2<3ud<)vv_I0#2GjgVoL!MRxj7~4d!s*8OWd`0d*}^Kg`lFarkSs2l&!8ouyP%2R8^KLjH+Cqd+Qel^&Z~SY~wh))Xj!=&QbEgSXbYS z)A|-hqxH+G2EdKF)5hgA&!u_3hbQBFg^bbhKKOp_XjD~}SSWT)PpP~@S?^Ua&dS|< zFwQFpw$#PXkue(HHx>1@>R5fSs(jn}Y9&1~9IdU8F(gZpPXZ~p;;ahbWQ>Lv-e1zI zN3inh4;!(i^U29^GKOS(hipiP!%TQx6;YGdUo*p1yOb9f&QNK|t9i%{T`IcobD_@%>0@y75}G%gJ>AuL zG8T16q))Blg_HklNlV^r_C)l+VsEcLqg$_v7xb%^yvsvFV^2Tj7N=!)%^B(SwC)6j z`?;LWwoKBo7Lx_2a6tbm8~U)%l-DFz5MngEuw}uQ=ieQp zk;VE30mN_(8eVXHJra8F86B@-mf(b8UMT4Nf&Um;VyJI$D9l%ac}*1a`v)!3gieeJ h+WKwRBeLknws#I@{G-4qK6%V5diTbAPr%<3_y^>23J?GQ literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/FlatTestFiles/textured2D-alpha.tga b/src/Magnum/Shaders/Test/FlatTestFiles/textured2D-alpha.tga new file mode 100644 index 0000000000000000000000000000000000000000..483d320354723a64980965be40dcdaa02cc14a4b GIT binary patch literal 19218 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 literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/FlatTestFiles/textured2D.tga b/src/Magnum/Shaders/Test/FlatTestFiles/textured2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..e8e799388c5b0ef4956fa7816b8af6ab174806f8 GIT binary patch literal 19218 zcmeI2OK)367=|PM0Xr6M&ct2-g(`{fmpDz6I7yS(aT4d!+!H&OCM{{3G_jqw2vJ~x zDg+3`qLqLJsGBZWvp@)h5d8-r0TL|Aui$e|Go8$QGyaa_lS<&|DEj7|_t^9J9iPki zG&Q}@bo7^P$~L{!^kU<$@d=#x6Zof;m$vmgK50Agn>^V4^U%p}FF_fa z5}=_;e)Cf`8qm-#10dRUsxZbkLvgB#C{$I^I1tx6f4h4Z<$5sMvmI-*H>gpMiney( z>-RSg#+wJi%>$vzmfw?)X|Px8t$o+Qur~l$9j^`eX}Sv~wMCT|l%=cQ@ibezgC&n44Qwy8-)Tl++y60=tU0P_Fo^Me}ZAIjDJ7@{kCEoShzOgjR z-^6FN#Oofi3hNf{9Us6>&*2fPylx<0Nybl;w@~!u#_(@3%3ELLZ#Fj`7FOuIf9x!N zF`jz!Ld4ne`EVL9U-QCzo}Oue0~h)gMtK8;MgP_sb{cW4y`Rn8pS|?!;n_z;Mv0yj z^N%l2J)9aqdkyC!I1^1yvaw+{v(N(6bYA!n!V{y>p?=4B$9GTxCB;Bw?m}58BATx# z9w%_nlF-nm8OqR0K|?(vnyz*LDOgcJNi-0dyHFO2sACj$0fJgeT)L(i%Fs(eLp>sw zp{8;k`gC-vZXhyup)3^9z$kUmkvf7J4O&ve;sHG;Qb=L3kZxTTwRL zZk4zq7IaRI z+kCbe{nmb%O`e1oZr(o}?1cZEQ7lAEUQbN2!hR*zwX!dL;q>P7A!aek(kI~sI#{f@ zpQZjx0GP7{@51FutczRT=~)NMZmp%8q1nl3s>?9jRTh}WQ8pB?u0R1_;gGeUdZe$g z7@1xFzO(R7Z%o|&+C#p@JX5iNQ&Mi6!I3JL)&5wZBl2Ha4^QA{UV9gn#a1G0B*uCo zEC>8Z~LaESteDvEEE~VT8GnC+zqTQ>3Ts&Szv(zu7u;PJIea~)*k9Xc^`CnN`a@_ zM+y9ZCeCN5MGCu~U2d7pSFZNq;y*WvBO8dqV@{W4%5ir7@Us8F&#uQ=Cwe7}^4j9P zI8F+^5-iFK%Q9<6Kf-pue1_NJCu87^j1C)m)$IJ%PMjsU9%o5Vc#{_{@xndMV4~SZ z1Ja(?&WM=2HV{Qdh77%Y{6bMqums$&g8~bTG1h&W`PxsrP0h3{Yiay&;#pv zxOvwZP!^cLhq3Pat?dw7Mm;s~s$X7P5rcyo*4%(s5~ZdNr=lP1M=$oZmUdAO$^v)m zN2_f`I7*`)_joHad8&d8Ym5s{o|fT&?c1C17zuBR1*AA!p2b#%KJ;k z^Ldx29Kesy#C##jxz(0*mjx4hU3SW=vP<;loTSJY9rFAHXWBvH}g7; zSOp;}DhN^0aiMxu*#9A~BV=Nls*RYYYD05GMM}7~M;)F{QO846&=FF^-M&8JM&|{m z4p7pGbgGSlhI-t9rKhZd5EYb#B2suACCklHlxUMkr`jlJsK-5C%B)sFhziO=5v%g@ zylN|=1HHk;88loFHFV`Ln~kx4xUf+C~5aM^D^M-j9(qAZJgbi8oo|5n%`Zz`2*J-3leO2a-$Guie!+z6=1C<|Qh z`>mqgm1Jkph;_W6zwMCsT2D{r>?rRINwQTLHbu+UU_b^A8-#wS4k1pWmd!=JqX literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/FlatTestFiles/textured3D-alpha-mask0.5.tga b/src/Magnum/Shaders/Test/FlatTestFiles/textured3D-alpha-mask0.5.tga new file mode 100644 index 0000000000000000000000000000000000000000..d4e6c467a3e32a17c8ba5df3c596a644a65db91d GIT binary patch literal 19218 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 Q`#RrF;M)oOznsAT0^aVL=>Px# literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/FlatTestFiles/textured3D.tga b/src/Magnum/Shaders/Test/FlatTestFiles/textured3D.tga new file mode 100644 index 0000000000000000000000000000000000000000..02f2122062ebfedf400910a2c07f8cb5bf13ad85 GIT binary patch literal 19218 zcmeI2S#J|p6vv}}1K#>TJmWE23nV}sCn0f2;w4VJBzEGgj-A9LX%Zl0Pwbg_Dcyh; zsag-sf#EGRa8OmxroS7geRS+RdbIttRz6e~9xM)0DazjPj!g zGwYZ?`9q}+xnYBXsR&tFa#EHiwE{>AIdRbkmku=vNXqo}8?)YDYLZSZ8zZ8;b#r+H z*87F51|&6j5A-I6SPezd zWGu;)KY{$~T>RotaHY|HJ{BP7LU+r|=%DY`txU@{_wG~wJ z^X$;+Q1crRH^Ti+v{m+Htd_NiDEfk;Q6@)$&)4sHJ?y@c3}e^>&ns`m=VM(H)jMT5 zS7b^MDJDt?ie{1W=MLyWCV8E#a#~ z)2guJs9-~tc!HP_`8X#Aq^_;#Q@u zwL-im5q*HMg%$KI+++~whldxNc|PrXsCa?A&MWT#y*$^n7PJ!cnVb^eYoUAY|7$Xm z1M*o?LEqKlJ@4Ik!CUgYf^A%|jiqekssFykprP!T7H@LL#;g94<^@-B(Ux4`;!kkB z{nnl+i{FDI*5O0kIIoGU#e2zT92SX9+2Q<6z!_VWU;)_SVY)h4Jdq$9axrbSmi9VJ zxSbssw(eibLaoL7R&(j{CKqgoY5$Nl;%9?ROm~DGNLb@j95!L(FgG}A?FzA-txPOw z-M^fPti^kzrgV9M7VVY-8VRu7VKzR=O`wp|chC_1c|1{IOEA8O zBa$;V&V}5JYkPUv&y37SJt~v*_P>jwartU2$OQeoXBv%{aMm^Csg!wf!up+zdwY3X zjV0n|h9)=!orHoHXmB369xqQE;7F(R_`bTYw0Pwv+&5_T*`-4k#}L|4G|Ea{YmytC zv+ZAzVvXw-jwE#NU^7F)LZMV#UDi_TP(HuU=A$t+`+qX(9z(I}voYA-S z@a7X@vlz*F5>s3_z;?DV$k@?Y4zNga{KnmKWR@E_EFD7`n9A!qc@2LxD2_}M1RMGf zz6+4UXuP;-WV9)YBb~4qcDBV}*P?hgW(NZ?xlXD*JRU<-rXMzc~rf^f$e z8n?zL?m2UTpDV91Wc5Cqr9qTY$i4k+ppo%aTl@_ce&r$8bB^S zqvFvNSWNPczapnXRu)i9!7NyLo&~RW(d#+gX1@^F_EpG2$1f(P;>%oK%m!rgNqJo- ze06WPSAHCL>~hfdHL(fG8j2>E4y#0&L{`fyUiULTr(5Xr3!zTI8x`Z=^jhOs({lOC2M+V*TM`r%+cL}KT8C}z%sy2;%Lh0B?FVMzp}mq>pSr9 G?7+X|FZ<>I literal 0 HcmV?d00001