From 74c75178dec542e4ac74f2881b4450ccd2e92a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 17 Mar 2020 22:48:10 +0100 Subject: [PATCH] Shaders: add a 2D variant of MeshVisualizer. --- doc/changelog.dox | 3 + doc/generated/primitives.cpp | 11 +- doc/generated/shaders.cpp | 28 +- doc/namespaces.dox | 39 +- doc/shaders-meshvisualizer2d.png | Bin 0 -> 23423 bytes ...lizer.png => shaders-meshvisualizer3d.png} | Bin 36260 -> 36258 bytes doc/snippets/MagnumShaders.cpp | 15 +- src/Magnum/Shaders/MeshVisualizer.cpp | 228 +++++++-- src/Magnum/Shaders/MeshVisualizer.h | 333 +++++++++++-- src/Magnum/Shaders/MeshVisualizer.vert | 22 + src/Magnum/Shaders/Shaders.h | 11 +- src/Magnum/Shaders/Test/CMakeLists.txt | 12 +- .../Shaders/Test/MeshVisualizerGLTest.cpp | 447 +++++++++++++++--- .../Shaders/Test/MeshVisualizerTest.cpp | 91 +++- .../defaults-wireframe2D.tga | Bin 0 -> 7870 bytes ...wireframe.tga => defaults-wireframe3D.tga} | Bin .../wireframe-nogeo2D.tga | Bin 0 -> 8218 bytes ...eframe-nogeo.tga => wireframe-nogeo3D.tga} | Bin .../wireframe-wide2D.tga | Bin 0 -> 7116 bytes ...ireframe-wide.tga => wireframe-wide3D.tga} | Bin .../MeshVisualizerTestFiles/wireframe2D.tga | Bin 0 -> 7442 bytes .../{wireframe.tga => wireframe3D.tga} | Bin src/Magnum/Shaders/visibility.h | 2 + 23 files changed, 1025 insertions(+), 217 deletions(-) create mode 100644 doc/shaders-meshvisualizer2d.png rename doc/{shaders-meshvisualizer.png => shaders-meshvisualizer3d.png} (68%) create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-wireframe2D.tga rename src/Magnum/Shaders/Test/MeshVisualizerTestFiles/{defaults-wireframe.tga => defaults-wireframe3D.tga} (100%) create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo2D.tga rename src/Magnum/Shaders/Test/MeshVisualizerTestFiles/{wireframe-nogeo.tga => wireframe-nogeo3D.tga} (100%) create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-wide2D.tga rename src/Magnum/Shaders/Test/MeshVisualizerTestFiles/{wireframe-wide.tga => wireframe-wide3D.tga} (100%) create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe2D.tga rename src/Magnum/Shaders/Test/MeshVisualizerTestFiles/{wireframe.tga => wireframe3D.tga} (100%) diff --git a/doc/changelog.dox b/doc/changelog.dox index 46def72a0..afb4ecd67 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -204,6 +204,7 @@ See also: @subsubsection changelog-latest-new-shaders Shaders library +- New @ref Shaders::MeshVisualizer2D for 2D mesh visualization - Texture coordinate transformation in @ref Shaders::DistanceFieldVector, @ref Shaders::Flat, @ref Shaders::Phong and @ref Shaders::Vector - New attribute definitions and an location allocation scheme in @@ -495,6 +496,8 @@ See also: @ref MeshTools::compressIndices(), @ref MeshTools::duplicate(), @ref MeshTools::removeDuplicatesInPlace() and @ref MeshTools::subdivide() / @ref MeshTools::subdivideInPlace() overloads instead +- @cpp Shaders::MeshVisualizer @ce is deprecated as the shader can now handle + both 2D and 3D, use @ref Shaders::MeshVisualizer3D instead @subsection changelog-latest-compatibility Potential compatibility breakages, removed APIs diff --git a/doc/generated/primitives.cpp b/doc/generated/primitives.cpp index 761c11327..3f4832e26 100644 --- a/doc/generated/primitives.cpp +++ b/doc/generated/primitives.cpp @@ -275,17 +275,12 @@ int PrimitiveVisualizer::exec() { } } - Shaders::MeshVisualizer wireframe2D{Shaders::MeshVisualizer::Flag::Wireframe}; + Shaders::MeshVisualizer2D wireframe2D{Shaders::MeshVisualizer2D::Flag::Wireframe}; wireframe2D.setColor(0x00000000_srgbaf) .setWireframeColor(OutlineColor) .setWireframeWidth(2.0f) .setViewportSize(Vector2{ImageSize}) - .setTransformationProjectionMatrix(Matrix4{ - /** @todo clean up once Matrix4 from Matrix3 constructor exists */ - {(Projection2D*Transformation2D)[0], 0.0f}, - {(Projection2D*Transformation2D)[1], 0.0f}, - {0.0f, 0.0f, 1.0f, 0.0f}, - {{(Projection2D*Transformation2D)[2].xy(), 0.0f}, 1.0f}}); + .setTransformationProjectionMatrix(Projection2D*Transformation2D); { Shaders::Flat2D flat; @@ -312,7 +307,7 @@ int PrimitiveVisualizer::exec() { } } - Shaders::MeshVisualizer wireframe3D{Shaders::MeshVisualizer::Flag::Wireframe}; + Shaders::MeshVisualizer3D wireframe3D{Shaders::MeshVisualizer3D::Flag::Wireframe}; wireframe3D.setColor(0x00000000_srgbaf) .setWireframeColor(OutlineColor) .setWireframeWidth(2.0f) diff --git a/doc/generated/shaders.cpp b/doc/generated/shaders.cpp index 11811ed56..5413bf63b 100644 --- a/doc/generated/shaders.cpp +++ b/doc/generated/shaders.cpp @@ -78,7 +78,8 @@ struct ShaderVisualizer: Platform::WindowlessApplication { int exec() override; std::string phong(); - std::string meshVisualizer(); + std::string meshVisualizer2D(); + std::string meshVisualizer3D(); std::string flat(); std::string vertexColor(); @@ -127,7 +128,8 @@ int ShaderVisualizer::exec() { GL::Renderer::setClearColor(0x000000_srgbaf); for(auto fun: {&ShaderVisualizer::phong, - &ShaderVisualizer::meshVisualizer, + &ShaderVisualizer::meshVisualizer2D, + &ShaderVisualizer::meshVisualizer3D, &ShaderVisualizer::flat, &ShaderVisualizer::vertexColor, &ShaderVisualizer::vector, @@ -167,12 +169,28 @@ std::string ShaderVisualizer::phong() { return "phong.png"; } -std::string ShaderVisualizer::meshVisualizer() { +std::string ShaderVisualizer::meshVisualizer2D() { + const Matrix3 projection = + Matrix3::projection(Vector2{3.0f})* + Matrix3::rotation(13.7_degf); + + Shaders::MeshVisualizer2D{Shaders::MeshVisualizer2D::Flag::Wireframe} + .setColor(BaseColor) + .setWireframeColor(OutlineColor) + .setWireframeWidth(2.0f) + .setViewportSize(Vector2{ImageSize}) + .setTransformationProjectionMatrix(projection) + .draw(MeshTools::compile(Primitives::circle2DSolid(8))); + + return "meshvisualizer2d.png"; +} + +std::string ShaderVisualizer::meshVisualizer3D() { const Matrix4 projection = Projection*Transformation* Matrix4::rotationZ(13.7_degf)* Matrix4::rotationX(-12.6_degf); - Shaders::MeshVisualizer{Shaders::MeshVisualizer::Flag::Wireframe} + Shaders::MeshVisualizer3D{Shaders::MeshVisualizer3D::Flag::Wireframe} .setColor(BaseColor) .setWireframeColor(OutlineColor) .setWireframeWidth(2.0f) @@ -180,7 +198,7 @@ std::string ShaderVisualizer::meshVisualizer() { .setTransformationProjectionMatrix(projection) .draw(MeshTools::compile(Primitives::icosphereSolid(1))); - return "meshvisualizer.png"; + return "meshvisualizer3d.png"; } std::string ShaderVisualizer::flat() { diff --git a/doc/namespaces.dox b/doc/namespaces.dox index 3b6adf4d3..2a77be49c 100644 --- a/doc/namespaces.dox +++ b/doc/namespaces.dox @@ -449,24 +449,21 @@ See @ref building, @ref cmake and @ref shaders for more information. @parblock -@m_div{m-col-m-3 m-col-t-6 m-text-center m-nopadt m-nopadx} -@image html shaders-flat.png +@m_div{m-col-m-4 m-col-t-6 m-text-center m-nopadt m-nopadx} +@image html shaders-flat.png width=256px @ref Flat @m_class{m-label m-success} **2D** @m_class{m-label m-primary} **3D** @m_enddiv -@m_div{m-col-m-3 m-col-t-6 m-text-center m-nopadt m-nopadx} -@image html shaders-phong.png +@m_div{m-col-m-4 m-col-t-6 m-text-center m-nopadt m-nopadx} +@image html shaders-phong.png width=256px @ref Phong @m_class{m-label m-primary} **3D** @m_enddiv -@m_div{m-col-m-3 m-col-t-6 m-text-center m-nopadt m-nopadx} -@image html shaders-vertexcolor.png -@ref VertexColor @m_class{m-label m-success} **2D** @m_class{m-label m-primary} **3D** -@m_enddiv +@m_div{m-clearfix-t} @m_enddiv -@m_div{m-col-m-3 m-col-t-6 m-text-center m-nopadt m-nopadx} -@image html shaders-meshvisualizer.png -@ref MeshVisualizer @m_class{m-label m-primary} **3D** +@m_div{m-col-m-4 m-push-t-3 m-push-m-0 m-col-t-6 m-text-center m-nopadt m-nopadx} +@image html shaders-vertexcolor.png width=256px +@ref VertexColor @m_class{m-label m-success} **2D** @m_class{m-label m-primary} **3D** @m_enddiv @endparblock @@ -475,13 +472,25 @@ See @ref building, @ref cmake and @ref shaders for more information. @parblock -@m_div{m-col-m-3 m-push-m-3 m-col-t-6 m-text-center m-nopadt m-nopadx} -@image html shaders-vector.png +@m_div{m-clearfix-m} @m_enddiv + +@m_div{m-col-m-3 m-col-t-6 m-text-center m-nopadt m-nopadx} +@image html shaders-meshvisualizer2d.png width=256px +@ref MeshVisualizer2D "MeshVisualizer2D" @m_class{m-label m-success} **2D** +@m_enddiv + +@m_div{m-col-m-3 m-col-t-6 m-text-center m-nopadt m-nopadx} +@image html shaders-meshvisualizer3d.png width=256px +@ref MeshVisualizer3D "MeshVisualizer3D" @m_class{m-label m-primary} **3D** +@m_enddiv + +@m_div{m-col-m-3 m-col-t-6 m-text-center m-nopadt m-nopadx} +@image html shaders-vector.png width=256px @ref Vector @m_class{m-label m-success} **2D** @m_class{m-label m-primary} **3D** @m_enddiv -@m_div{m-col-m-3 m-push-m-3 m-col-t-6 m-text-center m-nopadt m-nopadx} -@image html shaders-distancefieldvector.png +@m_div{m-col-m-3 m-col-t-6 m-text-center m-nopadt m-nopadx} +@image html shaders-distancefieldvector.png width=256px @ref DistanceFieldVector @m_class{m-label m-success} **2D** @m_class{m-label m-primary} **3D** @m_enddiv diff --git a/doc/shaders-meshvisualizer2d.png b/doc/shaders-meshvisualizer2d.png new file mode 100644 index 0000000000000000000000000000000000000000..d253ee1f4332c9ed2d2fe3d25566acecad2c747a GIT binary patch literal 23423 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 literal 0 HcmV?d00001 diff --git a/doc/shaders-meshvisualizer.png b/doc/shaders-meshvisualizer3d.png similarity index 68% rename from doc/shaders-meshvisualizer.png rename to doc/shaders-meshvisualizer3d.png index a2d8583d4a35914ee2ce189a82abbb7bc34fb53d..5022ecaff2d209b031d33bf841b69df4a73a534a 100644 GIT binary patch delta 11103 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 indexedPositions; GL::Buffer vertices{MeshTools::duplicate(indices, indexedPositions)}; GL::Mesh mesh; -mesh.addVertexBuffer(vertices, 0, Shaders::MeshVisualizer::Position{}); +mesh.addVertexBuffer(vertices, 0, Shaders::MeshVisualizer3D::Position{}); /* [MeshVisualizer-usage-no-geom1] */ } @@ -300,8 +300,9 @@ GL::Mesh mesh; /* [MeshVisualizer-usage-no-geom2] */ Matrix4 transformationMatrix, projectionMatrix; -Shaders::MeshVisualizer shader{Shaders::MeshVisualizer::Flag::Wireframe| - Shaders::MeshVisualizer::Flag::NoGeometryShader}; +Shaders::MeshVisualizer3D shader{ + Shaders::MeshVisualizer3D::Flag::Wireframe| + Shaders::MeshVisualizer3D::Flag::NoGeometryShader}; shader.setColor(0x2f83cc_rgbf) .setWireframeColor(0xdcdcdc_rgbf) .setTransformationProjectionMatrix(projectionMatrix*transformationMatrix) diff --git a/src/Magnum/Shaders/MeshVisualizer.cpp b/src/Magnum/Shaders/MeshVisualizer.cpp index 470237230..7c8347b1d 100644 --- a/src/Magnum/Shaders/MeshVisualizer.cpp +++ b/src/Magnum/Shaders/MeshVisualizer.cpp @@ -31,6 +31,7 @@ #include #include "Magnum/Math/Color.h" +#include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Matrix4.h" #include "Magnum/GL/Context.h" #include "Magnum/GL/Extensions.h" @@ -40,9 +41,11 @@ namespace Magnum { namespace Shaders { -MeshVisualizer::MeshVisualizer(const Flags flags): _flags{flags} { +namespace Implementation { + +MeshVisualizerBase::MeshVisualizerBase(FlagsBase flags): _flags{flags} { #ifndef MAGNUM_TARGET_GLES2 - if(flags & Flag::Wireframe && !(flags & Flag::NoGeometryShader)) { + if(_flags & FlagBase::Wireframe && !(_flags & FlagBase::NoGeometryShader)) { #ifndef MAGNUM_TARGET_GLES MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GL320); MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::geometry_shader4); @@ -51,7 +54,7 @@ MeshVisualizer::MeshVisualizer(const Flags flags): _flags{flags} { #endif } #else - if(_flags & Flag::Wireframe) + if(_flags & FlagBase::Wireframe) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::OES::standard_derivatives); #endif @@ -60,34 +63,85 @@ MeshVisualizer::MeshVisualizer(const Flags flags): _flags{flags} { if(!Utility::Resource::hasGroup("MagnumShaders")) importShaderResources(); #endif - Utility::Resource rs("MagnumShaders"); +} +GL::Version MeshVisualizerBase::setupShaders(GL::Shader& vert, GL::Shader& frag, const Utility::Resource& rs) const { #ifndef MAGNUM_TARGET_GLES const GL::Version version = GL::Context::current().supportedVersion({GL::Version::GL320, GL::Version::GL310, GL::Version::GL300, GL::Version::GL210}); - CORRADE_INTERNAL_ASSERT(!flags || flags & Flag::NoGeometryShader || version >= GL::Version::GL320); + CORRADE_INTERNAL_ASSERT(!_flags || _flags & FlagBase::NoGeometryShader || version >= GL::Version::GL320); #elif !defined(MAGNUM_TARGET_WEBGL) const GL::Version version = GL::Context::current().supportedVersion({GL::Version::GLES310, GL::Version::GLES300, GL::Version::GLES200}); - CORRADE_INTERNAL_ASSERT(!flags || flags & Flag::NoGeometryShader || version >= GL::Version::GLES310); + CORRADE_INTERNAL_ASSERT(!_flags || _flags & FlagBase::NoGeometryShader || version >= GL::Version::GLES310); #else const GL::Version version = GL::Context::current().supportedVersion({GL::Version::GLES300, GL::Version::GLES200}); #endif - GL::Shader vert = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Vertex); - GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment); + vert = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Vertex); + frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment); - vert.addSource(flags & Flag::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") - .addSource(flags & Flag::NoGeometryShader ? "#define NO_GEOMETRY_SHADER\n" : "") + vert.addSource(_flags & FlagBase::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") + .addSource(_flags & FlagBase::NoGeometryShader ? "#define NO_GEOMETRY_SHADER\n" : "") #ifdef MAGNUM_TARGET_WEBGL .addSource("#define SUBSCRIPTING_WORKAROUND\n") #elif defined(MAGNUM_TARGET_GLES2) .addSource(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::Angle ? "#define SUBSCRIPTING_WORKAROUND\n" : "") #endif + ; + frag.addSource(_flags & FlagBase::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") + .addSource(_flags & FlagBase::NoGeometryShader ? "#define NO_GEOMETRY_SHADER\n" : ""); + + return version; +} + +MeshVisualizerBase& MeshVisualizerBase::setViewportSize(const Vector2& size) { + /* Not asserting here, since the relation to wireframe is a bit vague. + Also it's an ugly hack that should be removed, ideally. */ + if(_flags & FlagBase::Wireframe && !(_flags & FlagBase::NoGeometryShader)) + setUniform(_viewportSizeUniform, size); + return *this; +} + +MeshVisualizerBase& MeshVisualizerBase::setColor(const Color4& color) { + setUniform(_colorUniform, color); + return *this; +} + +MeshVisualizerBase& MeshVisualizerBase::setWireframeColor(const Color4& color) { + CORRADE_ASSERT(_flags & FlagBase::Wireframe, + "Shaders::MeshVisualizer::setWireframeColor(): the shader was not created with wireframe enabled", *this); + setUniform(_wireframeColorUniform, color); + return *this; +} + +MeshVisualizerBase& MeshVisualizerBase::setWireframeWidth(const Float width) { + CORRADE_ASSERT(_flags & FlagBase::Wireframe, + "Shaders::MeshVisualizer::setWireframeWidth(): the shader was not created with wireframe enabled", *this); + setUniform(_wireframeWidthUniform, width); + return *this; +} + +MeshVisualizerBase& MeshVisualizerBase::setSmoothness(const Float smoothness) { + /* This is a bit vaguely related too, but less vague than setViewportSize() + so asserting. */ + CORRADE_ASSERT(_flags & FlagBase::Wireframe, + "Shaders::MeshVisualizer::setSmoothness(): the shader was not created with wireframe enabled", *this); + setUniform(_smoothnessUniform, smoothness); + return *this; +} + +} + +MeshVisualizer2D::MeshVisualizer2D(const Flags flags): Implementation::MeshVisualizerBase{Implementation::MeshVisualizerBase::FlagBase(UnsignedByte(flags))} { + Utility::Resource rs{"MagnumShaders"}; + GL::Shader vert{NoCreate}; + GL::Shader frag{NoCreate}; + const GL::Version version = setupShaders(vert, frag, rs); + + vert.addSource("#define TWO_DIMENSIONS\n") .addSource(rs.get("generic.glsl")) .addSource(rs.get("MeshVisualizer.vert")); - frag.addSource(flags & Flag::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") - .addSource(flags & Flag::NoGeometryShader ? "#define NO_GEOMETRY_SHADER\n" : "") - .addSource(rs.get("generic.glsl")) + frag.addSource(rs.get("generic.glsl")) .addSource(rs.get("MeshVisualizer.frag")); #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) @@ -96,6 +150,8 @@ MeshVisualizer::MeshVisualizer(const Flags flags): _flags{flags} { geom = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Geometry); geom->addSource(rs.get("MeshVisualizer.geom")); } + #else + static_cast(version); #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) @@ -149,7 +205,7 @@ MeshVisualizer::MeshVisualizer(const Flags flags): _flags{flags} { #ifdef MAGNUM_TARGET_GLES setTransformationProjectionMatrix({}); setColor(Color3(1.0f)); - if(_flags & Flag::Wireframe) { + if(flags & Flag::Wireframe) { /* Viewport size is zero by default */ setWireframeColor(Color3{0.0f}); setWireframeWidth(1.0f); @@ -158,53 +214,119 @@ MeshVisualizer::MeshVisualizer(const Flags flags): _flags{flags} { #endif } -MeshVisualizer& MeshVisualizer::setTransformationProjectionMatrix(const Matrix4& matrix) { +MeshVisualizer2D& MeshVisualizer2D::setTransformationProjectionMatrix(const Matrix3& matrix) { setUniform(_transformationProjectionMatrixUniform, matrix); return *this; } -MeshVisualizer& MeshVisualizer::setViewportSize(const Vector2& size) { - /* Not asserting here, since the relation to wireframe is a bit vague. - Also it's an ugly hack that should be removed, ideally. */ - if(_flags & Flag::Wireframe && !(_flags & Flag::NoGeometryShader)) - setUniform(_viewportSizeUniform, size); - return *this; -} +MeshVisualizer3D::MeshVisualizer3D(const Flags flags): Implementation::MeshVisualizerBase{Implementation::MeshVisualizerBase::FlagBase(UnsignedByte(flags))} { + Utility::Resource rs{"MagnumShaders"}; + GL::Shader vert{NoCreate}; + GL::Shader frag{NoCreate}; + const GL::Version version = setupShaders(vert, frag, rs); -MeshVisualizer& MeshVisualizer::setColor(const Color4& color) { - setUniform(_colorUniform, color); - return *this; -} + vert.addSource("#define THREE_DIMENSIONS\n") + .addSource(rs.get("generic.glsl")) + .addSource(rs.get("MeshVisualizer.vert")); + frag.addSource(rs.get("generic.glsl")) + .addSource(rs.get("MeshVisualizer.frag")); -MeshVisualizer& MeshVisualizer::setWireframeColor(const Color4& color) { - CORRADE_ASSERT(_flags & Flag::Wireframe, - "Shaders::MeshVisualizer::setWireframeColor(): the shader was not created with wireframe enabled", *this); - setUniform(_wireframeColorUniform, color); - return *this; + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + Containers::Optional geom; + if(flags & Flag::Wireframe && !(flags & Flag::NoGeometryShader)) { + geom = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Geometry); + geom->addSource(rs.get("MeshVisualizer.geom")); + } + #else + static_cast(version); + #endif + + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(geom) CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, *geom, frag})); + else + #endif + CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag})); + + attachShaders({vert, frag}); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(geom) attachShader(*geom); + #endif + + /* ES3 has this done in the shader directly */ + #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2) + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported(version)) + #endif + { + bindAttributeLocation(Position::Location, "position"); + + #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2) + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isVersionSupported(GL::Version::GL310)) + #endif + { + bindAttributeLocation(VertexIndex::Location, "vertexIndex"); + } + #endif + } + #endif + + CORRADE_INTERNAL_ASSERT_OUTPUT(link()); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported(version)) + #endif + { + _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); + _colorUniform = uniformLocation("color"); + if(flags & Flag::Wireframe) { + _wireframeColorUniform = uniformLocation("wireframeColor"); + _wireframeWidthUniform = uniformLocation("wireframeWidth"); + _smoothnessUniform = uniformLocation("smoothness"); + if(!(flags & Flag::NoGeometryShader)) + _viewportSizeUniform = uniformLocation("viewportSize"); + } + } + + /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ + #ifdef MAGNUM_TARGET_GLES + setTransformationProjectionMatrix({}); + setColor(Color3(1.0f)); + if(flags & Flag::Wireframe) { + /* Viewport size is zero by default */ + setWireframeColor(Color3{0.0f}); + setWireframeWidth(1.0f); + setSmoothness(2.0f); + } + #endif } -MeshVisualizer& MeshVisualizer::setWireframeWidth(const Float width) { - CORRADE_ASSERT(_flags & Flag::Wireframe, - "Shaders::MeshVisualizer::setWireframeWidth(): the shader was not created with wireframe enabled", *this); - setUniform(_wireframeWidthUniform, width); +MeshVisualizer3D& MeshVisualizer3D::setTransformationProjectionMatrix(const Matrix4& matrix) { + setUniform(_transformationProjectionMatrixUniform, matrix); return *this; } -MeshVisualizer& MeshVisualizer::setSmoothness(const Float smoothness) { - /* This is a bit vaguely related too, but less vague than setViewportSize() - so asserting. */ - CORRADE_ASSERT(_flags & Flag::Wireframe, - "Shaders::MeshVisualizer::setSmoothness(): the shader was not created with wireframe enabled", *this); - setUniform(_smoothnessUniform, smoothness); - return *this; +Debug& operator<<(Debug& debug, const MeshVisualizer2D::Flag value) { + debug << "Shaders::MeshVisualizer2D::Flag" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(v) case MeshVisualizer2D::Flag::v: return debug << "::" #v; + _c(NoGeometryShader) + _c(Wireframe) + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; } -Debug& operator<<(Debug& debug, const MeshVisualizer::Flag value) { - debug << "Shaders::MeshVisualizer::Flag" << Debug::nospace; +Debug& operator<<(Debug& debug, const MeshVisualizer3D::Flag value) { + debug << "Shaders::MeshVisualizer3D::Flag" << Debug::nospace; switch(value) { /* LCOV_EXCL_START */ - #define _c(v) case MeshVisualizer::Flag::v: return debug << "::" #v; + #define _c(v) case MeshVisualizer3D::Flag::v: return debug << "::" #v; _c(NoGeometryShader) _c(Wireframe) #undef _c @@ -214,11 +336,19 @@ Debug& operator<<(Debug& debug, const MeshVisualizer::Flag value) { return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; } -Debug& operator<<(Debug& debug, const MeshVisualizer::Flags value) { - return Containers::enumSetDebugOutput(debug, value, "Shaders::MeshVisualizer::Flags{}", { - MeshVisualizer::Flag::Wireframe, +Debug& operator<<(Debug& debug, const MeshVisualizer2D::Flags value) { + return Containers::enumSetDebugOutput(debug, value, "Shaders::MeshVisualizer2D::Flags{}", { + MeshVisualizer2D::Flag::Wireframe, + /* Wireframe contains this on ES2 so it's not reported there */ + MeshVisualizer2D::Flag::NoGeometryShader + }); +} + +Debug& operator<<(Debug& debug, const MeshVisualizer3D::Flags value) { + return Containers::enumSetDebugOutput(debug, value, "Shaders::MeshVisualizer3D::Flags{}", { + MeshVisualizer3D::Flag::Wireframe, /* Wireframe contains this on ES2 so it's not reported there */ - MeshVisualizer::Flag::NoGeometryShader + MeshVisualizer3D::Flag::NoGeometryShader }); } diff --git a/src/Magnum/Shaders/MeshVisualizer.h b/src/Magnum/Shaders/MeshVisualizer.h index b837350fb..e4cc505f6 100644 --- a/src/Magnum/Shaders/MeshVisualizer.h +++ b/src/Magnum/Shaders/MeshVisualizer.h @@ -26,25 +26,248 @@ */ /** @file - * @brief Class @ref Magnum::Shaders::MeshVisualizer + * @brief Class @ref Magnum::Shaders::MeshVisualizer2D, @ref Magnum::Shaders::MeshVisualizer3D */ +#include + +#include "Magnum/DimensionTraits.h" #include "Magnum/GL/AbstractShaderProgram.h" #include "Magnum/Shaders/Generic.h" #include "Magnum/Shaders/visibility.h" namespace Magnum { namespace Shaders { +namespace Implementation { + +class MAGNUM_SHADERS_EXPORT MeshVisualizerBase: public GL::AbstractShaderProgram { + protected: + enum class FlagBase: UnsignedByte { + #ifndef MAGNUM_TARGET_GLES2 + Wireframe = 1 << 0, + #else + Wireframe = (1 << 0) | (1 << 1), + #endif + NoGeometryShader = 1 << 1 + }; + typedef Containers::EnumSet FlagsBase; + + CORRADE_ENUMSET_FRIEND_OPERATORS(FlagsBase) + + explicit MeshVisualizerBase(FlagsBase flags); + explicit MeshVisualizerBase(NoCreateT) noexcept: GL::AbstractShaderProgram{NoCreate} {} + + MAGNUM_SHADERS_LOCAL GL::Version setupShaders(GL::Shader& vert, GL::Shader& frag, const Utility::Resource& rs) const; + + MeshVisualizerBase& setViewportSize(const Vector2& size); + MeshVisualizerBase& setColor(const Color4& color); + MeshVisualizerBase& setWireframeColor(const Color4& color); + MeshVisualizerBase& setWireframeWidth(Float width); + MeshVisualizerBase& setSmoothness(Float smoothness); + + /* Prevent accidentally calling irrelevant functions */ + #ifndef MAGNUM_TARGET_GLES + using GL::AbstractShaderProgram::drawTransformFeedback; + #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + using GL::AbstractShaderProgram::dispatchCompute; + #endif + + FlagsBase _flags; + Int _colorUniform{1}, + _wireframeColorUniform{2}, + _wireframeWidthUniform{3}, + _smoothnessUniform{4}, + _viewportSizeUniform{5}; +}; + +} + /** -@brief Mesh visualization shader +@brief 2D mesh visualization shader +@m_since_latest -Uses geometry shader to visualize wireframe of 3D meshes. You need to provide -the @ref Position attribute in your triangle mesh. By default, the shader -renders the mesh with a white color in an identity transformation. Use +Uses the geometry shader to visualize wireframe of 3D meshes. You need to +provide the @ref Position attribute in your triangle mesh. By default, the +shader renders the mesh with a white color in an identity transformation. Use @ref setTransformationProjectionMatrix(), @ref setColor() and others to configure the shader. -@image html shaders-meshvisualizer.png width=256px +@image html shaders-meshvisualizer2d.png width=256px + +This shader is a 2D variant of @ref MeshVisualizer3D with mostly identical +workflow. See its documentation for more information. +*/ +class MAGNUM_SHADERS_EXPORT MeshVisualizer2D: public Implementation::MeshVisualizerBase { + public: + /** + * @brief Vertex position + * + * @ref shaders-generic "Generic attribute", + * @ref Magnum::Vector2 "Vector2". + */ + typedef typename Generic2D::Position Position; + + /** + * @brief Vertex index + * + * See @ref MeshVisualizer3D::VertexIndex for more information. + */ + typedef GL::Attribute<4, Float> VertexIndex; + + enum: UnsignedInt { + /** + * Color shader output. @ref shaders-generic "Generic output", + * present always. Expects three- or four-component floating-point + * or normalized buffer attachment. + */ + ColorOutput = Generic2D::ColorOutput + }; + + /** + * @brief Flag + * + * @see @ref Flags, @ref MeshVisualizer2D() + */ + enum class Flag: UnsignedByte { + /** + * Visualize wireframe. On OpenGL ES 2.0 this also enables + * @ref Flag::NoGeometryShader. + */ + #ifndef MAGNUM_TARGET_GLES2 + Wireframe = 1 << 0, + #else + Wireframe = (1 << 0) | (1 << 1), + #endif + + /** + * Don't use a geometry shader for wireframe visualization. If + * enabled, you might need to provide also the @ref VertexIndex + * attribute in the mesh. In OpenGL ES 2.0 enabled alongside + * @ref Flag::Wireframe. + */ + NoGeometryShader = 1 << 1 + }; + + /** @brief Flags */ + typedef Containers::EnumSet Flags; + + /** + * @brief Constructor + * @param flags Flags + */ + explicit MeshVisualizer2D(Flags flags = {}); + + /** + * @brief Construct without creating the underlying OpenGL object + * + * The constructed instance is equivalent to a moved-from state. Useful + * in cases where you will overwrite the instance later anyway. Move + * another object over it to make it useful. + * + * This function can be safely used for constructing (and later + * destructing) objects even without any OpenGL context being active. + * However note that this is a low-level and a potentially dangerous + * API, see the documentation of @ref NoCreate for alternatives. + */ + explicit MeshVisualizer2D(NoCreateT) noexcept: Implementation::MeshVisualizerBase{NoCreate} {} + + /** @brief Copying is not allowed */ + MeshVisualizer2D(const MeshVisualizer2D&) = delete; + + /** @brief Move constructor */ + MeshVisualizer2D(MeshVisualizer2D&&) noexcept = default; + + /** @brief Copying is not allowed */ + MeshVisualizer2D& operator=(const MeshVisualizer2D&) = delete; + + /** @brief Move assignment */ + MeshVisualizer2D& operator=(MeshVisualizer2D&&) noexcept = default; + + /** @brief Flags */ + Flags flags() const { + return reinterpret_cast(Implementation::MeshVisualizerBase::_flags); + } + + /** + * @brief Set transformation and projection matrix + * @return Reference to self (for method chaining) + * + * Initial value is an identity matrix. + */ + MeshVisualizer2D& setTransformationProjectionMatrix(const Matrix3& matrix); + + /** + * @brief Set viewport size + * @return Reference to self (for method chaining) + * + * Has effect only if @ref Flag::Wireframe is enabled and geometry + * shaders are used, otherwise it does nothing. Initial value is a zero + * vector. + */ + MeshVisualizer2D& setViewportSize(const Vector2& size) { + return static_cast(Implementation::MeshVisualizerBase::setViewportSize(size)); + } + + /** + * @brief Set base object color + * @return Reference to self (for method chaining) + * + * Initial value is @cpp 0xffffffff_rgbaf @ce. + */ + MeshVisualizer2D& setColor(const Color4& color) { + return static_cast(Implementation::MeshVisualizerBase::setColor(color)); + } + + /** + * @brief Set wireframe color + * @return Reference to self (for method chaining) + * + * Initial value is @cpp 0x000000ff_rgbaf @ce. Expects that + * @ref Flag::Wireframe is enabled. + */ + MeshVisualizer2D& setWireframeColor(const Color4& color) { + return static_cast(Implementation::MeshVisualizerBase::setWireframeColor(color)); + } + + /** + * @brief Set wireframe width + * @return Reference to self (for method chaining) + * + * Value is in screen space (depending on @ref setViewportSize()), + * initial value is @cpp 1.0f @ce. Expects that @ref Flag::Wireframe is + * enabled. + */ + MeshVisualizer2D& setWireframeWidth(Float width) { + return static_cast(Implementation::MeshVisualizerBase::setWireframeWidth(width)); + } + + /** + * @brief Set line smoothness + * @return Reference to self (for method chaining) + * + * Value is in screen space (depending on @ref setViewportSize()), + * initial value is @cpp 2.0f @ce. Expects that @ref Flag::Wireframe is + * enabled. + */ + MeshVisualizer2D& setSmoothness(Float smoothness) { + return static_cast(Implementation::MeshVisualizerBase::setSmoothness(smoothness)); + } + + private: + Int _transformationProjectionMatrixUniform{0}; +}; + +/** +@brief 3D mesh visualization shader + +Uses the geometry shader to visualize wireframe of 3D meshes. You need to +provide the @ref Position attribute in your triangle mesh. By default, the +shader renders the mesh with a white color in an identity transformation. Use +@ref setTransformationProjectionMatrix(), @ref setColor() and others to +configure the shader. + +@image html shaders-meshvisualizer3d.png width=256px @section Shaders-MeshVisualizer-wireframe Wireframe visualization @@ -100,10 +323,10 @@ addition to the above*: Rendering setup the same as above. -@see @ref shaders +@see @ref shaders, @ref MeshVisualizer2D @todo Understand and add support wireframe width/smoothness without GS */ -class MAGNUM_SHADERS_EXPORT MeshVisualizer: public GL::AbstractShaderProgram { +class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisualizerBase { public: /** * @brief Vertex position @@ -111,7 +334,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer: public GL::AbstractShaderProgram { * @ref shaders-generic "Generic attribute", * @ref Magnum::Vector3 "Vector3". */ - typedef Generic3D::Position Position; + typedef typename Generic3D::Position Position; /** * @brief Vertex index @@ -157,8 +380,8 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer: public GL::AbstractShaderProgram { #endif /** - * Don't use geometry shader for wireframe visualization. If - * enabled, you might need to provide also @ref VertexIndex + * Don't use a geometry shader for wireframe visualization. If + * enabled, you might need to provide also the @ref VertexIndex * attribute in the mesh. In OpenGL ES 2.0 enabled alongside * @ref Flag::Wireframe. */ @@ -172,7 +395,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer: public GL::AbstractShaderProgram { * @brief Constructor * @param flags Flags */ - explicit MeshVisualizer(Flags flags = {}); + explicit MeshVisualizer3D(Flags flags = {}); /** * @brief Construct without creating the underlying OpenGL object @@ -186,22 +409,24 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer: public GL::AbstractShaderProgram { * However note that this is a low-level and a potentially dangerous * API, see the documentation of @ref NoCreate for alternatives. */ - explicit MeshVisualizer(NoCreateT) noexcept: GL::AbstractShaderProgram{NoCreate} {} + explicit MeshVisualizer3D(NoCreateT) noexcept: Implementation::MeshVisualizerBase{NoCreate} {} /** @brief Copying is not allowed */ - MeshVisualizer(const MeshVisualizer&) = delete; + MeshVisualizer3D(const MeshVisualizer3D&) = delete; /** @brief Move constructor */ - MeshVisualizer(MeshVisualizer&&) noexcept = default; + MeshVisualizer3D(MeshVisualizer3D&&) noexcept = default; /** @brief Copying is not allowed */ - MeshVisualizer& operator=(const MeshVisualizer&) = delete; + MeshVisualizer3D& operator=(const MeshVisualizer3D&) = delete; /** @brief Move assignment */ - MeshVisualizer& operator=(MeshVisualizer&&) noexcept = default; + MeshVisualizer3D& operator=(MeshVisualizer3D&&) noexcept = default; /** @brief Flags */ - Flags flags() const { return _flags; } + Flags flags() const { + return reinterpret_cast(Implementation::MeshVisualizerBase::_flags); + } /** * @brief Set transformation and projection matrix @@ -209,7 +434,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer: public GL::AbstractShaderProgram { * * Initial value is an identity matrix. */ - MeshVisualizer& setTransformationProjectionMatrix(const Matrix4& matrix); + MeshVisualizer3D& setTransformationProjectionMatrix(const Matrix4& matrix); /** * @brief Set viewport size @@ -218,7 +443,9 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer: public GL::AbstractShaderProgram { * Has effect only if @ref Flag::Wireframe is enabled and geometry * shaders are used. Initial value is a zero vector. */ - MeshVisualizer& setViewportSize(const Vector2& size); + MeshVisualizer3D& setViewportSize(const Vector2& size) { + return static_cast(Implementation::MeshVisualizerBase::setViewportSize(size)); + } /** * @brief Set base object color @@ -226,60 +453,70 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer: public GL::AbstractShaderProgram { * * Initial value is @cpp 0xffffffff_rgbaf @ce. */ - MeshVisualizer& setColor(const Color4& color); + MeshVisualizer3D& setColor(const Color4& color) { + return static_cast(Implementation::MeshVisualizerBase::setColor(color)); + } /** * @brief Set wireframe color * @return Reference to self (for method chaining) * - * Initial value is @cpp 0x000000ff_rgbaf @ce. Has effect only if + * Initial value is @cpp 0x000000ff_rgbaf @ce. Expects that * @ref Flag::Wireframe is enabled. */ - MeshVisualizer& setWireframeColor(const Color4& color); + MeshVisualizer3D& setWireframeColor(const Color4& color) { + return static_cast(Implementation::MeshVisualizerBase::setWireframeColor(color)); + } /** * @brief Set wireframe width * @return Reference to self (for method chaining) * - * Initial value is @cpp 1.0f @ce. Has effect only if @ref Flag::Wireframe - * is enabled. + * Initial value is @cpp 1.0f @ce. Has effect only if + * @ref Flag::Wireframe is enabled. */ - MeshVisualizer& setWireframeWidth(Float width); + MeshVisualizer3D& setWireframeWidth(Float width) { + return static_cast(Implementation::MeshVisualizerBase::setWireframeWidth(width)); + } /** * @brief Set line smoothness * @return Reference to self (for method chaining) * - * Initial value is @cpp 2.0f @ce. Has effect only if @ref Flag::Wireframe + * Value is in screen space (depending on @ref setViewportSize()), + * initial value is @cpp 2.0f @ce. Expects that @ref Flag::Wireframe * is enabled. */ - MeshVisualizer& setSmoothness(Float smoothness); + MeshVisualizer3D& setSmoothness(Float smoothness) { + return static_cast(Implementation::MeshVisualizerBase::setSmoothness(smoothness)); + } private: - /* Prevent accidentally calling irrelevant functions */ - #ifndef MAGNUM_TARGET_GLES - using GL::AbstractShaderProgram::drawTransformFeedback; - #endif - #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - using GL::AbstractShaderProgram::dispatchCompute; - #endif - - Flags _flags; - Int _transformationProjectionMatrixUniform{0}, - _colorUniform{1}, - _wireframeColorUniform{2}, - _wireframeWidthUniform{3}, - _smoothnessUniform{4}, - _viewportSizeUniform{5}; + Int _transformationProjectionMatrixUniform{0}; }; -/** @debugoperatorclassenum{MeshVisualizer,MeshVisualizer::Flag} */ -MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, MeshVisualizer::Flag value); +#ifdef MAGNUM_BUILD_DEPRECATED +/** +@brief 3D mesh visualizer shader +@m_deprecated_since_latest Use @ref MeshVisualizer3D instead. +*/ +typedef CORRADE_DEPRECATED("use MeshVisualizer3D instead") MeshVisualizer3D MeshVisualizer; +#endif + +/** @debugoperatorclassenum{MeshVisualizer2D,MeshVisualizer2D::Flag} */ +MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, MeshVisualizer2D::Flag value); + +/** @debugoperatorclassenum{MeshVisualizer3D,MeshVisualizer3D::Flag} */ +MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, MeshVisualizer3D::Flag value); + +/** @debugoperatorclassenum{MeshVisualizer2D,MeshVisualizer2D::Flags} */ +MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, MeshVisualizer2D::Flags value); -/** @debugoperatorclassenum{MeshVisualizer,MeshVisualizer::Flags} */ -MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, MeshVisualizer::Flags value); +/** @debugoperatorclassenum{MeshVisualizer3D,MeshVisualizer3D::Flags} */ +MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, MeshVisualizer3D::Flags value); -CORRADE_ENUMSET_OPERATORS(MeshVisualizer::Flags) +CORRADE_ENUMSET_OPERATORS(MeshVisualizer2D::Flags) +CORRADE_ENUMSET_OPERATORS(MeshVisualizer3D::Flags) }} diff --git a/src/Magnum/Shaders/MeshVisualizer.vert b/src/Magnum/Shaders/MeshVisualizer.vert index 3995126d8..c38a0a40b 100644 --- a/src/Magnum/Shaders/MeshVisualizer.vert +++ b/src/Magnum/Shaders/MeshVisualizer.vert @@ -31,16 +31,32 @@ #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif +#ifdef TWO_DIMENSIONS +uniform highp mat3 transformationProjectionMatrix + #ifndef GL_ES + = mat3(1.0) + #endif + ; +#elif defined(THREE_DIMENSIONS) uniform highp mat4 transformationProjectionMatrix #ifndef GL_ES = mat4(1.0) #endif ; +#else +#error +#endif #ifdef EXPLICIT_ATTRIB_LOCATION layout(location = POSITION_ATTRIBUTE_LOCATION) #endif +#ifdef TWO_DIMENSIONS +in highp vec2 position; +#elif defined(THREE_DIMENSIONS) in highp vec4 position; +#else +#error +#endif #if defined(WIREFRAME_RENDERING) && defined(NO_GEOMETRY_SHADER) #if (!defined(GL_ES) && __VERSION__ < 140) || (defined(GL_ES) && __VERSION__ < 300) @@ -55,7 +71,13 @@ out vec3 barycentric; #endif void main() { + #ifdef TWO_DIMENSIONS + gl_Position.xywz = vec4(transformationProjectionMatrix*vec3(position, 1.0), 0.0); + #elif defined(THREE_DIMENSIONS) gl_Position = transformationProjectionMatrix*position; + #else + #error + #endif #if defined(WIREFRAME_RENDERING) && defined(NO_GEOMETRY_SHADER) barycentric = vec3(0.0); diff --git a/src/Magnum/Shaders/Shaders.h b/src/Magnum/Shaders/Shaders.h index fce5f8f7a..5324e98d4 100644 --- a/src/Magnum/Shaders/Shaders.h +++ b/src/Magnum/Shaders/Shaders.h @@ -31,6 +31,10 @@ #include "Magnum/Types.h" +#ifdef MAGNUM_BUILD_DEPRECATED +#include +#endif + namespace Magnum { namespace Shaders { #ifndef DOXYGEN_GENERATING_OUTPUT @@ -48,7 +52,12 @@ typedef Flat<3> Flat3D; /* Generic is used only statically */ -class MeshVisualizer; +class MeshVisualizer2D; +class MeshVisualizer3D; +#ifdef MAGNUM_BUILD_DEPRECATED +typedef CORRADE_DEPRECATED("use MeshVisualizer3D instead") MeshVisualizer3D MeshVisualizer; +#endif + class Phong; template class Vector; diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index 42b77a8fd..3d232c177 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -153,10 +153,14 @@ if(BUILD_GL_TESTS) FILES FlatTestFiles/defaults.tga FlatTestFiles/colored3D.tga - MeshVisualizerTestFiles/defaults-wireframe.tga - MeshVisualizerTestFiles/wireframe.tga - MeshVisualizerTestFiles/wireframe-nogeo.tga - MeshVisualizerTestFiles/wireframe-wide.tga) + MeshVisualizerTestFiles/defaults-wireframe2D.tga + MeshVisualizerTestFiles/defaults-wireframe3D.tga + MeshVisualizerTestFiles/wireframe2D.tga + MeshVisualizerTestFiles/wireframe3D.tga + MeshVisualizerTestFiles/wireframe-nogeo2D.tga + MeshVisualizerTestFiles/wireframe-nogeo3D.tga + MeshVisualizerTestFiles/wireframe-wide2D.tga + MeshVisualizerTestFiles/wireframe-wide3D.tga) target_include_directories(ShadersMeshVisualizerGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) if(BUILD_PLUGINS_STATIC) if(WITH_ANYIMAGEIMPORTER) diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index f1bb801ca..cfb6773aa 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -43,9 +43,11 @@ #include "Magnum/ImageView.h" #include "Magnum/PixelFormat.h" #include "Magnum/Math/Color.h" +#include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Matrix4.h" #include "Magnum/MeshTools/Compile.h" #include "Magnum/MeshTools/Duplicate.h" +#include "Magnum/MeshTools/GenerateIndices.h" #include "Magnum/Primitives/Circle.h" #include "Magnum/Primitives/Icosphere.h" #include "Magnum/Primitives/UVSphere.h" @@ -60,25 +62,33 @@ namespace Magnum { namespace Shaders { namespace Test { namespace { struct MeshVisualizerGLTest: GL::OpenGLTester { explicit MeshVisualizerGLTest(); - void construct(); + void construct2D(); + void construct3D(); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - void constructWireframeGeometryShader(); + void constructWireframeGeometryShader2D(); + void constructWireframeGeometryShader3D(); #endif - void constructWireframeNoGeometryShader(); - void constructMove(); + void constructMove2D(); + void constructMove3D(); - void setWireframeNotEnabled(); + void setWireframeNotEnabled2D(); + void setWireframeNotEnabled3D(); void renderSetup(); void renderTeardown(); - void renderDefaults(); + void renderDefaults2D(); + void renderDefaults3D(); #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - void renderDefaultsWireframe(); + void renderDefaultsWireframe2D(); + void renderDefaultsWireframe3D(); #endif - void render(); - void renderWireframe(); + void render2D(); + void render3D(); + void renderWireframe2D(); + void renderWireframe3D(); private: PluginManager::Manager _manager{"nonexistent"}; @@ -106,40 +116,97 @@ using namespace Math::Literals; constexpr struct { const char* name; - MeshVisualizer::Flags flags; + MeshVisualizer2D::Flags flags; +} ConstructData2D[] { + {"", {}}, + {"wireframe w/o GS", MeshVisualizer2D::Flag::Wireframe|MeshVisualizer2D::Flag::NoGeometryShader}, +}; + +constexpr struct { + const char* name; + MeshVisualizer3D::Flags flags; +} ConstructData3D[] { + {"", {}}, + {"wireframe w/o GS", MeshVisualizer3D::Flag::Wireframe|MeshVisualizer3D::Flag::NoGeometryShader} +}; + +constexpr struct { + const char* name; + MeshVisualizer2D::Flags flags; + Float width, smoothness; + const char* file; + const char* fileXfail; +} WireframeData2D[] { + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + {"", MeshVisualizer2D::Flags{}, + 1.0f, 2.0f, "wireframe2D.tga", nullptr}, + {"wide/sharp", MeshVisualizer2D::Flags{}, + 3.0f, 1.0f, "wireframe-wide2D.tga", nullptr}, + #endif + {"no geometry shader", MeshVisualizer2D::Flag::NoGeometryShader, + 1.0f, 2.0f, "wireframe2D.tga", "wireframe-nogeo2D.tga"}, + {"no geometry shader, wide/sharp", MeshVisualizer2D::Flag::NoGeometryShader, + 3.0f, 1.0f, "wireframe-wide2D.tga", "wireframe-nogeo2D.tga"} +}; + +constexpr struct { + const char* name; + MeshVisualizer3D::Flags flags; Float width, smoothness; const char* file; const char* fileXfail; -} WireframeData[] { +} WireframeData3D[] { #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - {"", MeshVisualizer::Flags{}, 1.0f, 2.0f, "wireframe.tga", nullptr}, - {"wide/sharp", MeshVisualizer::Flags{}, 3.0f, 1.0f, "wireframe-wide.tga", nullptr}, + {"", MeshVisualizer3D::Flags{}, + 1.0f, 2.0f, "wireframe3D.tga", nullptr}, + {"wide/sharp", MeshVisualizer3D::Flags{}, + 3.0f, 1.0f, "wireframe-wide3D.tga", nullptr}, #endif - {"no geometry shader", MeshVisualizer::Flag::NoGeometryShader, 1.0f, 2.0f, "wireframe.tga", "wireframe-nogeo.tga"}, - {"no geometry shader, wide/sharp", MeshVisualizer::Flag::NoGeometryShader, 3.0f, 1.0f, "wireframe-wide.tga", "wireframe-nogeo.tga"} + {"no geometry shader", + MeshVisualizer3D::Flag::NoGeometryShader, + 1.0f, 2.0f, "wireframe3D.tga", "wireframe-nogeo3D.tga"}, + {"no geometry shader, wide/sharp", + MeshVisualizer3D::Flag::NoGeometryShader, + 3.0f, 1.0f, "wireframe-wide3D.tga", "wireframe-nogeo3D.tga"} }; MeshVisualizerGLTest::MeshVisualizerGLTest() { - addTests({&MeshVisualizerGLTest::construct, + addInstancedTests({&MeshVisualizerGLTest::construct2D}, + Containers::arraySize(ConstructData2D)); + + addInstancedTests({&MeshVisualizerGLTest::construct3D}, + Containers::arraySize(ConstructData3D)); + + addTests({ #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - &MeshVisualizerGLTest::constructWireframeGeometryShader, + &MeshVisualizerGLTest::constructWireframeGeometryShader2D, + &MeshVisualizerGLTest::constructWireframeGeometryShader3D, #endif - &MeshVisualizerGLTest::constructWireframeNoGeometryShader, - &MeshVisualizerGLTest::constructMove, + &MeshVisualizerGLTest::constructMove2D, + &MeshVisualizerGLTest::constructMove3D, - &MeshVisualizerGLTest::setWireframeNotEnabled}); + &MeshVisualizerGLTest::setWireframeNotEnabled2D, + &MeshVisualizerGLTest::setWireframeNotEnabled3D}); - addTests({&MeshVisualizerGLTest::renderDefaults, + addTests({&MeshVisualizerGLTest::renderDefaults2D, + &MeshVisualizerGLTest::renderDefaults3D, #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - &MeshVisualizerGLTest::renderDefaultsWireframe, + &MeshVisualizerGLTest::renderDefaultsWireframe2D, + &MeshVisualizerGLTest::renderDefaultsWireframe3D, #endif - &MeshVisualizerGLTest::render}, + &MeshVisualizerGLTest::render2D, + &MeshVisualizerGLTest::render3D}, + &MeshVisualizerGLTest::renderSetup, + &MeshVisualizerGLTest::renderTeardown); + + addInstancedTests({&MeshVisualizerGLTest::renderWireframe2D}, + Containers::arraySize(WireframeData2D), &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); - addInstancedTests({&MeshVisualizerGLTest::renderWireframe}, - Containers::arraySize(WireframeData), + addInstancedTests({&MeshVisualizerGLTest::renderWireframe3D}, + Containers::arraySize(WireframeData3D), &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); @@ -167,9 +234,29 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { } } -void MeshVisualizerGLTest::construct() { - MeshVisualizer shader; - CORRADE_COMPARE(shader.flags(), MeshVisualizer::Flags{}); +void MeshVisualizerGLTest::construct2D() { + auto&& data = ConstructData2D[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + MeshVisualizer2D shader{data.flags}; + CORRADE_COMPARE(shader.flags(), data.flags); + CORRADE_VERIFY(shader.id()); + { + #ifdef CORRADE_TARGET_APPLE + CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #endif + CORRADE_VERIFY(shader.validate().first); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); +} + +void MeshVisualizerGLTest::construct3D() { + auto&& data = ConstructData3D[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + MeshVisualizer3D shader{data.flags}; + CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_VERIFY(shader.id()); { #ifdef CORRADE_TARGET_APPLE @@ -182,7 +269,7 @@ void MeshVisualizerGLTest::construct() { } #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) -void MeshVisualizerGLTest::constructWireframeGeometryShader() { +void MeshVisualizerGLTest::constructWireframeGeometryShader2D() { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported")); @@ -196,8 +283,8 @@ void MeshVisualizerGLTest::constructWireframeGeometryShader() { Debug() << "Using" << GL::Extensions::NV::shader_noperspective_interpolation::string(); #endif - MeshVisualizer shader{MeshVisualizer::Flag::Wireframe}; - CORRADE_COMPARE(shader.flags(), MeshVisualizer::Flag::Wireframe); + MeshVisualizer2D shader{MeshVisualizer2D::Flag::Wireframe}; + CORRADE_COMPARE(shader.flags(), MeshVisualizer2D::Flag::Wireframe); { #ifdef CORRADE_TARGET_APPLE CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); @@ -206,11 +293,23 @@ void MeshVisualizerGLTest::constructWireframeGeometryShader() { CORRADE_VERIFY(shader.validate().first); } } -#endif -void MeshVisualizerGLTest::constructWireframeNoGeometryShader() { - MeshVisualizer shader{MeshVisualizer::Flag::Wireframe|MeshVisualizer::Flag::NoGeometryShader}; - CORRADE_COMPARE(shader.flags(), MeshVisualizer::Flag::Wireframe|MeshVisualizer::Flag::NoGeometryShader); +void MeshVisualizerGLTest::constructWireframeGeometryShader3D() { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported")); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() + std::string(" is not supported")); + #endif + + #ifdef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + Debug() << "Using" << GL::Extensions::NV::shader_noperspective_interpolation::string(); + #endif + + MeshVisualizer3D shader{MeshVisualizer3D::Flag::Wireframe}; + CORRADE_COMPARE(shader.flags(), MeshVisualizer3D::Flag::Wireframe); { #ifdef CORRADE_TARGET_APPLE CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); @@ -219,31 +318,66 @@ void MeshVisualizerGLTest::constructWireframeNoGeometryShader() { CORRADE_VERIFY(shader.validate().first); } } +#endif + +void MeshVisualizerGLTest::constructMove2D() { + MeshVisualizer2D a{MeshVisualizer2D::Flag::Wireframe|MeshVisualizer2D::Flag::NoGeometryShader}; + const GLuint id = a.id(); + CORRADE_VERIFY(id); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + MeshVisualizer2D b{std::move(a)}; + CORRADE_COMPARE(b.id(), id); + CORRADE_COMPARE(b.flags(), MeshVisualizer2D::Flag::Wireframe|MeshVisualizer2D::Flag::NoGeometryShader); + CORRADE_VERIFY(!a.id()); + + MeshVisualizer2D c{NoCreate}; + c = std::move(b); + CORRADE_COMPARE(c.id(), id); + CORRADE_COMPARE(c.flags(), MeshVisualizer2D::Flag::Wireframe|MeshVisualizer2D::Flag::NoGeometryShader); + CORRADE_VERIFY(!b.id()); +} -void MeshVisualizerGLTest::constructMove() { - MeshVisualizer a{MeshVisualizer::Flag::Wireframe|MeshVisualizer::Flag::NoGeometryShader}; +void MeshVisualizerGLTest::constructMove3D() { + MeshVisualizer3D a{MeshVisualizer3D::Flag::Wireframe|MeshVisualizer3D::Flag::NoGeometryShader}; const GLuint id = a.id(); CORRADE_VERIFY(id); MAGNUM_VERIFY_NO_GL_ERROR(); - MeshVisualizer b{std::move(a)}; + MeshVisualizer3D b{std::move(a)}; CORRADE_COMPARE(b.id(), id); - CORRADE_COMPARE(b.flags(), MeshVisualizer::Flag::Wireframe|MeshVisualizer::Flag::NoGeometryShader); + CORRADE_COMPARE(b.flags(), MeshVisualizer3D::Flag::Wireframe|MeshVisualizer3D::Flag::NoGeometryShader); CORRADE_VERIFY(!a.id()); - MeshVisualizer c{NoCreate}; + MeshVisualizer3D c{NoCreate}; c = std::move(b); CORRADE_COMPARE(c.id(), id); - CORRADE_COMPARE(c.flags(), MeshVisualizer::Flag::Wireframe|MeshVisualizer::Flag::NoGeometryShader); + CORRADE_COMPARE(c.flags(), MeshVisualizer3D::Flag::Wireframe|MeshVisualizer3D::Flag::NoGeometryShader); CORRADE_VERIFY(!b.id()); } -void MeshVisualizerGLTest::setWireframeNotEnabled() { +void MeshVisualizerGLTest::setWireframeNotEnabled2D() { + std::ostringstream out; + Error redirectError{&out}; + + MeshVisualizer2D shader; + shader.setWireframeColor({}) + .setWireframeWidth({}) + .setSmoothness({}); + + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizer::setWireframeColor(): the shader was not created with wireframe enabled\n" + "Shaders::MeshVisualizer::setWireframeWidth(): the shader was not created with wireframe enabled\n" + "Shaders::MeshVisualizer::setSmoothness(): the shader was not created with wireframe enabled\n"); +} + +void MeshVisualizerGLTest::setWireframeNotEnabled3D() { std::ostringstream out; Error redirectError{&out}; - MeshVisualizer shader; + MeshVisualizer3D shader; shader.setWireframeColor({}) .setWireframeWidth({}) .setSmoothness({}); @@ -281,10 +415,36 @@ void MeshVisualizerGLTest::renderTeardown() { _color = GL::Renderbuffer{NoCreate}; } -void MeshVisualizerGLTest::renderDefaults() { +void MeshVisualizerGLTest::renderDefaults2D() { + GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32)); + + MeshVisualizer2D{} + .draw(circle); + + 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."); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has differently rasterized edges on four pixels */ + const Float maxThreshold = 238.0f, meanThreshold = 0.298f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 238.0f, meanThreshold = 0.298f; + #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, "FlatTestFiles/defaults.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + +void MeshVisualizerGLTest::renderDefaults3D() { GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); - MeshVisualizer{} + MeshVisualizer3D{} .draw(sphere); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -308,7 +468,56 @@ void MeshVisualizerGLTest::renderDefaults() { } #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) -void MeshVisualizerGLTest::renderDefaultsWireframe() { +void MeshVisualizerGLTest::renderDefaultsWireframe2D() { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported")); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() + std::string(" is not supported")); + #endif + + #ifdef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + Debug() << "Using" << GL::Extensions::NV::shader_noperspective_interpolation::string(); + #endif + + GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(16)); + + MeshVisualizer2D shader{MeshVisualizer2D::Flag::Wireframe}; + shader.draw(circle); + + 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_EXPECT_FAIL("Defaults don't work for wireframe as line width is derived from viewport size."); + 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, "MeshVisualizerTestFiles/defaults-wireframe2D.tga"), + (DebugTools::CompareImageToFile{_manager})); + } + + /** @todo make this unnecessary */ + shader + .setViewportSize({80, 80}) + .draw(circle); + + 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, "MeshVisualizerTestFiles/defaults-wireframe2D.tga"), + /* AMD has off-by-one errors on edges compared to Intel */ + (DebugTools::CompareImageToFile{_manager, 1.0f, 0.082f})); +} + +void MeshVisualizerGLTest::renderDefaultsWireframe3D() { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported")); @@ -324,7 +533,7 @@ void MeshVisualizerGLTest::renderDefaultsWireframe() { GL::Mesh sphere = MeshTools::compile(Primitives::icosphereSolid(1)); - MeshVisualizer shader{MeshVisualizer::Flag::Wireframe}; + MeshVisualizer3D shader{MeshVisualizer3D::Flag::Wireframe}; shader.draw(sphere); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -338,7 +547,7 @@ void MeshVisualizerGLTest::renderDefaultsWireframe() { 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, "MeshVisualizerTestFiles/defaults-wireframe.tga"), + Utility::Directory::join(_testDir, "MeshVisualizerTestFiles/defaults-wireframe3D.tga"), (DebugTools::CompareImageToFile{_manager})); } @@ -352,16 +561,44 @@ void MeshVisualizerGLTest::renderDefaultsWireframe() { 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, "MeshVisualizerTestFiles/defaults-wireframe.tga"), + Utility::Directory::join(_testDir, "MeshVisualizerTestFiles/defaults-wireframe3D.tga"), /* AMD has off-by-one errors on edges compared to Intel */ (DebugTools::CompareImageToFile{_manager, 1.0f, 0.06f})); } #endif -void MeshVisualizerGLTest::render() { +void MeshVisualizerGLTest::render2D() { + GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32)); + + MeshVisualizer2D{} + .setColor(0x9999ff_rgbf) + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + .draw(circle); + + 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."); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has differently rasterized edges on four pixels */ + const Float maxThreshold = 170.0f, meanThreshold = 0.133f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 170.0f, meanThreshold = 0.456f; + #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, "FlatTestFiles/colored2D.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + +void MeshVisualizerGLTest::render3D() { GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); - MeshVisualizer{} + MeshVisualizer3D{} .setColor(0x9999ff_rgbf) .setTransformationProjectionMatrix( Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* @@ -390,13 +627,105 @@ void MeshVisualizerGLTest::render() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void MeshVisualizerGLTest::renderWireframe() { - auto&& data = WireframeData[testCaseInstanceId()]; +void MeshVisualizerGLTest::renderWireframe2D() { + auto&& data = WireframeData2D[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + #ifndef MAGNUM_TARGET_GLES + if(!(data.flags & MeshVisualizer2D::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported")); + #else + if(!(data.flags & MeshVisualizer2D::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() + std::string(" is not supported")); + #endif + + #ifdef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + Debug() << "Using" << GL::Extensions::NV::shader_noperspective_interpolation::string(); + #endif + #endif + + const Trade::MeshData circleData = Primitives::circle2DSolid(16); + + GL::Mesh circle{NoCreate}; + if(data.flags & MeshVisualizer2D::Flag::NoGeometryShader) { + /* Duplicate the vertices. The circle primitive is */ + const Trade::MeshData circleDataIndexed = + MeshTools::generateIndices(circleData); + circle = MeshTools::compile(MeshTools::duplicate(circleDataIndexed)); + + /* Supply also the vertex ID, if needed */ + #ifndef MAGNUM_TARGET_GLES2 + if(!GL::Context::current().isExtensionSupported()) + #endif + { + Containers::Array vertexIndex{circleDataIndexed.indexCount()}; + std::iota(vertexIndex.begin(), vertexIndex.end(), 0.0f); + + GL::Buffer vertexId; + vertexId.setData(vertexIndex); + circle.addVertexBuffer(std::move(vertexId), 0, MeshVisualizer2D::VertexIndex{}); + } + } else circle = MeshTools::compile(circleData); + + MeshVisualizer2D{data.flags|MeshVisualizer2D::Flag::Wireframe} + .setColor(0xffff99_rgbf) + .setWireframeColor(0x9999ff_rgbf) + .setWireframeWidth(data.width) + .setSmoothness(data.smoothness) + .setViewportSize({80, 80}) + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + .draw(circle); + + 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_EXPECT_FAIL_IF(data.flags & MeshVisualizer2D::Flag::NoGeometryShader, + "Line width is currently not configurable w/o geometry shader."); + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has differently rasterized edges on four pixels */ + const Float maxThreshold = 170.0f, meanThreshold = 0.327f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 170.0f, meanThreshold = 1.699f; + #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, "MeshVisualizerTestFiles", data.file}), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); + } + + /* Test it's not *too* off, at least */ + if(data.flags & MeshVisualizer2D::Flag::NoGeometryShader) { + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has differently rasterized edges on four pixels. Apple + A8 on more. */ + const Float maxThreshold = 170.0f, meanThreshold = 0.330f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 170.0f, meanThreshold = 2.077f; + #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, "MeshVisualizerTestFiles", data.fileXfail}), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); + } +} + +void MeshVisualizerGLTest::renderWireframe3D() { + auto&& data = WireframeData3D[testCaseInstanceId()]; setTestCaseDescription(data.name); #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) #ifndef MAGNUM_TARGET_GLES - if(!(data.flags & MeshVisualizer::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported()) + if(!(data.flags & MeshVisualizer3D::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported")); #else if(!(data.flags & MeshVisualizer::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported()) @@ -412,7 +741,7 @@ void MeshVisualizerGLTest::renderWireframe() { const Trade::MeshData sphereData = Primitives::icosphereSolid(1); GL::Mesh sphere{NoCreate}; - if(data.flags & MeshVisualizer::Flag::NoGeometryShader) { + if(data.flags & MeshVisualizer3D::Flag::NoGeometryShader) { /* Duplicate the vertices */ sphere = MeshTools::compile(MeshTools::duplicate(sphereData)); @@ -426,11 +755,11 @@ void MeshVisualizerGLTest::renderWireframe() { GL::Buffer vertexId; vertexId.setData(vertexIndex); - sphere.addVertexBuffer(std::move(vertexId), 0, MeshVisualizer::VertexIndex{}); + sphere.addVertexBuffer(std::move(vertexId), 0, MeshVisualizer3D::VertexIndex{}); } } else sphere = MeshTools::compile(sphereData); - MeshVisualizer{data.flags|MeshVisualizer::Flag::Wireframe} + MeshVisualizer3D{data.flags|MeshVisualizer3D::Flag::Wireframe} .setColor(0xffff99_rgbf) .setWireframeColor(0x9999ff_rgbf) .setWireframeWidth(data.width) @@ -450,7 +779,7 @@ void MeshVisualizerGLTest::renderWireframe() { CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); { - CORRADE_EXPECT_FAIL_IF(data.flags & MeshVisualizer::Flag::NoGeometryShader, + CORRADE_EXPECT_FAIL_IF(data.flags & MeshVisualizer3D::Flag::NoGeometryShader, "Line width is currently not configurable w/o geometry shader."); #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) /* SwiftShader has differently rasterized edges on four pixels */ @@ -467,7 +796,7 @@ void MeshVisualizerGLTest::renderWireframe() { } /* Test it's not *too* off, at least */ - if(data.flags & MeshVisualizer::Flag::NoGeometryShader) { + if(data.flags & MeshVisualizer3D::Flag::NoGeometryShader) { #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) /* SwiftShader has differently rasterized edges on four pixels. Apple A8 on more. */ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerTest.cpp index ccc2b2246..21bb9bab8 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerTest.cpp @@ -34,62 +34,111 @@ namespace Magnum { namespace Shaders { namespace Test { namespace { struct MeshVisualizerTest: TestSuite::Tester { explicit MeshVisualizerTest(); - void constructNoCreate(); - void constructCopy(); + void constructNoCreate2D(); + void constructNoCreate3D(); + + void constructCopy2D(); + void constructCopy3D(); void vertexIndexSameAsObjectId(); - void debugFlag(); - void debugFlags(); + void debugFlag2D(); + void debugFlag3D(); + void debugFlags2D(); + void debugFlags3D(); }; MeshVisualizerTest::MeshVisualizerTest() { - addTests({&MeshVisualizerTest::constructNoCreate, - &MeshVisualizerTest::constructCopy, + addTests({&MeshVisualizerTest::constructNoCreate2D, + &MeshVisualizerTest::constructNoCreate3D, + + &MeshVisualizerTest::constructCopy2D, + &MeshVisualizerTest::constructCopy3D, &MeshVisualizerTest::vertexIndexSameAsObjectId, - &MeshVisualizerTest::debugFlag, - &MeshVisualizerTest::debugFlags}); + &MeshVisualizerTest::debugFlag2D, + &MeshVisualizerTest::debugFlag3D, + &MeshVisualizerTest::debugFlags2D, + &MeshVisualizerTest::debugFlags3D}); +} + +void MeshVisualizerTest::constructNoCreate2D() { + { + MeshVisualizer2D shader{NoCreate}; + CORRADE_COMPARE(shader.id(), 0); + } + + CORRADE_VERIFY(true); } -void MeshVisualizerTest::constructNoCreate() { +void MeshVisualizerTest::constructNoCreate3D() { { - MeshVisualizer shader{NoCreate}; + MeshVisualizer3D shader{NoCreate}; CORRADE_COMPARE(shader.id(), 0); } CORRADE_VERIFY(true); } -void MeshVisualizerTest::constructCopy() { - CORRADE_VERIFY(!(std::is_constructible{})); - CORRADE_VERIFY(!(std::is_assignable{})); +void MeshVisualizerTest::constructCopy2D() { + CORRADE_VERIFY((std::is_constructible{})); + CORRADE_VERIFY(!(std::is_constructible{})); + + CORRADE_VERIFY((std::is_assignable{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +void MeshVisualizerTest::constructCopy3D() { + CORRADE_VERIFY((std::is_constructible{})); + CORRADE_VERIFY(!(std::is_constructible{})); + + CORRADE_VERIFY((std::is_assignable{})); + CORRADE_VERIFY(!(std::is_assignable{})); } void MeshVisualizerTest::vertexIndexSameAsObjectId() { #ifdef MAGNUM_TARGET_GLES2 CORRADE_SKIP("Object ID is not available on ES2."); #else - CORRADE_COMPARE(MeshVisualizer::VertexIndex::Location, Generic3D::ObjectId::Location); + CORRADE_COMPARE(MeshVisualizer2D::VertexIndex::Location, Generic2D::ObjectId::Location); + CORRADE_COMPARE(MeshVisualizer3D::VertexIndex::Location, Generic3D::ObjectId::Location); #endif } -void MeshVisualizerTest::debugFlag() { +void MeshVisualizerTest::debugFlag2D() { + std::ostringstream out; + + Debug{&out} << MeshVisualizer2D::Flag::Wireframe << MeshVisualizer2D::Flag(0xf0); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizer2D::Flag::Wireframe Shaders::MeshVisualizer2D::Flag(0xf0)\n"); +} + +void MeshVisualizerTest::debugFlag3D() { + std::ostringstream out; + + Debug{&out} << MeshVisualizer3D::Flag::Wireframe << MeshVisualizer3D::Flag(0xf0); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizer3D::Flag::Wireframe Shaders::MeshVisualizer3D::Flag(0xf0)\n"); +} + +void MeshVisualizerTest::debugFlags2D() { std::ostringstream out; - Debug{&out} << MeshVisualizer::Flag::Wireframe << MeshVisualizer::Flag(0xf0); - CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizer::Flag::Wireframe Shaders::MeshVisualizer::Flag(0xf0)\n"); + Debug{&out} << (MeshVisualizer2D::Flag::Wireframe|MeshVisualizer2D::Flag::NoGeometryShader) << MeshVisualizer2D::Flags{}; + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizer2D::Flag::Wireframe|Shaders::MeshVisualizer2D::Flag::NoGeometryShader Shaders::MeshVisualizer2D::Flags{}\n"); + #else + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizer2D::Flag::Wireframe Shaders::MeshVisualizer2D::Flags{}\n"); + #endif } -void MeshVisualizerTest::debugFlags() { +void MeshVisualizerTest::debugFlags3D() { std::ostringstream out; - Debug{&out} << (MeshVisualizer::Flag::Wireframe|MeshVisualizer::Flag::NoGeometryShader) << MeshVisualizer::Flags{}; + Debug{&out} << (MeshVisualizer3D::Flag::Wireframe|MeshVisualizer3D::Flag::NoGeometryShader) << MeshVisualizer3D::Flags{}; #ifndef MAGNUM_TARGET_GLES2 - CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizer::Flag::Wireframe|Shaders::MeshVisualizer::Flag::NoGeometryShader Shaders::MeshVisualizer::Flags{}\n"); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizer3D::Flag::Wireframe|Shaders::MeshVisualizer3D::Flag::NoGeometryShader Shaders::MeshVisualizer3D::Flags{}\n"); #else - CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizer::Flag::Wireframe Shaders::MeshVisualizer::Flags{}\n"); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizer3D::Flag::Wireframe Shaders::MeshVisualizer3D::Flags{}\n"); #endif } diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-wireframe2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-wireframe2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..04eaf5161ba1aa848ae9a17f6aefe0b0becc1244 GIT binary patch literal 7870 zcmdVfS%_8H6$aqC=XO;!2@<3Uojl|rZw`ophy)QqL{Sk62SfxB1QDVjB8VUaZM%lH zt72KPV+=%)0ue<-98f{T2SGu^2L}*DamEo9C)Db1opZaY+_?#KEN`kWZ0}eob{vb-%0Y9y)Yr{jg!frcImn=lrMRGQ>Ny-wYoK+}75%ckkY*Q>Wg!b7xbZTXf4r6x58u_;0~BckbNd$Bzr@?%lhlV$YsEiCaAp z1vMk49Kx|nE48+^-oAZ%1MsY)qhraEB`;sT{H~8Rx)~7#b(ocya|-89Et@lEPQe~i zlU=xQVbrKmr%#_wF^DLrbF(52t^u)GE48$=+`4t^TNvfHZ{Mz5xpMvb^&dWbXbZ+- z>Z4mGqM&9J#wAW%1ENEVXpS8_2EK(dGr1EcOt^OKS^|?1QBdb*%sFuli1xl{!Yi}V zvuDrVv13PyK}12FD~dS6c~itebM)v@z_AqB_U+r}&!7MF>CC+aixzF!vLz)UqM#15FB8`st_Lvqpovyz8^r$nXr%md z8iSrWbLRc~_nTQ%#uD9(h=MxIQrCbB*nn@*Vxt@1#ww<x=nwy(b79tAjYSR#K zF=@bqWMdFXjq+QZp){tP=)i#klO|2NapOil;l-pyHzT5;u0|Aakv4$`NwgSUdli;^ z1s*(jFl*MV?(XiCg@}SW%+de@E?@)xn;45FG`fx|ra8;jty>o^T=@L?^Vp)5{hTLf zA`0qiECCmj20TbMXqoDzBq=X{t-_U0k3u(g?AVhhPo`W%6x3msjws+Vu)t9SRkV8b zQV~?pR3rsOC8#!&!4xTM&6+i9*RGYq^5Ru2S#%@yQBX4q>w%*{gbpd{pTcNV zJ(OdmRc%z-)Fcw+;QK0A`MjPuabn!KaaXQfNg~OJD5$H25pa!Qfulf#4k>Dq2xf@p z=l;VqGWJuj)mw#9ky8QG7lol(7MI&1nC#iJXA2fA*tTt38X6G=btuvqirhT}EN~Qv z&>=-F06IS*39N{)T>QCLa_bXAZA7mUE2N5`>ZR1E2$g)Dl0^W&-nnyUQ&ZE!hYth0 zr>9uWqEAT0pjxDH)EfbVM}i6=TI52AP|89Q*hhi7uQRwf&<*OelcgFeT1tvsUrEHj z=kKbwNEfkOzkYq<#EAzF9xN{Vhe=4qpim|5k@y-wgG7T2C1UguMr>I@6+vdjpZm{z za5k};s^Td{>V)Gdw^kD0*9i+Kl8qZT0<*zg_D1NEkcvTJ)_-&nL?ckp2+$xyi8wL> zWhR`D!gGfl8@NG@RKU~~Nnb{mC@Wd)xBnj7nKNfbjvRUZ{CSSOfw?54B1~K&O*Mc6 z3K{_#WGI6=6GC4|wC>v@i8ZQoijWE+am%Xmoy}Uw1z#l;uU@@cx^(HLO_9laLvl$- zl^Ew+8xT04pb?-!hBBx#AqYfNv4|c*9Pdu@2316gmwe?!iAhGNoV=k4#lC&}rc9Zl zq!hvExFk%>;wUtL00a&wXas12E^cj+SHkc(B<3#iKPmw7vg9Z$IsML*{R*0{Kf%3w z_qgxgy}JlP$0cF^9aFe)2LcKpSl}pvDnp4_2qn!!*$;$&u+61JsmFnKSnNjYp3L*+ z&6^i5UR*Hixa?2Mmhc>7Um^xoa-EqZj5E74E=OM5Q_h*{AB9SF&$?4 zb1oqGnr_I*1^?$mIEd2GAWYQOXRwtsAr00WfXdyT+$ir3)|;qb%q{6J<|coGEjef& zjUt#q^0-*ThvaxJugSxI4PImfMNMAH)a*4=4PKcAMNMA+)$E044PMR$MU7rHf999w zpY3IT01wzFi2?dd5?@>@ACK}E5T0qhi250yYX<1E(ExqK>NK1I_@ws9pY}fK!{X2W YjQL3)O$Y4L?f?AoxZ9%rhY!*}0v&IStpET3 literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-wireframe.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-wireframe3D.tga similarity index 100% rename from src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-wireframe.tga rename to src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-wireframe3D.tga diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..dc8598dd67b49aadfe4c9f8328e924bc7bc629c7 GIT binary patch literal 8218 zcmdVfS8tR@6b4|g*VqOTMJPfE%0(`6m4Cx;qSpwLhy+tiF}?SWD58drAc)XH?+|(? z&-rG(wnxhiv5jr*zj@tZ+PACy6@_a4;eBfTAMcDLrcr6IdkqW zU+$;lGQ_Rg*ZME#&LzLNZQGF}N48zOxcvV88|&8j>9{1MV!(`XIJIca$?@Kb6&JQ| zuR6|o^{VB=hY{01ek>)7=r$8kRN**qOK<`8yVm$|`t)}*XI|d2B}I^c#&!Tjx3M!2 zQx4q1eVW!_WM}v6xw~>D=#5vePUvusZUYeoH6y0s@|)HK;?Am7d(NJn`|{txJ4k!l&)oGmHMZ zwbc^lctjM`j0)q<@de!EF7d_i$_G!JFs5_6cFlbEF3W^65d}4)Fz$#ifF=c)f#X|u z<3_B0dBweZkCrTXvT&h+hpB01|KzL~D`; z#>g%+E0)?y{IhA3y|nAxxtxcHf|^knH)Q}sfWZgNc&#KRc^4_zWyt&rGP?IOW+*7F zuU|)ARR&e$BM}8PqcF|^0<=VjBB;h`vB<_Mx{);;Vo}OV9z9ZRp3k4Z{`&Qtk%)qt zQ5ffp0a_0{O=l2BE2ZfSu-qZ$uR)bMaqytXP98X*KZ>j}%;<)if|`+j;gU^j5oem% zARHT{X<}R?f=Ys^S_=lSz*XU)30absn@E3+D_R3@UJW>kwY zKuf?;1l8!EGo`Bdq*#=UF2++{@bu}eHES4cxqLY|I@1wRP%|o4H3JCH5^zRAHA;&q zb@T{y@~0U8JbE-4DQ`u#D;9o5K_}u(&8QY*fX0+2qacjdVEal}N0yVu*-&e+#r&r&>?LPpwT()*cG?a&iUj#aRf}Pfzm!on@wFm zeC${r1`!2yWp?r~0<;l03Pk9Tq82~|Gg2GG-$~?7z)9`^cIk*S!%h*kzgDfbt`Wyq zTP|I4fZbfX)`GkCOKX$*gj5Weg?S!Y)aXIrk)Yy<7P$}vA{;@vj1Z#@7PY%2K3v=? zINTk^(AhYpoGQw`rH)0=#X^VzZ4bJnjJj)!{21u&!H68tj#Z0b6F5Q?7-hCn@rGrLDUAK6PGS0c#|~wK zq#CnJLMjHyGINirHV`xdG{{gQMlT4V2&mY>0u*L7WCe;-l#79KO2Kt(J6ENz>cvon zMpUU`-)5Y=6xIjJmMN@jZ{BoSjn^e16$A3jvETzri~tQXl!(y_LMUNU5}E+pgRP^C zG-`JCJC>c4jxJ+F#LALZD$&r44a%wg`xzcTbVxtNsN<55Nk)cG4 zKEjA_BD4`&$;z1I*na~Z;!afuoD;{mq@W_HWGNk$ZTn5ew&B{f7xU(shQ*H`r|5KC z5>jQxvAJTczySq~01Yyfh|xzFrL>_UxFW!`p`t|oWVlwv0p++zS)#BhDt7Ltw!@{Z zTX9IN6rPSt!YnrEen3D01Z&<bqa;N|5Mnjh^sCyNrU-ljcgmrDBN z+?g-knO)huImM(Sg$P|@^)KTzRPI1h^#BVT1)?AI(N7+svIW>)VSjs(HKINWFFx#C6mxfqn zN|y|nF?Pw3d)gHN8PGvf*8HV}{exntjcB{t4b_cU9YcC3NRJJPf~r~^xNzhiFy)kR z3B5vM>8*4N?GWRX)Dp)!nI&MUGK#~mdP&8A8DlQtS_T0q0oSMhtXRzWfK-@y`+;#8 z;(F@OGVigldu7sNb$cO!f~NFkw@IlF6$}|GLHlT7O}o5r9!n!D>DmX6^a!ogpKgu% z;jL%Cyv6Sak5BQW*JmC%`@{oyufKNaVZ#R-TG;6 ws&()3X)fNF{J&3!-TZO0+djH}=11Rd_}tvhAHYBRLwuXX{=~=q_V54y0me9%l>h($ literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo3D.tga similarity index 100% rename from src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo.tga rename to src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo3D.tga diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-wide2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-wide2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..ff5cc87c696fae9729bd890de93e33d1a7d70715 GIT binary patch literal 7116 zcmd6sdrKWx6vgk%C{0?5NdM`_%4hPC#F&)WhPFO1DpnCq#gu@g5=mo9Or@68rdUi7 zN-Cy`Xo?!~(cjv8?!Cw3Eu_u$s?2d_&R%=1J^MUoZlUmgq4;;HaH;TdVZO1kQDuhrrJN^Bm>+51E3~X;-jXhN%g8&&o;O-}i&nOrCc41})B*!23?s?6x4u8xO#=?+9}Q4 z7O1rj_SNgx7C_Jbe#Nd^z+eD?=76IJM0D*~e1^(tvyG7v7oDA-ugIHWa0JjIu_(&% zie5#HMP7Io*o!LGpC*@=onx=Jx3UTbM*y`J$tg0<5PG^L(nd*u(xU3S%Fi(=My(S~ ztgN&EIM(Z|0)r!fhEhg_N8+7DLRTrNgXu)gk)CxwCL9N>j*nN?!{7*@atT8gWm+13 zl2g(1<)bJBTSvV8=8ZjX-|nuQk3F6Rh`|5=%^^sTQlI!3lgyP|isy~vvM#$aG2wy# z{P=N2tqg-BfW~5?%u^|$%an2|D9fpooJyWb4X_Yw zdb@pnuJ`SW7d8)uquweoI09&_C(1mT5^BxGNapoO%(}(*&z?Ee=J0T3F$|6XDwi-` zQRc~%PzxnbCdNrcir8l2Z{nVPT=e z#?L5%hTrescZa2^DVI1Yipep6V^w=f$dQm#QC53_lqG`soar}iN}C0SH#g<+9(IHy z)#BtX5Y{lSHt}MI8UYDOv^h^N-Hml!yOo4VI+$23ZAuq(A8ri|f#8@~SU4$8rCWi; z>Y7dyXd#zK>RTVK6o5)S*X%@qu}ffSXBk$E2~%-2Pkux+z&NIsmVj{36S<9Y;d2{S zV2%%3T|+2=7<$RnBZnx`)Ds3(#lSSI*d?g}6IOASTx%o}#v~WbJdLH$O1p@!U%d+B z;uby*04IXdyplo*G4%HM$RTRt1QCVB_D(}JVZ~;NwZ>z1iYAN(kEyiZ`19`FFd%L{ zAOLU$DKcuvpfqYE5VBf?qfppQY8Fgcnps6z;w(wbC5-TB&?RW}@9d~!9h=fskzfcy z&~vA_LjsjiLX7}2Ht8ap2hv0zQd5z*cIAYnkJZQmXZS_q;R7_hmGSuT=~MoP7np(| z)RhwmpfNgVAvY1k@RM{=o*lVOC@f8^7VFjgh3h}!bAJuUTE!3djpYl|hL@CTF> ze<>RSi>}rQ2CX{Bno8~iG`)nuHVjy&w^Rn2S!{xZ1refY>&N*k> zmz2Qh!e_U+v#l*b)O1^WhFf?goTFARD>Z!?tLbZ|#PVeFqEwUD=$d{esNv@iNtII> zzl&;p$$Xzj$F0)Gotl2Us@W$w3FV2*H|cuC*2`_xJ}*}FbLc62uC4kf`{m%(glS1Hht>E|;Z%MCsnX9g|LG^8s{9D`&Oe2{#m{YnG^JisbuRv5_YbxP BIg0=Q literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-wide.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-wide3D.tga similarity index 100% rename from src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-wide.tga rename to src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-wide3D.tga diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..6735d24dd20088e57c5d94ea3e85d8d9705a6070 GIT binary patch literal 7442 zcmd7X>5o)J6b10^o}q~vV~8>82S4~9@i&3{zD0w(294~iY$77E2r9@TA|jjYd(^n2 z{?4nOW-OZz!q5!qV(Qho_niBxZr$pBuhqJ>HTG|MYkKRR){be@rlrl+rkq~1=)l^w zUwrnNkB-X>zoFfs|Lun#8eg}6|Kd}p-udyz*Z%n9^X8w9OM@T=%oxY%WvyEr8IE^+ zVAkJ%w~KeW-SHHL3q{;t(nilNSn&P(?|<;!ce8)`>D52~eDsQQx}$~wU`9;i@?zZf ztzWp3&OheDsk(9|{NX;F>k-_wB1W3<1C~YcuB*=cly9v2o9yw|@EM zk#Kzc_1CiEjo*IDRd4wkzz_i3TruawH55-?i6b-6dGFFC8L|A>v6{mW0NhMei6hQW zB#w)7=Bzz%;N`#m3OM4gJlnZti_XJWLP<9m0NgX~_{$YRh#uEQ-C!B<|CrU1lP3i{ z_risWuVV-Rj`C=qj4PVZJ*IV;h4gET>19TKQ{|lHdV9ah5+E0O|4uDAL0l+swG*hT(a?Bx5=+rRa@-%vSq6e9javv z0l+b*?Han!hW_E)i=A%kT+KAQf6vB^TX*cJRSW^ZR|~L> z7I{P-ijeZ6Mr0)i#nf4K`0$CPOO@ef9!e54MZpjNJV+>XDLu#|l9s!Be1J-`0v7V{ zs#OYktzrlOj#(XG=$f@a|6uN=OxhCzOdAyu>|eL;_1}M=$)cA4>2?eOz=Kpm*PFD6 zJP@HWX-^FhYUXCn#fvPKo;Xnp83KS~R^LwOdXpBBv=JwdgT!%QMV}nt6M>Yb9b30< z-?mMs*%xJ*(hUXx>%_Pg4e1DS5`@T&Nl$U^nNNq2)5J+2Z3azjGk?pDA3w8jp%PoE zrziu!gQP<@1ub$Cgvb$105i2)RBfAIA+!73kpszj;w(VwX8uz)dpDoObj)wqySEO; z5C9yYo`=xgf&w`SLga{&7Jx7Wlc3VtSd@&gbRCq=x$>39*`S>F3UAFH>)T}Wq1CHp zvkRZHOr>uS#9&Zx*-rT?gPw>`2$Cfp>$YHoG!|Y9ZSuJ5@O1V$w3IGoQoZig(tF*f ztNz(nA}AYr<&h)ha(r+GOA#ng<({h9MU9dSF_MJICya#C56%6hq~*+T{y8L^BWk?T zt1kA6*4-6-(cHFu`|eGfn7HhR=F%Xj*i?=wOjDVlCP0Q5Nx}>hMoT#T(5!TFpzCmR z@Hh`lk4mb*^{UTZ6+L4W?!w%;^Us~r?+4}5Ajn|l(ku%)q^JpyAx1LNEg>YF8Obq= zY(cU`rdnQShr(_R6hmc70l}O-b!Vkej%_%x&oF?*_}dZxPA;=PSEtrA&t@ZhoK%cVyNN5`eX%Bq7d&V9ehx~i0;kHrivz%CJiiG(x?+X=EHUV~G&pb*AVGx?Epif(iez7& z$qUBrqoH1?E4Hq=lZckx+>O`Xw0pO@R)lq2j#p*Og(G()s7iGUebTcpDK}m+cDL-Y z8)dy5v#2I={C(JbMM@0wbD#CFN!T?zC&9vAnTXrm7#bon82K zO8EdWV8)nBHYkFTk~oF8N3UjC#aE%XmttWyO0mFnYVl$pol>qq!eH6=qCRJ9Lmq!q5W_l!b6CmJy03iBcdTZ?ux`v z9^DP?VdPLAtw!VqJ|4aS540!rf%|no{-2=N5ZC=W