Browse Source

Shaders: object and primitive ID visualization in MeshVisualizer.

pull/432/head
Vladimír Vondruš 6 years ago
parent
commit
e3841ade7e
  1. 2
      doc/changelog.dox
  2. 1
      doc/generated/CMakeLists.txt
  3. 54
      doc/generated/shaders.cpp
  4. BIN
      doc/shaders-meshvisualizer2d-primitiveid.png
  5. BIN
      doc/shaders-meshvisualizer3d-primitiveid.png
  6. 34
      doc/snippets/MagnumShaders.cpp
  7. 196
      src/Magnum/Shaders/MeshVisualizer.cpp
  8. 81
      src/Magnum/Shaders/MeshVisualizer.frag
  9. 38
      src/Magnum/Shaders/MeshVisualizer.geom
  10. 292
      src/Magnum/Shaders/MeshVisualizer.h
  11. 43
      src/Magnum/Shaders/MeshVisualizer.vert
  12. 13
      src/Magnum/Shaders/Test/CMakeLists.txt
  13. 603
      src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp
  14. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-objectid2D.tga
  15. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-objectid3D.tga
  16. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-primitiveid2D.tga
  17. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-primitiveid3D.tga
  18. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectid2D.tga
  19. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectid3D.tga
  20. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/primitiveid-tn.tga
  21. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/primitiveid2D.tga
  22. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/primitiveid3D.tga
  23. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo-objectid2D.tga
  24. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo-objectid3D.tga
  25. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-objectid2D.tga
  26. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-objectid3D.tga
  27. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-primitiveid-tn.tga
  28. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-tn.tga

2
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

1
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

54
doc/generated/shaders.cpp

@ -41,6 +41,7 @@
#include <Magnum/Image.h>
#include <Magnum/ImageView.h>
#include <Magnum/PixelFormat.h>
#include <Magnum/DebugTools/ColorMap.h>
#include <Magnum/GL/Buffer.h>
#include <Magnum/GL/Framebuffer.h>
#include <Magnum/GL/Mesh.h>
@ -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)

BIN
doc/shaders-meshvisualizer2d-primitiveid.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
doc/shaders-meshvisualizer3d-primitiveid.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

34
doc/snippets/MagnumShaders.cpp

@ -27,6 +27,9 @@
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/ArrayViewStl.h>
#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<UnsignedInt> 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] */

196
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
});
}

81
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
}

38
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;

292
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<FlagBase> 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<MeshVisualizer2D&>(Implementation::MeshVisualizerBase::setColor(color));
@ -242,6 +299,18 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer2D: public Implementation::MeshVisuali
return static_cast<MeshVisualizer2D&>(Implementation::MeshVisualizerBase::setWireframeWidth(width));
}
#ifndef MAGNUM_TARGET_GLES2
/** @copydoc MeshVisualizer3D::setColorMapTransformation() */
MeshVisualizer2D& setColorMapTransformation(Float offset, Float scale) {
return static_cast<MeshVisualizer2D&>(Implementation::MeshVisualizerBase::setColorMapTransformation(offset, scale));
}
/** @copydoc MeshVisualizer3D::bindColorMapTexture() */
MeshVisualizer2D& bindColorMapTexture(GL::Texture2D& texture) {
return static_cast<MeshVisualizer2D&>(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<MeshVisualizer3D&>(Implementation::MeshVisualizerBase::setColor(color));
@ -680,6 +862,62 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisuali
return static_cast<MeshVisualizer3D&>(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<MeshVisualizer3D&>(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<MeshVisualizer3D&>(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
};

43
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
}

13
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

603
src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp

@ -32,6 +32,7 @@
#include <Corrade/Utility/Directory.h>
#include <Corrade/Utility/FormatStl.h>
#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<UnsignedInt> 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<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
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<UnsignedInt> 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<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
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<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
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<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
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<GL::Extensions::ARB::geometry_shader4>())
@ -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<GL::Extensions::ARB::geometry_shader4>())
CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported"));
#else
if(!(data.flags2D & MeshVisualizer2D::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported<GL::Extensions::EXT::geometry_shader>())
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<UnsignedInt> 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<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
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<GL::Extensions::ARB::geometry_shader4>())
CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported"));
#else
if(!(data.flags3D & MeshVisualizer3D::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported<GL::Extensions::EXT::geometry_shader>())
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<UnsignedInt> 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<GL::Extensions::NV::shader_noperspective_interpolation>()) {
/* 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<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
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<GL::Extensions::NV::shader_noperspective_interpolation>()) {
if(!GL::Context::current().isExtensionSupported<GL::Extensions::NV::shader_noperspective_interpolation>()) {
maxThreshold = 39.0f;
meanThreshold = 1.207f;
}

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-objectid2D.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-objectid3D.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-primitiveid2D.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-primitiveid3D.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectid2D.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectid3D.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/primitiveid-tn.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/primitiveid2D.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/primitiveid3D.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo-objectid2D.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo-objectid3D.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-objectid2D.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-objectid3D.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-primitiveid-tn.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-tn.tga

Binary file not shown.
Loading…
Cancel
Save