diff --git a/doc/changelog.dox b/doc/changelog.dox index 78fc6a8c0..e4eafdd63 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -207,6 +207,10 @@ See also: available also in multi-draw and instanced scenarios - @ref Shaders::FlatGL and @ref Shaders::PhongGL now support object ID textures in addition to uniform and per-vertex object ID +- @ref Shaders::MeshVisualizerGL2D and @ref Shaders::MeshVisualizerGL3D now + supports object ID textures same as @ref Shaders::FlatGL and + @ref Shaders::PhongGL, including also support for object ID texture + transformation, instanced texture offset and texture arrays - Support for instanced drawing in @ref Shaders::MeshVisualizerGL2D and @ref Shaders::MeshVisualizerGL3D for better feature parity with the other sahders diff --git a/src/Magnum/Shaders/FlatGL.cpp b/src/Magnum/Shaders/FlatGL.cpp index c529397ef..cf6722f4e 100644 --- a/src/Magnum/Shaders/FlatGL.cpp +++ b/src/Magnum/Shaders/FlatGL.cpp @@ -52,7 +52,7 @@ namespace { enum: Int { TextureUnit = 0, /* 1/2/3 taken by Phong (D/S/N), 4 by MeshVisualizer colormap */ - ObjectIdTextureUnit = 5 /* shared with Phong */ + ObjectIdTextureUnit = 5 /* shared with Phong and MeshVisualizer */ }; #ifndef MAGNUM_TARGET_GLES2 diff --git a/src/Magnum/Shaders/MeshVisualizer.frag b/src/Magnum/Shaders/MeshVisualizer.frag index 2cb67b5a0..3380afda8 100644 --- a/src/Magnum/Shaders/MeshVisualizer.frag +++ b/src/Magnum/Shaders/MeshVisualizer.frag @@ -74,7 +74,7 @@ uniform lowp float wireframeWidth ; #elif defined(TBN_DIRECTION) #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 10) +layout(location = 12) #endif uniform lowp float lineWidth #ifndef GL_ES @@ -191,8 +191,31 @@ layout(binding = 4) uniform lowp sampler2D colorMapTexture; #endif +#ifdef OBJECT_ID_TEXTURE +#ifdef EXPLICIT_BINDING +layout(binding = 5) +#endif +uniform lowp + #ifndef TEXTURE_ARRAYS + usampler2D + #else + usampler2DArray + #endif + objectIdTextureData; +#endif + /* Inputs */ +#ifdef OBJECT_ID_TEXTURE +in mediump + #ifndef TEXTURE_ARRAYS + vec2 + #else + vec3 + #endif + interpolatedTextureCoordinates; +#endif + #if defined(WIREFRAME_RENDERING) || defined(TBN_DIRECTION) #ifndef NO_GEOMETRY_SHADER #if !defined(GL_ES) || defined(GL_NV_shader_noperspective_interpolation) @@ -274,6 +297,9 @@ void main() { #ifdef INSTANCED_OBJECT_ID + interpolatedInstanceObjectId #endif + #ifdef OBJECT_ID_TEXTURE + + texture(objectIdTextureData, interpolatedTextureCoordinates).r + #endif #elif defined(PRIMITIVE_ID) gl_PrimitiveID #elif defined(PRIMITIVE_ID_FROM_VERTEX_ID) diff --git a/src/Magnum/Shaders/MeshVisualizer.geom b/src/Magnum/Shaders/MeshVisualizer.geom index e2a31d8f1..6aef91340 100644 --- a/src/Magnum/Shaders/MeshVisualizer.geom +++ b/src/Magnum/Shaders/MeshVisualizer.geom @@ -66,7 +66,7 @@ uniform lowp vec4 wireframeColor #if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION) #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 10) +layout(location = 12) #endif uniform lowp float lineWidth #ifndef GL_ES @@ -160,6 +160,15 @@ in highp vec4 bitangentEndpoint[]; in highp vec4 normalEndpoint[]; #endif +#ifdef TEXTURED +in mediump + #ifndef TEXTURE_ARRAYS + vec2 + #else + vec3 + #endif + interpolatedVsTextureCoordinates[]; +#endif #ifdef INSTANCED_OBJECT_ID flat in highp uint interpolatedVsInstanceObjectId[]; #endif @@ -184,6 +193,15 @@ noperspective #endif out lowp vec3 dist; +#ifdef TEXTURED +out mediump + #ifndef TEXTURE_ARRAYS + vec2 + #else + vec3 + #endif + interpolatedTextureCoordinates; +#endif #ifdef INSTANCED_OBJECT_ID flat out highp uint interpolatedInstanceObjectId; #endif @@ -346,6 +364,9 @@ void main() { #ifdef VERTEX_ID interpolatedMappedVertexId = interpolatedVsMappedVertexId[i]; #endif + #ifdef TEXTURED + interpolatedTextureCoordinates = interpolatedVsTextureCoordinates[i]; + #endif #if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION) backgroundColor = color; lineColor = wireframeColor; diff --git a/src/Magnum/Shaders/MeshVisualizer.vert b/src/Magnum/Shaders/MeshVisualizer.vert index 745a7192d..10af790d0 100644 --- a/src/Magnum/Shaders/MeshVisualizer.vert +++ b/src/Magnum/Shaders/MeshVisualizer.vert @@ -27,6 +27,10 @@ #extension GL_EXT_gpu_shader4: require #endif +#if defined(UNIFORM_BUFFERS) && defined(TEXTURE_ARRAYS) && !defined(GL_ES) +#extension GL_ARB_shader_bit_encoding: require +#endif + #ifdef MULTI_DRAW #ifndef GL_ES #extension GL_ARB_shader_draw_parameters: require @@ -49,7 +53,7 @@ #ifndef UNIFORM_BUFFERS #ifdef TWO_DIMENSIONS #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 7) +layout(location = 9) #endif uniform highp mat3 transformationProjectionMatrix #ifndef GL_ES @@ -58,7 +62,7 @@ uniform highp mat3 transformationProjectionMatrix ; #elif defined(THREE_DIMENSIONS) #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 7) +layout(location = 9) #endif uniform highp mat4 transformationMatrix #ifndef GL_ES @@ -66,7 +70,7 @@ uniform highp mat4 transformationMatrix #endif ; #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 8) +layout(location = 10) #endif uniform highp mat4 projectionMatrix #ifndef GL_ES @@ -92,7 +96,7 @@ uniform lowp vec2 colorMapOffsetScale #if defined(TANGENT_DIRECTION) || defined(BITANGENT_FROM_TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION) #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 9) +layout(location = 11) #endif uniform highp mat3 normalMatrix #ifndef GL_ES @@ -101,7 +105,7 @@ uniform highp mat3 normalMatrix ; #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 11) +layout(location = 13) #endif uniform highp float lineLength #ifndef GL_ES @@ -110,6 +114,25 @@ uniform highp float lineLength ; #endif +#ifdef TEXTURE_TRANSFORMATION +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 7) +#endif +uniform mediump mat3 textureMatrix + #ifndef GL_ES + = mat3(1.0) + #endif + ; +#endif + +#ifdef TEXTURE_ARRAYS +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 8) +#endif +/* mediump is just 2^10, which might not be enough, this is 2^16 */ +uniform highp uint textureLayer; /* defaults to zero */ +#endif + /* Uniform buffers */ #else @@ -156,6 +179,23 @@ layout(std140 #error #endif +#ifdef TEXTURE_TRANSFORMATION +struct TextureTransformationUniform { + highp vec4 rotationScaling; + highp vec4 offsetLayerReserved; + #define textureTransformation_offset offsetLayerReserved.xy + #define textureTransformation_layer offsetLayerReserved.z +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 3 + #endif +) uniform TextureTransformation { + TextureTransformationUniform textureTransformations[DRAW_COUNT]; +}; +#endif + /* Keep in sync with MeshVisualizer.geom and MeshVisualizer.frag. Can't "outsource" to a common file because the #extension directives need to be always before any code. */ @@ -238,6 +278,26 @@ layout(location = NORMAL_ATTRIBUTE_LOCATION) in highp vec3 normal; #endif +#ifdef TEXTURED +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = TEXTURECOORDINATES_ATTRIBUTE_LOCATION) +#endif +in mediump vec2 textureCoordinates; +#endif + +#ifdef INSTANCED_TEXTURE_OFFSET +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = TEXTURE_OFFSET_ATTRIBUTE_LOCATION) +#endif +in mediump + #ifndef TEXTURE_ARRAYS + vec2 + #else + vec3 + #endif + instancedTextureOffset; +#endif + #if defined(WIREFRAME_RENDERING) && defined(NO_GEOMETRY_SHADER) #if (!defined(GL_ES) && __VERSION__ < 140) || (defined(GL_ES) && __VERSION__ < 300) #ifdef EXPLICIT_ATTRIB_LOCATION @@ -277,6 +337,21 @@ in highp mat3 instancedNormalMatrix; /* Outputs */ +#ifdef TEXTURED +out mediump + #ifndef TEXTURE_ARRAYS + vec2 + #else + vec3 + #endif + #ifdef NO_GEOMETRY_SHADER + interpolatedTextureCoordinates + #else + interpolatedVsTextureCoordinates + #endif + ; +#endif + #if defined(WIREFRAME_RENDERING) && defined(NO_GEOMETRY_SHADER) out vec3 barycentric; #endif @@ -359,6 +434,12 @@ void main() { lowp float colorMapOffset = materials[materialId].material_colorMapOffset; lowp float colorMapScale = materials[materialId].material_colorMapScale; highp float lineLength = materials[materialId].material_lineLength; + #ifdef TEXTURE_TRANSFORMATION + mediump const mat3 textureMatrix = mat3(textureTransformations[drawId].rotationScaling.xy, 0.0, textureTransformations[drawId].rotationScaling.zw, 0.0, textureTransformations[drawId].textureTransformation_offset, 1.0); + #ifdef TEXTURE_ARRAYS + highp const uint textureLayer = floatBitsToUint(textureTransformations[drawId].textureTransformation_layer); + #endif + #endif #endif #ifdef TWO_DIMENSIONS @@ -398,6 +479,39 @@ void main() { normalEndpoint = projectionMatrix*(transformedPosition4 + vec4(normalize(finalNormalMatrix*normal)*lineLength, 0.0)); #endif + #ifdef TEXTURED + /* Texture coordinates, if needed */ + #ifdef NO_GEOMETRY_SHADER + interpolatedTextureCoordinates + #else + interpolatedVsTextureCoordinates + #endif + .xy = + #ifdef TEXTURE_TRANSFORMATION + (textureMatrix*vec3( + #ifdef INSTANCED_TEXTURE_OFFSET + instancedTextureOffset.xy + + #endif + textureCoordinates, 1.0)).xy + #else + textureCoordinates + #endif + ; + #ifdef TEXTURE_ARRAYS + #ifdef NO_GEOMETRY_SHADER + interpolatedTextureCoordinates + #else + interpolatedVsTextureCoordinates + #endif + .z = float( + #ifdef INSTANCED_TEXTURE_OFFSET + uint(instancedTextureOffset.z) + + #endif + textureLayer + ); + #endif + #endif + #if defined(WIREFRAME_RENDERING) && defined(NO_GEOMETRY_SHADER) barycentric = vec3(0.0); diff --git a/src/Magnum/Shaders/MeshVisualizerGL.cpp b/src/Magnum/Shaders/MeshVisualizerGL.cpp index 8d8c9e27e..246208d70 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.cpp +++ b/src/Magnum/Shaders/MeshVisualizerGL.cpp @@ -41,6 +41,7 @@ #ifndef MAGNUM_TARGET_GLES2 #include "Magnum/GL/Buffer.h" +#include "Magnum/GL/TextureArray.h" #endif #include "Magnum/Shaders/Implementation/CreateCompatibilityShader.h" @@ -50,7 +51,8 @@ namespace Magnum { namespace Shaders { namespace { enum: Int { /* First four taken by Phong (A/D/S/N) */ - ColorMapTextureUnit = 4 + ColorMapTextureUnit = 4, + ObjectIdTextureUnit = 5 /* shared with Flat and Phong */ }; #ifndef MAGNUM_TARGET_GLES2 @@ -62,8 +64,7 @@ namespace { TransformationProjectionBufferBinding = 1, TransformationBufferBinding = 1, DrawBufferBinding = 2, - /* Binding 3 is commonly used by TextureTransformationBufferBinding, - leave it reserved */ + TextureTransformationBufferBinding = 3, MaterialBufferBinding = 4, }; #endif @@ -93,6 +94,15 @@ MeshVisualizerGLBase::MeshVisualizerGLBase(FlagsBase flags "Shaders::MeshVisualizerGL: Flag::ObjectId, Flag::VertexId and Flag::PrimitiveId are mutually exclusive", ); #endif + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags & FlagBase::TextureTransformation) || flags >= FlagBase::ObjectIdTexture, + "Shaders::MeshVisualizerGL: texture transformation enabled but the shader is not textured", ); + CORRADE_ASSERT(!(flags & FlagBase::TextureArrays) || flags >= FlagBase::ObjectIdTexture, + "Shaders::MeshVisualizerGL: texture arrays enabled but the shader is not textured", ); + CORRADE_ASSERT(!(flags & FlagBase::UniformBuffers) || !(flags & FlagBase::TextureArrays) || flags >= (FlagBase::TextureArrays|FlagBase::TextureTransformation), + "Shaders::MeshVisualizerGL: texture arrays require texture transformation enabled as well if uniform buffers are used", ); + #endif + #ifndef MAGNUM_TARGET_GLES if(flags >= FlagBase::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); @@ -161,10 +171,14 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra vert.addSource(_flags & FlagBase::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") #ifndef MAGNUM_TARGET_GLES2 + .addSource(_flags >= FlagBase::ObjectIdTexture ? "#define TEXTURED\n" : "") + .addSource(_flags & FlagBase::TextureTransformation ? "#define TEXTURE_TRANSFORMATION\n" : "") + .addSource(_flags & FlagBase::TextureArrays ? "#define TEXTURE_ARRAYS\n" : "") .addSource(_flags >= FlagBase::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") #endif .addSource(_flags & FlagBase::InstancedTransformation ? "#define INSTANCED_TRANSFORMATION\n" : "") #ifndef MAGNUM_TARGET_GLES2 + .addSource(_flags >= FlagBase::InstancedTextureOffset ? "#define INSTANCED_TEXTURE_OFFSET\n" : "") .addSource(_flags & FlagBase::VertexId ? "#define VERTEX_ID\n" : "") .addSource(_flags >= FlagBase::PrimitiveIdFromVertexId ? "#define PRIMITIVE_ID_FROM_VERTEX_ID\n" : "") #endif @@ -189,6 +203,8 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra frag.addSource(_flags & FlagBase::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") #ifndef MAGNUM_TARGET_GLES2 .addSource(_flags & FlagBase::ObjectId ? "#define OBJECT_ID\n" : "") + .addSource(_flags >= FlagBase::ObjectIdTexture ? "#define OBJECT_ID_TEXTURE\n" : "") + .addSource(_flags & FlagBase::TextureArrays ? "#define TEXTURE_ARRAYS\n" : "") .addSource(_flags >= FlagBase::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") .addSource(_flags & FlagBase::VertexId ? "#define VERTEX_ID\n" : "") .addSource(_flags & FlagBase::PrimitiveId ? @@ -213,6 +229,26 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra } #ifndef MAGNUM_TARGET_GLES2 +MeshVisualizerGLBase& MeshVisualizerGLBase::setTextureMatrix(const Matrix3& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= FlagBase::UniformBuffers), + "Shaders::MeshVisualizerGL::setTextureMatrix(): the shader was created with uniform buffers enabled", *this); + #endif + CORRADE_ASSERT(_flags & FlagBase::TextureTransformation, + "Shaders::MeshVisualizerGL::setTextureMatrix(): the shader was not created with texture transformation enabled", *this); + setUniform(_textureMatrixUniform, matrix); + return *this; +} + +MeshVisualizerGLBase& MeshVisualizerGLBase::setTextureLayer(UnsignedInt id) { + CORRADE_ASSERT(!(_flags >= FlagBase::UniformBuffers), + "Shaders::MeshVisualizerGL::setTextureLayer(): the shader was created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & FlagBase::TextureArrays, + "Shaders::MeshVisualizerGL::setTextureLayer(): the shader was not created with texture arrays enabled", *this); + setUniform(_textureLayerUniform, id); + return *this; +} + MeshVisualizerGLBase& MeshVisualizerGLBase::setObjectId(UnsignedInt id) { CORRADE_ASSERT(!(_flags >= FlagBase::UniformBuffers), "Shaders::MeshVisualizerGL::setObjectId(): the shader was created with uniform buffers enabled", *this); @@ -282,6 +318,24 @@ MeshVisualizerGLBase& MeshVisualizerGLBase::setDrawOffset(const UnsignedInt offs return *this; } +MeshVisualizerGLBase& MeshVisualizerGLBase::bindTextureTransformationBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= FlagBase::UniformBuffers, + "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & FlagBase::TextureTransformation, + "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding); + return *this; +} + +MeshVisualizerGLBase& MeshVisualizerGLBase::bindTextureTransformationBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= FlagBase::UniformBuffers, + "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & FlagBase::TextureTransformation, + "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); + return *this; +} + MeshVisualizerGLBase& MeshVisualizerGLBase::bindMaterialBuffer(GL::Buffer& buffer) { CORRADE_ASSERT(_flags >= FlagBase::UniformBuffers, "Shaders::MeshVisualizerGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); @@ -304,6 +358,26 @@ MeshVisualizerGLBase& MeshVisualizerGLBase::bindColorMapTexture(GL::Texture2D& t texture.bind(ColorMapTextureUnit); return *this; } + +MeshVisualizerGLBase& MeshVisualizerGLBase::bindObjectIdTexture(GL::Texture2D& texture) { + CORRADE_ASSERT(_flags >= FlagBase::ObjectIdTexture, + "Shaders::MeshVisualizerGL::bindObjectIdTexture(): the shader was not created with object ID texture enabled", *this); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags & FlagBase::TextureArrays), + "Shaders::MeshVisualizerGL::bindObjectIdTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead", *this); + #endif + texture.bind(ObjectIdTextureUnit); + return *this; +} + +MeshVisualizerGLBase& MeshVisualizerGLBase::bindObjectIdTexture(GL::Texture2DArray& texture) { + CORRADE_ASSERT(_flags >= FlagBase::ObjectIdTexture, + "Shaders::MeshVisualizerGL::bindObjectIdTexture(): the shader was not created with object ID texture enabled", *this); + CORRADE_ASSERT(_flags & FlagBase::TextureArrays, + "Shaders::MeshVisualizerGL::bindObjectIdTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead", *this); + texture.bind(ObjectIdTextureUnit); + return *this; +} #endif } @@ -312,7 +386,7 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags #ifndef MAGNUM_TARGET_GLES2 , const UnsignedInt materialCount, const UnsignedInt drawCount #endif -): Implementation::MeshVisualizerGLBase{Implementation::MeshVisualizerGLBase::FlagBase(UnsignedShort(flags)) +): Implementation::MeshVisualizerGLBase{Implementation::MeshVisualizerGLBase::FlagBase(UnsignedInt(flags)) #ifndef MAGNUM_TARGET_GLES2 , materialCount, drawCount #endif @@ -371,6 +445,8 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags geom = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Geometry); (*geom) .addSource("#define WIREFRAME_RENDERING\n#define MAX_VERTICES 3\n") + .addSource(_flags >= FlagBase::ObjectIdTexture ? "#define TEXTURED\n" : "") + .addSource(_flags & FlagBase::TextureArrays ? "#define TEXTURE_ARRAYS\n" : "") .addSource(_flags >= FlagBase::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") .addSource(_flags & FlagBase::VertexId ? "#define VERTEX_ID\n" : "") .addSource(_flags & FlagBase::PrimitiveId ? @@ -414,11 +490,17 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags { bindAttributeLocation(Position::Location, "position"); #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::ObjectIdTexture) + bindAttributeLocation(TextureCoordinates::Location, "textureCoordinates"); if(flags >= Flag::InstancedObjectId) bindAttributeLocation(ObjectId::Location, "instanceObjectId"); #endif if(flags & Flag::InstancedTransformation) bindAttributeLocation(TransformationMatrix::Location, "instancedTransformationMatrix"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::InstancedTextureOffset) + bindAttributeLocation(TextureOffset::Location, "instancedTextureOffset"); + #endif #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2) #ifndef MAGNUM_TARGET_GLES if(!context.isVersionSupported(GL::Version::GL310)) @@ -448,6 +530,12 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags #endif { _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags & Flag::TextureTransformation) + _textureMatrixUniform = uniformLocation("textureMatrix"); + if(flags & Flag::TextureArrays) + _textureLayerUniform = uniformLocation("textureLayer"); + #endif if(flags & (Flag::Wireframe #ifndef MAGNUM_TARGET_GLES2 |Flag::ObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId @@ -478,10 +566,14 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags setUniform(uniformLocation("colorMapTexture"), ColorMapTextureUnit); } #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::ObjectIdTexture) + setUniform(uniformLocation("objectIdTextureData"), ObjectIdTextureUnit); if(flags >= Flag::UniformBuffers) { setUniformBlockBinding(uniformBlockIndex("TransformationProjection"), TransformationProjectionBufferBinding); setUniformBlockBinding(uniformBlockIndex("Draw"), DrawBufferBinding); setUniformBlockBinding(uniformBlockIndex("Material"), MaterialBufferBinding); + if(flags & Flag::TextureTransformation) + setUniformBlockBinding(uniformBlockIndex("TextureTransformation"), TextureTransformationBufferBinding); } #endif } @@ -585,7 +677,7 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags #ifndef MAGNUM_TARGET_GLES2 , const UnsignedInt materialCount, const UnsignedInt drawCount #endif -): Implementation::MeshVisualizerGLBase{Implementation::MeshVisualizerGLBase::FlagBase(UnsignedShort(flags)) +): Implementation::MeshVisualizerGLBase{Implementation::MeshVisualizerGLBase::FlagBase(UnsignedInt(flags)) #ifndef MAGNUM_TARGET_GLES2 , materialCount, drawCount #endif @@ -688,6 +780,8 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags (*geom) .addSource(Utility::formatString("#define MAX_VERTICES {}\n", maxVertices)) .addSource(flags & Flag::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") + .addSource(_flags >= FlagBase::ObjectIdTexture ? "#define TEXTURED\n" : "") + .addSource(_flags & FlagBase::TextureArrays ? "#define TEXTURE_ARRAYS\n" : "") .addSource(_flags >= FlagBase::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") .addSource(_flags & FlagBase::VertexId ? "#define VERTEX_ID\n" : "") .addSource(_flags & FlagBase::PrimitiveId ? @@ -734,6 +828,8 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags { bindAttributeLocation(Position::Location, "position"); #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::ObjectIdTexture) + bindAttributeLocation(TextureCoordinates::Location, "textureCoordinates"); if(flags >= Flag::InstancedObjectId) bindAttributeLocation(ObjectId::Location, "instanceObjectId"); #endif @@ -744,6 +840,10 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags bindAttributeLocation(NormalMatrix::Location, "instancedNormalMatrix"); #endif } + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::InstancedTextureOffset) + bindAttributeLocation(TextureOffset::Location, "instancedTextureOffset"); + #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) if(flags & Flag::TangentDirection || flags & Flag::BitangentFromTangentDirection) @@ -789,6 +889,12 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags { _transformationMatrixUniform = uniformLocation("transformationMatrix"); _projectionMatrixUniform = uniformLocation("projectionMatrix"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags & Flag::TextureTransformation) + _textureMatrixUniform = uniformLocation("textureMatrix"); + if(flags & Flag::TextureArrays) + _textureLayerUniform = uniformLocation("textureLayer"); + #endif if(flags & (Flag::Wireframe #ifndef MAGNUM_TARGET_GLES2 |Flag::ObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId @@ -832,11 +938,15 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags setUniform(uniformLocation("colorMapTexture"), ColorMapTextureUnit); } #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::ObjectIdTexture) + setUniform(uniformLocation("objectIdTextureData"), ObjectIdTextureUnit); if(flags >= Flag::UniformBuffers) { setUniformBlockBinding(uniformBlockIndex("Projection"), ProjectionBufferBinding); setUniformBlockBinding(uniformBlockIndex("Transformation"), TransformationBufferBinding); setUniformBlockBinding(uniformBlockIndex("Draw"), DrawBufferBinding); setUniformBlockBinding(uniformBlockIndex("Material"), MaterialBufferBinding); + if(flags & Flag::TextureTransformation) + setUniformBlockBinding(uniformBlockIndex("TextureTransformation"), TextureTransformationBufferBinding); } #endif } @@ -1016,6 +1126,14 @@ MeshVisualizerGL3D& MeshVisualizerGL3D::bindDrawBuffer(GL::Buffer& buffer, const #endif Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flag value) { + #ifndef MAGNUM_TARGET_GLES2 + /* Special case coming from the Flags printer. As both flags are a superset + of ObjectId, printing just one would result in + `Flag::InstancedObjectId|Flag(0x4000)` in the output. */ + if(value == MeshVisualizerGL2D::Flag(UnsignedInt(MeshVisualizerGL2D::Flag::InstancedObjectId|MeshVisualizerGL2D::Flag::ObjectIdTexture))) + return debug << MeshVisualizerGL2D::Flag::InstancedObjectId << Debug::nospace << "|" << Debug::nospace << MeshVisualizerGL2D::Flag::ObjectIdTexture; + #endif + debug << "Shaders::MeshVisualizerGL2D::Flag" << Debug::nospace; switch(value) { @@ -1024,11 +1142,14 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flag value) { _c(NoGeometryShader) _c(Wireframe) #ifndef MAGNUM_TARGET_GLES2 + _c(TextureTransformation) _c(ObjectId) _c(InstancedObjectId) + _c(ObjectIdTexture) #endif _c(InstancedTransformation) #ifndef MAGNUM_TARGET_GLES2 + _c(InstancedTextureOffset) _c(VertexId) #ifndef MAGNUM_TARGET_WEBGL _c(PrimitiveId) @@ -1038,15 +1159,24 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flag value) { #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) _c(MultiDraw) + _c(TextureArrays) #endif #undef _c /* LCOV_EXCL_STOP */ } - return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedInt(value)) << Debug::nospace << ")"; } Debug& operator<<(Debug& debug, const MeshVisualizerGL3D::Flag value) { + #ifndef MAGNUM_TARGET_GLES2 + /* Special case coming from the Flags printer. As both flags are a superset + of ObjectId, printing just one would result in + `Flag::InstancedObjectId|Flag(0x4000)` in the output. */ + if(value == MeshVisualizerGL3D::Flag(UnsignedInt(MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::ObjectIdTexture))) + return debug << MeshVisualizerGL3D::Flag::InstancedObjectId << Debug::nospace << "|" << Debug::nospace << MeshVisualizerGL3D::Flag::ObjectIdTexture; + #endif + debug << "Shaders::MeshVisualizerGL3D::Flag" << Debug::nospace; switch(value) { @@ -1061,11 +1191,14 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL3D::Flag value) { _c(NormalDirection) #endif #ifndef MAGNUM_TARGET_GLES2 + _c(TextureTransformation) _c(ObjectId) _c(InstancedObjectId) + _c(ObjectIdTexture) #endif _c(InstancedTransformation) #ifndef MAGNUM_TARGET_GLES2 + _c(InstancedTextureOffset) _c(VertexId) #ifndef MAGNUM_TARGET_WEBGL _c(PrimitiveId) @@ -1075,12 +1208,13 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL3D::Flag value) { #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) _c(MultiDraw) + _c(TextureArrays) #endif #undef _c /* LCOV_EXCL_STOP */ } - return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedInt(value)) << Debug::nospace << ")"; } Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flags value) { @@ -1090,7 +1224,12 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flags value) { there */ MeshVisualizerGL2D::Flag::NoGeometryShader, #ifndef MAGNUM_TARGET_GLES2 + /* Both are a superset of ObjectId, meaning printing just one would + result in `Flag::InstancedObjectId|Flag(0x4000)` in the output. So + we pass both and let the Flag printer deal with that. */ + MeshVisualizerGL2D::Flag(UnsignedInt(MeshVisualizerGL2D::Flag::InstancedObjectId|MeshVisualizerGL2D::Flag::ObjectIdTexture)), MeshVisualizerGL2D::Flag::InstancedObjectId, /* Superset of ObjectId */ + MeshVisualizerGL2D::Flag::ObjectIdTexture, /* Superset of ObjectId */ MeshVisualizerGL2D::Flag::ObjectId, #endif MeshVisualizerGL2D::Flag::InstancedTransformation, @@ -1121,7 +1260,12 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL3D::Flags value) { MeshVisualizerGL3D::Flag::NormalDirection, #endif #ifndef MAGNUM_TARGET_GLES2 + /* Both are a superset of ObjectId, meaning printing just one would + result in `Flag::InstancedObjectId|Flag(0x4000)` in the output. So + we pass both and let the Flag printer deal with that. */ + MeshVisualizerGL3D::Flag(UnsignedInt(MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::ObjectIdTexture)), MeshVisualizerGL3D::Flag::InstancedObjectId, /* Superset of ObjectId */ + MeshVisualizerGL3D::Flag::ObjectIdTexture, /* Superset of ObjectId */ MeshVisualizerGL3D::Flag::ObjectId, #endif MeshVisualizerGL3D::Flag::InstancedTransformation, diff --git a/src/Magnum/Shaders/MeshVisualizerGL.h b/src/Magnum/Shaders/MeshVisualizerGL.h index a4bc3ead2..08c095338 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.h +++ b/src/Magnum/Shaders/MeshVisualizerGL.h @@ -43,7 +43,7 @@ namespace Implementation { class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgram { protected: - enum class FlagBase: UnsignedShort { + enum class FlagBase: UnsignedInt { /* Unlike the public Wireframe flag, this one doesn't include NoGeometryShader on ES2 as that would make the checks too complex */ @@ -53,12 +53,16 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr #ifndef MAGNUM_TARGET_GLES2 ObjectId = 1 << 12, InstancedObjectId = (1 << 2)|ObjectId, + ObjectIdTexture = 1 << 14, + TextureTransformation = 1 << 15, + InstancedTextureOffset = (1 << 16)|TextureTransformation, VertexId = 1 << 3, PrimitiveId = 1 << 4, PrimitiveIdFromVertexId = (1 << 5)|PrimitiveId, /* bit 6, 7, 8, 9 used by 3D-specific TBN visualization */ UniformBuffers = 1 << 10, - MultiDraw = UniformBuffers|(1 << 11) + MultiDraw = UniformBuffers|(1 << 11), + TextureArrays = 1 << 17, #endif }; typedef Containers::EnumSet FlagsBase; @@ -74,6 +78,8 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr MAGNUM_SHADERS_LOCAL GL::Version setupShaders(GL::Shader& vert, GL::Shader& frag, const Utility::Resource& rs) const; + MeshVisualizerGLBase& setTextureMatrix(const Matrix3& matrix); + MeshVisualizerGLBase& setTextureLayer(UnsignedInt layer); MeshVisualizerGLBase& setObjectId(UnsignedInt id); MeshVisualizerGLBase& setColor(const Color4& color); MeshVisualizerGLBase& setWireframeColor(const Color4& color); @@ -81,10 +87,14 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr #ifndef MAGNUM_TARGET_GLES2 MeshVisualizerGLBase& setColorMapTransformation(Float offset, Float scale); MeshVisualizerGLBase& bindColorMapTexture(GL::Texture2D& texture); + MeshVisualizerGLBase& bindObjectIdTexture(GL::Texture2D& texture); + MeshVisualizerGLBase& bindObjectIdTexture(GL::Texture2DArray& texture); #endif #ifndef MAGNUM_TARGET_GLES2 MeshVisualizerGLBase& setDrawOffset(UnsignedInt offset); + MeshVisualizerGLBase& bindTextureTransformationBuffer(GL::Buffer& buffer); + MeshVisualizerGLBase& bindTextureTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); MeshVisualizerGLBase& bindMaterialBuffer(GL::Buffer& buffer); MeshVisualizerGLBase& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); #endif @@ -108,7 +118,9 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr _smoothnessUniform{4}; #ifndef MAGNUM_TARGET_GLES2 Int _colorMapOffsetScaleUniform{5}, - _objectIdUniform{6}; + _objectIdUniform{6}, + _textureMatrixUniform{7}, + _textureLayerUniform{8}; /* Used instead of all other uniforms except viewportSize when Flag::UniformBuffers is set, so it can alias them */ Int _drawOffsetUniform{1}; @@ -156,6 +168,11 @@ buffer with per-instance transformation to a mesh: @snippet MagnumShaders-gl.cpp MeshVisualizerGL2D-usage-instancing +If @ref Flag::ObjectIdTexture is used and @ref Flag::InstancedTextureOffset is +enabled, the @ref TextureOffset attribute (or @ref TextureOffsetLayer in case +@ref Flag::TextureArrays is enabled as well) then can supply per-instance +texture offset (or offset and layer). + @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} @requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, @gl_extension{EXT,instanced_arrays} or @gl_extension{NV,instanced_arrays} @@ -173,6 +190,11 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua */ typedef typename GenericGL2D::Position Position; + #ifndef MAGNUM_TARGET_GLES2 + /** @copydoc MeshVisualizerGL3D::TextureCoordinates */ + typedef GenericGL2D::TextureCoordinates TextureCoordinates; + #endif + /** * @brief Vertex index * @@ -211,6 +233,14 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua */ typedef GenericGL2D::TransformationMatrix TransformationMatrix; + #ifndef MAGNUM_TARGET_GLES2 + /** @copydoc MeshVisualizerGL3D::TextureOffset */ + typedef typename GenericGL2D::TextureOffset TextureOffset; + + /** @copydoc MeshVisualizerGL3D::TextureOffsetLayer */ + typedef typename GenericGL2D::TextureOffsetLayer TextureOffsetLayer; + #endif + enum: UnsignedInt { /** * Color shader output. @ref shaders-generic "Generic output", @@ -225,7 +255,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * * @see @ref Flags, @ref MeshVisualizerGL2D() */ - enum class Flag: UnsignedShort { + enum class Flag: UnsignedInt { /** * Visualize wireframe. On OpenGL ES 2.0 and WebGL this also * enables @ref Flag::NoGeometryShader. @@ -277,6 +307,22 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua */ InstancedObjectId = (1 << 2)|ObjectId, + /** + * Object ID texture. Retrieves object IDs from a texture bound + * with @ref bindObjectIdTexture(), outputting a sum of the object + * ID texture, the ID coming from @ref setObjectId() or + * @ref MeshVisualizerDrawUniform2D::objectId and possibly also the + * per-vertex ID, if @ref Flag::InstancedObjectId is enabled as + * well. Implicitly enables @ref Flag::ObjectId. + * @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} + * @requires_gles30 Object ID input requires integer support in + * shaders, which is not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID input requires integer support in + * shaders, which is not available in WebGL 1.0. + * @m_since_latest + */ + ObjectIdTexture = (1 << 14)|ObjectId, + /** @copydoc MeshVisualizerGL3D::Flag::VertexId */ VertexId = 1 << 3, @@ -311,6 +357,41 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua */ InstancedTransformation = 1 << 13, + #ifndef MAGNUM_TARGET_GLES2 + /** @copydoc MeshVisualizerGL3D::Flag::TextureTransformation */ + TextureTransformation = 1 << 15, + + /** + * Instanced texture offset for an object ID texture. Retrieves a + * per-instance offset vector from the @ref TextureOffset attribute + * and uses it together with the matrix coming from + * @ref setTextureMatrix() or + * @ref TextureTransformationUniform::rotationScaling and + * @ref TextureTransformationUniform::offset (first the + * per-instance vector, then the uniform matrix). Instanced texture + * scaling and rotation is not supported at the moment, you can + * specify that only via the uniform @ref setTextureMatrix(). + * Implicitly enables @ref Flag::TextureTransformation. See + * @ref Shaders-MeshVisualizerGL3D-instancing for more information. + * + * If @ref Flag::TextureArrays is set as well, a three-component + * @ref TextureOffsetLayer attribute can be used instead of + * @ref TextureOffset to specify per-instance texture layer, which + * gets added to the uniform layer numbers set by + * @ref setTextureLayer() or + * @ref TextureTransformationUniform::layer. + * @requires_gl33 Extension @gl_extension{EXT,gpu_shader4} and + * @gl_extension{ARB,instanced_arrays} + * @requires_gles30 Object ID input requires integer support in + * shaders, which is not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID input requires integer support in + * shaders, which is not available in WebGL 1.0. + * @m_since_latest + * @todoc rewrite the ext requirements once we have more textures + */ + InstancedTextureOffset = (1 << 16)|TextureTransformation, + #endif + #ifndef MAGNUM_TARGET_GLES2 /** * Use uniform buffers. Expects that uniform data are supplied via @@ -348,7 +429,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * relies on uniform buffers, which require WebGL 2.0. * @m_since_latest */ - MultiDraw = UniformBuffers|(1 << 11) + MultiDraw = UniformBuffers|(1 << 11), + + /** @copydoc MeshVisualizerGL3D::Flag::TextureArrays */ + TextureArrays = 1 << 17, #endif }; @@ -432,7 +516,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua /** @brief Flags */ Flags flags() const { - return Flag(UnsignedShort(Implementation::MeshVisualizerGLBase::_flags)); + return Flag(UnsignedInt(Implementation::MeshVisualizerGLBase::_flags)); } #ifndef MAGNUM_TARGET_GLES2 @@ -483,6 +567,18 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua */ MeshVisualizerGL2D& setTransformationProjectionMatrix(const Matrix3& matrix); + #ifndef MAGNUM_TARGET_GLES2 + /** @copydoc MeshVisualizerGL3D::setTextureMatrix() */ + MeshVisualizerGL2D& setTextureMatrix(const Matrix3& matrix) { + return static_cast(Implementation::MeshVisualizerGLBase::setTextureMatrix(matrix)); + } + + /** @copydoc MeshVisualizerGL3D::setTextureLayer() */ + MeshVisualizerGL2D& setTextureLayer(UnsignedInt layer) { + return static_cast(Implementation::MeshVisualizerGLBase::setTextureLayer(layer)); + } + #endif + /** * @brief Set viewport size * @return Reference to self (for method chaining) @@ -663,6 +759,15 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua */ MeshVisualizerGL2D& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + /** @copydoc MeshVisualizerGL3D::bindTextureTransformationBuffer(GL::Buffer&) */ + MeshVisualizerGL2D& bindTextureTransformationBuffer(GL::Buffer& buffer) { + return static_cast(Implementation::MeshVisualizerGLBase::bindTextureTransformationBuffer(buffer)); + } + /** @copydoc MeshVisualizerGL3D::bindTextureTransformationBuffer(GL::Buffer&, GLintptr, GLsizeiptr) */ + MeshVisualizerGL2D& bindTextureTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size) { + return static_cast(Implementation::MeshVisualizerGLBase::bindTextureTransformationBuffer(buffer, offset, size)); + } + /** * @brief Set a material uniform buffer * @return Reference to self (for method chaining) @@ -702,6 +807,16 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua MeshVisualizerGL2D& bindColorMapTexture(GL::Texture2D& texture) { return static_cast(Implementation::MeshVisualizerGLBase::bindColorMapTexture(texture)); } + + /** @copydoc MeshVisualizerGL3D::bindObjectIdTexture(GL::Texture2D&) */ + MeshVisualizerGL2D& bindObjectIdTexture(GL::Texture2D& texture) { + return static_cast(Implementation::MeshVisualizerGLBase::bindObjectIdTexture(texture)); + } + + /** @copydoc MeshVisualizerGL3D::bindObjectIdTexture(GL::Texture2DArray&) */ + MeshVisualizerGL2D& bindObjectIdTexture(GL::Texture2DArray& texture) { + return static_cast(Implementation::MeshVisualizerGLBase::bindObjectIdTexture(texture)); + } #endif /** @@ -742,7 +857,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua #endif private: - Int _transformationProjectionMatrixUniform{7}; + Int _transformationProjectionMatrixUniform{9}; }; /** @@ -880,6 +995,13 @@ the @f$ [0, 1] @f$ texture range. Various colormap presets are in the @snippet MagnumShaders-gl.cpp MeshVisualizerGL3D-usage-object-id +Consistently with the other shaders, textured object ID is also supported if +@ref Flag::ObjectIdTexture is enabled. In that case you need to provide also +the @ref TextureCoordinates attribute and bind an integer texture via +@ref bindObjectIdTexture(). @ref Flag::TextureTransformation then enables +texture transformation and @ref Flag::TextureArrays texture arrays for the +object ID texture. + If you enable @ref Flag::VertexId, the shader will use the color map to visualize how are vertices shared among primitives. That's useful for inspecting mesh connectivity --- primitives sharing vertices will have a smooth @@ -894,7 +1016,8 @@ 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_gl30 Extension @gl_extension{EXT,gpu_shader4} for object ID input +@requires_gl30 Extension @gl_extension{EXT,gpu_shader4} for object ID input, + @gl_extension{EXT,texture_array} for object ID texture arrays @requires_gl30 The `gl_VertexID` shader variable is not available on OpenGL 2.1. @requires_gl32 The `gl_PrimitiveID` shader variable is not available on OpenGL @@ -902,12 +1025,14 @@ non-indexed @ref MeshPrimitive::Triangles. @requires_gles32 The `gl_PrimitiveID` shader variable is not available on OpenGL ES 3.1 and lower. @requires_gles30 Object ID input requires integer support in shaders, which - is not available in OpenGL ES 2.0. + is not available in OpenGL ES 2.0. Texture arrays for object ID texture + arrays are not available in OpenGL ES 2.0. @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 Object ID input requires integer support in shaders, which - is not available in WebGL 1.0. + is not available in WebGL 1.0. Texture arrays for object ID texture + arrays are not available in WebGL 1.0. @requires_webgl20 `gl_VertexID` is not available in WebGL 1.0. @section Shaders-MeshVisualizerGL3D-instancing Instanced rendering @@ -924,6 +1049,11 @@ including a normal matrix attribute for correct TBN visualization: @snippet MagnumShaders-gl.cpp MeshVisualizerGL3D-usage-instancing +If @ref Flag::ObjectIdTexture is used and @ref Flag::InstancedTextureOffset is +enabled, the @ref TextureOffset attribute (or @ref TextureOffsetLayer in case +@ref Flag::TextureArrays is enabled as well) then can supply per-instance +texture offset (or offset and layer). + @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} @requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, @gl_extension{EXT,instanced_arrays} or @gl_extension{NV,instanced_arrays} @@ -1016,6 +1146,23 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua */ typedef typename GenericGL3D::Normal Normal; + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief 2D texture coordinates + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", + * @ref Magnum::Vector2 "Vector2". Used only if + * @ref Flag::ObjectIdTexture is enabled. + * @requires_gl33 Extension @gl_extension{EXT,gpu_shader4} + * @requires_gles30 Object ID input requires integer support in + * shaders, which is not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID input requires integer support in + * shaders, which is not available in WebGL 1.0. + */ + typedef GenericGL3D::TextureCoordinates TextureCoordinates; + #endif + /** * @brief Vertex index * @@ -1084,6 +1231,44 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua typedef GenericGL3D::NormalMatrix NormalMatrix; #endif + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief (Instanced) texture offset for an object ID texture + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", @ref Magnum::Vector2. Used + * only if @ref Flag::InstancedTextureOffset is set. + * @requires_gl33 Extension @gl_extension{EXT,gpu_shader4} and + * @gl_extension{ARB,instanced_arrays} + * @requires_gles30 Object ID input requires integer support in + * shaders, which is not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID input requires integer support in + * shaders, which is not available in WebGL 1.0. + */ + typedef typename GenericGL3D::TextureOffset TextureOffset; + + /** + * @brief (Instanced) texture offset and layer for an object ID texture + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", @ref Magnum::Vector3, with + * the last component interpreted as an integer. Use either this or the + * @ref TextureOffset attribute. First two components used only if + * @ref Flag::InstancedTextureOffset is set, third component only if + * @ref Flag::TextureArrays is set. + * @requires_gl33 Extension @gl_extension{EXT,gpu_shader4}, + * @gl_extension{EXT,texture_array} and + * @gl_extension{ARB,instanced_arrays} + * @requires_gles30 Object ID input requires integer support in + * shaders, which is not available in OpenGL ES 2.0. Texture + * arrays are not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID input requires integer support in + * shaders, which is not available in WebGL 1.0. Texture arrays + * are not available in WebGL 1.0. + */ + typedef typename GenericGL3D::TextureOffsetLayer TextureOffsetLayer; + #endif + enum: UnsignedInt { /** * Color shader output. @ref shaders-generic "Generic output", @@ -1098,7 +1283,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * * @see @ref Flags, @ref MeshVisualizer() */ - enum class Flag: UnsignedShort { + enum class Flag: UnsignedInt { /** * Visualize wireframe. On OpenGL ES 2.0 and WebGL this also * enables @ref Flag::NoGeometryShader. @@ -1155,6 +1340,22 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua */ InstancedObjectId = (1 << 2)|ObjectId, + /** + * Object ID texture. Retrieves object IDs from a texture bound + * with @ref bindObjectIdTexture(), outputting a sum of the object + * ID texture, the ID coming from @ref setObjectId() or + * @ref MeshVisualizerDrawUniform3D::objectId and possibly also the + * per-vertex ID, if @ref Flag::InstancedObjectId is enabled as + * well. Implicitly enables @ref Flag::ObjectId. + * @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} + * @requires_gles30 Object ID input requires integer support in + * shaders, which is not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID input requires integer support in + * shaders, which is not available in WebGL 1.0. + * @m_since_latest + */ + ObjectIdTexture = (1 << 14)|ObjectId, + /** * Visualize vertex ID (@cpp gl_VertexID @ce). Useful for * visualizing mesh connectivity --- primitives sharing vertices @@ -1294,6 +1495,54 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua */ InstancedTransformation = 1 << 13, + #ifndef MAGNUM_TARGET_GLES2 + /** + * Enable texture coordinate transformation for an object ID + * texture. If this flag is set, the shader expects that + * @ref Flag::ObjectIdTexture is enabled as well. + * @see @ref setTextureMatrix() + * @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} + * @requires_gles30 Object ID input requires integer support in + * shaders, which is not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID input requires integer support in + * shaders, which is not available in WebGL 1.0. + * @m_since_latest + * @todoc rewrite the ext requirements once we have more textures + */ + TextureTransformation = 1 << 15, + + /** + * Instanced texture offset for an object ID texture. Retrieves a + * per-instance offset vector from the @ref TextureOffset attribute + * and uses it together with the matrix coming from + * @ref setTextureMatrix() or + * @ref TextureTransformationUniform::rotationScaling and + * @ref TextureTransformationUniform::offset (first the + * per-instance vector, then the uniform matrix). Instanced texture + * scaling and rotation is not supported at the moment, you can + * specify that only via the uniform @ref setTextureMatrix(). + * Implicitly enables @ref Flag::TextureTransformation. See + * @ref Shaders-MeshVisualizerGL2D-instancing for more information. + * + * If @ref Flag::TextureArrays is set as well, a three-component + * @ref TextureOffsetLayer attribute can be used instead of + * @ref TextureOffset to specify per-instance texture layer, which + * gets added to the uniform layer numbers set by + * @ref setTextureLayer() or + * @ref TextureTransformationUniform::layer. + * @requires_gl30 Extension + * @requires_gl33 Extension @gl_extension{EXT,gpu_shader4} and + * @gl_extension{ARB,instanced_arrays} + * @requires_gles30 Object ID input requires integer support in + * shaders, which is not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID input requires integer support in + * shaders, which is not available in WebGL 1.0. + * @m_since_latest + * @todoc rewrite the ext requirements once we have more textures + */ + InstancedTextureOffset = (1 << 16)|TextureTransformation, + #endif + #ifndef MAGNUM_TARGET_GLES2 /** * Use uniform buffers. Expects that uniform data are supplied via @@ -1332,7 +1581,30 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * relies on uniform buffers, which require WebGL 2.0. * @m_since_latest */ - MultiDraw = UniformBuffers|(1 << 11) + MultiDraw = UniformBuffers|(1 << 11), + + /** + * Use 2D texture arrays for an object ID texture. Expects that the + * texture is supplied via + * @ref bindObjectIdTexture(GL::Texture2DArray&) and the layer + * is set via @ref setTextureLayer() or + * @ref TextureTransformationUniform::layer. If + * @ref Flag::InstancedTextureOffset is set as well and a + * three-component @ref TextureOffsetLayer attribute is used + * instead of @ref TextureOffset, the per-instance and uniform + * layer numbers are added together. + * @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} and + * @gl_extension{EXT,texture_array} + * @requires_gles30 Object ID input requires integer support in + * shaders, which is not available in OpenGL ES 2.0. Texture + * arrays are not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID input requires integer support in + * shaders, which is not available in WebGL 1.0. Texture + * arrays are not available in WebGL 1.0. + * @m_since_latest + * @todoc rewrite the ext requirements once we have more textures + */ + TextureArrays = 1 << 17 #endif }; @@ -1432,7 +1704,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua /** @brief Flags */ Flags flags() const { - return Flag(UnsignedShort(Implementation::MeshVisualizerGLBase::_flags)); + return Flag(UnsignedInt(Implementation::MeshVisualizerGLBase::_flags)); } #ifndef MAGNUM_TARGET_GLES2 @@ -1537,6 +1809,64 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua MeshVisualizerGL3D& setNormalMatrix(const Matrix3x3& matrix); #endif + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Set texture coordinate transformation matrix for an object ID texture + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with + * @ref Flag::TextureTransformation enabled. Initial value is an + * identity matrix. If @ref Flag::InstancedTextureOffset is set, the + * per-instance offset coming from the @ref TextureOffset attribute is + * applied first, before this matrix. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref TextureTransformationUniform::rotationScaling and + * @ref TextureTransformationUniform::offset and call + * @ref bindTextureTransformationBuffer() instead. + * @requires_gl33 Extension @gl_extension{EXT,gpu_shader4} and + * @gl_extension{ARB,instanced_arrays} + * @requires_gles30 Object ID input requires integer support in + * shaders, which is not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID input requires integer support in + * shaders, which is not available in WebGL 1.0. + * @todoc rewrite the ext requirements once we have more textures + */ + MeshVisualizerGL3D& setTextureMatrix(const Matrix3& matrix) { + return static_cast(Implementation::MeshVisualizerGLBase::setTextureMatrix(matrix)); + } + + /** + * @brief Set texture array layer for an object ID texture + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with @ref Flag::TextureArrays + * enabled. Initial value is @cpp 0 @ce. If + * @ref Flag::InstancedTextureOffset is set and a three-component + * @ref TextureOffsetLayer attribute is used instead of + * @ref TextureOffset, this value is added to the layer coming from the + * third component. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref TextureTransformationUniform::layer and call + * @ref bindTextureTransformationBuffer() instead. + * @requires_gl33 Extension @gl_extension{EXT,gpu_shader4} and + * @gl_extension{EXT,texture_array} + * @requires_gles30 Object ID input requires integer support in + * shaders, which is not available in OpenGL ES 2.0. Texture + * arrays are not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID input requires integer support in + * shaders, which is not available in WebGL 1.0.Texture arrays are + * not available in WebGL 1.0. + * @todoc rewrite the ext requirements once we have more textures + */ + MeshVisualizerGL3D& setTextureLayer(UnsignedInt layer) { + return static_cast(Implementation::MeshVisualizerGLBase::setTextureLayer(layer)); + } + #endif + /** * @brief Set viewport size * @return Reference to self (for method chaining) @@ -1816,6 +2146,34 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua */ MeshVisualizerGL3D& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + /** + * @brief Set a texture transformation uniform buffer for an object ID texture + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that both @ref Flag::UniformBuffers and + * @ref Flag::TextureTransformation is set. The buffer is expected to + * contain @ref drawCount() instances of + * @ref TextureTransformationUniform. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * Object ID input requires integer support in shaders, which is + * not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + * Object ID input requires integer support in shaders, which is + * not available in WebGL 1.0. + */ + MeshVisualizerGL3D& bindTextureTransformationBuffer(GL::Buffer& buffer) { + return static_cast(Implementation::MeshVisualizerGLBase::bindTextureTransformationBuffer(buffer)); + } + /** + * @overload + * @m_since_latest + */ + MeshVisualizerGL3D& bindTextureTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size) { + return static_cast(Implementation::MeshVisualizerGLBase::bindTextureTransformationBuffer(buffer, offset, size)); + } + /** * @brief Set a material uniform buffer * @return Reference to self (for method chaining) @@ -1871,6 +2229,53 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua MeshVisualizerGL3D& bindColorMapTexture(GL::Texture2D& texture) { return static_cast(Implementation::MeshVisualizerGLBase::bindColorMapTexture(texture)); } + + /** + * @brief Bind an object ID texture + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with @ref Flag::ObjectIdTexture + * enabled. If @ref Flag::TextureArrays is enabled as well, use + * @ref bindObjectIdTexture(GL::Texture2DArray&) instead. The texture + * needs to have an unsigned integer format. + * @see @ref setObjectId(), @ref Flag::TextureTransformation, + * @ref setTextureMatrix() + * @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} + * @requires_gles30 Object ID visualization requires integer support in + * shaders, which is not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID visualization requires integer support + * in shaders, which is not available in WebGL 1.0. + */ + MeshVisualizerGL3D& bindObjectIdTexture(GL::Texture2D& texture) { + return static_cast(Implementation::MeshVisualizerGLBase::bindObjectIdTexture(texture)); + } + + /** + * @brief Bind an object ID array texture + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with both + * @ref Flag::ObjectIdTexture and @ref Flag::TextureArrays enabled. If + * @ref Flag::UniformBuffers is not enabled, the layer is set via + * @ref setTextureLayer(); if @ref Flag::UniformBuffers is enabled, + * @ref Flag::TextureTransformation has to be enabled as well and the + * layer is set via @ref TextureTransformationUniform::layer. + * @see @ref setObjectId(), @ref Flag::TextureTransformation, + * @ref setTextureLayer() + * @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} and + * @gl_extension{EXT,texture_array} + * @requires_gles30 Object ID output requires integer support in + * shaders, which is not available in OpenGL ES 2.0. Texture + * arrays are not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID output requires integer support in + * shaders, which is not available in WebGL 1.0. Texture arrays + * are not available in WebGL 1.0. + */ + MeshVisualizerGL3D& bindObjectIdTexture(GL::Texture2DArray& texture) { + return static_cast(Implementation::MeshVisualizerGLBase::bindObjectIdTexture(texture)); + } #endif /** @@ -1911,12 +2316,12 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua #endif private: - Int _transformationMatrixUniform{7}, - _projectionMatrixUniform{8}; + Int _transformationMatrixUniform{9}, + _projectionMatrixUniform{10}; #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - Int _normalMatrixUniform{9}, - _lineWidthUniform{10}, - _lineLengthUniform{11}; + Int _normalMatrixUniform{11}, + _lineWidthUniform{12}, + _lineLengthUniform{13}; #endif }; diff --git a/src/Magnum/Shaders/PhongGL.cpp b/src/Magnum/Shaders/PhongGL.cpp index 186d9dae9..5fca3b52b 100644 --- a/src/Magnum/Shaders/PhongGL.cpp +++ b/src/Magnum/Shaders/PhongGL.cpp @@ -59,7 +59,7 @@ namespace { SpecularTextureUnit = 2, NormalTextureUnit = 3, /* 4 taken by MeshVisualizer colormap */ - ObjectIdTextureUnit = 5 /* shared with Flat */ + ObjectIdTextureUnit = 5 /* shared with Flat and MeshVisualizer */ }; #ifndef MAGNUM_TARGET_GLES2 diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index 2adffd2ca..e48a4d39b 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -212,6 +212,8 @@ if(BUILD_GL_TESTS) MeshVisualizerTestFiles/instancedobjectid3D.tga MeshVisualizerTestFiles/objectid2D.tga MeshVisualizerTestFiles/objectid3D.tga + MeshVisualizerTestFiles/objectidtexture2D.tga + MeshVisualizerTestFiles/objectidtexture3D.tga MeshVisualizerTestFiles/primitiveid-tn.tga MeshVisualizerTestFiles/primitiveid2D.tga MeshVisualizerTestFiles/primitiveid3D.tga @@ -225,6 +227,8 @@ if(BUILD_GL_TESTS) MeshVisualizerTestFiles/wireframe-instancedobjectid3D.tga MeshVisualizerTestFiles/wireframe-nogeo-instancedobjectid2D.tga MeshVisualizerTestFiles/wireframe-nogeo-instancedobjectid3D.tga + MeshVisualizerTestFiles/wireframe-objectidtexture2D.tga + MeshVisualizerTestFiles/wireframe-objectidtexture3D.tga MeshVisualizerTestFiles/wireframe-perspective.tga MeshVisualizerTestFiles/wireframe-primitiveid-tn.tga MeshVisualizerTestFiles/wireframe-tn-smooth.tga @@ -243,6 +247,10 @@ if(BUILD_GL_TESTS) MeshVisualizerTestFiles/instanced-vertexid3D.tga MeshVisualizerTestFiles/instanced-instancedobjectid2D.tga MeshVisualizerTestFiles/instanced-instancedobjectid3D.tga + MeshVisualizerTestFiles/instanced-instancedobjectidtexture2D.tga + MeshVisualizerTestFiles/instanced-instancedobjectidtexture3D.tga + MeshVisualizerTestFiles/instanced-objectidtexture2D.tga + MeshVisualizerTestFiles/instanced-objectidtexture3D.tga MeshVisualizerTestFiles/multidraw-wireframe2D.tga MeshVisualizerTestFiles/multidraw-wireframe3D.tga MeshVisualizerTestFiles/multidraw-wireframe-tbn3D.tga @@ -251,7 +259,9 @@ if(BUILD_GL_TESTS) MeshVisualizerTestFiles/multidraw-vertexid2D.tga MeshVisualizerTestFiles/multidraw-vertexid3D.tga MeshVisualizerTestFiles/multidraw-instancedobjectid2D.tga - MeshVisualizerTestFiles/multidraw-instancedobjectid3D.tga) + MeshVisualizerTestFiles/multidraw-instancedobjectid3D.tga + MeshVisualizerTestFiles/multidraw-objectidtexture2D.tga + MeshVisualizerTestFiles/multidraw-objectidtexture3D.tga) target_include_directories(ShadersMeshVisualizerGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) if(BUILD_PLUGINS_STATIC) if(WITH_ANYIMAGEIMPORTER) diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index f34975ad8..57f6cd8f7 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -64,6 +64,7 @@ #ifndef MAGNUM_TARGET_GLES2 #include "Magnum/GL/MeshView.h" +#include "Magnum/GL/TextureArray.h" #include "Magnum/MeshTools/Concatenate.h" #include "Magnum/MeshTools/GenerateIndices.h" #include "Magnum/Primitives/Cone.h" @@ -113,9 +114,23 @@ struct MeshVisualizerGLTest: GL::OpenGLTester { void bindBufferUniformBuffersNotEnabled2D(); void bindBufferUniformBuffersNotEnabled3D(); #endif + #ifndef MAGNUM_TARGET_GLES2 + void bindObjectIdTextureInvalid2D(); + void bindObjectIdTextureInvalid3D(); + void bindObjectIdTextureArrayInvalid2D(); + void bindObjectIdTextureArrayInvalid3D(); + #endif void setWireframeNotEnabled2D(); void setWireframeNotEnabled3D(); #ifndef MAGNUM_TARGET_GLES2 + void setTextureMatrixNotEnabled2D(); + void setTextureMatrixNotEnabled3D(); + void setTextureLayerNotArray2D(); + void setTextureLayerNotArray3D(); + void bindTextureTransformBufferNotEnabled2D(); + void bindTextureTransformBufferNotEnabled3D(); + #endif + #ifndef MAGNUM_TARGET_GLES2 void setObjectIdNotEnabled2D(); void setObjectIdNotEnabled3D(); void setColorMapNotEnabled2D(); @@ -225,6 +240,12 @@ constexpr struct { #ifndef MAGNUM_TARGET_GLES2 {"object ID", MeshVisualizerGL2D::Flag::ObjectId}, {"instanced object ID", MeshVisualizerGL2D::Flag::InstancedObjectId}, + {"object ID texture", MeshVisualizerGL2D::Flag::ObjectIdTexture}, + {"object ID texture array", MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays}, + {"object ID texture + instanced texture transformation", MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::InstancedTextureOffset}, + {"object ID texture array + instanced texture transformation", MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays|MeshVisualizerGL2D::Flag::InstancedTextureOffset}, + {"instanced object ID texture array + texture transformation", MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::InstancedObjectId|MeshVisualizerGL2D::Flag::TextureArrays|MeshVisualizerGL2D::Flag::TextureTransformation}, + {"wireframe + object ID texture + instanced texture transformation", MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::InstancedTextureOffset}, {"vertex ID", MeshVisualizerGL2D::Flag::VertexId}, #ifndef MAGNUM_TARGET_WEBGL {"primitive ID", MeshVisualizerGL2D::Flag::PrimitiveId}, @@ -256,6 +277,12 @@ constexpr struct { {"wireframe w/o GS", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 1, 1}, {"object ID", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::ObjectId, 1, 1}, {"instanced object ID", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::InstancedObjectId, 1, 1}, + {"object ID texture", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::ObjectIdTexture, 1, 1}, + {"object ID texture array", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays|MeshVisualizerGL2D::Flag::TextureTransformation, 1, 1}, + {"object ID texture + instanced texture transformation", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::InstancedTextureOffset, 1, 1}, + {"object ID texture array + instanced texture transformation", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays|MeshVisualizerGL2D::Flag::InstancedTextureOffset, 1, 1}, + {"instanced object ID texture array + texture transformation", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::InstancedObjectId|MeshVisualizerGL2D::Flag::TextureArrays|MeshVisualizerGL2D::Flag::TextureTransformation, 1, 1}, + {"wireframe + object ID texture + instanced texture transformation", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::InstancedTextureOffset, 1, 1}, {"vertex ID", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::VertexId, 1, 1}, #ifndef MAGNUM_TARGET_WEBGL {"primitive ID", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::PrimitiveId, 1, 1}, @@ -277,6 +304,12 @@ constexpr struct { #ifndef MAGNUM_TARGET_GLES2 {"object ID", MeshVisualizerGL3D::Flag::ObjectId}, {"instanced object ID", MeshVisualizerGL3D::Flag::InstancedObjectId}, + {"object ID texture", MeshVisualizerGL3D::Flag::ObjectIdTexture}, + {"object ID texture array", MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays}, + {"object ID texture + instanced texture transformation", MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::InstancedTextureOffset}, + {"object ID texture array + instanced texture transformation", MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays|MeshVisualizerGL3D::Flag::InstancedTextureOffset}, + {"instanced object ID texture array + texture transformation", MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::TextureArrays|MeshVisualizerGL3D::Flag::TextureTransformation}, + {"wireframe + object ID texture + instanced texture transformation", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::InstancedTextureOffset}, {"vertex ID", MeshVisualizerGL3D::Flag::VertexId}, #ifndef MAGNUM_TARGET_WEBGL {"primitive ID", MeshVisualizerGL3D::Flag::PrimitiveId}, @@ -325,6 +358,12 @@ constexpr struct { {"wireframe w/o GS", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 1, 1}, {"object ID", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::ObjectId, 1, 1}, {"instanced object ID", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::InstancedObjectId, 1, 1}, + {"object ID texture", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::ObjectIdTexture, 1, 1}, + {"object ID texture array", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays|MeshVisualizerGL3D::Flag::TextureTransformation, 1, 1}, + {"object ID texture + instanced texture transformation", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::InstancedTextureOffset, 1, 1}, + {"object ID texture array + instanced texture transformation", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays|MeshVisualizerGL3D::Flag::InstancedTextureOffset, 1, 1}, + {"instanced object ID texture array + texture transformation", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::TextureArrays|MeshVisualizerGL3D::Flag::TextureTransformation, 1, 1}, + {"wireframe + object ID texture + instanced texture transformation", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::InstancedTextureOffset, 1, 1}, {"vertex ID", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::VertexId, 1, 1}, #ifndef MAGNUM_TARGET_WEBGL {"primitive ID", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::PrimitiveId, 1, 1}, @@ -367,7 +406,17 @@ constexpr struct { ": Flag::ObjectId, Flag::VertexId and Flag::PrimitiveId are mutually exclusive"}, {"both object and vertex ID", MeshVisualizerGL2D::Flag::ObjectId|MeshVisualizerGL2D::Flag::VertexId, - ": Flag::ObjectId, Flag::VertexId and Flag::PrimitiveId are mutually exclusive"} + ": Flag::ObjectId, Flag::VertexId and Flag::PrimitiveId are mutually exclusive"}, + {"texture transformation but not textured", + /* ObjectId shares bits with ObjectIdTexture but should still trigger + the assert */ + MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectId, + ": texture transformation enabled but the shader is not textured"}, + {"texture arrays but not textured", + /* ObjectId shares bits with ObjectIdTexture but should still trigger + the assert */ + MeshVisualizerGL2D::Flag::TextureArrays|MeshVisualizerGL2D::Flag::ObjectId, + ": texture arrays enabled but the shader is not textured"} #endif }; @@ -408,6 +457,16 @@ constexpr struct { {"both vertex and primitive ID", MeshVisualizerGL3D::Flag::VertexId|MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId, ": Flag::ObjectId, Flag::VertexId and Flag::PrimitiveId are mutually exclusive"}, + {"texture transformation but not textured", + /* ObjectId shares bits with ObjectIdTexture but should still trigger + the assert */ + MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectId, + ": texture transformation enabled but the shader is not textured"}, + {"texture arrays but not textured", + /* ObjectId shares bits with ObjectIdTexture but should still trigger + the assert */ + MeshVisualizerGL3D::Flag::TextureArrays|MeshVisualizerGL3D::Flag::ObjectId, + ": texture arrays enabled but the shader is not textured"}, #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) {"geometry shader disabled but needed", @@ -436,6 +495,46 @@ constexpr struct { }; #endif +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + MeshVisualizerGL2D::Flags flags2D; + MeshVisualizerGL3D::Flags flags3D; + const char* message; +} BindObjectIdTextureInvalidData[]{ + {"not textured", + /* ObjectId shares bits with ObjectIdTexture but should still trigger + the assert */ + MeshVisualizerGL2D::Flag::ObjectId, + MeshVisualizerGL3D::Flag::ObjectId, + "Shaders::MeshVisualizerGL::bindObjectIdTexture(): the shader was not created with object ID texture enabled\n"}, + {"array", + MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays, + MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays, + "Shaders::MeshVisualizerGL::bindObjectIdTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead\n"} +}; + +constexpr struct { + const char* name; + MeshVisualizerGL2D::Flags flags2D; + MeshVisualizerGL3D::Flags flags3D; + const char* message; +} BindObjectIdTextureArrayInvalidData[]{ + {"not textured", + /* ObjectId shares bits with ObjectIdTexture but should still trigger + the assert */ + /* ObjectId shares bits with ObjectIdTexture but should still trigger + the assert */ + MeshVisualizerGL2D::Flag::ObjectId, + MeshVisualizerGL3D::Flag::ObjectId, + "Shaders::MeshVisualizerGL::bindObjectIdTexture(): the shader was not created with object ID texture enabled\n"}, + {"not array", + MeshVisualizerGL2D::Flag::ObjectIdTexture, + MeshVisualizerGL3D::Flag::ObjectIdTexture, + "Shaders::MeshVisualizerGL::bindObjectIdTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead\n"} +}; +#endif + #ifndef MAGNUM_TARGET_GLES2 constexpr struct { const char* name; @@ -490,48 +589,96 @@ constexpr struct { }; #ifndef MAGNUM_TARGET_GLES2 -constexpr struct { +const struct { const char* name; MeshVisualizerGL2D::Flags flags2D; MeshVisualizerGL3D::Flags flags3D; + Matrix3 textureTransformation; + bool flip; + Int layer; const char* file2D; const char* file3D; } ObjectVertexPrimitiveIdData[] { {"object ID", MeshVisualizerGL2D::Flag::ObjectId, MeshVisualizerGL3D::Flag::ObjectId, + {}, false, 0, "objectid2D.tga", "objectid3D.tga"}, {"instanced object ID", MeshVisualizerGL2D::Flag::InstancedObjectId, MeshVisualizerGL3D::Flag::InstancedObjectId, + {}, false, 0, "instancedobjectid2D.tga", "instancedobjectid3D.tga"}, + {"textured object ID", + MeshVisualizerGL2D::Flag::ObjectIdTexture, + MeshVisualizerGL3D::Flag::ObjectIdTexture, + {}, false, 0, + "objectidtexture2D.tga", "objectidtexture3D.tga"}, + {"textured object ID, texture transformation", + MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureTransformation, + MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), true, 0, + "objectidtexture2D.tga", "objectidtexture3D.tga"}, + {"texture array object ID, first layer", + MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays, + MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays, + {}, false, 0, + "objectidtexture2D.tga", "objectidtexture3D.tga"}, + {"texture array object ID, arbitrary layer", + MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays, + MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays, + {}, false, 6, + "objectidtexture2D.tga", "objectidtexture3D.tga"}, + {"texture array object ID, texture transformation, arbitrary layer", + MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays|MeshVisualizerGL2D::Flag::TextureTransformation, + MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays|MeshVisualizerGL3D::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), true, 6, + "objectidtexture2D.tga", "objectidtexture3D.tga"}, {"vertex ID", MeshVisualizerGL2D::Flag::VertexId, MeshVisualizerGL3D::Flag::VertexId, + {}, false, 0, "vertexid2D.tga", "vertexid3D.tga"}, #ifndef MAGNUM_TARGET_WEBGL {"primitive ID", MeshVisualizerGL2D::Flag::PrimitiveId, MeshVisualizerGL3D::Flag::PrimitiveId, + {}, false, 0, "primitiveid2D.tga", "primitiveid3D.tga"}, #endif {"primitive ID from vertex ID", MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId, MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId, + {}, false, 0, "primitiveid2D.tga", "primitiveid3D.tga"}, {"wireframe + instanced object ID", MeshVisualizerGL2D::Flag::InstancedObjectId|MeshVisualizerGL2D::Flag::Wireframe, MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::Wireframe, + {}, false, 0, "wireframe-instancedobjectid2D.tga", "wireframe-instancedobjectid3D.tga"}, {"wireframe + instanced object ID, no geometry shader", MeshVisualizerGL2D::Flag::InstancedObjectId|MeshVisualizerGL2D::Flag::Wireframe| MeshVisualizerGL2D::Flag::NoGeometryShader, MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::Wireframe| MeshVisualizerGL3D::Flag::NoGeometryShader, + {}, false, 0, "wireframe-nogeo-instancedobjectid2D.tga", "wireframe-nogeo-instancedobjectid3D.tga"}, + /* These two are here to test that all required texture-related attributes + are properly passed through the GS */ + {"wireframe + textured object ID", + MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::Wireframe, + MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::Wireframe, + {}, false, 0, + "wireframe-objectidtexture2D.tga", "wireframe-objectidtexture3D.tga"}, + {"wireframe + texture array object ID, texture transformation, arbitrary layer", + MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays|MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::Wireframe, + MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays|MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::Wireframe, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), true, 6, + "wireframe-objectidtexture2D.tga", "wireframe-objectidtexture3D.tga"}, {"wireframe + vertex ID", MeshVisualizerGL2D::Flag::VertexId|MeshVisualizerGL2D::Flag::Wireframe, MeshVisualizerGL3D::Flag::VertexId|MeshVisualizerGL3D::Flag::Wireframe, + {}, false, 0, "wireframe-vertexid2D.tga", "wireframe-vertexid3D.tga"} }; #endif @@ -621,6 +768,18 @@ const struct { MeshVisualizerGL2D::Flag::InstancedObjectId, /* SwiftShader has a few rounding errors on edges */ 133.0f, 0.12f}, + {"textured object ID", "instanced-objectidtexture2D.tga", + MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::InstancedTextureOffset, + /* SwiftShader has a few rounding errors on edges */ + 146.7f, 0.097f}, + {"instanced textured object ID", "instanced-instancedobjectidtexture2D.tga", + MeshVisualizerGL2D::Flag::InstancedObjectId|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::InstancedTextureOffset, + /* SwiftShader has a few rounding errors on edges */ + 133.0f, 0.071f}, + {"instanced textured array object ID", "instanced-instancedobjectidtexture2D.tga", + MeshVisualizerGL2D::Flag::InstancedObjectId|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::InstancedTextureOffset|MeshVisualizerGL2D::Flag::TextureArrays, + /* SwiftShader has a few rounding errors on edges */ + 133.0f, 0.071f}, #endif }; @@ -653,6 +812,18 @@ const struct { MeshVisualizerGL3D::Flag::InstancedObjectId, /* SwiftShader has an off-by-one error on certain colors */ 0.334f, 0.042f}, + {"textured object ID", "instanced-objectidtexture3D.tga", + MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::InstancedTextureOffset, + /* SwiftShader has a few rounding errors on edges */ + 28.67f, 0.097f}, + {"instanced textured object ID", "instanced-instancedobjectidtexture3D.tga", + MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::InstancedTextureOffset, + /* SwiftShader has a few rounding errors on edges */ + 32.67f, 0.101f}, + {"instanced textured array object ID", "instanced-instancedobjectidtexture3D.tga", + MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::InstancedTextureOffset|MeshVisualizerGL3D::Flag::TextureArrays, + /* SwiftShader has a few rounding errors on edges */ + 32.67f, 0.101f}, #endif }; @@ -686,6 +857,14 @@ constexpr struct { MeshVisualizerGL2D::Flag::InstancedObjectId, 1, 1, 16, 0.0f, 0.0f}, + {"bind with offset, textured object ID", "multidraw-objectidtexture2D.tga", + MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectIdTexture, + 1, 1, 16, + 0.0f, 0.0f}, + {"bind with offset, textured array object ID", "multidraw-objectidtexture2D.tga", + MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays, + 1, 1, 16, + 0.0f, 0.0f}, #ifndef MAGNUM_TARGET_WEBGL {"draw offset, wireframe", "multidraw-wireframe2D.tga", MeshVisualizerGL2D::Flag::Wireframe, @@ -707,6 +886,14 @@ constexpr struct { MeshVisualizerGL2D::Flag::InstancedObjectId, 2, 3, 1, 0.0f, 0.0f}, + {"draw offset, textured object ID", "multidraw-objectidtexture2D.tga", + MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectIdTexture, + 2, 3, 1, + 0.0f, 0.0f}, + {"draw offset, textured array object ID", "multidraw-objectidtexture2D.tga", + MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays, + 2, 3, 1, + 0.0f, 0.0f}, #ifndef MAGNUM_TARGET_WEBGL {"multidraw, wireframe", "multidraw-wireframe2D.tga", MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::Wireframe, @@ -728,6 +915,14 @@ constexpr struct { MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::InstancedObjectId, 2, 3, 1, 0.0f, 0.0f}, + {"multidraw, textured object ID", "multidraw-objectidtexture2D.tga", + MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectIdTexture, + 2, 3, 1, + 0.0f, 0.0f}, + {"multidraw, textured array object ID", "multidraw-objectidtexture2D.tga", + MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::TextureTransformation|MeshVisualizerGL2D::Flag::ObjectIdTexture|MeshVisualizerGL2D::Flag::TextureArrays, + 2, 3, 1, + 0.0f, 0.0f}, }; constexpr struct { @@ -760,6 +955,14 @@ constexpr struct { MeshVisualizerGL3D::Flag::InstancedObjectId, 1, 1, 16, 0.0f, 0.0f}, + {"bind with offset, textured object ID", "multidraw-objectidtexture3D.tga", + MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectIdTexture, + 1, 1, 16, + 0.0f, 0.0f}, + {"bind with offset, textured array object ID", "multidraw-objectidtexture3D.tga", + MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays, + 1, 1, 16, + 0.0f, 0.0f}, #ifndef MAGNUM_TARGET_WEBGL {"draw offset, wireframe", "multidraw-wireframe3D.tga", MeshVisualizerGL3D::Flag::Wireframe, @@ -782,6 +985,14 @@ constexpr struct { MeshVisualizerGL3D::Flag::InstancedObjectId, 2, 3, 1, 0.0f, 0.0f}, + {"draw offset, textured object ID", "multidraw-objectidtexture3D.tga", + MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectIdTexture, + 2, 3, 1, + 0.0f, 0.0f}, + {"draw offset, textured array object ID", "multidraw-objectidtexture3D.tga", + MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays, + 2, 3, 1, + 0.0f, 0.0f}, #ifndef MAGNUM_TARGET_WEBGL {"multidraw, wireframe", "multidraw-wireframe3D.tga", MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe, @@ -804,6 +1015,14 @@ constexpr struct { MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::InstancedObjectId, 2, 3, 1, 0.0f, 0.0f}, + {"multidraw, textured object ID", "multidraw-objectidtexture3D.tga", + MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectIdTexture, + 2, 3, 1, + 0.0f, 0.0f}, + {"multidraw, textured array object ID", "multidraw-objectidtexture3D.tga", + MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::TextureTransformation|MeshVisualizerGL3D::Flag::ObjectIdTexture|MeshVisualizerGL3D::Flag::TextureArrays, + 2, 3, 1, + 0.0f, 0.0f}, }; #endif @@ -856,9 +1075,29 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { &MeshVisualizerGLTest::bindBufferUniformBuffersNotEnabled2D, &MeshVisualizerGLTest::bindBufferUniformBuffersNotEnabled3D, #endif + }); + + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&MeshVisualizerGLTest::bindObjectIdTextureInvalid2D, + &MeshVisualizerGLTest::bindObjectIdTextureInvalid3D}, + Containers::arraySize(BindObjectIdTextureInvalidData)); + addInstancedTests({&MeshVisualizerGLTest::bindObjectIdTextureArrayInvalid2D, + &MeshVisualizerGLTest::bindObjectIdTextureArrayInvalid3D}, + Containers::arraySize(BindObjectIdTextureArrayInvalidData)); + #endif + + addTests({ &MeshVisualizerGLTest::setWireframeNotEnabled2D, &MeshVisualizerGLTest::setWireframeNotEnabled3D, #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::setTextureMatrixNotEnabled2D, + &MeshVisualizerGLTest::setTextureMatrixNotEnabled3D, + &MeshVisualizerGLTest::setTextureLayerNotArray2D, + &MeshVisualizerGLTest::setTextureLayerNotArray3D, + &MeshVisualizerGLTest::bindTextureTransformBufferNotEnabled2D, + &MeshVisualizerGLTest::bindTextureTransformBufferNotEnabled3D, + #endif + #ifndef MAGNUM_TARGET_GLES2 &MeshVisualizerGLTest::setObjectIdNotEnabled2D, &MeshVisualizerGLTest::setObjectIdNotEnabled3D, &MeshVisualizerGLTest::setColorMapNotEnabled2D, @@ -1540,6 +1779,8 @@ void MeshVisualizerGLTest::setUniformUniformBuffersEnabled2D() { MeshVisualizerGL2D shader{MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader}; shader.setTransformationProjectionMatrix({}) + .setTextureMatrix({}) + .setTextureLayer({}) /* setViewportSize() works on both UBOs and classic */ .setObjectId({}) .setColor({}) @@ -1549,6 +1790,8 @@ void MeshVisualizerGLTest::setUniformUniformBuffersEnabled2D() { .setSmoothness({}); CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL2D::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::setTextureMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::setTextureLayer(): the shader was created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL::setObjectId(): the shader was created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL::setColor(): the shader was created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL::setWireframeColor(): the shader was created with uniform buffers enabled\n" @@ -1573,6 +1816,8 @@ void MeshVisualizerGLTest::setUniformUniformBuffersEnabled3D() { MeshVisualizerGL3D shader{MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader}; shader.setProjectionMatrix({}) .setTransformationMatrix({}) + .setTextureMatrix({}) + .setTextureLayer({}) /* setViewportSize() works on both UBOs and classic */ .setObjectId({}) .setColor({}) @@ -1583,6 +1828,8 @@ void MeshVisualizerGLTest::setUniformUniformBuffersEnabled3D() { CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL3D::setProjectionMatrix(): the shader was created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL3D::setTransformationMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::setTextureMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::setTextureLayer(): the shader was created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL::setObjectId(): the shader was created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL::setColor(): the shader was created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL::setWireframeColor(): the shader was created with uniform buffers enabled\n" @@ -1618,6 +1865,8 @@ void MeshVisualizerGLTest::bindBufferUniformBuffersNotEnabled2D() { .bindTransformationProjectionBuffer(buffer, 0, 16) .bindDrawBuffer(buffer) .bindDrawBuffer(buffer, 0, 16) + .bindTextureTransformationBuffer(buffer) + .bindTextureTransformationBuffer(buffer, 0, 16) .bindMaterialBuffer(buffer) .bindMaterialBuffer(buffer, 0, 16) .setDrawOffset(0); @@ -1626,6 +1875,8 @@ void MeshVisualizerGLTest::bindBufferUniformBuffersNotEnabled2D() { "Shaders::MeshVisualizerGL2D::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL2D::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL2D::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n"); @@ -1647,6 +1898,8 @@ void MeshVisualizerGLTest::bindBufferUniformBuffersNotEnabled3D() { .bindTransformationBuffer(buffer, 0, 16) .bindDrawBuffer(buffer) .bindDrawBuffer(buffer, 0, 16) + .bindTextureTransformationBuffer(buffer) + .bindTextureTransformationBuffer(buffer, 0, 16) .bindMaterialBuffer(buffer) .bindMaterialBuffer(buffer, 0, 16) .setDrawOffset(0); @@ -1657,12 +1910,106 @@ void MeshVisualizerGLTest::bindBufferUniformBuffersNotEnabled3D() { "Shaders::MeshVisualizerGL3D::bindTransformationBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL3D::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL3D::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::MeshVisualizerGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n"); } #endif +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGLTest::bindObjectIdTextureInvalid2D() { + auto&& data = BindObjectIdTextureInvalidData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + #ifndef MAGNUM_TARGET_GLES + if((data.flags2D & MeshVisualizerGL2D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + + GL::Texture2D texture; + MeshVisualizerGL2D shader{data.flags2D}; + + std::ostringstream out; + Error redirectError{&out}; + shader.bindObjectIdTexture(texture); + CORRADE_COMPARE(out.str(), data.message); +} + +void MeshVisualizerGLTest::bindObjectIdTextureInvalid3D() { + auto&& data = BindObjectIdTextureInvalidData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + #ifndef MAGNUM_TARGET_GLES + if((data.flags3D & MeshVisualizerGL3D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + + GL::Texture2D texture; + MeshVisualizerGL3D shader{data.flags3D}; + + std::ostringstream out; + Error redirectError{&out}; + shader.bindObjectIdTexture(texture); + CORRADE_COMPARE(out.str(), data.message); +} + +void MeshVisualizerGLTest::bindObjectIdTextureArrayInvalid2D() { + auto&& data = BindObjectIdTextureArrayInvalidData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + GL::Texture2DArray textureArray; + MeshVisualizerGL2D shader{data.flags2D}; + shader.bindObjectIdTexture(textureArray); + + CORRADE_COMPARE(out.str(), data.message); +} + +void MeshVisualizerGLTest::bindObjectIdTextureArrayInvalid3D() { + auto&& data = BindObjectIdTextureArrayInvalidData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + GL::Texture2DArray textureArray; + MeshVisualizerGL3D shader{data.flags3D}; + shader.bindObjectIdTexture(textureArray); + + CORRADE_COMPARE(out.str(), data.message); +} +#endif + void MeshVisualizerGLTest::setWireframeNotEnabled2D() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -1732,6 +2079,108 @@ void MeshVisualizerGLTest::setWireframeNotEnabled3D() { "Shaders::MeshVisualizerGL3D::setSmoothness(): the shader was not created with wireframe or TBN direction enabled\n"); } +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGLTest::setTextureMatrixNotEnabled2D() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MeshVisualizerGL2D shader{MeshVisualizerGL2D::Flag::ObjectIdTexture}; + + std::ostringstream out; + Error redirectError{&out}; + shader.setTextureMatrix({}); + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizerGL::setTextureMatrix(): the shader was not created with texture transformation enabled\n"); +} + +void MeshVisualizerGLTest::setTextureMatrixNotEnabled3D() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MeshVisualizerGL3D shader{MeshVisualizerGL3D::Flag::ObjectIdTexture}; + + std::ostringstream out; + Error redirectError{&out}; + shader.setTextureMatrix({}); + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizerGL::setTextureMatrix(): the shader was not created with texture transformation enabled\n"); +} + +void MeshVisualizerGLTest::setTextureLayerNotArray2D() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MeshVisualizerGL2D shader{MeshVisualizerGL2D::Flag::ObjectIdTexture}; + + std::ostringstream out; + Error redirectError{&out}; + shader.setTextureLayer(37); + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizerGL::setTextureLayer(): the shader was not created with texture arrays enabled\n"); +} + +void MeshVisualizerGLTest::setTextureLayerNotArray3D() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MeshVisualizerGL3D shader{MeshVisualizerGL3D::Flag::ObjectIdTexture}; + + std::ostringstream out; + Error redirectError{&out}; + shader.setTextureLayer(37); + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizerGL::setTextureLayer(): the shader was not created with texture arrays enabled\n"); +} + +void MeshVisualizerGLTest::bindTextureTransformBufferNotEnabled2D() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + GL::Buffer buffer{GL::Buffer::TargetHint::Uniform}; + MeshVisualizerGL2D shader{MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::ObjectIdTexture}; + + std::ostringstream out; + Error redirectError{&out}; + shader.bindTextureTransformationBuffer(buffer) + .bindTextureTransformationBuffer(buffer, 0, 16); + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled\n" + "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled\n"); +} + +void MeshVisualizerGLTest::bindTextureTransformBufferNotEnabled3D() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + GL::Buffer buffer{GL::Buffer::TargetHint::Uniform}; + MeshVisualizerGL2D shader{MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::ObjectIdTexture}; + + std::ostringstream out; + Error redirectError{&out}; + shader.bindTextureTransformationBuffer(buffer) + .bindTextureTransformationBuffer(buffer, 0, 16); + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled\n" + "Shaders::MeshVisualizerGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled\n"); +} +#endif + #ifndef MAGNUM_TARGET_GLES2 void MeshVisualizerGLTest::setObjectIdNotEnabled2D() { #ifdef CORRADE_NO_ASSERT @@ -3003,6 +3452,11 @@ template void MeshVisualizerGLTest::renderObjectV CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_GLES + if((data.flags2D & MeshVisualizerGL2D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + /* Interestingly for PrimitiveIdFromVertexId gl_VertexID in SwiftShader works -- maybe it works only for nonindexed triangle draws? */ if(data.flags2D & MeshVisualizerGL2D::Flag::VertexId && !GL::Context::current().isExtensionSupported()) @@ -3026,7 +3480,10 @@ template void MeshVisualizerGLTest::renderObjectV #endif #endif - Trade::MeshData circleData = Primitives::circle2DSolid(16); + Primitives::Circle2DFlags circleFlags; + if(data.flags2D & MeshVisualizerGL2D::Flag::ObjectIdTexture) + circleFlags |= Primitives::Circle2DFlag::TextureCoordinates; + Trade::MeshData circleData = Primitives::circle2DSolid(16, circleFlags); /* Add the instanced Object ID data even if visualizing just uniform object ID, to test the attribute isn't accidentally accessed always */ @@ -3054,12 +3511,45 @@ template void MeshVisualizerGLTest::renderObjectV GL::Mesh circle = MeshTools::compile(circleData); - MeshVisualizerGL2D shader{data.flags2D|flag}; + MeshVisualizerGL2D::Flags flags = data.flags2D|flag; + if(flag == MeshVisualizerGL2D::Flag::UniformBuffers && (data.flags2D & MeshVisualizerGL2D::Flag::TextureArrays) && !(data.flags2D & MeshVisualizerGL2D::Flag::TextureTransformation)) { + CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); + flags |= MeshVisualizerGL2D::Flag::TextureTransformation; + } + MeshVisualizerGL2D shader{flags}; shader /* Shouldn't assert (nor warn) when wireframe is not enabled */ .setViewportSize({80, 80}) .bindColorMapTexture(_colorMapTexture); + GL::Texture2D texture{NoCreate}; + GL::Texture2DArray textureArray{NoCreate}; + if(data.flags2D >= MeshVisualizerGL2D::Flag::ObjectIdTexture) { + const UnsignedShort imageData[]{1, 0, 0, 7}; + const UnsignedShort imageDataFlipped[]{7, 0, 0, 1}; + ImageView2D image{PixelFormat::R16UI, {2, 2}, data.flip ? imageDataFlipped : imageData}; + + if(data.flags2D & MeshVisualizerGL2D::Flag::TextureArrays) { + textureArray = GL::Texture2DArray{}; + textureArray.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, {image.size(), data.layer + 1}) + .setSubImage(0, {0, 0, data.layer}, image); + shader.bindObjectIdTexture(textureArray); + if(flag != MeshVisualizerGL2D::Flag::UniformBuffers && data.layer != 0) + shader.setTextureLayer(data.layer); /* to verify the default */ + } else { + texture = GL::Texture2D{}; + texture.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, image.size()) + .setSubImage(0, {}, image); + shader.bindObjectIdTexture(texture); + } + } + if(flag == MeshVisualizerGL2D::Flag{}) { /* Remove blue so it's clear the (wireframe) background and mapped ID colors got mixed */ @@ -3083,6 +3573,8 @@ template void MeshVisualizerGLTest::renderObjectV the whole colormap due to the repeat wrapping */ else shader.setColorMapTransformation(0.5f, -1.0f/16.0f); + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); shader.draw(circle); } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { /* See above for comments */ @@ -3094,6 +3586,11 @@ template void MeshVisualizerGLTest::renderObjectV MeshVisualizerDrawUniform2D{} .setObjectId(8) }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setTextureMatrix(data.textureTransformation) + .setLayer(data.layer) + }}; MeshVisualizerMaterialUniform materialUniformData[1]; materialUniformData->setColor(0xffff00_rgbf); if(data.flags2D & MeshVisualizerGL2D::Flag::Wireframe) @@ -3103,6 +3600,10 @@ template void MeshVisualizerGLTest::renderObjectV else materialUniformData->setColorMapTransformation(0.5f, -1.0f/16.0f); GL::Buffer materialUniform{materialUniformData}; + /* Also take into account the case when texture transform needs to be + enabled for texture arrays, so not data.flags but flags */ + if(flags & MeshVisualizerGL2D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); shader .bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) @@ -3143,6 +3644,11 @@ template void MeshVisualizerGLTest::renderObjectV CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_GLES + if((data.flags2D & MeshVisualizerGL2D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + /* Interestingly for PrimitiveIdFromVertexId gl_VertexID in SwiftShader works -- maybe it works only for nonindexed triangle draws? */ if(data.flags3D & MeshVisualizerGL3D::Flag::VertexId && !GL::Context::current().isExtensionSupported()) @@ -3166,7 +3672,10 @@ template void MeshVisualizerGLTest::renderObjectV #endif #endif - Trade::MeshData sphereData = Primitives::uvSphereSolid(4, 8); + Primitives::UVSphereFlags sphereFlags; + if(data.flags2D & MeshVisualizerGL2D::Flag::ObjectIdTexture) + sphereFlags |= Primitives::UVSphereFlag::TextureCoordinates; + Trade::MeshData sphereData = Primitives::uvSphereSolid(4, 8, sphereFlags); /* Add the instanced Object ID data even if visualizing just uniform object ID, to test the attribute isn't accidentally accessed always */ @@ -3189,12 +3698,45 @@ template void MeshVisualizerGLTest::renderObjectV GL::Mesh sphere = MeshTools::compile(sphereData); - MeshVisualizerGL3D shader{data.flags3D|flag}; + MeshVisualizerGL3D::Flags flags = data.flags3D|flag; + if(flag == MeshVisualizerGL3D::Flag::UniformBuffers && (data.flags3D & MeshVisualizerGL3D::Flag::TextureArrays) && !(data.flags3D & MeshVisualizerGL3D::Flag::TextureTransformation)) { + CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); + flags |= MeshVisualizerGL3D::Flag::TextureTransformation; + } + MeshVisualizerGL3D shader{flags}; shader /* Shouldn't assert (nor warn) when wireframe is not enabled */ .setViewportSize({80, 80}) .bindColorMapTexture(_colorMapTexture); + GL::Texture2D texture{NoCreate}; + GL::Texture2DArray textureArray{NoCreate}; + if(data.flags3D >= MeshVisualizerGL3D::Flag::ObjectIdTexture) { + const UnsignedShort imageData[]{1, 0, 0, UnsignedShort(sphere.count()/6 - 1)}; + const UnsignedShort imageDataFlipped[]{UnsignedShort(sphere.count()/6 - 1), 0, 0, 1}; + ImageView2D image{PixelFormat::R16UI, {2, 2}, data.flip ? imageDataFlipped : imageData}; + + if(data.flags2D & MeshVisualizerGL2D::Flag::TextureArrays) { + textureArray = GL::Texture2DArray{}; + textureArray.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, {image.size(), data.layer + 1}) + .setSubImage(0, {0, 0, data.layer}, image); + shader.bindObjectIdTexture(textureArray); + if(flag != MeshVisualizerGL3D::Flag::UniformBuffers && data.layer != 0) + shader.setTextureLayer(data.layer); /* to verify the default */ + } else { + texture = GL::Texture2D{}; + texture.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, image.size()) + .setSubImage(0, {}, image); + shader.bindObjectIdTexture(texture); + } + } + if(flag == MeshVisualizerGL3D::Flag{}) { /* Remove blue so it's clear the wireframe background and mapped ID colors got mixed */ @@ -3221,6 +3763,8 @@ template void MeshVisualizerGLTest::renderObjectV the whole colormap due to the repeat wrapping */ else shader.setColorMapTransformation(0.5f, -1.0f/(sphere.count()/3)); + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); shader.draw(sphere); } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { /* See above for comments */ @@ -3240,6 +3784,11 @@ template void MeshVisualizerGLTest::renderObjectV MeshVisualizerDrawUniform3D{} .setObjectId(sphere.count()/6) }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setTextureMatrix(data.textureTransformation) + .setLayer(data.layer) + }}; MeshVisualizerMaterialUniform materialUniformData[1]; materialUniformData->setColor(0xffff00_rgbf); if(data.flags3D & MeshVisualizerGL3D::Flag::Wireframe) @@ -3249,6 +3798,10 @@ template void MeshVisualizerGLTest::renderObjectV else materialUniformData->setColorMapTransformation(0.5f, -1.0f/(sphere.count()/3)); GL::Buffer materialUniform{materialUniformData}; + /* Also take into account the case when texture transform needs to be + enabled for texture arrays, so not data.flags but flags */ + if(flags & MeshVisualizerGL3D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); shader .bindProjectionBuffer(projectionUniform) .bindTransformationBuffer(transformationUniform) @@ -3544,6 +4097,18 @@ template void MeshVisualizerGLTest::renderInstanc CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_GLES + if((data.flags & MeshVisualizerGL2D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + /* Interestingly enough, on SwiftShader it only fails in case UBOs are + used. Dafuq is this buggy crap?! */ + if(data.flags & MeshVisualizerGL2D::Flag::VertexId && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP("gl_VertexID not supported"); + #endif + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::instanced_arrays::string() << "is not supported."); @@ -3559,7 +4124,7 @@ template void MeshVisualizerGLTest::renderInstanc #endif #endif - Trade::MeshData circleData = Primitives::circle2DSolid(8); + Trade::MeshData circleData = Primitives::circle2DSolid(8, Primitives::Circle2DFlag::TextureCoordinates); /* For a GS-less wireframe we have to deindex the mesh (but first turn the triangle fan into an indexed mesh) */ if(data.flags & MeshVisualizerGL2D::Flag::NoGeometryShader) @@ -3569,19 +4134,27 @@ template void MeshVisualizerGLTest::renderInstanc /* Three circles, each in a different location */ struct { Matrix3 transformation; + Vector3 textureOffsetLayer; UnsignedInt objectId; } instanceData[] { - /* 6 gets added to objectId, wrapping it around to 0, making it - visually close to the multidraw test */ - {Matrix3::translation({-1.25f, -1.25f}), 6}, - {Matrix3::translation({ 1.25f, -1.25f}), 10}, - {Matrix3::translation({ 0.00f, 1.25f}), 14}, + {Matrix3::translation({-1.25f, -1.25f}), + /* 6 gets added to objectId, wrapping it around to 0, making it + visually close to the multidraw test */ + {0.0f, 0.0f, 0.0f}, 6}, + {Matrix3::translation({ 1.25f, -1.25f}), + {1.0f, 0.0f, 1.0f}, 10}, + {Matrix3::translation({ 0.00f, 1.25f}), + #ifndef MAGNUM_TARGET_GLES2 + data.flags & MeshVisualizerGL2D::Flag::TextureArrays ? Vector3{0.0f, 0.0f, 2.0f} : + #endif + Vector3{0.5f, 1.0f, 2.0f}, 14}, }; circle .addVertexBufferInstanced(GL::Buffer{instanceData}, 1, 0, MeshVisualizerGL2D::TransformationMatrix{}, #ifndef MAGNUM_TARGET_GLES2 + MeshVisualizerGL2D::TextureOffsetLayer{}, MeshVisualizerGL2D::ObjectId{} #else 4 @@ -3597,14 +4170,82 @@ template void MeshVisualizerGLTest::renderInstanc shader.bindColorMapTexture(_colorMapTexture); #endif + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2D objectIdTexture{NoCreate}; + GL::Texture2DArray objectIdTextureArray{NoCreate}; + if(data.flags >= MeshVisualizerGL2D::Flag::ObjectIdTexture) { + /* This should match transformation done for the diffuse/normal + texture */ + if(data.flags & MeshVisualizerGL2D::Flag::TextureArrays) { + /* 2 extra slices as a base offset, each slice has half height, + second slice has the data in the right half */ + const UnsignedShort imageData[]{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 5, 0, 0, 0, + 0, 5, 0, 0, + + 0, 0, 3, 0, + 0, 0, 0, 3, + + 1, 0, 0, 0, + 0, 1, 0, 0 + }; + ImageView3D image{PixelFormat::R16UI, {4, 2, 5}, imageData}; + + objectIdTextureArray = GL::Texture2DArray{}; + objectIdTextureArray.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, image.size()) + .setSubImage(0, {}, image); + shader.bindObjectIdTexture(objectIdTextureArray); + } else { + /* First is taken from bottom left, second from bottom right, third + from top center */ + const UnsignedShort imageData[]{ + 5, 0, 3, 0, + 0, 5, 0, 3, + 0, 1, 0, 0, + 0, 0, 1, 0 + }; + ImageView2D image{PixelFormat::R16UI, {4, 4}, imageData}; + + objectIdTexture = GL::Texture2D{}; + objectIdTexture.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, image.size()) + .setSubImage(0, {}, image); + shader.bindObjectIdTexture(objectIdTexture); + } + } + #endif + if(flag == MeshVisualizerGL2D::Flag{}) { shader .setColor(0xffffcc_rgbf) .setTransformationProjectionMatrix( Matrix3::projection({2.1f, 2.1f})* Matrix3::scaling(Vector2{0.4f})); + + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & MeshVisualizerGL2D::Flag::TextureTransformation) + shader.setTextureMatrix(Matrix3::scaling( + /* Slices of the texture array have half the height */ + data.flags & MeshVisualizerGL2D::Flag::TextureArrays ? Vector2::xScale(0.5f) : Vector2{0.5f} + )); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & MeshVisualizerGL2D::Flag::TextureArrays) + shader.setTextureLayer(2); /* base offset */ + #endif + if(data.flags & MeshVisualizerGL2D::Flag::Wireframe) shader.setWireframeColor(0xcc0000_rgbf); + #ifndef MAGNUM_TARGET_GLES2 if(data.flags & MeshVisualizerGL2D::Flag::VertexId) shader.setColorMapTransformation(0.5f/circleData.vertexCount(), 1.0f/circleData.vertexCount()); @@ -3631,6 +4272,16 @@ template void MeshVisualizerGLTest::renderInstanc MeshVisualizerDrawUniform2D{} .setObjectId(6) }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setTextureMatrix(Matrix3::scaling( + #ifndef MAGNUM_TARGET_GLES2 + /* Slices of the texture array have half the height */ + data.flags & MeshVisualizerGL2D::Flag::TextureArrays ? Vector2::xScale(0.5f) : + #endif + Vector2{0.5f})) + .setLayer(2) /* base offset */ + }}; MeshVisualizerMaterialUniform materialUniformData[1]; (*materialUniformData) .setColor(0xffffcc_rgbf) @@ -3640,6 +4291,8 @@ template void MeshVisualizerGLTest::renderInstanc else if(data.flags & MeshVisualizerGL2D::Flag::ObjectId) materialUniformData->setColorMapTransformation(0.5f/12, 1.0f/12); GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, materialUniformData}; + if(data.flags & MeshVisualizerGL2D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) .bindMaterialBuffer(materialUniform) @@ -3702,6 +4355,18 @@ template void MeshVisualizerGLTest::renderInstanc CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_GLES + if((data.flags & MeshVisualizerGL3D::Flag::TextureArrays) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::texture_array::string() << "is not supported."); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + /* Interestingly enough, on SwiftShader it only fails in case UBOs are + used. Dafuq is this buggy crap?! */ + if(data.flags & MeshVisualizerGL3D::Flag::VertexId && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP("gl_VertexID not supported"); + #endif + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::instanced_arrays::string() << "is not supported."); @@ -3717,7 +4382,7 @@ template void MeshVisualizerGLTest::renderInstanc #endif #endif - Trade::MeshData sphereData = Primitives::uvSphereSolid(2, 4, Primitives::UVSphereFlag::Tangents); + Trade::MeshData sphereData = Primitives::uvSphereSolid(2, 4, Primitives::UVSphereFlag::TextureCoordinates|Primitives::UVSphereFlag::Tangents); /* For a GS-less wireframe we have to deindex the mesh */ if(data.flags & MeshVisualizerGL3D::Flag::NoGeometryShader) sphereData = MeshTools::duplicate(sphereData); @@ -3729,6 +4394,7 @@ template void MeshVisualizerGLTest::renderInstanc struct { Matrix4 transformation; Matrix3x3 normal; + Vector3 textureOffsetLayer; UnsignedInt objectId; } instanceData[] { {Matrix4::translation(Math::gather<'z', 'y', 'x'>(Vector3{-1.25f, -1.25f, 0.0f}))*Matrix4::rotationY(-45.0_degf)*Matrix4::rotationX(45.0_degf), @@ -3737,13 +4403,16 @@ template void MeshVisualizerGLTest::renderInstanc (Matrix4::rotationY(-45.0_degf)*Matrix4::rotationX(45.0_degf)).normalMatrix(), /* 6 gets added to the uniform objectId, wrapping it around to 0, making it visually close to the multidraw test */ - 6}, + {0.0f, 0.0f, 0.0f}, 6}, {Matrix4::translation(Math::gather<'z', 'y', 'x'>(Vector3{ 1.25f, -1.25f, 0.0f})), {}, - 10}, + {1.0f, 0.0f, 1.0f}, 10}, {Matrix4::translation(Math::gather<'z', 'y', 'x'>(Vector3{ 0.0f, 1.0f, -1.0f})), {}, - 14} + #ifndef MAGNUM_TARGET_GLES2 + data.flags & MeshVisualizerGL3D::Flag::TextureArrays ? Vector3{0.0f, 0.0f, 2.0f} : + #endif + Vector3{0.5f, 1.0f, 2.0f}, 14} }; sphere @@ -3755,6 +4424,7 @@ template void MeshVisualizerGLTest::renderInstanc sizeof(Matrix3x3), #endif #ifndef MAGNUM_TARGET_GLES2 + MeshVisualizerGL3D::TextureOffsetLayer{}, MeshVisualizerGL3D::ObjectId{} #else 4 @@ -3770,6 +4440,60 @@ template void MeshVisualizerGLTest::renderInstanc shader.bindColorMapTexture(_colorMapTexture); #endif + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2D objectIdTexture{NoCreate}; + GL::Texture2DArray objectIdTextureArray{NoCreate}; + if(data.flags >= MeshVisualizerGL3D::Flag::ObjectIdTexture) { + /* This should match transformation done for the diffuse/normal + texture */ + if(data.flags & MeshVisualizerGL3D::Flag::TextureArrays) { + /* 2 extra slices as a base offset, each slice has half height, + second slice has the data in the right half */ + const UnsignedShort imageData[]{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 5, 0, 0, 0, + 0, 5, 0, 0, + + 0, 0, 3, 0, + 0, 0, 0, 3, + + 1, 0, 0, 0, + 0, 1, 0, 0 + }; + ImageView3D image{PixelFormat::R16UI, {4, 2, 5}, imageData}; + + objectIdTextureArray = GL::Texture2DArray{}; + objectIdTextureArray.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, image.size()) + .setSubImage(0, {}, image); + shader.bindObjectIdTexture(objectIdTextureArray); + } else { + /* First is taken from bottom left, second from bottom right, third + from top center (there I just duplicate the pixel on both + sides) */ + const UnsignedShort imageData[]{ + 5, 0, 3, 0, + 0, 5, 0, 3, + 0, 1, 0, 0, + 0, 0, 1, 0 + }; + ImageView2D image{PixelFormat::R16UI, {4, 4}, imageData}; + + objectIdTexture = GL::Texture2D{}; + objectIdTexture.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, image.size()) + .setSubImage(0, {}, image); + shader.bindObjectIdTexture(objectIdTexture); + } + } + #endif + if(flag == MeshVisualizerGL3D::Flag{}) { shader .setColor(0xffffcc_rgbf) @@ -3779,14 +4503,30 @@ template void MeshVisualizerGLTest::renderInstanc Matrix4::scaling(Vector3{0.4f})) .setProjectionMatrix( Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) if(data.flags & (MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentDirection|MeshVisualizerGL3D::Flag::NormalDirection)) shader .setNormalMatrix(Matrix4::rotationY(90.0_degf).normalMatrix()) .setLineLength(0.25f); #endif + + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & MeshVisualizerGL3D::Flag::TextureTransformation) + shader.setTextureMatrix(Matrix3::scaling( + /* Slices of the texture array have half the height */ + data.flags & MeshVisualizerGL3D::Flag::TextureArrays ? Vector2::xScale(0.5f) : Vector2{0.5f} + )); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & MeshVisualizerGL3D::Flag::TextureArrays) + shader.setTextureLayer(2); /* base offset */ + #endif + if(data.flags & MeshVisualizerGL3D::Flag::Wireframe) shader.setWireframeColor(0xcc0000_rgbf); + #ifndef MAGNUM_TARGET_GLES2 if(data.flags & MeshVisualizerGL3D::Flag::VertexId) shader.setColorMapTransformation(0.5f/sphereData.vertexCount(), 1.0f/sphereData.vertexCount()); @@ -3818,6 +4558,16 @@ template void MeshVisualizerGLTest::renderInstanc .setNormalMatrix(Matrix4::rotationY(90.0_degf).normalMatrix()) .setObjectId(6) }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setTextureMatrix(Matrix3::scaling( + #ifndef MAGNUM_TARGET_GLES2 + /* Slices of the texture array have half the height */ + data.flags & MeshVisualizerGL3D::Flag::TextureArrays ? Vector2::xScale(0.5f) : + #endif + Vector2{0.5f})) + .setLayer(2) /* base offset */ + }}; MeshVisualizerMaterialUniform materialUniformData[1]; (*materialUniformData) .setColor(0xffffcc_rgbf) @@ -3828,6 +4578,8 @@ template void MeshVisualizerGLTest::renderInstanc else if(data.flags & MeshVisualizerGL3D::Flag::ObjectId) materialUniformData->setColorMapTransformation(0.5f/12, 1.0f/12); GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, materialUniformData}; + if(data.flags & MeshVisualizerGL3D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindProjectionBuffer(projectionUniform) .bindTransformationBuffer(transformationUniform) .bindDrawBuffer(drawUniform) @@ -3901,10 +4653,64 @@ void MeshVisualizerGLTest::renderMulti2D() { CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); #endif + MeshVisualizerGL2D shader{MeshVisualizerGL2D::Flag::UniformBuffers|data.flags, data.materialCount, data.drawCount}; + shader.setViewportSize(Vector2{RenderSize}); + if(data.flags & (MeshVisualizerGL2D::Flag::VertexId|MeshVisualizerGL2D::Flag::ObjectId)) + shader.bindColorMapTexture(_colorMapTexture); + + GL::Texture2D objectIdTexture{NoCreate}; + GL::Texture2DArray objectIdTextureArray{NoCreate}; + if(data.flags >= MeshVisualizerGL2D::Flag::ObjectIdTexture) { + /* This should match transformation done for the diffuse/normal + texture */ + if(data.flags & MeshVisualizerGL2D::Flag::TextureArrays) { + /* Each slice has half height, second slice has the data in the + right half */ + const UnsignedShort imageData[]{ + 5, 0, 0, 0, + 0, 5, 0, 0, + + 0, 0, 3, 0, + 0, 0, 0, 3, + + 1, 0, 0, 0, + 0, 1, 0, 0 + }; + ImageView3D image{PixelFormat::R16UI, {4, 2, 3}, imageData}; + + objectIdTextureArray = GL::Texture2DArray{}; + objectIdTextureArray.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, image.size()) + .setSubImage(0, {}, image); + shader.bindObjectIdTexture(objectIdTextureArray); + } else { + /* First is taken from bottom left, second from bottom right, third + from top center (there I just duplicate the pixel on both + sides) */ + const UnsignedShort imageData[]{ + 5, 0, 3, 0, + 0, 5, 0, 3, + 0, 1, 0, 0, + 0, 0, 1, 0 + }; + ImageView2D image{PixelFormat::R16UI, {4, 4}, imageData}; + + objectIdTexture = GL::Texture2D{}; + objectIdTexture.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, image.size()) + .setSubImage(0, {}, image); + shader.bindObjectIdTexture(objectIdTexture); + } + } + /* Circle is a fan, plane is a strip, make it indexed first */ - Trade::MeshData circleData = MeshTools::generateIndices(Primitives::circle2DSolid(8)); - Trade::MeshData squareData = MeshTools::generateIndices(Primitives::squareSolid()); - Trade::MeshData triangleData = MeshTools::generateIndices(Primitives::circle2DSolid(3)); + Trade::MeshData circleData = MeshTools::generateIndices(Primitives::circle2DSolid(8, Primitives::Circle2DFlag::TextureCoordinates)); + Trade::MeshData squareData = MeshTools::generateIndices(Primitives::squareSolid(Primitives::SquareFlag::TextureCoordinates)); + Trade::MeshData triangleData = MeshTools::generateIndices(Primitives::circle2DSolid(3, Primitives::Circle2DFlag::TextureCoordinates)); /* For instanced object ID rendering we have to add the object ID attribute. Use the same numbers for all meshes, it'll get differentiated by the per-draw object ID. */ @@ -3994,6 +4800,33 @@ void MeshVisualizerGLTest::renderMulti2D() { ); GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData}; + Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; + textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + data.flags & MeshVisualizerGL2D::Flag::TextureArrays ? + Matrix3::scaling(Vector2::xScale(0.5f))* + Matrix3::translation({0.0f, 0.0f}) : + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.0f, 0.0f})) + .setLayer(0); /* ignored if not array */ + textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + data.flags & MeshVisualizerGL2D::Flag::TextureArrays ? + Matrix3::scaling(Vector2::xScale(0.5f))* + Matrix3::translation({1.0f, 0.0f}) : + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({1.0f, 0.0f})) + .setLayer(1); /* ignored if not array */ + textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + data.flags & MeshVisualizerGL2D::Flag::TextureArrays ? + Matrix3::scaling(Vector2::xScale(0.5f))* + Matrix3::translation({0.0f, 0.0f}) : + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.5f, 1.0f})) + .setLayer(2); /* ignored if not array */ + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; + Containers::Array drawData{2*data.uniformIncrement + 1}; /* Material offsets are zero if we have single draw, as those are done with UBO offset bindings instead. */ @@ -4008,11 +4841,6 @@ void MeshVisualizerGLTest::renderMulti2D() { .setObjectId(8); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - MeshVisualizerGL2D shader{MeshVisualizerGL2D::Flag::UniformBuffers|data.flags, data.materialCount, data.drawCount}; - shader.setViewportSize(Vector2{RenderSize}); - if(data.flags & (MeshVisualizerGL2D::Flag::VertexId|MeshVisualizerGL2D::Flag::ObjectId)) - shader.bindColorMapTexture(_colorMapTexture); - /* Just one draw, rebinding UBOs each time */ if(data.drawCount == 1) { shader.bindMaterialBuffer(materialUniform, @@ -4024,6 +4852,10 @@ void MeshVisualizerGLTest::renderMulti2D() { shader.bindDrawBuffer(drawUniform, 0*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform2D), sizeof(MeshVisualizerDrawUniform2D)); + if(data.flags & MeshVisualizerGL2D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 0*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); shader.draw(circle); shader.bindMaterialBuffer(materialUniform, @@ -4035,6 +4867,10 @@ void MeshVisualizerGLTest::renderMulti2D() { shader.bindDrawBuffer(drawUniform, 1*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform2D), sizeof(MeshVisualizerDrawUniform2D)); + if(data.flags & MeshVisualizerGL2D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 1*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); shader.draw(square); shader.bindMaterialBuffer(materialUniform, @@ -4046,6 +4882,10 @@ void MeshVisualizerGLTest::renderMulti2D() { shader.bindDrawBuffer(drawUniform, 2*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform2D), sizeof(MeshVisualizerDrawUniform2D)); + if(data.flags & MeshVisualizerGL2D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 2*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); shader.draw(triangle); /* Otherwise using the draw offset / multidraw */ @@ -4053,6 +4893,8 @@ void MeshVisualizerGLTest::renderMulti2D() { shader.bindMaterialBuffer(materialUniform) .bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform); + if(data.flags & MeshVisualizerGL2D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); if(data.flags >= MeshVisualizerGL2D::Flag::MultiDraw) shader.draw({circle, square, triangle}); @@ -4131,12 +4973,66 @@ void MeshVisualizerGLTest::renderMulti3D() { CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); #endif + MeshVisualizerGL3D shader{MeshVisualizerGL3D::Flag::UniformBuffers|data.flags, data.materialCount, data.drawCount}; + shader.setViewportSize(Vector2{RenderSize}); + if(data.flags & (MeshVisualizerGL3D::Flag::VertexId|MeshVisualizerGL3D::Flag::ObjectId)) + shader.bindColorMapTexture(_colorMapTexture); + + GL::Texture2D objectIdTexture{NoCreate}; + GL::Texture2DArray objectIdTextureArray{NoCreate}; + if(data.flags >= MeshVisualizerGL3D::Flag::ObjectIdTexture) { + /* This should match transformation done for the diffuse/normal + texture */ + if(data.flags & MeshVisualizerGL3D::Flag::TextureArrays) { + /* Each slice has half height, second slice has the data in the + right half */ + const UnsignedShort imageData[]{ + 5, 0, 0, 0, + 0, 5, 0, 0, + + 0, 0, 3, 0, + 0, 0, 0, 3, + + 1, 0, 0, 0, + 0, 1, 0, 0 + }; + ImageView3D image{PixelFormat::R16UI, {4, 2, 3}, imageData}; + + objectIdTextureArray = GL::Texture2DArray{}; + objectIdTextureArray.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, image.size()) + .setSubImage(0, {}, image); + shader.bindObjectIdTexture(objectIdTextureArray); + } else { + /* First is taken from bottom left, second from bottom right, third + from top center (there I just duplicate the pixel on both + sides) */ + const UnsignedShort imageData[]{ + 5, 0, 3, 0, + 0, 5, 0, 3, + 0, 1, 0, 0, + 0, 0, 1, 0 + }; + ImageView2D image{PixelFormat::R16UI, {4, 4}, imageData}; + + objectIdTexture = GL::Texture2D{}; + objectIdTexture.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, image.size()) + .setSubImage(0, {}, image); + shader.bindObjectIdTexture(objectIdTexture); + } + } + /* We don't visualize tangents for the sphere, but concatenate() will ignore the tangents of others if the first mesh doesn't have them */ - Trade::MeshData sphereData = Primitives::uvSphereSolid(2, 4, Primitives::UVSphereFlag::Tangents); + Trade::MeshData sphereData = Primitives::uvSphereSolid(2, 4, Primitives::UVSphereFlag::Tangents|Primitives::UVSphereFlag::TextureCoordinates); /* Plane is a strip, make it indexed first */ - Trade::MeshData planeData = MeshTools::generateIndices(Primitives::planeSolid(Primitives::PlaneFlag::Tangents)); - Trade::MeshData coneData = Primitives::coneSolid(1, 8, 1.0f, Primitives::ConeFlag::Tangents); + Trade::MeshData planeData = MeshTools::generateIndices(Primitives::planeSolid(Primitives::PlaneFlag::Tangents|Primitives::PlaneFlag::TextureCoordinates)); + Trade::MeshData coneData = Primitives::coneSolid(1, 8, 1.0f, Primitives::ConeFlag::Tangents|Primitives::ConeFlag::TextureCoordinates); /* For instanced object ID rendering we have to add the object ID attribute. Use the same numbers for all meshes, it'll get differentiated by the per-draw object ID. */ @@ -4179,6 +5075,7 @@ void MeshVisualizerGLTest::renderMulti3D() { Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) ) }}; + shader.bindProjectionBuffer(projectionUniform); /* Some drivers have uniform offset alignment as high as 256, which means the subsequent sets of uniforms have to be aligned to a multiply of it. @@ -4234,6 +5131,33 @@ void MeshVisualizerGLTest::renderMulti3D() { ); GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, transformationData}; + Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; + textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + data.flags & MeshVisualizerGL3D::Flag::TextureArrays ? + Matrix3::scaling(Vector2::xScale(0.5f))* + Matrix3::translation({0.0f, 0.0f}) : + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.0f, 0.0f})) + .setLayer(0); /* ignored if not array */ + textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + data.flags & MeshVisualizerGL3D::Flag::TextureArrays ? + Matrix3::scaling(Vector2::xScale(0.5f))* + Matrix3::translation({1.0f, 0.0f}) : + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({1.0f, 0.0f})) + .setLayer(1); /* ignored if not array */ + textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + data.flags & MeshVisualizerGL3D::Flag::TextureArrays ? + Matrix3::scaling(Vector2::xScale(0.5f))* + Matrix3::translation({0.0f, 0.0f}) : + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.5f, 1.0f})) + .setLayer(2); /* ignored if not array */ + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; + Containers::Array drawData{2*data.uniformIncrement + 1}; /* Material offsets are zero if we have single draw, as those are done with UBO offset bindings instead. Also no need to supply a normal matrix. */ @@ -4248,12 +5172,6 @@ void MeshVisualizerGLTest::renderMulti3D() { .setObjectId(20); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - MeshVisualizerGL3D shader{MeshVisualizerGL3D::Flag::UniformBuffers|data.flags, data.materialCount, data.drawCount}; - shader.setViewportSize(Vector2{RenderSize}) - .bindProjectionBuffer(projectionUniform); - if(data.flags & (MeshVisualizerGL3D::Flag::VertexId|MeshVisualizerGL3D::Flag::ObjectId)) - shader.bindColorMapTexture(_colorMapTexture); - /* Just one draw, rebinding UBOs each time */ if(data.drawCount == 1) { shader.bindMaterialBuffer(materialUniform, @@ -4265,6 +5183,10 @@ void MeshVisualizerGLTest::renderMulti3D() { shader.bindDrawBuffer(drawUniform, 0*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform3D), sizeof(MeshVisualizerDrawUniform3D)); + if(data.flags & MeshVisualizerGL3D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 0*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); shader.draw(sphere); shader.bindMaterialBuffer(materialUniform, @@ -4276,6 +5198,10 @@ void MeshVisualizerGLTest::renderMulti3D() { shader.bindDrawBuffer(drawUniform, 1*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform3D), sizeof(MeshVisualizerDrawUniform3D)); + if(data.flags & MeshVisualizerGL3D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 1*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); shader.draw(plane); shader.bindMaterialBuffer(materialUniform, @@ -4287,6 +5213,10 @@ void MeshVisualizerGLTest::renderMulti3D() { shader.bindDrawBuffer(drawUniform, 2*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform3D), sizeof(MeshVisualizerDrawUniform3D)); + if(data.flags & MeshVisualizerGL3D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 2*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); shader.draw(cone); /* Otherwise using the draw offset / multidraw */ @@ -4294,6 +5224,8 @@ void MeshVisualizerGLTest::renderMulti3D() { shader.bindMaterialBuffer(materialUniform) .bindTransformationBuffer(transformationUniform) .bindDrawBuffer(drawUniform); + if(data.flags & MeshVisualizerGL3D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); if(data.flags >= MeshVisualizerGL3D::Flag::MultiDraw) shader.draw({sphere, plane, cone}); diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp index f13e7a11b..3e329227d 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp @@ -117,15 +117,15 @@ void MeshVisualizerGL_Test::vertexIndexSameAsObjectId() { void MeshVisualizerGL_Test::debugFlag2D() { std::ostringstream out; - Debug{&out} << MeshVisualizerGL2D::Flag::Wireframe << MeshVisualizerGL2D::Flag(0xf0); - CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL2D::Flag::Wireframe Shaders::MeshVisualizerGL2D::Flag(0xf0)\n"); + Debug{&out} << MeshVisualizerGL2D::Flag::Wireframe << MeshVisualizerGL2D::Flag(0xbad00000); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL2D::Flag::Wireframe Shaders::MeshVisualizerGL2D::Flag(0xbad00000)\n"); } void MeshVisualizerGL_Test::debugFlag3D() { std::ostringstream out; - Debug{&out} << MeshVisualizerGL3D::Flag::Wireframe << MeshVisualizerGL3D::Flag(0xf0); - CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL3D::Flag::Wireframe Shaders::MeshVisualizerGL3D::Flag(0xf0)\n"); + Debug{&out} << MeshVisualizerGL3D::Flag::Wireframe << MeshVisualizerGL3D::Flag(0xbad00000); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL3D::Flag::Wireframe Shaders::MeshVisualizerGL3D::Flag(0xbad00000)\n"); } void MeshVisualizerGL_Test::debugFlags2D() { @@ -152,12 +152,20 @@ void MeshVisualizerGL_Test::debugFlags3D() { #ifndef MAGNUM_TARGET_GLES2 void MeshVisualizerGL_Test::debugFlagsSupersets2D() { - /* InstancedObjectId is a superset of ObjectId so only one should be - printed */ + /* InstancedObjectId and ObjectIdTexture are a superset of ObjectId so only + one should be printed, but if there are both then both should be */ { std::ostringstream out; - Debug{&out} << (MeshVisualizerGL2D::Flag::InstancedObjectId|MeshVisualizerGL2D::Flag::ObjectId); + Debug{&out} << (MeshVisualizerGL2D::Flag::ObjectId|MeshVisualizerGL2D::Flag::InstancedObjectId); CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL2D::Flag::InstancedObjectId\n"); + } { + std::ostringstream out; + Debug{&out} << (MeshVisualizerGL2D::Flag::ObjectId|MeshVisualizerGL2D::Flag::ObjectIdTexture); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL2D::Flag::ObjectIdTexture\n"); + } { + std::ostringstream out; + Debug{&out} << (MeshVisualizerGL2D::Flag::ObjectId|MeshVisualizerGL2D::Flag::InstancedObjectId|MeshVisualizerGL2D::Flag::ObjectIdTexture); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL2D::Flag::InstancedObjectId|Shaders::MeshVisualizerGL2D::Flag::ObjectIdTexture\n"); } /* MultiDraw is a superset of UniformBuffers so only one should be printed */ @@ -169,12 +177,20 @@ void MeshVisualizerGL_Test::debugFlagsSupersets2D() { } void MeshVisualizerGL_Test::debugFlagsSupersets3D() { - /* InstancedObjectId is a superset of ObjectId so only one should be - printed */ + /* InstancedObjectId and ObjectIdTexture are a superset of ObjectId so only + one should be printed, but if there are both then both should be */ { std::ostringstream out; - Debug{&out} << (MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::ObjectId); + Debug{&out} << (MeshVisualizerGL3D::Flag::ObjectId|MeshVisualizerGL3D::Flag::InstancedObjectId); CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL3D::Flag::InstancedObjectId\n"); + } { + std::ostringstream out; + Debug{&out} << (MeshVisualizerGL3D::Flag::ObjectId|MeshVisualizerGL3D::Flag::ObjectIdTexture); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL3D::Flag::ObjectIdTexture\n"); + } { + std::ostringstream out; + Debug{&out} << (MeshVisualizerGL3D::Flag::ObjectId|MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::ObjectIdTexture); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL3D::Flag::InstancedObjectId|Shaders::MeshVisualizerGL3D::Flag::ObjectIdTexture\n"); } /* MultiDraw is a superset of UniformBuffers so only one should be printed */ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/instanced-instancedobjectidtexture2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/instanced-instancedobjectidtexture2D.tga new file mode 100644 index 000000000..c3cc543d2 Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/instanced-instancedobjectidtexture2D.tga differ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/instanced-instancedobjectidtexture3D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/instanced-instancedobjectidtexture3D.tga new file mode 100644 index 000000000..9883f1b2c Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/instanced-instancedobjectidtexture3D.tga differ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/instanced-objectidtexture2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/instanced-objectidtexture2D.tga new file mode 100644 index 000000000..6aab4b545 Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/instanced-objectidtexture2D.tga differ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/instanced-objectidtexture3D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/instanced-objectidtexture3D.tga new file mode 100644 index 000000000..607f51b8e Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/instanced-objectidtexture3D.tga differ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-objectidtexture2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-objectidtexture2D.tga new file mode 100644 index 000000000..0369da261 Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-objectidtexture2D.tga differ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-objectidtexture3D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-objectidtexture3D.tga new file mode 100644 index 000000000..da59d3b51 Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-objectidtexture3D.tga differ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectidtexture2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectidtexture2D.tga new file mode 100644 index 000000000..73334a18e Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectidtexture2D.tga differ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectidtexture3D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectidtexture3D.tga new file mode 100644 index 000000000..2d7cd0b17 Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/objectidtexture3D.tga differ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-objectidtexture2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-objectidtexture2D.tga new file mode 100644 index 000000000..14fb642c2 Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-objectidtexture2D.tga differ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-objectidtexture3D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-objectidtexture3D.tga new file mode 100644 index 000000000..7768ff376 Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-objectidtexture3D.tga differ