From e3841ade7eba766c85a3fedea4202537094720f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 2 Apr 2020 00:26:01 +0200 Subject: [PATCH] Shaders: object and primitive ID visualization in MeshVisualizer. --- doc/changelog.dox | 2 + doc/generated/CMakeLists.txt | 1 + doc/generated/shaders.cpp | 54 ++ doc/shaders-meshvisualizer2d-primitiveid.png | Bin 0 -> 7845 bytes doc/shaders-meshvisualizer3d-primitiveid.png | Bin 0 -> 13779 bytes doc/snippets/MagnumShaders.cpp | 34 + src/Magnum/Shaders/MeshVisualizer.cpp | 196 +++++- src/Magnum/Shaders/MeshVisualizer.frag | 81 ++- src/Magnum/Shaders/MeshVisualizer.geom | 38 +- src/Magnum/Shaders/MeshVisualizer.h | 292 ++++++++- src/Magnum/Shaders/MeshVisualizer.vert | 43 +- src/Magnum/Shaders/Test/CMakeLists.txt | 13 +- .../Shaders/Test/MeshVisualizerGLTest.cpp | 603 +++++++++++++++++- .../defaults-objectid2D.tga | Bin 0 -> 2610 bytes .../defaults-objectid3D.tga | Bin 0 -> 1608 bytes .../defaults-primitiveid2D.tga | Bin 0 -> 2610 bytes .../defaults-primitiveid3D.tga | Bin 0 -> 1608 bytes .../MeshVisualizerTestFiles/objectid2D.tga | Bin 0 -> 1718 bytes .../MeshVisualizerTestFiles/objectid3D.tga | Bin 0 -> 2494 bytes .../primitiveid-tn.tga | Bin 0 -> 988 bytes .../MeshVisualizerTestFiles/primitiveid2D.tga | Bin 0 -> 2508 bytes .../MeshVisualizerTestFiles/primitiveid3D.tga | Bin 0 -> 2418 bytes .../wireframe-nogeo-objectid2D.tga | Bin 0 -> 8353 bytes .../wireframe-nogeo-objectid3D.tga | Bin 0 -> 7703 bytes .../wireframe-objectid2D.tga | Bin 0 -> 7604 bytes .../wireframe-objectid3D.tga | Bin 0 -> 6820 bytes .../wireframe-primitiveid-tn.tga | Bin 0 -> 1688 bytes .../MeshVisualizerTestFiles/wireframe-tn.tga | Bin 1676 -> 0 bytes 28 files changed, 1281 insertions(+), 76 deletions(-) create mode 100644 doc/shaders-meshvisualizer2d-primitiveid.png create mode 100644 doc/shaders-meshvisualizer3d-primitiveid.png create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-objectid2D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-objectid3D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-primitiveid2D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-primitiveid3D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectid2D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectid3D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/primitiveid-tn.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/primitiveid2D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/primitiveid3D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo-objectid2D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo-objectid3D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-objectid2D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-objectid3D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-primitiveid-tn.tga delete mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-tn.tga diff --git a/doc/changelog.dox b/doc/changelog.dox index dac2dfc14..a393ef820 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -221,6 +221,8 @@ See also: - New @ref Shaders::MeshVisualizer2D for 2D mesh visualization - Tangent space visualization in @ref Shaders::MeshVisualizer3D +- Object and primitive ID visualization in @ref Shaders::MeshVisualizer2D + and @ref Shaders::MeshVisualizer3D - Texture coordinate transformation in @ref Shaders::DistanceFieldVector, @ref Shaders::Flat, @ref Shaders::Phong and @ref Shaders::Vector - Ability to render Per-instance / per-vertex object ID in @ref Shaders::Flat diff --git a/doc/generated/CMakeLists.txt b/doc/generated/CMakeLists.txt index 284395717..1eed805d5 100644 --- a/doc/generated/CMakeLists.txt +++ b/doc/generated/CMakeLists.txt @@ -60,6 +60,7 @@ target_link_libraries(easings add_executable(shaders shaders.cpp) target_link_libraries(shaders Magnum::Magnum + Magnum::DebugTools Magnum::MeshTools Magnum::Primitives Magnum::Shaders diff --git a/doc/generated/shaders.cpp b/doc/generated/shaders.cpp index 388e712df..94dbbd8d1 100644 --- a/doc/generated/shaders.cpp +++ b/doc/generated/shaders.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -79,7 +80,9 @@ struct ShaderVisualizer: Platform::WindowlessApplication { std::string phong(); std::string meshVisualizer2D(); + std::string meshVisualizer2DPrimitiveId(); std::string meshVisualizer3D(); + std::string meshVisualizer3DPrimitiveId(); std::string flat(); std::string vertexColor(); @@ -129,7 +132,9 @@ int ShaderVisualizer::exec() { for(auto fun: {&ShaderVisualizer::phong, &ShaderVisualizer::meshVisualizer2D, + &ShaderVisualizer::meshVisualizer2DPrimitiveId, &ShaderVisualizer::meshVisualizer3D, + &ShaderVisualizer::meshVisualizer3DPrimitiveId, &ShaderVisualizer::flat, &ShaderVisualizer::vertexColor, &ShaderVisualizer::vector, @@ -185,6 +190,30 @@ std::string ShaderVisualizer::meshVisualizer2D() { return "meshvisualizer2d.png"; } +std::string ShaderVisualizer::meshVisualizer2DPrimitiveId() { + const Matrix3 projection = + Matrix3::projection(Vector2{3.0f})* + Matrix3::rotation(13.7_degf); + + const auto map = DebugTools::ColorMap::turbo(); + const Vector2i size{Int(map.size()), 1}; + GL::Texture2D colorMapTexture; + colorMapTexture + .setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::Repeat) + .setStorage(1, GL::TextureFormat::SRGB8Alpha8, size) + .setSubImage(0, {}, ImageView2D{PixelFormat::RGB8Srgb, size, map}); + + Shaders::MeshVisualizer2D{Shaders::MeshVisualizer2D::Flag::PrimitiveId} + .setTransformationProjectionMatrix(projection) + .setColorMapTransformation(1.0f/255.0f, 1.0f/8.0f) + .bindColorMapTexture(colorMapTexture) + .draw(MeshTools::compile(Primitives::circle2DSolid(8))); + + return "meshvisualizer2d-primitiveid.png"; +} + std::string ShaderVisualizer::meshVisualizer3D() { const Matrix4 transformation = Transformation* Matrix4::rotationZ(13.7_degf)* @@ -208,6 +237,31 @@ std::string ShaderVisualizer::meshVisualizer3D() { return "meshvisualizer3d.png"; } +std::string ShaderVisualizer::meshVisualizer3DPrimitiveId() { + const Matrix4 transformation = Transformation* + Matrix4::rotationZ(13.7_degf)* + Matrix4::rotationX(-12.6_degf); + + const auto map = DebugTools::ColorMap::turbo(); + const Vector2i size{Int(map.size()), 1}; + GL::Texture2D colorMapTexture; + colorMapTexture + .setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::Repeat) + .setStorage(1, GL::TextureFormat::SRGB8Alpha8, size) + .setSubImage(0, {}, ImageView2D{PixelFormat::RGB8Srgb, size, map}); + + Shaders::MeshVisualizer3D{Shaders::MeshVisualizer3D::Flag::PrimitiveId} + .setTransformationMatrix(transformation) + .setProjectionMatrix(Projection) + .setColorMapTransformation(1.0f/255.0f, 1.0f/32.0f) + .bindColorMapTexture(colorMapTexture) + .draw(MeshTools::compile(Primitives::uvSphereSolid(4, 8))); + + return "meshvisualizer3d-primitiveid.png"; +} + std::string ShaderVisualizer::flat() { Shaders::Flat3D{} .setColor(BaseColor) diff --git a/doc/shaders-meshvisualizer2d-primitiveid.png b/doc/shaders-meshvisualizer2d-primitiveid.png new file mode 100644 index 0000000000000000000000000000000000000000..3d3ec25b61171cc205f6ff7772135b1faa770da7 GIT binary patch literal 7845 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 T#Odk~68*vb7W?w|xJ3L9!GQ)K literal 0 HcmV?d00001 diff --git a/doc/shaders-meshvisualizer3d-primitiveid.png b/doc/shaders-meshvisualizer3d-primitiveid.png new file mode 100644 index 0000000000000000000000000000000000000000..45c35f6857e1f4c14b47f7ebc30e4bdd7ba198e8 GIT binary patch literal 13779 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 H=Jx*q5u2C} literal 0 HcmV?d00001 diff --git a/doc/snippets/MagnumShaders.cpp b/doc/snippets/MagnumShaders.cpp index 7ab1a1e4e..b6174b29d 100644 --- a/doc/snippets/MagnumShaders.cpp +++ b/doc/snippets/MagnumShaders.cpp @@ -27,6 +27,9 @@ #include #include +#include "Magnum/ImageView.h" +#include "Magnum/PixelFormat.h" +#include "Magnum/DebugTools/ColorMap.h" #include "Magnum/GL/Buffer.h" #include "Magnum/GL/DefaultFramebuffer.h" #include "Magnum/GL/Framebuffer.h" @@ -34,9 +37,11 @@ #include "Magnum/GL/Renderbuffer.h" #include "Magnum/GL/RenderbufferFormat.h" #include "Magnum/GL/Texture.h" +#include "Magnum/GL/TextureFormat.h" #include "Magnum/Math/Color.h" #include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Matrix4.h" +#include "Magnum/Math/FunctionsBatch.h" #include "Magnum/MeshTools/Duplicate.h" #include "Magnum/Shaders/DistanceFieldVector.h" #include "Magnum/Shaders/Flat.h" @@ -358,6 +363,35 @@ shader.setColor(0x2f83cc_rgbf) .draw(mesh); /* [MeshVisualizer-usage-no-geom2] */ } + +#ifndef MAGNUM_TARGET_GLES2 +{ +GL::Mesh mesh; +Containers::ArrayView objectIds; +Matrix4 transformationMatrix, projectionMatrix; +/* [MeshVisualizer-usage-object-id] */ +const auto map = DebugTools::ColorMap::turbo(); +const Vector2i size{Int(map.size()), 1}; + +GL::Texture2D colorMapTexture; +colorMapTexture + .setMinificationFilter(SamplerFilter::Linear) + .setMagnificationFilter(SamplerFilter::Linear) + .setWrapping(SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::RGBA8, size) + .setSubImage(0, {}, ImageView2D{PixelFormat::RGB8Srgb, size, map}); + +Shaders::MeshVisualizer3D shader{ + Shaders::MeshVisualizer3D::Flag::InstancedObjectId}; +shader.setColorMapTransformation(0.0f, 1.0f/Math::max(objectIds)) + .setTransformationMatrix(transformationMatrix) + .setProjectionMatrix(projectionMatrix) + .bindColorMapTexture(colorMapTexture) + .draw(mesh); +/* [MeshVisualizer-usage-object-id] */ +} +#endif + #if !defined(__GNUC__) || defined(__clang__) || __GNUC__*100 + __GNUC_MINOR__ >= 500 { /* [Phong-usage-colored1] */ diff --git a/src/Magnum/Shaders/MeshVisualizer.cpp b/src/Magnum/Shaders/MeshVisualizer.cpp index cb433ba96..7ac9a5794 100644 --- a/src/Magnum/Shaders/MeshVisualizer.cpp +++ b/src/Magnum/Shaders/MeshVisualizer.cpp @@ -37,11 +37,19 @@ #include "Magnum/GL/Context.h" #include "Magnum/GL/Extensions.h" #include "Magnum/GL/Shader.h" +#include "Magnum/GL/Texture.h" #include "Magnum/Shaders/Implementation/CreateCompatibilityShader.h" namespace Magnum { namespace Shaders { +namespace { + enum: Int { + /* First four taken by Phong (A/D/S/N) */ + ColorMapTextureUnit = 4 + }; +} + namespace Implementation { MeshVisualizerBase::MeshVisualizerBase(FlagsBase flags): _flags{flags} { @@ -59,6 +67,16 @@ MeshVisualizerBase::MeshVisualizerBase(FlagsBase flags): _flags{flags} { MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::OES::standard_derivatives); #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(_flags & FlagBase::PrimitiveId && !(_flags >= FlagBase::PrimitiveIdFromVertexId)) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GL320); + #else + MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GLES320); + #endif + } + #endif + #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ if(!Utility::Resource::hasGroup("MagnumShaders")) @@ -69,10 +87,12 @@ MeshVisualizerBase::MeshVisualizerBase(FlagsBase flags): _flags{flags} { 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 & FlagBase::NoGeometryShader || version >= GL::Version::GL320); + /* Extended in MeshVisualizer3D for TBN visualization */ + CORRADE_INTERNAL_ASSERT(!(_flags & FlagBase::Wireframe) || _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 & FlagBase::NoGeometryShader || version >= GL::Version::GLES310); + /* Extended in MeshVisualizer3D for TBN visualization */ + CORRADE_INTERNAL_ASSERT(!(_flags & FlagBase::Wireframe) || _flags & FlagBase::NoGeometryShader || version >= GL::Version::GLES310); #else const GL::Version version = GL::Context::current().supportedVersion({GL::Version::GLES300, GL::Version::GLES200}); #endif @@ -82,6 +102,10 @@ GL::Version MeshVisualizerBase::setupShaders(GL::Shader& vert, GL::Shader& frag, vert.addSource(_flags & FlagBase::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") .addSource(_flags & FlagBase::NoGeometryShader ? "#define NO_GEOMETRY_SHADER\n" : "") + #ifndef MAGNUM_TARGET_GLES2 + .addSource(_flags & FlagBase::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") + .addSource(_flags >= FlagBase::PrimitiveIdFromVertexId ? "#define PRIMITIVE_ID_FROM_VERTEX_ID\n" : "") + #endif #ifdef MAGNUM_TARGET_WEBGL .addSource("#define SUBSCRIPTING_WORKAROUND\n") #elif defined(MAGNUM_TARGET_GLES2) @@ -90,14 +114,27 @@ GL::Version MeshVisualizerBase::setupShaders(GL::Shader& vert, GL::Shader& frag, #endif ; frag.addSource(_flags & FlagBase::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") - .addSource(_flags & FlagBase::NoGeometryShader ? "#define NO_GEOMETRY_SHADER\n" : ""); + .addSource(_flags & FlagBase::NoGeometryShader ? "#define NO_GEOMETRY_SHADER\n" : "") + #ifndef MAGNUM_TARGET_GLES2 + .addSource(_flags & FlagBase::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") + .addSource(_flags & FlagBase::PrimitiveId ? + (_flags >= FlagBase::PrimitiveIdFromVertexId ? + "#define PRIMITIVE_ID_FROM_VERTEX_ID\n" : + "#define PRIMITIVE_ID\n") : "") + #endif + ; return version; } MeshVisualizerBase& MeshVisualizerBase::setColor(const Color4& color) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(_flags & (FlagBase::Wireframe|FlagBase::InstancedObjectId|FlagBase::PrimitiveId), + "Shaders::MeshVisualizer::setColor(): the shader was not created with wireframe or object/primitive ID enabled", *this); + #else CORRADE_ASSERT(_flags & FlagBase::Wireframe, "Shaders::MeshVisualizer::setColor(): the shader was not created with wireframe enabled", *this); + #endif setUniform(_colorUniform, color); return *this; } @@ -116,11 +153,37 @@ MeshVisualizerBase& MeshVisualizerBase::setWireframeWidth(const Float width) { return *this; } +#ifndef MAGNUM_TARGET_GLES2 +MeshVisualizerBase& MeshVisualizerBase::setColorMapTransformation(const Float offset, const Float scale) { + CORRADE_ASSERT(_flags & (FlagBase::InstancedObjectId|FlagBase::PrimitiveId), + "Shaders::MeshVisualizer::setColorMapTransformation(): the shader was not created with object/primitive ID enabled", *this); + setUniform(_colorMapOffsetScaleUniform, Vector2{offset, scale}); + return *this; } -MeshVisualizer2D::MeshVisualizer2D(const Flags flags): Implementation::MeshVisualizerBase{Implementation::MeshVisualizerBase::FlagBase(UnsignedByte(flags))} { +MeshVisualizerBase& MeshVisualizerBase::bindColorMapTexture(GL::Texture2D& texture) { + CORRADE_ASSERT(_flags & (FlagBase::InstancedObjectId|FlagBase::PrimitiveId), + "Shaders::MeshVisualizer::bindColorMapTexture(): the shader was not created with object/primitive ID enabled", *this); + texture.bind(ColorMapTextureUnit); + return *this; +} +#endif + +} + +MeshVisualizer2D::MeshVisualizer2D(const Flags flags): Implementation::MeshVisualizerBase{Implementation::MeshVisualizerBase::FlagBase(UnsignedShort(flags))} { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(flags & ((Flag::Wireframe|Flag::InstancedObjectId|Flag::PrimitiveIdFromVertexId) & ~Flag::NoGeometryShader), + "Shaders::MeshVisualizer2D: at least one visualization feature has to be enabled", ); + #else CORRADE_ASSERT(flags & (Flag::Wireframe & ~Flag::NoGeometryShader), "Shaders::MeshVisualizer2D: at least Flag::Wireframe has to be enabled", ); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags & Flag::InstancedObjectId) || !(flags & Flag::PrimitiveIdFromVertexId), + "Shaders::MeshVisualizer2D: Flag::InstancedObjectId and Flag::PrimitiveId are mutually exclusive", ); + #endif Utility::Resource rs{"MagnumShaders"}; GL::Shader vert{NoCreate}; @@ -139,6 +202,11 @@ MeshVisualizer2D::MeshVisualizer2D(const Flags flags): Implementation::MeshVisua geom = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Geometry); (*geom) .addSource("#define WIREFRAME_RENDERING\n#define MAX_VERTICES 3\n") + .addSource(_flags & FlagBase::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") + .addSource(_flags & FlagBase::PrimitiveId ? + (_flags >= FlagBase::PrimitiveIdFromVertexId ? + "#define PRIMITIVE_ID_FROM_VERTEX_ID\n" : + "#define PRIMITIVE_ID\n") : "") .addSource(rs.get("MeshVisualizer.geom")); } #else @@ -163,7 +231,10 @@ MeshVisualizer2D::MeshVisualizer2D(const Flags flags): Implementation::MeshVisua #endif { bindAttributeLocation(Position::Location, "position"); - + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::InstancedObjectId) + bindAttributeLocation(ObjectId::Location, "instanceObjectId"); + #endif #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2) #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isVersionSupported(GL::Version::GL310)) @@ -182,26 +253,46 @@ MeshVisualizer2D::MeshVisualizer2D(const Flags flags): Implementation::MeshVisua #endif { _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); - if(flags & Flag::Wireframe) { + if(flags & (Flag::Wireframe + #ifndef MAGNUM_TARGET_GLES2 + |Flag::InstancedObjectId|Flag::PrimitiveIdFromVertexId + #endif + )) _colorUniform = uniformLocation("color"); + if(flags & Flag::Wireframe) { _wireframeColorUniform = uniformLocation("wireframeColor"); _wireframeWidthUniform = uniformLocation("wireframeWidth"); _smoothnessUniform = uniformLocation("smoothness"); if(!(flags & Flag::NoGeometryShader)) _viewportSizeUniform = uniformLocation("viewportSize"); } + #ifndef MAGNUM_TARGET_GLES2 + if(flags & (Flag::InstancedObjectId|Flag::PrimitiveIdFromVertexId)) { + _colorMapOffsetScaleUniform = uniformLocation("colorMapOffsetScale"); + setUniform(uniformLocation("colorMapTexture"), ColorMapTextureUnit); + } + #endif } /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ #ifdef MAGNUM_TARGET_GLES setTransformationProjectionMatrix({}); - if(flags & Flag::Wireframe) { + if(flags & (Flag::Wireframe + #ifndef MAGNUM_TARGET_GLES2 + |Flag::InstancedObjectId|Flag::PrimitiveIdFromVertexId + #endif + )) setColor(Color3(1.0f)); + if(flags & Flag::Wireframe) { /* Viewport size is zero by default */ setWireframeColor(Color3{0.0f}); setWireframeWidth(1.0f); setSmoothness(2.0f); } + #ifndef MAGNUM_TARGET_GLES2 + if(flags & (Flag::InstancedObjectId|Flag::PrimitiveIdFromVertexId)) + setColorMapTransformation(1.0f/512.0f, 1.0f/256.0f); + #endif #endif } @@ -227,24 +318,39 @@ MeshVisualizer2D& MeshVisualizer2D::setSmoothness(const Float smoothness) { return *this; } -MeshVisualizer3D::MeshVisualizer3D(const Flags flags): Implementation::MeshVisualizerBase{Implementation::MeshVisualizerBase::FlagBase(UnsignedByte(flags))} { +MeshVisualizer3D::MeshVisualizer3D(const Flags flags): Implementation::MeshVisualizerBase{Implementation::MeshVisualizerBase::FlagBase(UnsignedShort(flags))} { #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - CORRADE_ASSERT(flags & ((Flag::Wireframe|Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection) & ~Flag::NoGeometryShader), + CORRADE_ASSERT(flags & ((Flag::Wireframe|Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection|Flag::InstancedObjectId|Flag::PrimitiveIdFromVertexId) & ~Flag::NoGeometryShader), "Shaders::MeshVisualizer3D: at least one visualization feature has to be enabled", ); CORRADE_ASSERT(!(flags & Flag::NoGeometryShader && flags & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection)), "Shaders::MeshVisualizer3D: geometry shader has to be enabled when rendering TBN direction", ); CORRADE_ASSERT(!(flags & Flag::BitangentDirection && flags & Flag::BitangentFromTangentDirection), "Shaders::MeshVisualizer3D: Flag::BitangentDirection and Flag::BitangentFromTangentDirection are mutually exclusive", ); + #elif !defined(MAGNUM_TARGET_GLES2) + CORRADE_ASSERT(flags & ((Flag::Wireframe|Flag::InstancedObjectId|Flag::PrimitiveIdFromVertexId) & ~Flag::NoGeometryShader), + "Shaders::MeshVisualizer3D: at least one visualization feature has to be enabled", ); #else CORRADE_ASSERT(flags & (Flag::Wireframe & ~Flag::NoGeometryShader), "Shaders::MeshVisualizer3D: at least Flag::Wireframe has to be enabled", ); #endif + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags & Flag::InstancedObjectId) || !(flags & Flag::PrimitiveIdFromVertexId), + "Shaders::MeshVisualizer3D: Flag::InstancedObjectId and Flag::PrimitiveId are mutually exclusive", ); + #endif + Utility::Resource rs{"MagnumShaders"}; GL::Shader vert{NoCreate}; GL::Shader frag{NoCreate}; const GL::Version version = setupShaders(vert, frag, rs); + /* Expands the check done for wireframe in MeshVisualizerBase with TBN */ + #ifndef MAGNUM_TARGET_GLES + CORRADE_INTERNAL_ASSERT(!(flags & (Flag::NormalDirection|Flag::TangentDirection|Flag::BitangentDirection|Flag::BitangentFromTangentDirection)) || version >= GL::Version::GL320); + #elif !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + CORRADE_INTERNAL_ASSERT(!(flags & (Flag::NormalDirection|Flag::TangentDirection|Flag::BitangentDirection|Flag::BitangentFromTangentDirection)) || version >= GL::Version::GLES310); + #endif + vert.addSource("#define THREE_DIMENSIONS\n") #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) .addSource(flags & Flag::TangentDirection ? "#define TANGENT_DIRECTION\n" : "") @@ -275,6 +381,11 @@ MeshVisualizer3D::MeshVisualizer3D(const Flags flags): Implementation::MeshVisua (*geom) .addSource(Utility::formatString("#define MAX_VERTICES {}\n", maxVertices)) .addSource(flags & Flag::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") + .addSource(_flags & FlagBase::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") + .addSource(_flags & FlagBase::PrimitiveId ? + (_flags >= FlagBase::PrimitiveIdFromVertexId ? + "#define PRIMITIVE_ID_FROM_VERTEX_ID\n" : + "#define PRIMITIVE_ID\n") : "") .addSource(flags & Flag::TangentDirection ? "#define TANGENT_DIRECTION\n" : "") .addSource(flags & (Flag::BitangentDirection|Flag::BitangentFromTangentDirection) ? "#define BITANGENT_DIRECTION\n" : "") .addSource(flags & Flag::NormalDirection ? "#define NORMAL_DIRECTION\n" : "") @@ -302,7 +413,10 @@ MeshVisualizer3D::MeshVisualizer3D(const Flags flags): Implementation::MeshVisua #endif { bindAttributeLocation(Position::Location, "position"); - + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::InstancedObjectId) + bindAttributeLocation(ObjectId::Location, "instanceObjectId"); + #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) if(flags & Flag::TangentDirection || flags & Flag::BitangentFromTangentDirection) @@ -333,8 +447,13 @@ MeshVisualizer3D::MeshVisualizer3D(const Flags flags): Implementation::MeshVisua { _transformationMatrixUniform = uniformLocation("transformationMatrix"); _projectionMatrixUniform = uniformLocation("projectionMatrix"); - if(flags & Flag::Wireframe) { + if(flags & (Flag::Wireframe + #ifndef MAGNUM_TARGET_GLES2 + |Flag::InstancedObjectId|Flag::PrimitiveIdFromVertexId + #endif + )) _colorUniform = uniformLocation("color"); + if(flags & Flag::Wireframe) { _wireframeColorUniform = uniformLocation("wireframeColor"); _wireframeWidthUniform = uniformLocation("wireframeWidth"); } @@ -347,6 +466,12 @@ MeshVisualizer3D::MeshVisualizer3D(const Flags flags): Implementation::MeshVisua if(!(flags & Flag::NoGeometryShader)) _viewportSizeUniform = uniformLocation("viewportSize"); } + #ifndef MAGNUM_TARGET_GLES2 + if(flags & (Flag::InstancedObjectId|Flag::PrimitiveIdFromVertexId)) { + _colorMapOffsetScaleUniform = uniformLocation("colorMapOffsetScale"); + setUniform(uniformLocation("colorMapTexture"), ColorMapTextureUnit); + } + #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) if(flags & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection)) { _normalMatrixUniform = uniformLocation("normalMatrix"); @@ -360,8 +485,13 @@ MeshVisualizer3D::MeshVisualizer3D(const Flags flags): Implementation::MeshVisua #ifdef MAGNUM_TARGET_GLES setTransformationMatrix({}); setProjectionMatrix({}); - if(flags & Flag::Wireframe) { + if(flags & (Flag::Wireframe + #ifndef MAGNUM_TARGET_GLES2 + |Flag::InstancedObjectId|Flag::PrimitiveIdFromVertexId + #endif + )) setColor(Color3(1.0f)); + if(flags & Flag::Wireframe) { /* Viewport size is zero by default */ setWireframeColor(Color3{0.0f}); setWireframeWidth(1.0f); @@ -373,6 +503,10 @@ MeshVisualizer3D::MeshVisualizer3D(const Flags flags): Implementation::MeshVisua )) { setSmoothness(2.0f); } + #ifndef MAGNUM_TARGET_GLES2 + if(flags & (Flag::InstancedObjectId|Flag::PrimitiveIdFromVertexId)) + setColorMapTransformation(1.0f/512.0f, 1.0f/256.0f); + #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) if(flags & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection)) { setNormalMatrix({}); @@ -454,6 +588,13 @@ Debug& operator<<(Debug& debug, const MeshVisualizer2D::Flag value) { #define _c(v) case MeshVisualizer2D::Flag::v: return debug << "::" #v; _c(NoGeometryShader) _c(Wireframe) + #ifndef MAGNUM_TARGET_GLES2 + _c(InstancedObjectId) + #ifndef MAGNUM_TARGET_WEBGL + _c(PrimitiveId) + #endif + _c(PrimitiveIdFromVertexId) + #endif #undef _c /* LCOV_EXCL_STOP */ } @@ -475,6 +616,13 @@ Debug& operator<<(Debug& debug, const MeshVisualizer3D::Flag value) { _c(BitangentDirection) _c(NormalDirection) #endif + #ifndef MAGNUM_TARGET_GLES2 + _c(InstancedObjectId) + #ifndef MAGNUM_TARGET_WEBGL + _c(PrimitiveId) + #endif + _c(PrimitiveIdFromVertexId) + #endif #undef _c /* LCOV_EXCL_STOP */ } @@ -486,7 +634,14 @@ 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 + MeshVisualizer2D::Flag::NoGeometryShader, + #ifndef MAGNUM_TARGET_GLES2 + MeshVisualizer2D::Flag::InstancedObjectId, + MeshVisualizer2D::Flag::PrimitiveIdFromVertexId, /* Superset of PrimitiveId */ + #ifndef MAGNUM_TARGET_WEBGL + MeshVisualizer2D::Flag::PrimitiveId + #endif + #endif }); } @@ -494,7 +649,20 @@ 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 */ - MeshVisualizer3D::Flag::NoGeometryShader + MeshVisualizer3D::Flag::NoGeometryShader, + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + MeshVisualizer3D::Flag::TangentDirection, + MeshVisualizer3D::Flag::BitangentFromTangentDirection, + MeshVisualizer3D::Flag::BitangentDirection, + MeshVisualizer3D::Flag::NormalDirection, + #endif + #ifndef MAGNUM_TARGET_GLES2 + MeshVisualizer3D::Flag::InstancedObjectId, + MeshVisualizer3D::Flag::PrimitiveIdFromVertexId, /* Superset of PrimitiveId */ + #ifndef MAGNUM_TARGET_WEBGL + MeshVisualizer3D::Flag::PrimitiveId + #endif + #endif }); } diff --git a/src/Magnum/Shaders/MeshVisualizer.frag b/src/Magnum/Shaders/MeshVisualizer.frag index 7ca72e3af..433878b9d 100644 --- a/src/Magnum/Shaders/MeshVisualizer.frag +++ b/src/Magnum/Shaders/MeshVisualizer.frag @@ -44,7 +44,7 @@ #extension GL_NV_shader_noperspective_interpolation: require #endif -#if defined(WIREFRAME_RENDERING) && !defined(TBN_DIRECTION) +#if (defined(WIREFRAME_RENDERING) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID) || defined(INSTANCED_OBJECT_ID)) && !defined(TBN_DIRECTION) #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 1) #endif @@ -75,7 +75,7 @@ uniform lowp float wireframeWidth ; #elif defined(TBN_DIRECTION) #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 8) +layout(location = 9) #endif uniform lowp float lineWidth #ifndef GL_ES @@ -93,7 +93,27 @@ uniform lowp float smoothness = 2.0 #endif ; +#endif + +#if defined(INSTANCED_OBJECT_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID) +#ifdef EXPLICIT_TEXTURE_LAYER +layout(binding = 4) +#endif +uniform lowp sampler2D colorMapTexture; + +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 6) +#endif +uniform lowp vec2 colorMapOffsetScale + #ifndef GL_ES + = vec2(1.0/512.0, 1.0/256.0) + #endif + ; +#define colorMapOffset colorMapOffsetScale.x +#define colorMapScale colorMapOffsetScale.y +#endif +#if defined(WIREFRAME_RENDERING) || defined(TBN_DIRECTION) #ifndef NO_GEOMETRY_SHADER #if !defined(GL_ES) || defined(GL_NV_shader_noperspective_interpolation) noperspective @@ -104,6 +124,13 @@ in lowp vec3 barycentric; #endif #endif +#ifdef PRIMITIVE_ID_FROM_VERTEX_ID +flat in highp uint interpolatedPrimitiveId; +#endif +#ifdef INSTANCED_OBJECT_ID +flat in highp uint interpolatedInstanceObjectId; +#endif + #ifdef TBN_DIRECTION in lowp vec4 backgroundColor; in lowp vec4 lineColor; @@ -117,6 +144,23 @@ out lowp vec4 fragmentColor; #endif void main() { + /* Map object/primitive ID to a color. Will be either combined with the + wireframe background color (if wireframe is enabled), ignored (if + rendering TBN direction) or used as-is if nothing else is enabled */ + #if defined(INSTANCED_OBJECT_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID) + lowp vec4 faceColor = texture(colorMapTexture, vec2(colorMapOffset + float( + #ifdef INSTANCED_OBJECT_ID + interpolatedInstanceObjectId + #elif defined(PRIMITIVE_ID) + gl_PrimitiveID + #elif defined(PRIMITIVE_ID_FROM_VERTEX_ID) + interpolatedPrimitiveId + #else + #error mosra messed up + #endif + )*colorMapScale, 0.0)); + #endif + /* 1. For wireframe the line is on the triangle edges, thus dist = 0 at vertices and dist = w (= wireframeWidth) at center of smoothed edge. 2. For antialiased TBN (drawn alone) the line is in the center of the @@ -137,6 +181,24 @@ void main() { 0 -S -w 0 w S -w 0 w */ #if defined(WIREFRAME_RENDERING) || defined(TBN_DIRECTION) + + /* Fill with background color first: + + 1. For wireframe alone the color is supplied directly from the color + uniform + 2. For TBN alone the backgroundColor is supplied from the GS + + If primitive/object ID is enabled, it multiplies the color. + */ + #if defined(WIREFRAME_RENDERING) && !defined(TBN_DIRECTION) + fragmentColor = color; + #else + fragmentColor = backgroundColor; + #endif + #if defined(INSTANCED_OBJECT_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID) + fragmentColor *= faceColor; + #endif + #ifndef NO_GEOMETRY_SHADER /* Distance to nearest side. If signed distance is involved when rendering TBN alone, we need to find an absolute distance */ @@ -146,7 +208,7 @@ void main() { lowp const float nearest = min(min(dist.x, dist.y), dist.z); #endif - /* Smooth step between two colors based on distance. + /* Mix in the line color: 1. For wireframe alone the width and colors are supplied directly from wireframeWidth, wireframeColor, color uniforms @@ -158,10 +220,11 @@ void main() { */ fragmentColor = mix( #if defined(WIREFRAME_RENDERING) && !defined(TBN_DIRECTION) - wireframeColor, color, + wireframeColor, #else - lineColor, backgroundColor, + lineColor, #endif + fragmentColor, #ifdef WIREFRAME_RENDERING smoothstep(wireframeWidth - smoothness, wireframeWidth + smoothness, nearest) @@ -178,10 +241,14 @@ void main() { const lowp vec3 d = fwidth(barycentric); const lowp vec3 factor = smoothstep(vec3(0.0), d*1.5, barycentric); const lowp float nearest = min(min(factor.x, factor.y), factor.z); - fragmentColor = mix(wireframeColor, color, nearest); + fragmentColor = mix(wireframeColor, fragmentColor, nearest); #endif + /* Object / Primitive ID visualization using a colormap */ + #elif defined(INSTANCED_OBJECT_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID) + fragmentColor = color*faceColor; + #else - #error neither wireframe or TBN direction is enabled, huh? + #error no visualization enabled, huh? #endif } diff --git a/src/Magnum/Shaders/MeshVisualizer.geom b/src/Magnum/Shaders/MeshVisualizer.geom index c5b9659bb..912bd5a5e 100644 --- a/src/Magnum/Shaders/MeshVisualizer.geom +++ b/src/Magnum/Shaders/MeshVisualizer.geom @@ -41,7 +41,7 @@ uniform lowp vec2 viewportSize; /* defaults to zero */ layout(triangles) in; -#if (defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION)) && defined(WIREFRAME_RENDERING) +#if (defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION)) && (defined(WIREFRAME_RENDERING) || defined(INSTANCED_OBJECT_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID)) #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 1) #endif @@ -63,7 +63,7 @@ uniform lowp vec4 wireframeColor #if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION) #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 8) +layout(location = 9) #endif uniform lowp float lineWidth #ifndef GL_ES @@ -99,6 +99,15 @@ noperspective #endif out lowp vec3 dist; +#ifdef PRIMITIVE_ID_FROM_VERTEX_ID +flat in highp uint interpolatedVsPrimitiveId[]; +flat out highp uint interpolatedPrimitiveId; +#endif +#ifdef INSTANCED_OBJECT_ID +flat in highp uint interpolatedVsInstanceObjectId[]; +flat out highp uint interpolatedInstanceObjectId; +#endif + #if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION) out lowp vec4 backgroundColor; out lowp vec4 lineColor; @@ -124,7 +133,7 @@ void emitQuad(vec4 position, vec2 positionScreen, vec4 endpoint, vec2 endpointSc and to 0 otherwise. See the fragment shader for details. */ vec2 direction = normalize(endpointScreen - positionScreen); - #ifdef WIREFRAME_RENDERING + #if defined(WIREFRAME_RENDERING) || defined(INSTANCED_OBJECT_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID) float edgeDistance = 0.0; vec2 halfSide = lineWidth*vec2(-direction.y, direction.x); #else @@ -157,6 +166,20 @@ void emitQuad(vec4 position, vec2 positionScreen, vec4 endpoint, vec2 endpointSc #endif void main() { + /* Passthrough for unchanged variables */ + #ifdef PRIMITIVE_ID_FROM_VERTEX_ID + interpolatedPrimitiveId = interpolatedVsPrimitiveId[0]; + #elif defined(PRIMITIVE_ID) + /* This has to be done explicitly, otherwise the fragment input is + undefined. Interestingly enough this worked well on Mesa / Intel with + the GS emitting always just 3 vertices, but not anymore when it emits + also the TBN direction. */ + gl_PrimitiveID = gl_PrimitiveIDIn; + #endif + #ifdef INSTANCED_OBJECT_ID + interpolatedInstanceObjectId = interpolatedVsInstanceObjectId[0]; + #endif + /* Screen position of each vertex */ vec2 p[3]; #ifdef TANGENT_DIRECTION @@ -181,7 +204,7 @@ void main() { #endif } - #ifdef WIREFRAME_RENDERING + #if defined(WIREFRAME_RENDERING) || defined(INSTANCED_OBJECT_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID) /* Vector of each triangle side */ const vec2 v[3] = vec2[3]( p[2]-p[1], @@ -192,10 +215,15 @@ void main() { /* Compute area using perp-dot product */ const float area = abs(dot(vec2(-v[1].y, v[1].x), v[2])); - /* Add distance to opposite side to each vertex */ + /* If wireframe is enabled, add distance to opposite side to each vertex. + Otherwise make all distances the same to avoid any lines being drawn. */ for(int i = 0; i != 3; ++i) { dist = vec3(0.0, 0.0, 0.0); + #ifdef WIREFRAME_RENDERING dist[i] = area/length(v[i]); + #else + dist = vec3(area/length(v[i])); + #endif #if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION) backgroundColor = color; lineColor = wireframeColor; diff --git a/src/Magnum/Shaders/MeshVisualizer.h b/src/Magnum/Shaders/MeshVisualizer.h index 32816213f..d6a280a24 100644 --- a/src/Magnum/Shaders/MeshVisualizer.h +++ b/src/Magnum/Shaders/MeshVisualizer.h @@ -42,13 +42,18 @@ namespace Implementation { class MAGNUM_SHADERS_EXPORT MeshVisualizerBase: public GL::AbstractShaderProgram { protected: - enum class FlagBase: UnsignedByte { + enum class FlagBase: UnsignedShort { #ifndef MAGNUM_TARGET_GLES2 Wireframe = 1 << 0, #else Wireframe = (1 << 0) | (1 << 1), #endif - NoGeometryShader = 1 << 1 + NoGeometryShader = 1 << 1, + #ifndef MAGNUM_TARGET_GLES2 + InstancedObjectId = 1 << 2, + PrimitiveId = 1 << 3, + PrimitiveIdFromVertexId = (1 << 4)|PrimitiveId + #endif }; typedef Containers::EnumSet FlagsBase; @@ -62,6 +67,8 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerBase: public GL::AbstractShaderProgram MeshVisualizerBase& setColor(const Color4& color); MeshVisualizerBase& setWireframeColor(const Color4& color); MeshVisualizerBase& setWireframeWidth(Float width); + MeshVisualizerBase& setColorMapTransformation(Float offset, Float scale); + MeshVisualizerBase& bindColorMapTexture(GL::Texture2D& texture); /* Prevent accidentally calling irrelevant functions */ #ifndef MAGNUM_TARGET_GLES @@ -77,6 +84,9 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerBase: public GL::AbstractShaderProgram _wireframeWidthUniform{3}, _smoothnessUniform{4}, _viewportSizeUniform{5}; + #ifndef MAGNUM_TARGET_GLES2 + Int _colorMapOffsetScaleUniform{6}; + #endif }; } @@ -85,12 +95,24 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerBase: public GL::AbstractShaderProgram @brief 2D mesh visualization shader @m_since_latest -Uses the geometry shader to visualize wireframe of 3D meshes. You need to -provide the @ref Position attribute in your triangle mesh. Use -@ref setTransformationProjectionMatrix(), @ref setColor() and others to +Visualizes wireframe, per-vertex/per-instance object ID or primitive ID of 2D +meshes. You need to provide the @ref Position attribute in your triangle mesh. +Use @ref setTransformationProjectionMatrix(), @ref setColor() and others to configure the shader. +@m_class{m-row} + +@parblock + +@m_div{m-col-m-4 m-col-t-6 m-push-m-2 m-nopadt m-nopadx} @image html shaders-meshvisualizer2d.png width=256px +@m_enddiv + +@m_div{m-col-m-4 m-col-t-6 m-push-m-2 m-nopadt m-nopadx} +@image html shaders-meshvisualizer2d-primitiveid.png width=256px +@m_enddiv + +@endparblock The shader expects that you enable wireframe visualization by passing an appropriate @ref Flag to the constructor --- there's no default behavior with @@ -114,6 +136,21 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer2D: public Implementation::MeshVisuali */ typedef GL::Attribute<4, Float> VertexIndex; + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief (Instanced) object ID + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", @ref Magnum::UnsignedInt. + * Used only if @ref Flag::InstancedObjectId is set. + * @requires_gles30 Object ID input requires integer attributes, which + * are not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID input requires integer attributes, which + * are not available in WebGL 1.0. + */ + typedef Generic3D::ObjectId ObjectId; + #endif + enum: UnsignedInt { /** * Color shader output. @ref shaders-generic "Generic output", @@ -128,7 +165,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer2D: public Implementation::MeshVisuali * * @see @ref Flags, @ref MeshVisualizer2D() */ - enum class Flag: UnsignedByte { + enum class Flag: UnsignedShort { /** * Visualize wireframe. On OpenGL ES 2.0 this also enables * @ref Flag::NoGeometryShader. @@ -145,7 +182,24 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer2D: public Implementation::MeshVisuali * attribute in the mesh. In OpenGL ES 2.0 enabled alongside * @ref Flag::Wireframe. */ - NoGeometryShader = 1 << 1 + NoGeometryShader = 1 << 1, + + #ifndef MAGNUM_TARGET_GLES2 + /** @copydoc MeshVisualizer3D::Flag::InstancedObjectId */ + InstancedObjectId = 1 << 2, + + #ifndef MAGNUM_TARGET_WEBGL + /** @copydoc MeshVisualizer3D::Flag::PrimitiveId */ + PrimitiveId = 1 << 3, + #endif + + /** @copydoc MeshVisualizer3D::Flag::PrimitiveIdFromVertexId */ + #ifndef MAGNUM_TARGET_WEBGL + PrimitiveIdFromVertexId = (1 << 4)|PrimitiveId + #else + PrimitiveIdFromVertexId = (1 << 4)|(1 << 3) + #endif + #endif }; /** @brief Flags */ @@ -212,8 +266,11 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer2D: public Implementation::MeshVisuali * @brief Set base object color * @return Reference to self (for method chaining) * - * Initial value is @cpp 0xffffffff_rgbaf @ce. Expects that - * @ref Flag::Wireframe is enabled. + * Initial value is @cpp 0xffffffff_rgbaf @ce. Expects that either + * @ref Flag::Wireframe or @ref Flag::InstancedObjectId / + * @ref Flag::PrimitiveId is enabled. In case of the latter, the color + * is multiplied with the color map coming from + * @ref bindColorMapTexture(). */ MeshVisualizer2D& setColor(const Color4& color) { return static_cast(Implementation::MeshVisualizerBase::setColor(color)); @@ -242,6 +299,18 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer2D: public Implementation::MeshVisuali return static_cast(Implementation::MeshVisualizerBase::setWireframeWidth(width)); } + #ifndef MAGNUM_TARGET_GLES2 + /** @copydoc MeshVisualizer3D::setColorMapTransformation() */ + MeshVisualizer2D& setColorMapTransformation(Float offset, Float scale) { + return static_cast(Implementation::MeshVisualizerBase::setColorMapTransformation(offset, scale)); + } + + /** @copydoc MeshVisualizer3D::bindColorMapTexture() */ + MeshVisualizer2D& bindColorMapTexture(GL::Texture2D& texture) { + return static_cast(Implementation::MeshVisualizerBase::bindColorMapTexture(texture)); + } + #endif + /** * @brief Set line smoothness * @return Reference to self (for method chaining) @@ -259,16 +328,32 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer2D: public Implementation::MeshVisuali /** @brief 3D mesh visualization shader -Uses the geometry shader to visualize wireframe or tangent space of 3D meshes. -You need to provide the @ref Position attribute in your triangle mesh at the -very least. Use @ref setTransformationProjectionMatrix(), @ref setColor() and -others to configure the shader. +Visualizes wireframe, per-vertex/per-instance object ID, primitive ID or +tangent space of 3D meshes. You need to provide the @ref Position attribute in +your triangle mesh at the very least. Use @ref setTransformationProjectionMatrix(), +@ref setColor() and others to configure the shader. + +@m_class{m-row} +@parblock + +@m_div{m-col-m-4 m-col-t-6 m-push-m-2 m-text-center m-nopadt m-nopadx} @image html shaders-meshvisualizer3d.png width=256px +@ref Shaders-MeshVisualizer-wireframe, \n +@ref Shaders-MeshVisualizer-tbn +@m_enddiv + +@m_div{m-col-m-4 m-col-t-6 m-push-m-2 m-text-center m-nopadt m-nopadx} +@image html shaders-meshvisualizer3d-primitiveid.png width=256px +@ref Shaders-MeshVisualizer-object-id +@m_enddiv -The shader expects that you enable either wireframe visualization or tangent -space visualization by passing an appropriate @ref Flag to the constructor --- -there's no default behavior with nothing enabled. +@endparblock + +The shader expects that you enable wireframe visualization, tangent space +visualization or object/primitive ID visualization by passing an appropriate +@ref Flag to the constructor --- there's no default behavior with nothing +enabled. @section Shaders-MeshVisualizer-wireframe Wireframe visualization @@ -355,6 +440,37 @@ Rendering setup: @snippet MagnumShaders.cpp MeshVisualizer-usage-tbn2 +@section Shaders-MeshVisualizer-object-id Object and primitive ID visualization + +If the mesh contains a per-vertex (or instanced) @ref ObjectId, it can be +visualized by enabling @ref Flag::InstancedObjectId. For the actual +visualization you need to provide a color map using @ref bindColorMapTexture() +and use @ref setColorMapTransformation() to map given range of discrete IDs to +the @f$ [0, 1] @f$ texture range. Various colormap presets are in the +@ref DebugTools::ColorMap namespace. Example usage: + +@snippet MagnumShaders.cpp MeshVisualizer-usage-object-id + +If you enable @ref Flag::PrimitiveId instead, the shader will use the color map +to visualize the order in which primitives are drawn. That's useful for example +to see how well is the mesh optimized for a post-transform vertex cache. This +by default relies on the @glsl gl_PrimitiveID @ce GLSL builtin; with +@ref Flag::PrimitiveIdFromVertexId it's emulated using @glsl gl_VertexID @ce, +expecting you to draw a non-indexed triangle mesh. You can use +@ref MeshTools::duplicate() (and potentially @ref MeshTools::generateIndices()) +to conveniently convert the mesh to a non-indexed @ref MeshPrimitive::Triangles. + +@requires_gl32 The `gl_PrimitiveID` shader variable is not available on OpenGL + 3.1 and lower. +@requires_gl30 The `gl_VertexID` shader variable is not available on OpenGL + 2.1. +@requires_gles32 The `gl_PrimitiveID` shader variable is not available on + OpenGL ES 3.1 and lower. +@requires_gles30 The `gl_VertexID` shader variable is not available on OpenGL + ES 2.0. +@requires_gles `gl_PrimitiveID` is not available in WebGL. +@requires_webgl20 `gl_VertexID` is not available in WebGL 1.0. + @see @ref shaders, @ref MeshVisualizer2D @todo Understand and add support wireframe width/smoothness without GS */ @@ -427,6 +543,21 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisuali */ typedef GL::Attribute<4, Float> VertexIndex; + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief (Instanced) object ID + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", @ref Magnum::UnsignedInt. + * Used only if @ref Flag::InstancedObjectId is set. + * @requires_gles30 Object ID input requires integer attributes, which + * are not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID input requires integer attributes, which + * are not available in WebGL 1.0. + */ + typedef Generic3D::ObjectId ObjectId; + #endif + enum: UnsignedInt { /** * Color shader output. @ref shaders-generic "Generic output", @@ -441,7 +572,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisuali * * @see @ref Flags, @ref MeshVisualizer() */ - enum class Flag: UnsignedByte { + enum class Flag: UnsignedShort { /** * Visualize wireframe. On OpenGL ES 2.0 this also enables * @ref Flag::NoGeometryShader. @@ -465,6 +596,54 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisuali */ NoGeometryShader = 1 << 1, + #ifndef MAGNUM_TARGET_GLES2 + /** + * Visualize instanced object ID. You need to provide the + * @ref ObjectId attribute in the mesh. Mutually exclusive with + * @ref Flag::PrimitiveId. + * @requires_gles30 Object ID input requires integer attributes, + * which are not available in OpenGL ES 2.0 or WebGL 1.0. + * @m_since_latest + */ + InstancedObjectId = 1 << 2, + + #ifndef MAGNUM_TARGET_WEBGL + /** + * Visualize primitive ID (@cpp gl_PrimitiveID @ce). Mutually + * exclusive with @ref Flag::InstancedObjectId. See also + * @ref Flag::PrimitiveIdFromVertexId. + * @requires_gl32 The `gl_PrimitiveID` shader variable is not + * available on OpenGL 3.1 and lower. + * @requires_gles30 Not defined in OpenGL ES 2.0. + * @requires_gles32 The `gl_PrimitiveID` shader variable is not + * available on OpenGL ES 3.1 and lower. + * @requires_gles `gl_PrimitiveID` is not available in WebGL. + * @m_since_latest + */ + PrimitiveId = 1 << 3, + #endif + + /** + * Visualize primitive ID on a non-indexed triangle mesh using + * @cpp gl_VertexID/3 @ce. Implicitly enables + * @ref Flag::PrimitiveId, mutually exclusive with + * @ref Flag::InstancedObjectId. Usable on OpenGL < 3.2, + * OpenGL ES < 3.2 and WebGL where @cpp gl_PrimitiveID @ce is not + * available. + * @requires_gl30 The `gl_VertexID` shader variable is not + * available on OpenGL 2.1. + * @requires_gles30 The `gl_VertexID` shader variable is not + * available on OpenGL ES 2.0. + * @requires_webgl20 `gl_VertexID` is not available in WebGL 1.0. + * @m_since_latest + */ + #ifndef MAGNUM_TARGET_WEBGL + PrimitiveIdFromVertexId = (1 << 4)|PrimitiveId, + #else + PrimitiveIdFromVertexId = (1 << 4)|(1 << 3), + #endif + #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) /** * Visualize tangent direction with red lines pointing out of @@ -479,7 +658,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisuali * @requires_gles Geometry shaders are not available in WebGL. * @m_since_latest */ - TangentDirection = 1 << 2, + TangentDirection = 1 << 5, /** * Visualize bitangent direction with green lines pointing out of @@ -496,7 +675,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisuali * @requires_gles Geometry shaders are not available in WebGL. * @m_since_latest */ - BitangentFromTangentDirection = 1 << 3, + BitangentFromTangentDirection = 1 << 6, /** * Visualize bitangent direction with green lines pointing out of @@ -513,7 +692,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisuali * @requires_gles Geometry shaders are not available in WebGL. * @m_since_latest */ - BitangentDirection = 1 << 4, + BitangentDirection = 1 << 7, /** * Visualize normal direction with blue lines pointing out of @@ -527,7 +706,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisuali * @requires_gles Geometry shaders are not available in WebGL. * @m_since_latest */ - NormalDirection = 1 << 5 + NormalDirection = 1 << 8 #endif }; @@ -650,8 +829,11 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisuali * @brief Set base object color * @return Reference to self (for method chaining) * - * Initial value is @cpp 0xffffffff_rgbaf @ce. Expects that - * @ref Flag::Wireframe is enabled. + * Initial value is @cpp 0xffffffff_rgbaf @ce. Expects that either + * @ref Flag::Wireframe or @ref Flag::InstancedObjectId / + * @ref Flag::PrimitiveId is enabled. In case of the latter, the color + * is multiplied with the color map coming from + * @ref bindColorMapTexture(). */ MeshVisualizer3D& setColor(const Color4& color) { return static_cast(Implementation::MeshVisualizerBase::setColor(color)); @@ -680,6 +862,62 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisuali return static_cast(Implementation::MeshVisualizerBase::setWireframeWidth(width)); } + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Set color map transformation + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Offset and scale applied to the input value coming either from the + * @ref ObjectId attribute or @glsl gl_PrimitiveID @ce, resulting value + * is then used to fetch a color from a color map bound with + * @ref bindColorMapTexture(). Initial value is @cpp 1.0f/512.0f @ce + * and @cpp 1.0/256.0f @ce, meaning that for a 256-entry colormap the + * first 256 values get an exact color from it and the next values will + * be either clamped to last color or repeated depending on the color + * map texture wrapping mode. Expects that either + * @ref Flag::InstancedObjectId or @ref Flag::PrimitiveId / + * @ref Flag::PrimitiveIdFromVertexId is enabled. + * + * Note that this shader doesn't directly offer a + * @ref Flat::setObjectId() "setObjectId()" uniform that's used to + * offset the per-vertex / per-instance ID. Instead, you need to encode + * the base offset into the @p offset parameter. + * @requires_gles30 Object ID visualization requires integer attributes + * while primitive ID visualization requires the `gl_VertexID` / + * `gl_PrimitiveID` builtins, neither of which is available in + * OpenGL ES 2.0. + * @requires_webgl20 Object ID visualization requires integer + * attributes while primitive ID visualization requires at least + * the `gl_VertexID` builtin, neither of which is available in + * WebGL 1. + */ + MeshVisualizer3D& setColorMapTransformation(Float offset, Float scale) { + return static_cast(Implementation::MeshVisualizerBase::setColorMapTransformation(offset, scale)); + } + + /** + * @brief Bind a color map texture + * @return Reference to self (for method chaining) + * @m_since_latest + * + * See also @ref setColorMapTransformation(). Expects that either + * @ref Flag::InstancedObjectId or @ref Flag::PrimitiveId / + * @ref Flag::PrimitiveIdFromVertexId is enabled. + * @requires_gles30 Object ID visualization requires integer attributes + * while primitive ID visualization requires the `gl_VertexID` / + * `gl_PrimitiveID` builtins, neither of which is available in + * OpenGL ES 2.0. + * @requires_webgl20 Object ID visualization requires integer + * attributes while primitive ID visualization requires at least + * the `gl_VertexID` builtin, neither of which is available in + * WebGL 1. + */ + MeshVisualizer3D& bindColorMapTexture(GL::Texture2D& texture) { + return static_cast(Implementation::MeshVisualizerBase::bindColorMapTexture(texture)); + } + #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) /** * @brief Set line width @@ -734,11 +972,11 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisuali private: Int _transformationMatrixUniform{0}, - _projectionMatrixUniform{6}; + _projectionMatrixUniform{7}; #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - Int _normalMatrixUniform{7}, - _lineWidthUniform{8}, - _lineLengthUniform{9}; + Int _normalMatrixUniform{8}, + _lineWidthUniform{9}, + _lineLengthUniform{10}; #endif }; diff --git a/src/Magnum/Shaders/MeshVisualizer.vert b/src/Magnum/Shaders/MeshVisualizer.vert index 63352b9d1..11108f30f 100644 --- a/src/Magnum/Shaders/MeshVisualizer.vert +++ b/src/Magnum/Shaders/MeshVisualizer.vert @@ -47,7 +47,7 @@ uniform highp mat4 transformationMatrix #endif ; #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 6) +layout(location = 7) #endif uniform highp mat4 projectionMatrix #ifndef GL_ES @@ -60,7 +60,7 @@ uniform highp mat4 projectionMatrix #if defined(TANGENT_DIRECTION) || defined(BITANGENT_FROM_TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION) #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 7) +layout(location = 8) #endif uniform highp mat3 normalMatrix #ifndef GL_ES @@ -69,7 +69,7 @@ uniform highp mat3 normalMatrix ; #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 9) +layout(location = 10) #endif uniform highp float lineLength #ifndef GL_ES @@ -122,6 +122,27 @@ in lowp float vertexIndex; out vec3 barycentric; #endif +#ifdef INSTANCED_OBJECT_ID +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = OBJECT_ID_ATTRIBUTE_LOCATION) +#endif +in highp uint instanceObjectId; + +#if defined(NO_GEOMETRY_SHADER) || (!defined(WIREFRAME_RENDERING) && !defined(TANGENT_DIRECTION) && !defined(BITANGENT_DIRECTION) && !defined(BITANGENT_FROM_TANGENT_DIRECTION) && !defined(NORMAL_DIRECTION)) +flat out highp uint interpolatedInstanceObjectId; +#else +flat out highp uint interpolatedVsInstanceObjectId; +#endif +#endif + +#ifdef PRIMITIVE_ID_FROM_VERTEX_ID +#if defined(NO_GEOMETRY_SHADER) || (!defined(WIREFRAME_RENDERING) && !defined(TANGENT_DIRECTION) && !defined(BITANGENT_DIRECTION) && !defined(BITANGENT_FROM_TANGENT_DIRECTION) && !defined(NORMAL_DIRECTION)) +flat out highp uint interpolatedPrimitiveId; +#else +flat out highp uint interpolatedVsPrimitiveId; +#endif +#endif + #ifdef TANGENT_DIRECTION out highp vec4 tangentEndpoint; #endif @@ -171,6 +192,22 @@ void main() { #else barycentric[gl_VertexID % 3] = 1.0; #endif + #endif + #ifdef INSTANCED_OBJECT_ID + #if defined(NO_GEOMETRY_SHADER) || (!defined(WIREFRAME_RENDERING) && !defined(TANGENT_DIRECTION) && !defined(BITANGENT_DIRECTION) && !defined(BITANGENT_FROM_TANGENT_DIRECTION) && !defined(NORMAL_DIRECTION)) + interpolatedInstanceObjectId + #else + interpolatedVsInstanceObjectId + #endif + = instanceObjectId; + #endif + #ifdef PRIMITIVE_ID_FROM_VERTEX_ID + #if defined(NO_GEOMETRY_SHADER) || (!defined(WIREFRAME_RENDERING) && !defined(TANGENT_DIRECTION) && !defined(BITANGENT_DIRECTION) && !defined(BITANGENT_FROM_TANGENT_DIRECTION) && !defined(NORMAL_DIRECTION)) + interpolatedPrimitiveId + #else + interpolatedVsPrimitiveId + #endif + = uint(gl_VertexID/3); #endif } diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index cf13c938e..53a1d5a76 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -152,15 +152,26 @@ if(BUILD_GL_TESTS) MagnumOpenGLTester FILES MeshVisualizerTestFiles/bitangents-from-tangents.tga + MeshVisualizerTestFiles/defaults-objectid2D.tga + MeshVisualizerTestFiles/defaults-objectid3D.tga + MeshVisualizerTestFiles/defaults-primitiveid2D.tga + MeshVisualizerTestFiles/defaults-primitiveid3D.tga MeshVisualizerTestFiles/defaults-tbn.tga MeshVisualizerTestFiles/defaults-wireframe2D.tga MeshVisualizerTestFiles/defaults-wireframe3D.tga + MeshVisualizerTestFiles/objectid2D.tga + MeshVisualizerTestFiles/objectid3D.tga + MeshVisualizerTestFiles/primitiveid-tn.tga + MeshVisualizerTestFiles/primitiveid2D.tga + MeshVisualizerTestFiles/primitiveid3D.tga MeshVisualizerTestFiles/tbn-wide.tga MeshVisualizerTestFiles/tbn.tga MeshVisualizerTestFiles/wireframe-nogeo2D.tga MeshVisualizerTestFiles/wireframe-nogeo3D.tga + MeshVisualizerTestFiles/wireframe-nogeo-objectid2D.tga + MeshVisualizerTestFiles/wireframe-nogeo-objectid3D.tga MeshVisualizerTestFiles/wireframe-perspective.tga - MeshVisualizerTestFiles/wireframe-tn.tga + MeshVisualizerTestFiles/wireframe-primitiveid-tn.tga MeshVisualizerTestFiles/wireframe-tn-smooth.tga MeshVisualizerTestFiles/wireframe-wide2D.tga MeshVisualizerTestFiles/wireframe-wide3D.tga diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index f475fd75d..4a173cce1 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -32,6 +32,7 @@ #include #include +#include "Magnum/DebugTools/ColorMap.h" #include "Magnum/DebugTools/CompareImage.h" #include "Magnum/GL/Context.h" #include "Magnum/GL/Extensions.h" @@ -40,12 +41,15 @@ #include "Magnum/GL/Mesh.h" #include "Magnum/GL/Renderbuffer.h" #include "Magnum/GL/RenderbufferFormat.h" +#include "Magnum/GL/Texture.h" +#include "Magnum/GL/TextureFormat.h" #include "Magnum/Image.h" #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/Combine.h" #include "Magnum/MeshTools/Compile.h" #include "Magnum/MeshTools/Duplicate.h" #include "Magnum/MeshTools/GenerateIndices.h" @@ -84,6 +88,10 @@ struct MeshVisualizerGLTest: GL::OpenGLTester { void setWireframeNotEnabled2D(); void setWireframeNotEnabled3D(); + #ifndef MAGNUM_TARGET_GLES2 + void setColorMapNotEnabled2D(); + void setColorMapNotEnabled3D(); + #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) void setTangentBitangentNormalNotEnabled3D(); #endif @@ -94,10 +102,22 @@ struct MeshVisualizerGLTest: GL::OpenGLTester { #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) void renderDefaultsWireframe2D(); void renderDefaultsWireframe3D(); + #endif + #ifndef MAGNUM_TARGET_GLES2 + void renderDefaultsObjectId2D(); + void renderDefaultsObjectId3D(); + void renderDefaultsPrimitiveId2D(); + void renderDefaultsPrimitiveId3D(); + #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) void renderDefaultsTangentBitangentNormal(); #endif void renderWireframe2D(); void renderWireframe3D(); + #ifndef MAGNUM_TARGET_GLES2 + void renderObjectPrimitiveId2D(); + void renderObjectPrimitiveId3D(); + #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) void renderWireframe3DPerspective(); void renderTangentBitangentNormal(); @@ -109,10 +129,10 @@ struct MeshVisualizerGLTest: GL::OpenGLTester { GL::Renderbuffer _color{NoCreate}, _depth{NoCreate}; + GL::Framebuffer _framebuffer{NoCreate}; #ifndef MAGNUM_TARGET_GLES2 - GL::Renderbuffer _objectId{NoCreate}; + GL::Texture2D _colorMapTexture; #endif - GL::Framebuffer _framebuffer{NoCreate}; }; /* @@ -122,8 +142,8 @@ struct MeshVisualizerGLTest: GL::OpenGLTester { - Mesa AMD - SwiftShader ES2/ES3 - ARM Mali (Huawei P10) ES2/ES3 (except TBN visualization) - - WebGL 1 / 2 (on Mesa Intel) - - iPhone 6 w/ iOS 12.4 + - WebGL 1 / 2 (on Mesa Intel) (except primitive/object ID) + - iPhone 6 w/ iOS 12.4 (except primitive/object ID) */ using namespace Math::Literals; @@ -133,13 +153,27 @@ constexpr struct { MeshVisualizer2D::Flags flags; } ConstructData2D[] { {"wireframe w/o GS", MeshVisualizer2D::Flag::Wireframe|MeshVisualizer2D::Flag::NoGeometryShader}, + #ifndef MAGNUM_TARGET_GLES2 + {"object ID", MeshVisualizer2D::Flag::InstancedObjectId}, + #ifndef MAGNUM_TARGET_WEBGL + {"primitive ID", MeshVisualizer2D::Flag::PrimitiveId}, + #endif + {"primitive ID from vertex ID", MeshVisualizer2D::Flag::PrimitiveIdFromVertexId} + #endif }; constexpr struct { const char* name; MeshVisualizer3D::Flags flags; } ConstructData3D[] { - {"wireframe w/o GS", MeshVisualizer3D::Flag::Wireframe|MeshVisualizer3D::Flag::NoGeometryShader} + {"wireframe w/o GS", MeshVisualizer3D::Flag::Wireframe|MeshVisualizer3D::Flag::NoGeometryShader}, + #ifndef MAGNUM_TARGET_GLES2 + {"object ID", MeshVisualizer3D::Flag::InstancedObjectId}, + #ifndef MAGNUM_TARGET_WEBGL + {"primitive ID", MeshVisualizer3D::Flag::InstancedObjectId}, + #endif + {"primitive ID from vertex ID", MeshVisualizer3D::Flag::PrimitiveIdFromVertexId} + #endif }; #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) @@ -154,7 +188,8 @@ constexpr struct { {"normal direction", MeshVisualizer3D::Flag::NormalDirection}, {"tbn direction", MeshVisualizer3D::Flag::TangentDirection|MeshVisualizer3D::Flag::BitangentDirection|MeshVisualizer3D::Flag::NormalDirection}, {"tbn direction with bitangent from tangent", MeshVisualizer3D::Flag::TangentDirection|MeshVisualizer3D::Flag::BitangentFromTangentDirection|MeshVisualizer3D::Flag::NormalDirection}, - {"wireframe + t/n direction", MeshVisualizer3D::Flag::Wireframe|MeshVisualizer3D::Flag::TangentDirection|MeshVisualizer3D::Flag::NormalDirection} + {"wireframe + t/n direction", MeshVisualizer3D::Flag::Wireframe|MeshVisualizer3D::Flag::TangentDirection|MeshVisualizer3D::Flag::NormalDirection}, + {"wireframe + object id + t/n direction", MeshVisualizer3D::Flag::Wireframe|MeshVisualizer3D::Flag::InstancedObjectId|MeshVisualizer3D::Flag::TangentDirection|MeshVisualizer3D::Flag::NormalDirection} }; #endif @@ -165,7 +200,17 @@ constexpr struct { } ConstructInvalidData2D[] { {"no feature enabled", MeshVisualizer2D::Flag::NoGeometryShader, /* not a feature flag */ - "at least Flag::Wireframe has to be enabled"} + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + "at least one visualization feature has to be enabled" + #else + "at least Flag::Wireframe has to be enabled" + #endif + }, + #ifndef MAGNUM_TARGET_GLES2 + {"both object and primitive id", + MeshVisualizer2D::Flag::InstancedObjectId|MeshVisualizer2D::Flag::PrimitiveIdFromVertexId, + "Flag::InstancedObjectId and Flag::PrimitiveId are mutually exclusive"} + #endif }; constexpr struct { @@ -180,8 +225,26 @@ constexpr struct { #else "at least Flag::Wireframe has to be enabled" #endif - } + }, + #ifndef MAGNUM_TARGET_GLES2 + {"both object and primitive id", + MeshVisualizer3D::Flag::InstancedObjectId|MeshVisualizer3D::Flag::PrimitiveIdFromVertexId, + "Flag::InstancedObjectId and Flag::PrimitiveId are mutually exclusive"} + #endif +}; + +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + SamplerFilter filter; + SamplerWrapping wrapping; +} ObjectIdDefaultsData[] { + {"nearest, clamp", SamplerFilter::Nearest, SamplerWrapping::ClampToEdge}, + {"nearest, repeat", SamplerFilter::Nearest, SamplerWrapping::Repeat}, + {"linear, clamp", SamplerFilter::Linear, SamplerWrapping::ClampToEdge}, + {"linear, repeat", SamplerFilter::Linear, SamplerWrapping::Repeat} }; +#endif constexpr struct { const char* name; @@ -223,6 +286,41 @@ constexpr struct { 3.0f, 1.0f, "wireframe-wide3D.tga", "wireframe-nogeo3D.tga"} }; +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + MeshVisualizer2D::Flags flags2D; + MeshVisualizer3D::Flags flags3D; + const char* file2D; + const char* file3D; +} ObjectPrimitiveIdData[] { + {"object ID", + MeshVisualizer2D::Flag::InstancedObjectId, + MeshVisualizer3D::Flag::InstancedObjectId, + "objectid2D.tga", "objectid3D.tga"}, + #ifndef MAGNUM_TARGET_WEBGL + {"primitive ID", + MeshVisualizer2D::Flag::PrimitiveId, + MeshVisualizer3D::Flag::PrimitiveId, + "primitiveid2D.tga", "primitiveid3D.tga"}, + #endif + {"primitive ID from vertex ID", + MeshVisualizer2D::Flag::PrimitiveIdFromVertexId, + MeshVisualizer3D::Flag::PrimitiveIdFromVertexId, + "primitiveid2D.tga", "primitiveid3D.tga"}, + {"wireframe + object ID", + MeshVisualizer2D::Flag::InstancedObjectId|MeshVisualizer2D::Flag::Wireframe, + MeshVisualizer3D::Flag::InstancedObjectId|MeshVisualizer3D::Flag::Wireframe, + "wireframe-objectid2D.tga", "wireframe-objectid3D.tga"}, + {"wireframe + object ID, no geometry shader", + MeshVisualizer2D::Flag::InstancedObjectId|MeshVisualizer2D::Flag::Wireframe| + MeshVisualizer2D::Flag::NoGeometryShader, + MeshVisualizer3D::Flag::InstancedObjectId|MeshVisualizer3D::Flag::Wireframe| + MeshVisualizer3D::Flag::NoGeometryShader, + "wireframe-nogeo-objectid2D.tga", "wireframe-nogeo-objectid3D.tga"} +}; +#endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) constexpr struct { const char* name; @@ -258,22 +356,29 @@ constexpr struct { {"only bitangent from tangent", MeshVisualizer3D::Flag::BitangentFromTangentDirection, {}, false, 2.0f, 1.0f, 0.6f, 1.0f, "bitangents-from-tangents.tga"}, - {"wireframe + tangents + normals, single pass", + {"wireframe + primitive ID + tangents + normals, single pass", MeshVisualizer3D::Flag::Wireframe| + MeshVisualizer3D::Flag::PrimitiveId| MeshVisualizer3D::Flag::TangentDirection| MeshVisualizer3D::Flag::NormalDirection, {}, - false, 2.0f, 1.0f, 0.6f, 1.0f, "wireframe-tn.tga"}, - {"wireframe, rendering all, but only tangents + normals present", + false, 2.0f, 1.0f, 0.6f, 1.0f, "wireframe-primitiveid-tn.tga"}, + {"wireframe + primitive ID, rendering all, but only tangents + normals present", MeshVisualizer3D::Flag::Wireframe| + MeshVisualizer3D::Flag::PrimitiveId| MeshVisualizer3D::Flag::TangentDirection| MeshVisualizer3D::Flag::BitangentDirection| MeshVisualizer3D::Flag::NormalDirection, {}, - true, 2.0f, 1.0f, 0.6f, 1.0f, "wireframe-tn.tga"}, + true, 2.0f, 1.0f, 0.6f, 1.0f, "wireframe-primitiveid-tn.tga"}, {"wireframe + tangents + normals, two passes", MeshVisualizer3D::Flag::TangentDirection| MeshVisualizer3D::Flag::NormalDirection, MeshVisualizer3D::Flag::Wireframe, false, 2.0f, 1.0f, 0.6f, 1.0f, "wireframe-tn-smooth.tga"}, + {"primitive ID + tangents + normals", + MeshVisualizer3D::Flag::PrimitiveId| + MeshVisualizer3D::Flag::TangentDirection| + MeshVisualizer3D::Flag::NormalDirection, {}, + false, 2.0f, 1.0f, 0.6f, 1.0f, "primitiveid-tn.tga"} }; #endif @@ -307,6 +412,10 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { &MeshVisualizerGLTest::setWireframeNotEnabled2D, &MeshVisualizerGLTest::setWireframeNotEnabled3D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::setColorMapNotEnabled2D, + &MeshVisualizerGLTest::setColorMapNotEnabled3D, + #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) &MeshVisualizerGLTest::setTangentBitangentNormalNotEnabled3D, #endif @@ -314,8 +423,27 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) addTests({&MeshVisualizerGLTest::renderDefaultsWireframe2D, - &MeshVisualizerGLTest::renderDefaultsWireframe3D, - &MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal}, + &MeshVisualizerGLTest::renderDefaultsWireframe3D}, + &MeshVisualizerGLTest::renderSetup, + &MeshVisualizerGLTest::renderTeardown); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&MeshVisualizerGLTest::renderDefaultsObjectId2D, + &MeshVisualizerGLTest::renderDefaultsObjectId3D}, + Containers::arraySize(ObjectIdDefaultsData), + &MeshVisualizerGLTest::renderSetup, + &MeshVisualizerGLTest::renderTeardown); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + addTests({ + &MeshVisualizerGLTest::renderDefaultsPrimitiveId2D, + &MeshVisualizerGLTest::renderDefaultsPrimitiveId3D, + #ifndef MAGNUM_TARGET_WEBGL + &MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal + #endif + }, &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); #endif @@ -330,6 +458,14 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&MeshVisualizerGLTest::renderObjectPrimitiveId2D, + &MeshVisualizerGLTest::renderObjectPrimitiveId3D}, + Containers::arraySize(ObjectPrimitiveIdData), + &MeshVisualizerGLTest::renderSetup, + &MeshVisualizerGLTest::renderTeardown); + #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) addTests({&MeshVisualizerGLTest::renderWireframe3DPerspective}, &MeshVisualizerGLTest::renderSetup, @@ -363,12 +499,36 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { { _testDir = SHADERS_TEST_DIR; } + + /* Set up a color map texture for use by object / primitive ID tests */ + #ifndef MAGNUM_TARGET_GLES2 + { + const auto map = DebugTools::ColorMap::turbo(); + const Vector2i size{Int(map.size()), 1}; + _colorMapTexture + .setMinificationFilter(SamplerFilter::Linear) + .setMagnificationFilter(SamplerFilter::Linear) + .setWrapping(SamplerWrapping::Repeat) + .setStorage(1, GL::TextureFormat::RGB8, size) + .setSubImage(0, {}, ImageView2D{PixelFormat::RGB8Srgb, size, map}); + } + #endif } void MeshVisualizerGLTest::construct2D() { auto&& data = ConstructData2D[testCaseInstanceId()]; setTestCaseDescription(data.name); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(data.flags & MeshVisualizer2D::Flag::PrimitiveId && !(data.flags >= MeshVisualizer2D::Flag::PrimitiveIdFromVertexId) && + #ifndef MAGNUM_TARGET_GLES + !GL::Context::current().isVersionSupported(GL::Version::GL320) + #else + !GL::Context::current().isVersionSupported(GL::Version::GLES320) + #endif + ) CORRADE_SKIP("gl_PrimitiveID not supported."); + #endif + MeshVisualizer2D shader{data.flags}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_VERIFY(shader.id()); @@ -386,6 +546,16 @@ void MeshVisualizerGLTest::construct3D() { auto&& data = ConstructData3D[testCaseInstanceId()]; setTestCaseDescription(data.name); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(data.flags & MeshVisualizer3D::Flag::PrimitiveId && !(data.flags >= MeshVisualizer3D::Flag::PrimitiveIdFromVertexId) && + #ifndef MAGNUM_TARGET_GLES + !GL::Context::current().isVersionSupported(GL::Version::GL320) + #else + !GL::Context::current().isVersionSupported(GL::Version::GLES320) + #endif + ) CORRADE_SKIP("gl_PrimitiveID not supported."); + #endif + MeshVisualizer3D shader{data.flags}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_VERIFY(shader.id()); @@ -554,13 +724,23 @@ void MeshVisualizerGLTest::setWireframeNotEnabled2D() { is just wireframe in case of 2D), so fake it with a NoCreate */ MeshVisualizer2D shader{NoCreate}; shader - .setColor({}) + .setColor({}); + + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizer::setColor(): the shader was not created with wireframe or object/primitive ID enabled\n"); + #else + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizer::setColor(): the shader was not created with wireframe enabled\n"); + #endif + + out.str({}); + shader .setWireframeColor({}) .setWireframeWidth({}) .setSmoothness({}); CORRADE_COMPARE(out.str(), - "Shaders::MeshVisualizer::setColor(): the shader was not created with wireframe enabled\n" "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::MeshVisualizer2D::setSmoothness(): the shader was not created with wireframe enabled\n"); @@ -575,18 +755,58 @@ void MeshVisualizerGLTest::setWireframeNotEnabled3D() { with a NoCreate */ MeshVisualizer3D shader{NoCreate}; shader - .setColor({}) + .setColor({}); + + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizer::setColor(): the shader was not created with wireframe or object/primitive ID enabled\n"); + #else + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizer::setColor(): the shader was not created with wireframe enabled\n"); + #endif + + out.str({}); + shader .setWireframeColor({}) .setWireframeWidth({}) .setSmoothness({}); CORRADE_COMPARE(out.str(), - "Shaders::MeshVisualizer::setColor(): the shader was not created with wireframe enabled\n" "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::MeshVisualizer3D::setSmoothness(): the shader was not created with wireframe or TBN direction enabled\n"); } +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGLTest::setColorMapNotEnabled2D() { + std::ostringstream out; + Error redirectError{&out}; + + GL::Texture2D texture; + MeshVisualizer2D shader{NoCreate}; + shader.setColorMapTransformation({}, {}) + .bindColorMapTexture(texture); + + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizer::setColorMapTransformation(): the shader was not created with object/primitive ID enabled\n" + "Shaders::MeshVisualizer::bindColorMapTexture(): the shader was not created with object/primitive ID enabled\n"); +} + +void MeshVisualizerGLTest::setColorMapNotEnabled3D() { + std::ostringstream out; + Error redirectError{&out}; + + GL::Texture2D texture; + MeshVisualizer3D shader{NoCreate}; + shader.setColorMapTransformation({}, {}) + .bindColorMapTexture(texture); + + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizer::setColorMapTransformation(): the shader was not created with object/primitive ID enabled\n" + "Shaders::MeshVisualizer::bindColorMapTexture(): the shader was not created with object/primitive ID enabled\n"); +} +#endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) void MeshVisualizerGLTest::setTangentBitangentNormalNotEnabled3D() { #ifndef MAGNUM_TARGET_GLES @@ -746,7 +966,177 @@ void MeshVisualizerGLTest::renderDefaultsWireframe3D() { /* AMD has off-by-one errors on edges compared to Intel */ (DebugTools::CompareImageToFile{_manager, 1.0f, 0.06f})); } +#endif + +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGLTest::renderDefaultsObjectId2D() { + auto&& data = ObjectIdDefaultsData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + /* Configure a texture with preset filtering and wrapping. The goal here is + that the default config should be filtering/wrapping-independent for the + first 256 items */ + const auto map = DebugTools::ColorMap::turbo(); + const Vector2i size{Int(map.size()), 1}; + GL::Texture2D colorMapTexture; + colorMapTexture + .setMinificationFilter(data.filter) + .setMagnificationFilter(data.filter) + .setWrapping(data.wrapping) + .setStorage(1, GL::TextureFormat::RGB8, size) + .setSubImage(0, {}, ImageView2D{PixelFormat::RGB8Srgb, size, map}); + + /* Generate per-face IDs going from 0 to 240 to cover the whole range */ + Containers::Array ids{16}; + for(std::size_t i = 0; i != ids.size(); ++i) ids[i] = i*16; + GL::Mesh circle = MeshTools::compile(MeshTools::combineFaceAttributes( + MeshTools::generateIndices(Primitives::circle2DSolid(16)), { + Trade::MeshAttributeData{Trade::MeshAttribute::ObjectId, + Containers::arrayView(ids)} + })); + + MeshVisualizer2D{MeshVisualizer2D::Flag::InstancedObjectId} + .bindColorMapTexture(colorMapTexture) + .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_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-objectid2D.tga"), + /* SwiftShader has a few rounding errors on edges */ + (DebugTools::CompareImageToFile{_manager, 150.67f, 0.45f})); +} + +void MeshVisualizerGLTest::renderDefaultsObjectId3D() { + auto&& data = ObjectIdDefaultsData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + /* Configure a texture with preset filtering and wrapping. The goal here is + that the default config should be filtering/wrapping-independent for the + first 256 items */ + const auto map = DebugTools::ColorMap::turbo(); + const Vector2i size{Int(map.size()), 1}; + GL::Texture2D colorMapTexture; + colorMapTexture + .setMinificationFilter(data.filter) + .setMagnificationFilter(data.filter) + .setWrapping(data.wrapping) + .setStorage(1, GL::TextureFormat::RGB8, size) + .setSubImage(0, {}, ImageView2D{PixelFormat::RGB8Srgb, size, map}); + + /* Generate per-face IDs going from 0 to 228 to cover the whole range */ + Containers::Array ids{20}; + for(std::size_t i = 0; i != ids.size(); ++i) ids[i] = i*12; + GL::Mesh icosphere = MeshTools::compile(MeshTools::combineFaceAttributes( + Primitives::icosphereSolid(0), { + Trade::MeshAttributeData{Trade::MeshAttribute::ObjectId, + Containers::arrayView(ids)} + })); + + MeshVisualizer3D{MeshVisualizer3D::Flag::InstancedObjectId} + .bindColorMapTexture(colorMapTexture) + .draw(icosphere); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(_testDir, "MeshVisualizerTestFiles/defaults-objectid3D.tga"), + /* SwiftShader has a few rounding errors on edges */ + (DebugTools::CompareImageToFile{_manager, 150.67f, 0.165f})); +} +void MeshVisualizerGLTest::renderDefaultsPrimitiveId2D() { + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + MeshVisualizer2D::Flags flags; + #ifdef MAGNUM_TARGET_WEBGL + flags = MeshVisualizer2D::Flag::PrimitiveIdFromVertexId; + #else + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isVersionSupported(GL::Version::GL320)) + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES320)) + #endif + { + Debug{} << "Using primitive ID from vertex ID"; + flags = MeshVisualizer2D::Flag::PrimitiveIdFromVertexId; + } + else flags = MeshVisualizer2D::Flag::PrimitiveId; + #endif + + Trade::MeshData circleData = Primitives::circle2DSolid(16); + if(flags >= MeshVisualizer2D::Flag::PrimitiveIdFromVertexId) + circleData = MeshTools::duplicate(MeshTools::generateIndices(circleData)); + + MeshVisualizer2D{flags} + .bindColorMapTexture(_colorMapTexture) + .draw(MeshTools::compile(circleData)); + + 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-primitiveid2D.tga"), + /* SwiftShader has a few rounding errors on edges */ + (DebugTools::CompareImageToFile{_manager, 76.67f, 0.23f})); +} + +void MeshVisualizerGLTest::renderDefaultsPrimitiveId3D() { + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + MeshVisualizer2D::Flags flags; + #ifdef MAGNUM_TARGET_WEBGL + flags = MeshVisualizer2D::Flag::PrimitiveIdFromVertexId; + #else + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isVersionSupported(GL::Version::GL320)) + #else + if(!GL::Context::current().isVersionSupported(GL::Version::GLES320)) + #endif + { + Debug{} << "Using primitive ID from vertex ID"; + flags = MeshVisualizer2D::Flag::PrimitiveIdFromVertexId; + } + else flags = MeshVisualizer2D::Flag::PrimitiveId; + #endif + + Trade::MeshData icosphereData = Primitives::icosphereSolid(0); + if(flags >= MeshVisualizer2D::Flag::PrimitiveIdFromVertexId) + icosphereData = MeshTools::duplicate(icosphereData); + + MeshVisualizer2D{flags} + .bindColorMapTexture(_colorMapTexture) + .draw(MeshTools::compile(icosphereData)); + + 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-primitiveid3D.tga"), + /* SwiftShader has a few rounding errors on edges */ + (DebugTools::CompareImageToFile{_manager, 88.34f, 0.071f})); +} +#endif + +#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) void MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal() { #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) @@ -974,6 +1364,178 @@ void MeshVisualizerGLTest::renderWireframe3D() { } } +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGLTest::renderObjectPrimitiveId2D() { + auto&& data = ObjectPrimitiveIdData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags2D & MeshVisualizer2D::Flag::PrimitiveId && !(data.flags2D >= MeshVisualizer2D::Flag::PrimitiveIdFromVertexId) && + #ifndef MAGNUM_TARGET_GLES + !GL::Context::current().isVersionSupported(GL::Version::GL320) + #else + !GL::Context::current().isVersionSupported(GL::Version::GLES320) + #endif + ) CORRADE_SKIP("gl_PrimitiveID not supported."); + + #ifndef MAGNUM_TARGET_GLES + if(!(data.flags2D & MeshVisualizer2D::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported")); + #else + if(!(data.flags2D & MeshVisualizer2D::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() + std::string(" is not supported")); + #endif + #endif + + Trade::MeshData circleData = Primitives::circle2DSolid(16); + + if(data.flags2D & MeshVisualizer2D::Flag::InstancedObjectId) { + Containers::Array ids{16}; + /* Each two faces share the same ID */ + for(std::size_t i = 0; i != ids.size(); ++i) ids[i] = i/2; + circleData = MeshTools::combineFaceAttributes( + MeshTools::generateIndices(circleData), { + Trade::MeshAttributeData{Trade::MeshAttribute::ObjectId, + Containers::arrayView(ids)} + }); + } + + /* Duplicate the data if using primitive ID from vertex ID or if geometry + shader is disabled */ + if(data.flags2D >= MeshVisualizer2D::Flag::PrimitiveIdFromVertexId) + circleData = MeshTools::generateIndices(circleData); + if(data.flags2D >= MeshVisualizer2D::Flag::PrimitiveIdFromVertexId || + data.flags2D & MeshVisualizer2D::Flag::NoGeometryShader) + circleData = MeshTools::duplicate(circleData); + + GL::Mesh circle = MeshTools::compile(circleData); + + MeshVisualizer2D shader{data.flags2D}; + shader + /* Remove blue so it's clear the (wireframe) background and mapped ID + colors got mixed */ + .setColor(0xffff00_rgbf) + /* Shouldn't assert (nor warn) when wireframe is not enabled */ + .setViewportSize({80, 80}) + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + /* Should cover the first half of the colormap, in reverse order; for + primitive ID the whole colormap due to the repeat wrapping */ + .setColorMapTransformation(0.5f, -1.0f/16.0f) + .bindColorMapTexture(_colorMapTexture); + + /* OTOH the wireframe color should stay at full channels, not mixed */ + if(data.flags3D & MeshVisualizer3D::Flag::Wireframe) + shader.setWireframeColor(0xffffff_rgbf); + + 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_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.file2D}), + /* AMD has slight off-by-one errors compared to Intel, SwiftShader a + bit more */ + (DebugTools::CompareImageToFile{_manager, 4.0f, 0.141f})); +} + +void MeshVisualizerGLTest::renderObjectPrimitiveId3D() { + auto&& data = ObjectPrimitiveIdData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags3D & MeshVisualizer3D::Flag::PrimitiveId && !(data.flags3D >= MeshVisualizer3D::Flag::PrimitiveIdFromVertexId) && + #ifndef MAGNUM_TARGET_GLES + !GL::Context::current().isVersionSupported(GL::Version::GL320) + #else + !GL::Context::current().isVersionSupported(GL::Version::GLES320) + #endif + ) CORRADE_SKIP("gl_PrimitiveID not supported."); + + #ifndef MAGNUM_TARGET_GLES + if(!(data.flags3D & MeshVisualizer3D::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported")); + #else + if(!(data.flags3D & MeshVisualizer3D::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() + std::string(" is not supported")); + #endif + #endif + + Trade::MeshData icosphereData = Primitives::icosphereSolid(1); + + if(data.flags3D & MeshVisualizer3D::Flag::InstancedObjectId) { + Containers::Array ids{80}; + /* Each four faces share the same ID */ + for(std::size_t i = 0; i != ids.size(); ++i) ids[i] = i/4; + icosphereData = MeshTools::combineFaceAttributes( + icosphereData, { + Trade::MeshAttributeData{Trade::MeshAttribute::ObjectId, + Containers::arrayView(ids)} + }); + } + + /* Duplicate the data if using primitive ID from vertex ID or if geometry + shader is disabled */ + if(data.flags3D >= MeshVisualizer3D::Flag::PrimitiveIdFromVertexId || + data.flags3D & MeshVisualizer3D::Flag::NoGeometryShader) + icosphereData = MeshTools::duplicate(icosphereData); + + GL::Mesh circle = MeshTools::compile(icosphereData); + + MeshVisualizer3D shader{data.flags3D}; + shader + /* Remove blue so it's clear the wireframe background and mapped ID + colors got mixed */ + .setColor(0xffff00_rgbf) + /* Shouldn't assert (nor warn) when wireframe is not enabled */ + .setViewportSize({80, 80}) + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)) + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + /* Should cover the first half of the colormap, in reverse order; for + primitive ID the whole colormap due to the repeat wrapping */ + .setColorMapTransformation(0.5f, -1.0f/40.0f) + .bindColorMapTexture(_colorMapTexture); + + /* OTOH the wireframe color should stay at full channels, not mixed */ + if(data.flags2D & MeshVisualizer2D::Flag::Wireframe) + shader.setWireframeColor(0xffffff_rgbf); + + 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."); + + /* Release build has 1 pixel slightly off. Huh. AMD has additional + off-by-one errors compared to Intel. If + GL_NV_shader_noperspective_interpolation is not supported, the artifacts + are bigger when wireframe is enabled. */ + Float maxThreshold = 1.0f, meanThreshold = 0.026f; + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(data.flags3D & MeshVisualizer3D::Flag::Wireframe && !GL::Context::current().isExtensionSupported()) { + /* SwiftShader has a bit more rounding errors */ + maxThreshold = 238.0f; + meanThreshold = 1.957f; + } + #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.file3D}), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} +#endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) void MeshVisualizerGLTest::renderWireframe3DPerspective() { #ifndef MAGNUM_TARGET_GLES @@ -1131,6 +1693,9 @@ void MeshVisualizerGLTest::renderTangentBitangentNormal() { if(data.flags & MeshVisualizer3D::Flag::Wireframe) shader .setColor(0xffff99_rgbf) .setWireframeColor(0x9999ff_rgbf); + if(data.flags & MeshVisualizer3D::Flag::PrimitiveId) shader + .bindColorMapTexture(_colorMapTexture) + .setColorMapTransformation(1.0f/512.0f, 0.5f); shader.draw(mesh); @@ -1141,7 +1706,7 @@ void MeshVisualizerGLTest::renderTangentBitangentNormal() { are bigger. */ Float maxThreshold = 1.334f, meanThreshold = 0.008f; #ifdef MAGNUM_TARGET_GLES - if(!(data.flags & MeshVisualizer3D::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported()) { + if(!GL::Context::current().isExtensionSupported()) { maxThreshold = 39.0f; meanThreshold = 1.207f; } diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-objectid2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-objectid2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..e0569ef45b17ed354a08bfffcf1efb95621096b2 GIT binary patch literal 2610 zcmZXWyKdA#6ozdjX(*6Ffzr~^flw--h$yJ@477+>pzwWsTW{7`2$2>o8%09m5vb@9 zyZ{wt5CtvQ@1LDX25mG)vCsF-nOS?rb`)KU{NG`880|%8ySux|{=47Ft>-^={F*2^ zFFDuoE28DR)JmTy*{LLb*8+lMhe~qKS+|2I}b5(JLdb+>FSBd`@1u zDN&L4BUj{=8weBR4SD5yq6zX1dF46<`G#>D#_h-}*ASB+KOwJNO+@?OJ`S*&J~e&B z;+;F6X~K#ag{BJL>ah~17?c#=2V5g(aIuQW9XE_(+{_BNn4*Ad=AO6oFC8!CI9}1e zqQAI-ixor-Z|U#3u1EtHQ#6kIdoA8Np2Iu1uBhiVyoZZr!~|Xk+};Vem|}3eV15Pj z6Q4Ss!jIs(lb-7i&){MSF*=^pKc~O=Foi=YR2FhgG<%qOI2NK?w`Tdr6>Yhi+I2`8+MDW3oARNc1ms8y0Gnr z-D9`d+92N6#_6#84ST@WP1QE@c8aZ=I$YTi(;rcdQ~lnTTBDAraujnst+SMp-b?Vb z&gOx4f~WN=Z;PKr2CYtfzr@+qyi5p6m3&G?EwZ zZ~q-1C9oB22}=OmkW2XW>Ejf>gzFM6a8jHEVQbwTgO(vJoFwrSF?Zq);@V}jwO*i~ z2Kotlfu6d$mwJL!H18U>cl#(eW zrM9R8>i@f>$8NE63VS=l?y&m}yT$IXi@?sYd+c_@HrNwqXUrsHCb9=?vtgIm19nMm zZx`58?DERib0bAPa%xWDoWfFPs41#{w#0a}8EQpUua>AIYK$7#Bbo`c3hfxpj>xDQ zQM_Qpf)T~-h%ozHh1UT$a82GncXcVmOZu1e7uTf_x6gHW6Yv_Y3oB;d3-C7J4O})}7R&;zu_`R>=f9uRun*aa+ literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-objectid3D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-objectid3D.tga new file mode 100644 index 0000000000000000000000000000000000000000..e31b4737213b417a5690a279394621b5f9fb79e4 GIT binary patch literal 1608 zcmZ|Py>8nu6oz32*}Az?FQH?%AZyny*^695@4-KkDp{&5E4C2BNIV1?dmG&{_7b{w zE{dk_#~vQAKx_CL_>h)N@{l3C2rauufIHg_<0>(zW=WFhRm@C zY>ug|EU^=|%&fqUSYg&PuEGvjg{iI7*dD9RK4C4k#ac{lrNcH@msyXsSZ~%bZonFB zz|>Yoti;C5cGv>jnYE0YurrMB1dE(6etdrS+$3Jx=yUi_k9Dd;iI1mef$J5?^;cH- z3fJF}H$-0Gg}F@8bSk{UHKpX5dX3k(rZ2CFyullDnWE)u@fOz-%08NRcxNtC){O7* z9$zy)xwdP-2V7ewuZVn$Z_Q;f_zix8Yuja|g75J?UgFVkmCb}txJoCvO6(TD#Z_kV zg2)f}!Ca=OOpo{xSE(jfN#EgjxXN0-BJz9u-dq;_&FPo><=1aA{U+f496UXLj{AEv zU*i5h%{2$P&kIlUEpT6l%uC$Y%{;C%?(2-F^{;W?7jx~I-1iYr`@6<{-!t!UuM_iF zSGd;|p6ac~y$;P)qjIlXJk|4nd!1)K;(jj7b!Oy#j_`E8cKCvQjOQ-%3IF@BT4y}q zbe{1e_I`r>+>4*bw|o=pHi@smb`13!hM^@kW~M1M*kGnxOw(_%9`lXD9fT{iTpZo^3vy^oJ literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-primitiveid2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-primitiveid2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..e6b8b91c2c6542040e22501c1efe4683c4fd9b16 GIT binary patch literal 2610 zcmZXW&1=*^7>5(Oy?Co26a+yK1VNN)wOZRAym_iOZ{ECl^5jkOJ^9*fH{G&UDy5}= zr_XP8rXAU3UYfkmGw-CCnIutkFY>?R=s4PruD7Go+IW?+(cZtjJDDX^z%Spq8I3? zt9z>__$Bf(kQd06lM~ED{{)`FTLj*SXYewLdN0MR@kZylbw8jsPE9G9Qc`M*I-vf) zOM2`UJEyR>Gwcq#U$a~64!a2K9J|ME*KC75b#}&;WL$~t0o$zECH8<_Qrp`F_6)nc zv-R9aQBRzjQ#hxv)H!O3>Ypt!9&L_VQPry@>WCVn2KI<%0nZ|J3(BIHsTl@w+Hzazjb#+Is2^= zqqeZNo{dNlS+BnMDFD~-5o-RFIVh~Nley0T}BCnkN Wo^w~KWBa|wxz>t~uL-~Rb^H%G`q6g) literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-primitiveid3D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-primitiveid3D.tga new file mode 100644 index 0000000000000000000000000000000000000000..013604f446b4e0fef299f666bc0e6ed6b9b7115b GIT binary patch literal 1608 zcmZ|P&2G~`6o%oc*s;rw6?ae}At50^MTHf+F1ZzUC_lEbV`DpMVl_l<7gg3=NpFew zo1QruB#3er^UTD~%zQH;JP+w}6|TaI@Hm^z{#7F$et-FN`gZ;Xef*EaeY{M2Ke>QKeU)3dY1yfK%lw0tez;#xx45AzQ1%w?)I<9ocv z*NjiD?HchB*OtjEO1{B2=CWA$HGYk2+hwH+-{X6{#G~Pg&4f?5qLW+^yTNa8#Y|pM z@&kS_m#GxfBYwmc)#QrwEq;qD*76l4zr*j$Wzm0Kd~_e)UdZ&Dfctas^!z#Q@6CLP z`~Ni89OOPPJk7VjeH}6{abGv{xX!q*GoIGJ#(iJRwP$kQM?CHC8uxwAyu&>w<}p{e z=L%1G>v7MaxiTvE+~O(EBknoRe2e?JFxQ!p`#HkX`P$(N_A#Ej%qRTs!)l%JfYW)# zkJ$SO_H!@(`tl9m#JWx5E3h3y{f1#^iET5}lp1U_(=Ddyw^)z)&4vwFmsyK#u+}Wz vl-M4tv2=S@*a54|2Hv6Q9I*mZTcw!YW6R7G)dy^D7H@p)gau6P$N$tHY=!Z7 literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectid2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectid2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..6e6a7b4af48d51f1544923139d608260d22b7d8f GIT binary patch literal 1718 zcmZwINphP&6vpwEm)Uv86|(aUT$M%EK1Xs31|bk2F^EC72Fvm#a*14kOL(7D<^QFd zG8IR~E5G-9EsUC`G3Jw*J`d)>c;@Hr?XBWU_@?^en{Qv0pNN<`rdGZuGU|+4c}Hxh zH`K~oVoSZHR^AX5bw#bbR#4Z}%1dHLy`xsXC0gp1T6sbA)IGKGoEWGFYULTRceOG1 z_o~;#!uQHo*OBuCU*ftXU1E&ORtj8~-rx!T*YE^y zaQ(#8d5o`d{d7%5hRc>jj%$iayuf9O0xxk*<;Ho0Z*fhrro6;uAyMJF6$M`7GDVFS zc#bzSZ}1$~t=u^e7~kT$#cRC7Wr_}8<0;$r!#uSyxqN;UzdN+IeIsAbo~B){m{Rd(er68=yJTg574Zf z=>~LtTFNC=UN4Oe1*sOBR;K;0Z;H1 zzMpxIr+9)7Gw<*K?rU&+%sF72`K}f!EIM(=&Qo ze1n&`%sy?SSK?cIgHKOk&Uq@l#0%&4X&${AukZ|)X$MFc-{3W#;?u6M!gqLsC(iBe zGI}k(!(&`#ccank@D`8oX_pE(Pmgza=-h62th3P@X!W#wT4K9DzTFL&!BpEl)S7a; z$J*~Hw|lVt*UC#mm##}xZufBe6P4RN-cE&b`wp;EsocIJ?363F?+|+{mD_iWT~f-g NgciP*w({xS^an=>0z&`* literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectid3D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectid3D.tga new file mode 100644 index 0000000000000000000000000000000000000000..90e05699ef0a042b127b88194a0fbb3024ef658c GIT binary patch literal 2494 zcmZveJ8m3D5QclG%o#H?*8vnsk)Vj{R4Ef4U{656**tf4_Pty_R)%dDFyO#xyyBxISWP{FZ3~vq_9`jL}>2Rp>j$mfIL= zlQfU{75EF7JI2Oy$%l+NzQOOc|HQS&VGjfi)L^MxeWo%hRkFcQ&R^kS10hSr@J93I@KWa#8W*>X<>pQ2d zFjKSjw#3qJ53%S01d825vPDOa+$_kIQMbv`}dyZJ=GW z2{$iklZ~%kOwL!$wZ%>RBJhCvuGj;Q0@r8JTcYME0l5==NL&{87Sdeo;`E5@Q;qfY ztSG?TJ+mXs7kGnz50f$Gvgz~Z)i`ZT@qz_+=U)cWs{X%_fy%<;Ww@Y@12`VxIRb5X z4&`b+-10CU^$$K?E?$O|3ebNBb=^a3!35+`=_*C7eq>1V& zcnVslj0ZIDC%fR)k8kS+ZidetI-C@;avkyPpvS~D+#FvC=c%K~%wYPR)E{xTPz$`i z7ZRR_Y1MPUJLDCzua~k%caq~=4`(+Z8<6liQiO-IT|x}*k4K6SSO0LfMPBz2r~vW| zv+wFPkGo=%2;6;KJ%zB=Er`W)85wsj34g^BWw$%11(GFk%PGg7OH1j-c{rQn@6ZeG zYD;X`fD7~)e!J-=R6Ep{=+gt;pJVL=p72~7hzW$dr{caw)v_pWk=?a!#hUvj$tJKv z^AUHnj2{VF9=QYeq8wRF+p?DsU)`E*>^~O*36O1LAKW8{*%QorX`Yf_si)w YtWD_ejQ_w&{?E|4{^Z}|Z!-V?56QRlegFUf literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/primitiveid-tn.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/primitiveid-tn.tga new file mode 100644 index 0000000000000000000000000000000000000000..fddb6b1b40ad32a1637c86fc7d0639d56fc45374 GIT binary patch literal 988 zcmd6m!Ab&A6o#+eMbvIu^%(8zA$+FE(NQPI(Qz6TBBBT?1j!{$n=A06RnAM>G$IA@RrAG@R{SXAGDO5dh;`A04R D&UpRG literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/primitiveid2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/primitiveid2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..0450636506342b17ea8476f535b1fd8fccce4e08 GIT binary patch literal 2508 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 literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-objectid2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-objectid2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..5ed8df360ea63f9bf3dbda21b193c495f2565a41 GIT binary patch literal 7604 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 X<9{sknSZ%H`cB^j^FOWs%h%*BAEYzz literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-tn.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-tn.tga deleted file mode 100644 index 2aa934dd40c9a422afb94d4ca2209d12adf24f34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1676 zcmd6oS5H(?6ov0~W+L&8HxrDBi9d+n1X2a1i4KCGhypXzDfA9Q?|taK_cnCDWxz~G zOpNit2{-4=S!|AZUi##5s|G&J;IJwEO-{1!_L>;JY_PA0j%e0G%d z;`xbVthqVLV(~|CEXW45RaCT4Q880fQ#Ci2b$RK{xVbS)Ha0kpf)N2bnl%I_3!m|( zrq+>>_0rPOmX@-$HRvw;?X8&;2!O;v5M6#Kh^={StZ;X?x4V0(VZ@03bRHyDi*6A{3+FQ=zXg(HaI0ia8ILlWcRv5u5Ibn{pwtl*}kjh4F)G_4qVQ%~2;J3rWJPCmzB|?6s%*ZfS zYrWqeB~|9tm3MJ*s;+Ly<7t?j{Cswn%dy}$41|L%HN&Wznu-IqQha}BCx&1P=45gv zED4Ufq@vUUbH(K<*xQrDhSjc>6@~`$nFktSS0G?W!9rPTVl*klONd`JGc$CVURTe~ z@-cYDe1po-wexM6hUj%tf#F`@uny*z}*_#G=WD2G7zGcghqK7?Q~EB;bvs!ekIDNXNjy zw$nM--d?n|6)QFv3^CJr48p52!Vw!Rj z7?Q~ECFt($z4JOGI}o5g(wwfX|VWB7Hj-?KUMu-A7r*!_q5j61%I^>_Qf`492~p$|ea