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 000000000..d253ee1f4 Binary files /dev/null and b/doc/shaders-meshvisualizer2d.png differ 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 a2d8583d4..5022ecaff 100644 Binary files a/doc/shaders-meshvisualizer.png and b/doc/shaders-meshvisualizer3d.png differ diff --git a/doc/snippets/MagnumShaders.cpp b/doc/snippets/MagnumShaders.cpp index d94808e26..7d63b07fe 100644 --- a/doc/snippets/MagnumShaders.cpp +++ b/doc/snippets/MagnumShaders.cpp @@ -95,7 +95,7 @@ mesh.addVertexBuffer(vertices, 0, /* [shaders-generic] */ /* [shaders-meshvisualizer] */ -Shaders::MeshVisualizer visualizerShader{Shaders::MeshVisualizer::Flag::Wireframe}; +Shaders::MeshVisualizer3D visualizerShader{Shaders::MeshVisualizer3D::Flag::Wireframe}; visualizerShader .setColor(0x2f83cc_rgbf) .setWireframeColor(0xdcdcdc_rgbf) @@ -254,7 +254,7 @@ GL::Buffer vertices; vertices.setData(data, GL::BufferUsage::StaticDraw); GL::Mesh mesh; -mesh.addVertexBuffer(vertices, 0, Shaders::MeshVisualizer::Position{}); +mesh.addVertexBuffer(vertices, 0, Shaders::MeshVisualizer3D::Position{}); /* [MeshVisualizer-usage-geom1] */ /* [MeshVisualizer-usage-geom2] */ @@ -262,7 +262,7 @@ Matrix4 transformationMatrix = Matrix4::translation(Vector3::zAxis(-5.0f)); Matrix4 projectionMatrix = Matrix4::perspectiveProjection(35.0_degf, 1.0f, 0.001f, 100.0f); -Shaders::MeshVisualizer shader{Shaders::MeshVisualizer::Flag::Wireframe}; +Shaders::MeshVisualizer3D shader{Shaders::MeshVisualizer3D::Flag::Wireframe}; shader.setColor(0x2f83cc_rgbf) .setWireframeColor(0xdcdcdc_rgbf) .setViewportSize(Vector2{GL::defaultFramebuffer.viewport().size()}) @@ -277,7 +277,7 @@ std::iota(vertexIndex.begin(), vertexIndex.end(), 0.0f); GL::Buffer vertexIndices; vertexIndices.setData(vertexIndex, GL::BufferUsage::StaticDraw); -mesh.addVertexBuffer(vertexIndices, 0, Shaders::MeshVisualizer::VertexIndex{}); +mesh.addVertexBuffer(vertexIndices, 0, Shaders::MeshVisualizer3D::VertexIndex{}); /* [MeshVisualizer-usage-no-geom-old] */ } #endif @@ -291,7 +291,7 @@ Containers::StridedArrayView1D 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 000000000..04eaf5161 Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-wireframe2D.tga differ 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 000000000..dc8598dd6 Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo2D.tga differ 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 000000000..ff5cc87c6 Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-wide2D.tga differ 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 000000000..6735d24dd Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe2D.tga differ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe3D.tga similarity index 100% rename from src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe.tga rename to src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe3D.tga diff --git a/src/Magnum/Shaders/visibility.h b/src/Magnum/Shaders/visibility.h index fb4f47213..6163576ee 100644 --- a/src/Magnum/Shaders/visibility.h +++ b/src/Magnum/Shaders/visibility.h @@ -39,8 +39,10 @@ #else #define MAGNUM_SHADERS_EXPORT CORRADE_VISIBILITY_STATIC #endif +#define MAGNUM_SHADERS_LOCAL CORRADE_VISIBILITY_LOCAL #else #define MAGNUM_SHADERS_EXPORT +#define MAGNUM_SHADERS_LOCAL #endif #endif