diff --git a/src/Magnum/Shaders/CMakeLists.txt b/src/Magnum/Shaders/CMakeLists.txt index 740522eba..565fdd7cf 100644 --- a/src/Magnum/Shaders/CMakeLists.txt +++ b/src/Magnum/Shaders/CMakeLists.txt @@ -39,6 +39,7 @@ set(MagnumShaders_SRCS set(MagnumShaders_GracefulAssert_SRCS DistanceFieldVectorGL.cpp FlatGL.cpp + LineGL.cpp MeshVisualizerGL.cpp PhongGL.cpp VectorGL.cpp @@ -53,6 +54,7 @@ set(MagnumShaders_HEADERS FlatGL.h Generic.h GenericGL.h + LineGL.h MeshVisualizer.h MeshVisualizerGL.h Phong.h diff --git a/src/Magnum/Shaders/Line.frag b/src/Magnum/Shaders/Line.frag new file mode 100644 index 000000000..2ab435f05 --- /dev/null +++ b/src/Magnum/Shaders/Line.frag @@ -0,0 +1,202 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#if defined(OBJECT_ID) && !defined(GL_ES) && !defined(NEW_GLSL) +#extension GL_EXT_gpu_shader4: require +#endif + +#ifndef NEW_GLSL +#define fragmentColor gl_FragColor +#define in varying +#endif + +#ifndef RUNTIME_CONST +#define const +#endif + +/* Uniforms */ + +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 2) +#endif +uniform mediump float width + #ifndef GL_ES + = 1.0 + #endif + ; + +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 3) +#endif +uniform mediump float smoothness + #ifndef GL_ES + = 0.0 + #endif + ; + +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 4) +#endif +uniform lowp vec4 backgroundColor + #ifndef GL_ES + = vec4(0.0) + #endif + ; + +#ifndef UNIFORM_BUFFERS +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 5) +#endif +uniform lowp vec4 color + #ifndef GL_ES + = vec4(1.0) + #endif + ; + +#ifdef OBJECT_ID +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 6) +#endif +/* mediump is just 2^10, which might not be enough, this is 2^16 */ +uniform highp uint objectId; /* defaults to zero */ +#endif + +/* Uniform buffers */ + +#else +#ifndef MULTI_DRAW +#if DRAW_COUNT > 1 +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 0) +#endif +uniform highp uint drawOffset + #ifndef GL_ES + = 0u + #endif + ; +#else +#define drawOffset 0u +#endif +#define drawId drawOffset +#endif + +struct DrawUniform { + highp uvec4 materialIdReservedObjectIdReservedReserved; + #define draw_materialIdReserved materialIdReservedObjectIdReservedReserved.x + #define draw_objectId materialIdReservedObjectIdReservedReserved.y +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 2 + #endif +) uniform Draw { + DrawUniform draws[DRAW_COUNT]; +}; + +struct MaterialUniform { + lowp vec4 color; +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 4 + #endif +) uniform Material { + MaterialUniform materials[MATERIAL_COUNT]; +}; +#endif + +/* Inputs */ + +in highp vec2 centerDistanceSigned; +in highp float halfSegmentLength; + +#ifdef VERTEX_COLOR +in lowp vec4 interpolatedVertexColor; +#endif + +#ifdef INSTANCED_OBJECT_ID +flat in highp uint interpolatedInstanceObjectId; +#endif + +/* Outputs */ + +#ifdef NEW_GLSL +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = COLOR_OUTPUT_ATTRIBUTE_LOCATION) +#endif +out lowp vec4 fragmentColor; +#endif +#ifdef OBJECT_ID +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = OBJECT_ID_OUTPUT_ATTRIBUTE_LOCATION) +#endif +/* mediump is just 2^10, which might not be enough, this is 2^16 */ +out highp uint fragmentObjectId; +#endif + +#ifdef MULTI_DRAW +flat in highp uint drawId; +#endif + +void main() { + #ifdef UNIFORM_BUFFERS + #ifdef OBJECT_ID + highp const uint objectId = draws[drawId].draw_objectId; + #endif + #if MATERIAL_COUNT > 1 + mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; + #else + #define materialId 0u + #endif + lowp const vec4 color = materials[materialId].color; + #endif + + /* Pixels with `abs(centerDistanceSigned) <= [d+w,w]` are foreground, + pixels with `abs(centerDistanceSigned) > [d+w+s,w+s]` are background, + smoothstep in between */ + highp const vec2 edge = vec2(halfSegmentLength+0.5*width, width*0.5); + lowp const vec2 factor = smoothstep( // TODO CSE + edge - vec2(smoothness), + edge + vec2(smoothness), abs(centerDistanceSigned)); +// lowp const vec2 factor = step(edge, abs(centerDistanceSigned)); + +// fragmentColor.ba = vec2(0.0); + fragmentColor = mix( + #ifdef VERTEX_COLOR + interpolatedVertexColor* + #endif + color, backgroundColor, max(factor.x, factor.y)); // TODO huh*/ + + #ifdef OBJECT_ID + // TODO how to handle smoothness here? + fragmentObjectId = all(lessThan(abs(centerDistanceSigned), edge)) ? + #ifdef INSTANCED_OBJECT_ID + interpolatedInstanceObjectId + + #endif + objectId : 0u; + #endif +} diff --git a/src/Magnum/Shaders/Line.h b/src/Magnum/Shaders/Line.h new file mode 100644 index 000000000..c21fbc892 --- /dev/null +++ b/src/Magnum/Shaders/Line.h @@ -0,0 +1,95 @@ +#ifndef Magnum_Shaders_Line_h +#define Magnum_Shaders_Line_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Struct @ref Magnum::Shaders::LineDrawUniform, @ref Magnum::Shaders::LineMaterialUniform + */ + +#include "Magnum/Magnum.h" +#include "Magnum/Math/Color.h" + +namespace Magnum { namespace Shaders { + +struct LineDrawUniform { + /** @brief Construct with default parameters */ + constexpr explicit LineDrawUniform(DefaultInitT = DefaultInit) noexcept: materialId{0}, objectId{0} {} + + /** @brief Construct without initializing the contents */ + explicit LineDrawUniform(NoInitT) noexcept {} + + LineDrawUniform& setMaterialId(UnsignedInt id) { + materialId = id; + return *this; + } + + LineDrawUniform& setObjectId(UnsignedInt id) { + objectId = id; + return *this; + } + + /* This field is an UnsignedInt in the shader and materialId is extracted + as (value & 0xffff), so the order has to be different on BE */ + #ifndef CORRADE_TARGET_BIG_ENDIAN + alignas(4) UnsignedShort materialId; + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + UnsignedShort:16; /* reserved for skinOffset */ + #endif + #else + alignas(4) UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort materialId; + #endif + + UnsignedInt objectId; + + /* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + Int:32; + Int:32; + #endif +}; + +struct LineMaterialUniform { + /** @brief Construct with default parameters */ + constexpr explicit LineMaterialUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f} {} + + /** @brief Construct without initializing the contents */ + explicit LineMaterialUniform(NoInitT) noexcept: color{NoInit} {} + + LineMaterialUniform& setColor(const Color4& color) { + this->color = color; + return *this; + } + + Color4 color; +}; + +}} + +#endif diff --git a/src/Magnum/Shaders/Line.vert b/src/Magnum/Shaders/Line.vert new file mode 100644 index 000000000..6aeac513e --- /dev/null +++ b/src/Magnum/Shaders/Line.vert @@ -0,0 +1,402 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#if defined(INSTANCED_OBJECT_ID) && !defined(GL_ES) && !defined(NEW_GLSL) +#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 +#else /* covers WebGL as well */ +#extension GL_ANGLE_multi_draw: require +#endif +#endif + +#ifndef NEW_GLSL +#define in attribute +#define out varying +#endif + +#ifndef RUNTIME_CONST +#define const +#endif + +/* Uniforms */ + +/* This one is for both classic and UBOs, as it's usually set globally instead + of changing per-draw */ +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 0) +#endif +uniform lowp vec2 viewportSize; /* defaults to zero */ + +#ifndef UNIFORM_BUFFERS +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 1) +#endif +#ifdef TWO_DIMENSIONS +uniform highp mat3 transformationProjectionMatrix + #ifndef GL_ES + = mat3(1.0) + #endif + ; +#elif defined(THREE_DIMENSIONS) +uniform highp mat4 transformationProjectionMatrix + #ifndef GL_ES + = mat4(1.0) + #endif + ; +#else +#error +#endif + +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 2) +#endif +uniform mediump float width + #ifndef GL_ES + = 1.0 + #endif + ; + +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 3) +#endif +uniform mediump float smoothness + #ifndef GL_ES + = 0.0 + #endif + ; + +/* Uniform buffers */ + +#else +#if DRAW_COUNT > 1 +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 1) +#endif +uniform highp uint drawOffset + #ifndef GL_ES + = 0u + #endif + ; +#else +#define drawOffset 0u +#endif + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 1 + #endif +) uniform TransformationProjection { + highp + #ifdef TWO_DIMENSIONS + /* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for + details */ + mat3x4 + #elif defined(THREE_DIMENSIONS) + mat4 + #else + #error + #endif + transformationProjectionMatrices[DRAW_COUNT]; +}; +#endif + +/* Inputs */ + +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = POSITION_ATTRIBUTE_LOCATION) +#endif +#ifdef TWO_DIMENSIONS +in highp vec2 position; +#elif defined(THREE_DIMENSIONS) +in highp vec4 position; +#else +#error +#endif + +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = LINE_DIRECTION_ATTRIBUTE_LOCATION) +#endif +#ifdef TWO_DIMENSIONS +in highp vec2 direction_; +#elif defined(THREE_DIMENSIONS) +in highp vec3 direction_; +#else +#error +#endif + +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = LINE_NEIGHBOR_DIRECTION_ATTRIBUTE_LOCATION) +#endif +#ifdef TWO_DIMENSIONS +in highp vec2 neighborDirection_; +#elif defined(THREE_DIMENSIONS) +in highp vec3 neighborDirection_; +#else +#error +#endif + +#ifdef VERTEX_COLOR +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = COLOR_ATTRIBUTE_LOCATION) +#endif +in lowp vec4 vertexColor; +#endif + +#ifdef INSTANCED_OBJECT_ID +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = OBJECT_ID_ATTRIBUTE_LOCATION) +#endif +in highp uint instanceObjectId; +#endif + +#ifdef INSTANCED_TRANSFORMATION +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = TRANSFORMATION_MATRIX_ATTRIBUTE_LOCATION) +#endif +#ifdef TWO_DIMENSIONS +in highp mat3 instancedTransformationMatrix; +#elif defined(THREE_DIMENSIONS) +in highp mat4 instancedTransformationMatrix; +#else +#error +#endif +#endif + +/* Outputs */ + +out highp vec2 centerDistanceSigned; +out highp float halfSegmentLength; + +#ifdef VERTEX_COLOR +out lowp vec4 interpolatedVertexColor; +#endif + +#ifdef INSTANCED_OBJECT_ID +flat out highp uint interpolatedInstanceObjectId; +#endif + +#ifdef MULTI_DRAW +flat out highp uint drawId; +#endif + +/* Same as Math::Vector2::perpendicular() */ +vec2 perpendicular(vec2 a) { + return vec2(-a.y, a.x); +} + +void main() { + #ifdef UNIFORM_BUFFERS + #ifdef MULTI_DRAW + drawId = drawOffset + uint( + #ifndef GL_ES + gl_DrawIDARB /* Using GL_ARB_shader_draw_parameters, not GLSL 4.6 */ + #else + gl_DrawID + #endif + ); + #else + #define drawId drawOffset + #endif + + #ifdef TWO_DIMENSIONS + highp const mat3 transformationProjectionMatrix = mat3(transformationProjectionMatrices[drawId]); + #elif defined(THREE_DIMENSIONS) + highp const mat4 transformationProjectionMatrix = transformationProjectionMatrices[drawId]; + #else + #error + #endif + #endif + + #ifdef TWO_DIMENSIONS + const vec2 lineCenterPosition = (transformationProjectionMatrix* + #ifdef INSTANCED_TRANSFORMATION + instancedTransformationMatrix* + #endif + vec3(position, 1.0)).xy; + + // TODO take prev/next positions, this makes no sense + const vec2 direction = (transformationProjectionMatrix* + #ifdef INSTANCED_TRANSFORMATION + instancedTransformationMatrix* + #endif + vec3(position + direction_, 1.0)).xy - lineCenterPosition; + + const vec2 neighborDirection = (transformationProjectionMatrix* + #ifdef INSTANCED_TRANSFORMATION + instancedTransformationMatrix* + #endif + vec3(position + neighborDirection_, 1.0)).xy - lineCenterPosition; + + const float directionLength = length(direction); + // TODO since this is used just for AA, set to a special value in case of + // a line joint that shouldn't be AAd? + halfSegmentLength = length(direction*0.5*viewportSize/2.0); + // TODO zero-sized lines better? + const vec2 directionNormalized = directionLength == 0.0 ? vec2(1.0, 0.0) : direction/directionLength; + + /* Line width includes also twice the smoothness radius, some extra padding + on top, and is rounded to whole pixels. So for the edge distance we need + half of it. */ + // TODO ref the paper here; actually just drop all that, smoothstep FTW + const float edgeDistance = ceil(width + 2.0*smoothness)*0.5; + + /* Copied from MeshTools::generateLines() internals for completenes. The + position is always either `A` or `B` for all four quad corners, the `d` + comes in the direction attribute, `pd`/`nd` in neighborDirection and + the vertex order, which is (4n +) 0/1/2/3, in gl_VertexID. + + 0-d->-------2-d-> + | / \ + A---------B nd + | / \ v + 1-d->---3-d-> \ + \ \ . + nd . . + v C . */ + // TODO redo all this here + /* The perpendicular direction is rotating 90° counterclockwise. Which + means for points 1 and 3 (i.e., gl_VertexID not divisible by 2) we need + to negate it to point the other way. */ + // TODO zero-sized lines do what? + const float edgeSign = (gl_VertexID & 1) == 0 ? 1.0 : -1.0; + const float capSign = (gl_VertexID & 2) == 0 ? -1.0 : 1.0; + const float edgeDistanceSigned = edgeDistance*edgeSign; +// vec2 edgeDirection = perpendicular(directionNormalized)*edgeDistanceSigned*2.0/viewportSize; + +// float centerDirection + vec2 edgeDirection; + + /* Distance to center, passed to the fragment shader. It's chosen in a way + that interpolates to a zero vector in the quad center, and the area + where `abs(centerDirection) <= [d+w,w]` is inside the line. Inside the + line strip (where there's no line caps, shown on the left) the X value + is always 0, resulting in no antialiasing done on the beginning/end edge + in order to join tightly with the neigboring segments. + + 0------------------2 0-----------------------------2 + [0,+w]-------------[0,+w] [-d-w,+w]------------------[+d+w,+w] + | | | | | | + [0,0] [0,0] [0,0] [-d-w,0] [0,0] [+d+w,0] + | | | | | | + [0,-w]-------------[0,-w] [-d-w,-w]------------------[+d+w,-w] + 1------------------3 1-----------------------------3 */ + // TODO handle caps + centerDistanceSigned.y = edgeDistanceSigned; + + /* If the neighbor direction is a NaN, it means we're at the line cap -- + the quad gets extended beyond A or B in the direction of the segment, + as shown with the points 0 and 1: + + 0--+--- + | | + | A--d->-- + | | + 1--+--- + + For points 0 and 1 it'll be in the negative direction `d`, for points 2 + and 3 in positive `d`. */ + if(all(isnan(neighborDirection))) { + edgeDirection = + (directionNormalized*capSign + + perpendicular(directionNormalized)*edgeSign)*edgeDistance*2.0/viewportSize + ; + centerDistanceSigned.x = (edgeDistance + halfSegmentLength)*capSign; + + /* Otherwise we need to create a tight joint with the neighboring line + segment, as shown with the points 2 and 3. Given normalized direction + `d` and neighbor direction `nd`, `normalized(d + nd)` is the "average" + direction of the two and `perpendicular(normalized(d + nd))` gives us + the direction from B to 2: + + --------+----2 + | / alpha/2 + w | / j + |/ + --d->-B + alpha/2 / \ + / nd + / v + ----3 + + With `alpha` being the angle between `d` and `nd`, `alpha/2` appears in + two right triangles and the following holds, `w` being the edge distance + from above, and `j` being the length that's needed to scale `perpendicular(normalized(d + nd))` to get point 2: + + |d + nd| w 2 w + sin(alpha/2) = -------- = --- --> j = -------- + 2 |d| j |d + nd| + + Point 3 is then just in the opposite direction; for the other side it's + done equivalently. */ + } else { +// edgeDirection = perpendicular(directionNormalized)*edgeSign*edgeDistance*2.0/viewportSize; + + const vec2 averageDirection = capSign*directionNormalized + normalize(neighborDirection); + const float averageDirectionLength = length(averageDirection); + edgeDirection = perpendicular(averageDirection)*(capSign*edgeSign*edgeDistance*4.0/averageDirectionLength)/viewportSize; + +// const float ex = sqrt((4.0*edgeDistance*edgeDistance/(averageDirectionLength*averageDirectionLength)) - edgeDistance*edgeDistance); + const float ex = sqrt(dot(edgeDirection, edgeDirection) - edgeDistance*edgeDistance); + +// edgeDirection = -perpendicular(averageDirection)*(1.0*edgeDistance*edgeSign*capSign/averageDirectionLength) +// *2.0/viewportSize; + + // TODO it can't be 0!! it also has to include the skew in here but + // then somehow be marked to not smooth + centerDistanceSigned.x = halfSegmentLength*capSign;// + ex*sign(dot(direction*capSign, edgeDirection)); + } + + // TODO cap ends / joints, depending on neighborDirection size + + gl_Position.xyzw = vec4(lineCenterPosition + edgeDirection, 0.0, 1.0); + #elif defined(THREE_DIMENSIONS) + // TODO 3D, how to handle perspective? multiply edgeDistance with w? + gl_Position = transformationProjectionMatrix* + #ifdef INSTANCED_TRANSFORMATION + instancedTransformationMatrix* + #endif + position; + #else + #error + #endif + + #ifdef VERTEX_COLOR + /* Vertex colors, if enabled */ + interpolatedVertexColor = vertexColor; + #endif + + #ifdef INSTANCED_OBJECT_ID + /* Instanced object ID, if enabled */ + interpolatedInstanceObjectId = instanceObjectId; + #endif +} diff --git a/src/Magnum/Shaders/LineGL.cpp b/src/Magnum/Shaders/LineGL.cpp new file mode 100644 index 000000000..ad776868f --- /dev/null +++ b/src/Magnum/Shaders/LineGL.cpp @@ -0,0 +1,417 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "LineGL.h" + +#include +#include + +#include "Magnum/GL/Context.h" +#include "Magnum/GL/Extensions.h" +#include "Magnum/GL/Shader.h" +#include "Magnum/Math/Color.h" +#include "Magnum/Math/Matrix3.h" +#include "Magnum/Math/Matrix4.h" +#include "Magnum/Shaders/Implementation/CreateCompatibilityShader.h" + +#ifndef MAGNUM_TARGET_GLES2 +#include + +#include "Magnum/GL/Buffer.h" +#endif + +namespace Magnum { namespace Shaders { + +namespace { + enum: Int { + /* 0/1/2/3 taken by Phong (A/D/S/N), 4 by MeshVisualizer colormap, 5 by + object ID textures, 6 by Vector */ + TextureUnit = 7 + }; + + #ifndef MAGNUM_TARGET_GLES2 + enum: Int { + /* Not using the zero binding to avoid conflicts with + ProjectionBufferBinding from other shaders which can likely stay + bound to the same buffer for the whole time */ + TransformationProjectionBufferBinding = 1, + DrawBufferBinding = 2, + MaterialBufferBinding = 3 + }; + #endif +} + +template typename LineGL::CompileState LineGL::compile(const Configuration& configuration) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), + "Shaders::LineGL: material count can't be zero", CompileState{NoCreate}); + CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), + "Shaders::LineGL: draw count can't be zero", CompileState{NoCreate}); + #endif + + #ifndef MAGNUM_TARGET_GLES + if(configuration.flags() >= Flag::UniformBuffers) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); + #endif + #ifndef MAGNUM_TARGET_GLES2 + if(configuration.flags() >= Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters); + #elif !defined(MAGNUM_TARGET_WEBGL) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ANGLE::multi_draw); + #else + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::WEBGL::multi_draw); + #endif + } + #endif + + #ifdef MAGNUM_BUILD_STATIC + /* Import resources on static build, if not already */ + if(!Utility::Resource::hasGroup("MagnumShadersGL")) + importShaderResources(); + #endif + Utility::Resource rs("MagnumShadersGL"); + + const GL::Context& context = GL::Context::current(); + + const GL::Version version = context.supportedVersion({ + #ifndef MAGNUM_TARGET_GLES + GL::Version::GL320, GL::Version::GL310, GL::Version::GL300, GL::Version::GL210 + #else + GL::Version::GLES300, GL::Version::GLES200 + #endif + }); + + GL::Shader vert = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Vertex); + GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment); + + vert.addSource(configuration.flags() & Flag::VertexColor ? "#define VERTEX_COLOR\n" : "") + .addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n" : "#define THREE_DIMENSIONS\n") + #ifndef MAGNUM_TARGET_GLES2 + .addSource(configuration.flags() >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") + #endif + .addSource(configuration.flags() & Flag::InstancedTransformation ? "#define INSTANCED_TRANSFORMATION\n" : ""); + #ifndef MAGNUM_TARGET_GLES2 + if(configuration.flags() >= Flag::UniformBuffers) { + vert.addSource(Utility::formatString( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n", + configuration.drawCount())); + vert.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); + } + #endif + vert.addSource(rs.getString("generic.glsl")) + .addSource(rs.getString("Line.vert")); + frag.addSource(configuration.flags() & Flag::VertexColor ? "#define VERTEX_COLOR\n" : "") + #ifndef MAGNUM_TARGET_GLES2 + .addSource(configuration.flags() & Flag::ObjectId ? "#define OBJECT_ID\n" : "") + .addSource(configuration.flags() >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") + #endif + ; + #ifndef MAGNUM_TARGET_GLES2 + if(configuration.flags() >= Flag::UniformBuffers) { + frag.addSource(Utility::formatString( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n", + configuration.drawCount(), + configuration.materialCount())); + frag.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); + } + #endif + frag.addSource(rs.getString("generic.glsl")) + .addSource(rs.getString("Line.frag")); + + vert.submitCompile(); + frag.submitCompile(); + + LineGL out{NoInit}; + out._flags = configuration.flags(); + #ifndef MAGNUM_TARGET_GLES2 + out._materialCount = configuration.materialCount(); + out._drawCount = configuration.drawCount(); + #endif + + out.attachShaders({vert, frag}); + + /* ES3 has this done in the shader directly and doesn't even provide + bindFragmentDataLocation() */ + #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2) + #ifndef MAGNUM_TARGET_GLES + if(!context.isExtensionSupported(version)) + #endif + { + out.bindAttributeLocation(Position::Location, "position"); + out.bindAttributeLocation(LineDirection::Location, "direction"); + out.bindAttributeLocation(LineNeighborDirection::Location, "neighborDirection"); + if(configuration.flags() & Flag::VertexColor) + out.bindAttributeLocation(Color3::Location, "vertexColor"); /* Color4 is the same */ + #ifndef MAGNUM_TARGET_GLES2 + if(configuration.flags() & Flag::ObjectId) { + out.bindFragmentDataLocation(ColorOutput, "color"); + out.bindFragmentDataLocation(ObjectIdOutput, "objectId"); + } + if(configuration.flags() >= Flag::InstancedObjectId) + out.bindAttributeLocation(ObjectId::Location, "instanceObjectId"); + #endif + if(configuration.flags() & Flag::InstancedTransformation) + out.bindAttributeLocation(TransformationMatrix::Location, "instancedTransformationMatrix"); + } + #endif + + out.submitLink(); + + return CompileState{std::move(out), std::move(vert), std::move(frag), version}; +} + +template LineGL::LineGL(CompileState&& state): LineGL{static_cast(std::move(state))} { + #ifdef CORRADE_GRACEFUL_ASSERT + /* When graceful assertions fire from within compile(), we get a NoCreate'd + CompileState. Exiting makes it possible to test the assert. */ + if(!id()) return; + #endif + + CORRADE_INTERNAL_ASSERT_OUTPUT(checkLink({GL::Shader(state._vert), GL::Shader(state._frag)})); + + const GL::Context& context = GL::Context::current(); + const GL::Version version = state._version; + + #ifndef MAGNUM_TARGET_GLES + if(!context.isExtensionSupported(version)) + #endif + { + _viewportSizeUniform = uniformLocation("viewportSize"); + #ifndef MAGNUM_TARGET_GLES2 + if(_flags >= Flag::UniformBuffers) { + if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"); + } else + #endif + { + _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); + _widthUniform = uniformLocation("width"); + _smoothnessUniform = uniformLocation("smoothness"); + _backgroundColorUniform = uniformLocation("backgroundColor"); + _colorUniform = uniformLocation("color"); + #ifndef MAGNUM_TARGET_GLES2 + if(_flags & Flag::ObjectId) _objectIdUniform = uniformLocation("objectId"); + #endif + } + } + + #ifndef MAGNUM_TARGET_GLES + if(!context.isExtensionSupported(version)) + #endif + { + #ifndef MAGNUM_TARGET_GLES2 + if(_flags >= Flag::UniformBuffers) { + setUniformBlockBinding(uniformBlockIndex("TransformationProjection"), TransformationProjectionBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Draw"), DrawBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Material"), MaterialBufferBinding); + } + #endif + } + + /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ + #ifdef MAGNUM_TARGET_GLES + #ifndef MAGNUM_TARGET_GLES2 + if(_flags >= Flag::UniformBuffers) { + /* Draw offset is zero by default */ + } else + #endif + { + setTransformationProjectionMatrix(MatrixTypeFor{Math::IdentityInit}); + setColor(Magnum::Color4{1.0f}); + /* Object ID is zero by default */ + } + #endif + + static_cast(version); + static_cast(context); +} + +template LineGL::LineGL(const Configuration& configuration): LineGL{compile(configuration)} {} + +template LineGL::LineGL(NoInitT) {} + +template LineGL& LineGL::setTransformationProjectionMatrix(const MatrixTypeFor& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::LineGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled", *this); + #endif + setUniform(_transformationProjectionMatrixUniform, matrix); + return *this; +} + +template LineGL& LineGL::setViewportSize(const Vector2& size) { + setUniform(_viewportSizeUniform, size); + return *this; +} + +template LineGL& LineGL::setWidth(const Float width) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::LineGL::setColor(): the shader was created with uniform buffers enabled", *this); + #endif + setUniform(_widthUniform, width); + return *this; +} + +template LineGL& LineGL::setSmoothness(const Float smoothness) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::LineGL::setColor(): the shader was created with uniform buffers enabled", *this); + #endif + setUniform(_smoothnessUniform, smoothness); + return *this; +} + +template LineGL& LineGL::setBackgroundColor(const Magnum::Color4& color) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::LineGL::setColor(): the shader was created with uniform buffers enabled", *this); + #endif + setUniform(_backgroundColorUniform, color); + return *this; +} + +template LineGL& LineGL::setColor(const Magnum::Color4& color) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::LineGL::setColor(): the shader was created with uniform buffers enabled", *this); + #endif + setUniform(_colorUniform, color); + return *this; +} + +#ifndef MAGNUM_TARGET_GLES2 +template LineGL& LineGL::setObjectId(UnsignedInt id) { + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::LineGL::setObjectId(): the shader was created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & Flag::ObjectId, + "Shaders::LineGL::setObjectId(): the shader was not created with object ID enabled", *this); + setUniform(_objectIdUniform, id); + return *this; +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +template LineGL& LineGL::setDrawOffset(const UnsignedInt offset) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::LineGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(offset < _drawCount, + "Shaders::LineGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); + if(_drawCount > 1) setUniform(_drawOffsetUniform, offset); + return *this; +} + +template LineGL& LineGL::bindTransformationProjectionBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::LineGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); + return *this; +} + +template LineGL& LineGL::bindTransformationProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::LineGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); + return *this; +} + +template LineGL& LineGL::bindDrawBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::LineGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding); + return *this; +} + +template LineGL& LineGL::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::LineGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); + return *this; +} + +template LineGL& LineGL::bindMaterialBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::LineGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding); + return *this; +} + +template LineGL& LineGL::bindMaterialBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::LineGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); + return *this; +} +#endif + +template class MAGNUM_SHADERS_EXPORT LineGL<2>; +template class MAGNUM_SHADERS_EXPORT LineGL<3>; + +namespace Implementation { + +Debug& operator<<(Debug& debug, const LineGLFlag value) { + debug << "Shaders::LineGL::Flag" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(v) case LineGLFlag::v: return debug << "::" #v; + _c(VertexColor) + #ifndef MAGNUM_TARGET_GLES2 + _c(ObjectId) + _c(InstancedObjectId) + #endif + _c(InstancedTransformation) + #ifndef MAGNUM_TARGET_GLES2 + _c(UniformBuffers) + _c(MultiDraw) + #endif + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedShort(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, const LineGLFlags value) { + return Containers::enumSetDebugOutput(debug, value, "Shaders::LineGL::Flags{}", { + LineGLFlag::VertexColor, + #ifndef MAGNUM_TARGET_GLES2 + LineGLFlag::InstancedObjectId, /* Superset of ObjectId */ + LineGLFlag::ObjectId, + #endif + LineGLFlag::InstancedTransformation, + #ifndef MAGNUM_TARGET_GLES2 + LineGLFlag::MultiDraw, /* Superset of UniformBuffers */ + LineGLFlag::UniformBuffers + #endif + }); +} + +} + +}} diff --git a/src/Magnum/Shaders/LineGL.h b/src/Magnum/Shaders/LineGL.h new file mode 100644 index 000000000..d0353fbad --- /dev/null +++ b/src/Magnum/Shaders/LineGL.h @@ -0,0 +1,281 @@ +#ifndef Magnum_Shaders_LineGL_h +#define Magnum_Shaders_LineGL_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "Magnum/DimensionTraits.h" +#include "Magnum/GL/AbstractShaderProgram.h" +#include "Magnum/Shaders/GenericGL.h" +#include "Magnum/Shaders/glShaderWrapper.h" +#include "Magnum/Shaders/visibility.h" + +namespace Magnum { namespace Shaders { + +namespace Implementation { + enum class LineGLFlag: UnsignedShort { + VertexColor = 1 << 0, + #ifndef MAGNUM_TARGET_GLES2 + ObjectId = 1 << 1, + InstancedObjectId = (1 << 2)|ObjectId, + #endif + InstancedTransformation = 1 << 3, + #ifndef MAGNUM_TARGET_GLES2 + UniformBuffers = 1 << 4, + MultiDraw = UniformBuffers|(1 << 5) + #endif + }; + typedef Containers::EnumSet LineGLFlags; +} + +template class MAGNUM_SHADERS_EXPORT LineGL: public GL::AbstractShaderProgram { + public: + class Configuration; + class CompileState; + + typedef typename GenericGL::Position Position; + + // TODO move to Generic + typedef GL::Attribute<3, VectorTypeFor> LineDirection; + typedef GL::Attribute<5, VectorTypeFor> LineNeighborDirection; + + typedef typename GenericGL::Color3 Color3; + + typedef typename GenericGL::Color4 Color4; + + #ifndef MAGNUM_TARGET_GLES2 + typedef typename GenericGL::ObjectId ObjectId; + #endif + + typedef typename GenericGL::TransformationMatrix TransformationMatrix; + + enum: UnsignedInt { + ColorOutput = GenericGL::ColorOutput, + + #ifndef MAGNUM_TARGET_GLES2 + ObjectIdOutput = GenericGL::ObjectIdOutput + #endif + }; + + #ifdef DOXYGEN_GENERATING_OUTPUT + /** + * @brief Flag + * + * @see @ref Flags, @ref flags() + */ + enum class Flag: UnsignedShort { + VertexColor = 1 << 0, + #ifndef MAGNUM_TARGET_GLES2 + ObjectId = 1 << 1, + InstancedObjectId = (1 << 2)|ObjectId, + #endif + InstancedTransformation = 1 << 3, + #ifndef MAGNUM_TARGET_GLES2 + UniformBuffers = 1 << 4, + MultiDraw = UniformBuffers|(1 << 5) + #endif + }; + + /** + * @brief Flags + * + * @see @ref flags() + */ + typedef Containers::EnumSet Flags; + #else + /* Done this way to be prepared for possible future diversion of 2D + and 3D flags (e.g. introducing 3D-specific features) */ + typedef Implementation::LineGLFlag Flag; + typedef Implementation::LineGLFlags Flags; + #endif + + static CompileState compile(const Configuration& configuration = Configuration{}); + + explicit LineGL(const Configuration& configuration = Configuration{}); + + explicit LineGL(CompileState&& state); + + explicit LineGL(NoCreateT) noexcept: GL::AbstractShaderProgram{NoCreate} {} + + /** @brief Copying is not allowed */ + LineGL(const LineGL&) = delete; + + /** @brief Move constructor */ + LineGL(LineGL&&) noexcept = default; + + /** @brief Copying is not allowed */ + LineGL& operator=(const LineGL&) = delete; + + /** @brief Move assignment */ + LineGL& operator=(LineGL&&) noexcept = default; + + /** @brief Flags */ + Flags flags() const { return _flags; } + + #ifndef MAGNUM_TARGET_GLES2 + UnsignedInt materialCount() const { return _materialCount; } + + UnsignedInt drawCount() const { return _drawCount; } + #endif + + // TODO split projection for 3D? + LineGL& setTransformationProjectionMatrix(const MatrixTypeFor& matrix); + + LineGL& setViewportSize(const Vector2& size); + + LineGL& setWidth(Float width); + + LineGL& setSmoothness(Float smoothness); + + LineGL& setBackgroundColor(const Magnum::Color4& color); + + LineGL& setColor(const Magnum::Color4& color); + + #ifndef MAGNUM_TARGET_GLES2 + LineGL& setObjectId(UnsignedInt id); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + LineGL& setDrawOffset(UnsignedInt offset); + + LineGL& bindTransformationProjectionBuffer(GL::Buffer& buffer); + LineGL& bindTransformationProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + LineGL& bindDrawBuffer(GL::Buffer& buffer); + LineGL& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + LineGL& bindMaterialBuffer(GL::Buffer& buffer); + LineGL& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + #endif + + // TODO draw() overloads, as a macro in AbstractShaderProgram + + private: + /* Creates the GL shader program object but does nothing else. + Internal, used by compile(). */ + explicit LineGL(NoInitT); + + Flags _flags; + #ifndef MAGNUM_TARGET_GLES2 + UnsignedInt _materialCount{}, _drawCount{}; + #endif + Int _viewportSizeUniform{0}, + _transformationProjectionMatrixUniform{1}, + _widthUniform{2}, + _smoothnessUniform{3}, + _backgroundColorUniform{4}, + _colorUniform{5}; + #ifndef MAGNUM_TARGET_GLES2 + Int _objectIdUniform{6}; + /* Used instead of all other uniforms except viewportSize when + Flag::UniformBuffers is set, so it can alias them */ + Int _drawOffsetUniform{1}; + #endif +}; + +/** +@brief Configuration + +*/ +template class LineGL::Configuration { + public: + explicit Configuration() = default; + + Configuration& setFlags(Flags flags) { + _flags = flags; + return *this; + } + + Flags flags() const { return _flags; } + + #ifndef MAGNUM_TARGET_GLES2 + Configuration& setMaterialCount(UnsignedInt count) { + _materialCount = count; + return *this; + } + + UnsignedInt materialCount() const { return _materialCount; } + + Configuration& setDrawCount(UnsignedInt count) { + _drawCount = count; + return *this; + } + + UnsignedInt drawCount() const { return _drawCount; } + #endif + + private: + Flags _flags; + #ifndef MAGNUM_TARGET_GLES2 + UnsignedInt _materialCount{1}; + UnsignedInt _drawCount{1}; + #endif +}; + +/** +@brief Asynchronous compilation state + +Returned by @ref compile(). See @ref shaders-async for more information. +*/ +template class LineGL::CompileState: public LineGL { + /* Everything deliberately private except for the inheritance */ + friend class LineGL; + + explicit CompileState(NoCreateT): LineGL{NoCreate}, _vert{NoCreate}, _frag{NoCreate} {} + + explicit CompileState(LineGL&& shader, GL::Shader&& vert, GL::Shader&& frag, GL::Version version): LineGL{std::move(shader)}, _vert{std::move(vert)}, _frag{std::move(frag)}, _version{version} {} + + Implementation::GLShaderWrapper _vert, _frag; + GL::Version _version; +}; + +/** +@brief 2D line OpenGL shader +@m_since_latest +*/ +typedef LineGL<2> LineGL2D; + +/** +@brief 3D LineGL OpenGL shader +@m_since_latest +*/ +typedef LineGL<3> LineGL3D; + +#ifdef DOXYGEN_GENERATING_OUTPUT +/** @debugoperatorclassenum{LineGL,LineGL::Flag} */ +template Debug& operator<<(Debug& debug, LineGL::Flag value); + +/** @debugoperatorclassenum{LineGL,LineGL::Flags} */ +template Debug& operator<<(Debug& debug, LineGL::Flags value); +#else +namespace Implementation { + MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, LineGLFlag value); + MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, LineGLFlags value); + CORRADE_ENUMSET_OPERATORS(LineGLFlags) +} +#endif + +}} + +#endif diff --git a/src/Magnum/Shaders/Shaders.h b/src/Magnum/Shaders/Shaders.h index 53441b7dc..d693b7350 100644 --- a/src/Magnum/Shaders/Shaders.h +++ b/src/Magnum/Shaders/Shaders.h @@ -58,6 +58,10 @@ typedef CORRADE_DEPRECATED("use FlatGL3D instead") FlatGL3D Flat3D; /* Generic is used only statically */ +template class LineGL; +typedef LineGL<2> LineGL2D; +typedef LineGL<3> LineGL3D; + class MeshVisualizerGL2D; class MeshVisualizerGL3D; #ifdef MAGNUM_BUILD_DEPRECATED diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index 91ab6651a..d304f8e42 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -30,6 +30,7 @@ set(CMAKE_FOLDER "Magnum/Shaders/Test") corrade_add_test(ShadersDistanceFieldVectorTest DistanceFieldVectorTest.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersFlatTest FlatTest.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersGenericTest GenericTest.cpp LIBRARIES MagnumShaders) +corrade_add_test(ShadersLineTest LineTest.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersGLShaderWrapperTest GLShaderWrapperTest.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersMeshVisualizerTest MeshVisualizerTest.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersPhongTest PhongTest.cpp LIBRARIES MagnumShaders) @@ -40,6 +41,7 @@ corrade_add_test(ShadersVectorTest VectorTest.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersDistanceFieldVectorGL_Test DistanceFieldVectorGL_Test.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersFlatGL_Test FlatGL_Test.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersGenericGL_Test GenericGL_Test.cpp LIBRARIES MagnumShaders) +corrade_add_test(ShadersLineGL_Test LineGL_Test.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersMeshVisualizerGL_Test MeshVisualizerGL_Test.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersPhongGL_Test PhongGL_Test.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersVectorGL_Test VectorGL_Test.cpp LIBRARIES MagnumShaders) @@ -165,6 +167,35 @@ if(MAGNUM_BUILD_GL_TESTS) endif() endif() + set(ShadersLineGLTest_SRCS LineGLTest.cpp) + if(CORRADE_TARGET_IOS) + list(APPEND ShadersLineGLTest_SRCS TestFiles LineTestFiles) + endif() + corrade_add_test(ShadersLineGLTest ${ShadersLineGLTest_SRCS} + LIBRARIES + MagnumDebugTools + MagnumMeshTools + MagnumPrimitives + MagnumShadersTestLib + MagnumOpenGLTester) + target_include_directories(ShadersLineGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) + if(MAGNUM_BUILD_PLUGINS_STATIC) + if(MAGNUM_WITH_ANYIMAGEIMPORTER) + target_link_libraries(ShadersLineGLTest PRIVATE AnyImageImporter) + endif() + if(MAGNUM_WITH_TGAIMPORTER) + target_link_libraries(ShadersLineGLTest PRIVATE TgaImporter) + endif() + else() + # So the plugins get properly built when building the test + if(MAGNUM_WITH_ANYIMAGEIMPORTER) + add_dependencies(ShadersLineGLTest AnyImageImporter) + endif() + if(MAGNUM_WITH_TGAIMPORTER) + add_dependencies(ShadersLineGLTest TgaImporter) + endif() + endif() + corrade_add_test(ShadersGLShaderWrapperGLTest GLShaderWrapperGLTest.cpp LIBRARIES MagnumShaders MagnumOpenGLTester) set(ShadersMeshVisualizerGLTest_SRCS MeshVisualizerGLTest.cpp) diff --git a/src/Magnum/Shaders/Test/LineGLTest.cpp b/src/Magnum/Shaders/Test/LineGLTest.cpp new file mode 100644 index 000000000..289da6e8b --- /dev/null +++ b/src/Magnum/Shaders/Test/LineGLTest.cpp @@ -0,0 +1,754 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "Magnum/Image.h" +#include "Magnum/PixelFormat.h" +#include "Magnum/DebugTools/CompareImage.h" +#include "Magnum/GL/Buffer.h" +#include "Magnum/GL/Extensions.h" +#include "Magnum/GL/Framebuffer.h" +#include "Magnum/GL/Mesh.h" +#include "Magnum/GL/OpenGLTester.h" +#include "Magnum/GL/Renderbuffer.h" +#include "Magnum/GL/RenderbufferFormat.h" +#include "Magnum/Math/Color.h" +#include "Magnum/Math/Matrix3.h" +#include "Magnum/Math/Matrix4.h" +#include "Magnum/Shaders/Generic.h" +#include "Magnum/Shaders/Line.h" +#include "Magnum/Shaders/LineGL.h" +#include "Magnum/Trade/AbstractImporter.h" + +#include "configure.h" + +namespace Magnum { namespace Shaders { namespace Test { namespace { + +struct LineGLTest: GL::OpenGLTester { + explicit LineGLTest(); + + template void construct(); + template void constructAsync(); + #ifndef MAGNUM_TARGET_GLES2 + template void constructUniformBuffers(); + template void constructUniformBuffersAsync(); + #endif + + template void constructMove(); + #ifndef MAGNUM_TARGET_GLES2 + template void constructMoveUniformBuffers(); + #endif + + // template void constructInvalid(); + #ifndef MAGNUM_TARGET_GLES2 + template void constructUniformBuffersInvalid(); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + template void setUniformUniformBuffersEnabled(); + template void bindBufferUniformBuffersNotEnabled(); + #endif + #ifndef MAGNUM_TARGET_GLES2 + template void setObjectIdNotEnabled(); + #endif + #ifndef MAGNUM_TARGET_GLES2 + template void setWrongDrawOffset(); + #endif + + void renderSetup(); + void renderTeardown(); + + template void renderDefaults2D(); + template void renderDefaults3D(); + + template void render2D(); + template void render3D(); + + template void renderVertexColor2D(); + template void renderVertexColor3D(); + + #ifndef MAGNUM_TARGET_GLES2 + void renderObjectIdSetup(); + void renderObjectIdTeardown(); + + template void renderObjectId2D(); + template void renderObjectId3D(); + #endif + + template void renderInstanced2D(); + template void renderInstanced3D(); + + #ifndef MAGNUM_TARGET_GLES2 + void renderMulti2D(); + void renderMulti3D(); + #endif + + private: + PluginManager::Manager _manager{"nonexistent"}; + Containers::String _testDir; + + GL::Renderbuffer _color{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Renderbuffer _objectId{NoCreate}; + #endif + GL::Framebuffer _framebuffer{NoCreate}; +}; + +using namespace Math::Literals; + +const struct { + const char* name; + LineGL2D::Flags flags; +} ConstructData[]{ + {"", {}}, + {"vertex colors", LineGL2D::Flag::VertexColor}, + #ifndef MAGNUM_TARGET_GLES2 + {"object ID", LineGL2D::Flag::ObjectId}, + {"instanced object ID", LineGL2D::Flag::InstancedObjectId}, + #endif + {"instanced transformation", LineGL2D::Flag::InstancedTransformation}, +}; + +#ifndef MAGNUM_TARGET_GLES2 +const struct { + const char* name; + LineGL2D::Flags flags; + UnsignedInt materialCount, drawCount; +} ConstructUniformBuffersData[]{ + {"classic fallback", {}, 1, 1}, + {"", LineGL2D::Flag::UniformBuffers, 1, 1}, + /* SwiftShader has 256 uniform vectors at most, per-draw is 4+1 in 3D case + and 3+1 in 2D, per-material 1 */ + {"multiple materials, draws", LineGL2D::Flag::UniformBuffers, 16, 48}, + {"object ID", LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::ObjectId, 1, 1}, + {"instanced object ID", LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::InstancedObjectId, 1, 1}, + {"multidraw with all the things", LineGL2D::Flag::MultiDraw|LineGL2D::Flag::ObjectId|LineGL2D::Flag::InstancedTransformation|LineGL2D::Flag::InstancedObjectId, 16, 48} +}; +#endif + +// const struct { +// const char* name; +// LineGL2D::Flags flags; +// const char* message; +// } ConstructInvalidData[]{ +// }; + +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + LineGL2D::Flags flags; + UnsignedInt materialCount, drawCount; + const char* message; +} ConstructUniformBuffersInvalidData[]{ + {"zero draws", LineGL2D::Flag::UniformBuffers, 1, 0, + "draw count can't be zero"}, + {"zero materials", LineGL2D::Flag::UniformBuffers, 0, 1, + "material count can't be zero"} +}; +#endif + +LineGLTest::LineGLTest() { + addInstancedTests({ + &LineGLTest::construct<2>, + // &LineGLTest::construct<3> + + }, + Containers::arraySize(ConstructData)); + + addTests({ + &LineGLTest::constructAsync<2>, + // &LineGLTest::constructAsync<3> + + }); + + #ifndef MAGNUM_TARGET_GLES2 + // addInstancedTests({ + // &LineGLTest::constructUniformBuffers<2>, + // &LineGLTest::constructUniformBuffers<3>}, + // Containers::arraySize(ConstructUniformBuffersData)); + // + // addTests({ + // &LineGLTest::constructUniformBuffersAsync<2>, + // &LineGLTest::constructUniformBuffersAsync<3>}); + #endif + + addTests({ + &LineGLTest::constructMove<2>, + // &LineGLTest::constructMove<3>, + + // #ifndef MAGNUM_TARGET_GLES2 + // &LineGLTest::constructMoveUniformBuffers<2>, + // &LineGLTest::constructMoveUniformBuffers<3>, + // #endif + }); + + // addInstancedTests({ + // &LineGLTest::constructInvalid<2>, + // &LineGLTest::constructInvalid<3>}, + // Containers::arraySize(ConstructInvalidData)); + + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({ + &LineGLTest::constructUniformBuffersInvalid<2>, + // &LineGLTest::constructUniformBuffersInvalid<3> + + }, + Containers::arraySize(ConstructUniformBuffersInvalidData)); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + addTests({ + // &LineGLTest::setUniformUniformBuffersEnabled<2>, + // &LineGLTest::setUniformUniformBuffersEnabled<3>, + &LineGLTest::bindBufferUniformBuffersNotEnabled<2>, + // &LineGLTest::bindBufferUniformBuffersNotEnabled<3>, + &LineGLTest::setObjectIdNotEnabled<2>, + // &LineGLTest::setObjectIdNotEnabled<3>, + // &LineGLTest::setWrongDrawOffset<2>, + // &LineGLTest::setWrongDrawOffset<3> + + }); + #endif + + /* MSVC needs explicit type due to default template args */ + addTests({ + &LineGLTest::renderDefaults2D, + #ifndef MAGNUM_TARGET_GLES2 + // &LineGLTest::renderDefaults2D, + #endif + // &LineGLTest::renderDefaults3D, + // #ifndef MAGNUM_TARGET_GLES2 + // &LineGLTest::renderDefaults3D, + // #endif + }, + &LineGLTest::renderSetup, + &LineGLTest::renderTeardown); + + /* Load the plugins directly from the build tree. Otherwise they're either + static and already loaded or not present in the build tree */ + #ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT_OUTPUT(_manager.load(ANYIMAGEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + #ifdef TGAIMPORTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT_OUTPUT(_manager.load(TGAIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif +} + +template void LineGLTest::construct() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + auto&& data = ConstructData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if((data.flags & LineGL2D::Flag::ObjectId) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); + #endif + + LineGL shader{typename LineGL::Configuration{} + .setFlags(data.flags)}; + CORRADE_COMPARE(shader.flags(), data.flags); + CORRADE_VERIFY(shader.id()); + { + #if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES) + CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #endif + CORRADE_VERIFY(shader.validate().first); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); +} + +template void LineGLTest::constructAsync() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + typename LineGL::CompileState state = LineGL::compile(typename LineGL::Configuration{} + .setFlags(LineGL2D::Flag::VertexColor)); + CORRADE_COMPARE(state.flags(), LineGL2D::Flag::VertexColor); + + while(!state.isLinkFinished()) + Utility::System::sleep(100); + + LineGL shader{std::move(state)}; + CORRADE_COMPARE(shader.flags(), LineGL2D::Flag::VertexColor); + + CORRADE_VERIFY(shader.id()); + { + #if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES) + CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #endif + CORRADE_VERIFY(shader.validate().first); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); +} + +#ifndef MAGNUM_TARGET_GLES2 +template void LineGLTest::constructUniformBuffers() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + auto&& data = ConstructUniformBuffersData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if((data.flags & LineGL2D::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + if((data.flags & LineGL2D::Flag::ObjectId) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); + #endif + + if(data.flags >= LineGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + + LineGL shader{typename LineGL::Configuration{} + .setFlags(data.flags) + .setMaterialCount(data.materialCount) + .setDrawCount(data.drawCount)}; + CORRADE_COMPARE(shader.flags(), data.flags); + CORRADE_COMPARE(shader.materialCount(), data.materialCount); + CORRADE_COMPARE(shader.drawCount(), data.drawCount); + CORRADE_VERIFY(shader.id()); + { + #if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES) + CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #endif + CORRADE_VERIFY(shader.validate().first); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); +} + +template void LineGLTest::constructUniformBuffersAsync() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + typename LineGL::CompileState state = LineGL::compile(typename LineGL::Configuration{} + .setFlags(LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::VertexColor) + .setMaterialCount(16) + .setDrawCount(48)); + CORRADE_COMPARE(state.flags(), LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::VertexColor); + CORRADE_COMPARE(state.materialCount(), 16); + CORRADE_COMPARE(state.drawCount(), 48); + + while(!state.isLinkFinished()) + Utility::System::sleep(100); + + LineGL shader{std::move(state)}; + CORRADE_COMPARE(shader.flags(), LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::VertexColor); + CORRADE_COMPARE(shader.materialCount(), 16); + CORRADE_COMPARE(shader.drawCount(), 48); + CORRADE_VERIFY(shader.id()); + { + #if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES) + CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #endif + CORRADE_VERIFY(shader.validate().first); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); +} + +#endif + +template void LineGLTest::constructMove() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + LineGL a{typename LineGL::Configuration{} + .setFlags(LineGL::Flag::VertexColor)}; + const GLuint id = a.id(); + CORRADE_VERIFY(id); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + LineGL b{std::move(a)}; + CORRADE_COMPARE(b.id(), id); + CORRADE_COMPARE(b.flags(), LineGL::Flag::VertexColor); + CORRADE_VERIFY(!a.id()); + + LineGL c{NoCreate}; + c = std::move(b); + CORRADE_COMPARE(c.id(), id); + CORRADE_COMPARE(c.flags(), LineGL::Flag::VertexColor); + CORRADE_VERIFY(!b.id()); +} + +#ifndef MAGNUM_TARGET_GLES2 +template void LineGLTest::constructMoveUniformBuffers() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + LineGL a{typename LineGL::Configuration{} + .setFlags(LineGL::Flag::UniformBuffers) + .setMaterialCount(2) + .setDrawCount(5)}; + const GLuint id = a.id(); + CORRADE_VERIFY(id); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + LineGL b{std::move(a)}; + CORRADE_COMPARE(b.id(), id); + CORRADE_COMPARE(b.flags(), LineGL::Flag::UniformBuffers); + CORRADE_COMPARE(b.materialCount(), 2); + CORRADE_COMPARE(b.drawCount(), 5); + CORRADE_VERIFY(!a.id()); + + LineGL c{NoCreate}; + c = std::move(b); + CORRADE_COMPARE(c.id(), id); + CORRADE_COMPARE(c.flags(), LineGL::Flag::UniformBuffers); + CORRADE_COMPARE(c.materialCount(), 2); + CORRADE_COMPARE(c.drawCount(), 5); + CORRADE_VERIFY(!b.id()); +} +#endif + +// template void LineGLTest::constructInvalid() { +// auto&& data = ConstructInvalidData[testCaseInstanceId()]; +// setTestCaseTemplateName(Utility::format("{}", dimensions)); +// setTestCaseDescription(data.name); +// +// CORRADE_SKIP_IF_NO_ASSERT(); +// +// std::ostringstream out; +// Error redirectError{&out}; +// LineGL{data.flags}; +// CORRADE_COMPARE(out.str(), Utility::formatString( +// "Shaders::LineGL: {}\n", data.message)); +// } + +#ifndef MAGNUM_TARGET_GLES2 +template void LineGLTest::constructUniformBuffersInvalid() { + auto&& data = ConstructUniformBuffersInvalidData[testCaseInstanceId()]; + setTestCaseTemplateName(Utility::format("{}", dimensions)); + setTestCaseDescription(data.name); + + CORRADE_SKIP_IF_NO_ASSERT(); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + LineGL{typename LineGL::Configuration{} + .setFlags(data.flags) + .setMaterialCount(data.materialCount) + .setDrawCount(data.drawCount)}; + CORRADE_COMPARE(out.str(), Utility::formatString( + "Shaders::LineGL: {}\n", data.message)); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +template void LineGLTest::setUniformUniformBuffersEnabled() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + CORRADE_SKIP_IF_NO_ASSERT(); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + LineGL shader{typename LineGL::Configuration{} + .setFlags(LineGL::Flag::UniformBuffers)}; + + /* This should work fine */ + shader.setViewportSize({}); + + std::ostringstream out; + Error redirectError{&out}; + shader.setTransformationProjectionMatrix({}) + .setWidth({}) + .setSmoothness({}) + .setBackgroundColor({}) + .setColor({}) + .setObjectId({}); + CORRADE_COMPARE(out.str(), + "Shaders::LineGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::LineGL::setWidth(): the shader was created with uniform buffers enabled\n" + "Shaders::LineGL::setSmoothness(): the shader was created with uniform buffers enabled\n" + "Shaders::LineGL::setBackgroundColor(): the shader was created with uniform buffers enabled\n" + "Shaders::LineGL::setColor(): the shader was created with uniform buffers enabled\n" + "Shaders::LineGL::setObjectId(): the shader was created with uniform buffers enabled\n"); +} + +template void LineGLTest::bindBufferUniformBuffersNotEnabled() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + CORRADE_SKIP_IF_NO_ASSERT(); + + std::ostringstream out; + Error redirectError{&out}; + + GL::Buffer buffer; + LineGL shader; + shader.bindTransformationProjectionBuffer(buffer) + .bindTransformationProjectionBuffer(buffer, 0, 16) + .bindDrawBuffer(buffer) + .bindDrawBuffer(buffer, 0, 16) + .bindMaterialBuffer(buffer) + .bindMaterialBuffer(buffer, 0, 16) + .setDrawOffset(0); + CORRADE_COMPARE(out.str(), + "Shaders::LineGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::LineGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::LineGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::LineGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::LineGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::LineGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::LineGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n"); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +template void LineGLTest::setObjectIdNotEnabled() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + CORRADE_SKIP_IF_NO_ASSERT(); + + LineGL shader; + + std::ostringstream out; + Error redirectError{&out}; + shader.setObjectId(33376); + CORRADE_COMPARE(out.str(), + "Shaders::LineGL::setObjectId(): the shader was not created with object ID enabled\n"); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +template void LineGLTest::setWrongDrawOffset() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + CORRADE_SKIP_IF_NO_ASSERT(); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + LineGL shader{typename LineGL::Configuration{} + .setFlags(LineGL::Flag::UniformBuffers) + .setMaterialCount(2) + .setDrawCount(5)}; + + std::ostringstream out; + Error redirectError{&out}; + shader.setDrawOffset(5); + CORRADE_COMPARE(out.str(), + "Shaders::LineGL::setDrawOffset(): draw offset 5 is out of bounds for 5 draws\n"); +} +#endif + +constexpr Vector2i RenderSize{80, 80}; + +void LineGLTest::renderSetup() { + /* Pick a color that's directly representable on RGBA4 as well to reduce + artifacts */ + GL::Renderer::setClearColor(0x111111_rgbf); + GL::Renderer::enable(GL::Renderer::Feature::FaceCulling); + + _color = GL::Renderbuffer{}; + _color.setStorage( + #if !defined(MAGNUM_TARGET_GLES2) || !defined(MAGNUM_TARGET_WEBGL) + GL::RenderbufferFormat::RGBA8, + #else + GL::RenderbufferFormat::RGBA4, + #endif + RenderSize); + _framebuffer = GL::Framebuffer{{{}, RenderSize}}; + _framebuffer.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _color) + .clear(GL::FramebufferClear::Color) + .bind(); +} + +void LineGLTest::renderTeardown() { + _framebuffer = GL::Framebuffer{NoCreate}; + _color = GL::Renderbuffer{NoCreate}; +} + +template void LineGLTest::renderDefaults2D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == LineGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + + // TODO drop this crap, use MeshTools once things settle down + struct Vertex { + Vector2 neighborDirection; + Vector2 position; + Vector2 direction; + } vertices[]{ + /* Two connected line segments, down and then to the right */ + {Vector2{Constants::nan()}, {-0.25f, 0.5f}, {}}, + {Vector2{Constants::nan()}, {-0.25f, 0.5f}, {}}, + {{}, {-0.5f, -0.5f}, {}}, + {{}, {-0.5f, -0.5f}, {}}, + {{}, {-0.5f, -0.5f}, {}}, + {{}, {-0.5f, -0.5f}, {}}, + {Vector2{Constants::nan()}, {0.5f, -0.25f}, {}}, + {Vector2{Constants::nan()}, {0.5f, -0.25f}, {}}, + + /* Singular horizontal and vertical segment */ + {Vector2{Constants::nan()}, {-0.75f, 0.25f}, {}}, + {Vector2{Constants::nan()}, {-0.75f, 0.25f}, {}}, + {Vector2{Constants::nan()}, {-0.75f, 0.75f}, {}}, + {Vector2{Constants::nan()}, {-0.75f, 0.75f}, {}}, + + {Vector2{Constants::nan()}, {-0.25f, -0.75f}, {}}, + {Vector2{Constants::nan()}, {-0.25f, -0.75f}, {}}, + {Vector2{Constants::nan()}, {0.75f, -0.75f}, {}}, + {Vector2{Constants::nan()}, {0.75f, -0.75f}, {}}, + + /* A single point */ + {Vector2{Constants::nan()}, {0.5f, 0.5f}, {}}, + {Vector2{Constants::nan()}, {0.5f, 0.5f}, {}}, + {Vector2{Constants::nan()}, {0.5f, 0.5f}, {}}, + {Vector2{Constants::nan()}, {0.5f, 0.5f}, {}}, + }; + + /* Prev directions */ + for(std::size_t i = 4; i < Containers::arraySize(vertices); i += 4) { + if(Math::isNan(vertices[i + 0].neighborDirection)) continue; + vertices[i + 0].neighborDirection = vertices[i + 1].neighborDirection = + vertices[i - 4].position - vertices[i + 0].position; + } + /* Segment directions */ + for(std::size_t i = 0; i < Containers::arraySize(vertices); i += 4) { + vertices[i + 0].direction = vertices[i + 1].direction = + vertices[i + 2].direction = vertices[i + 3].direction = + vertices[i + 2].position - vertices[i + 0].position; + } + /* Next directions */ + for(std::size_t i = 2; i < Containers::arraySize(vertices) - 4; i += 4) { + if(Math::isNan(vertices[i + 0].neighborDirection)) continue; + vertices[i + 0].neighborDirection = vertices[i + 1].neighborDirection = + vertices[i + 4].position - vertices[i + 0].position; + } + + !Debug{} << Containers::stridedArrayView(vertices).slice(&Vertex::position).prefix(8); + !Debug{} << Containers::stridedArrayView(vertices).slice(&Vertex::direction).prefix(8); + !Debug{} << Containers::stridedArrayView(vertices).slice(&Vertex::neighborDirection).prefix(8); + + const UnsignedInt indices[]{ + 0, 1, 2, 2, 1, 3, + 4, 5, 6, 6, 5, 7, + 8, 9, 10, 10, 9, 11, + 12, 13, 14, 14, 13, 15, + 16, 17, 18, 18, 17, 19 + }; + + GL::Mesh lines; + lines.addVertexBuffer(GL::Buffer{vertices}, 0, + LineGL2D::LineNeighborDirection{}, + LineGL2D::Position{}, + LineGL2D::LineDirection{}) + .setIndexBuffer(GL::Buffer{indices}, 0, GL::MeshIndexType::UnsignedInt) + .setCount(Containers::arraySize(indices)); + + LineGL2D shader{LineGL2D::Configuration{} + .setFlags(flag)}; + shader.setViewportSize(Vector2{RenderSize}); + shader + .setWidth(5.0f) + .setSmoothness(1.0f) + // .setBackgroundColor(0xff0000ff_rgbaf) + // .setColor(0x557766_rgbf) + ; + + if(flag == LineGL2D::Flag{}) { + shader.draw(lines); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == LineGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + LineDrawUniform{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + LineMaterialUniform{} + }}; + shader + .bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(lines); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Path::join(_testDir, "LineTestFiles/defaults2D.tga"), + /* SwiftShader has 8 different pixels on the edges */ + (DebugTools::CompareImageToFile{_manager, 238.0f, 0.2975f})); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Shaders::Test::LineGLTest) diff --git a/src/Magnum/Shaders/Test/LineGL_Test.cpp b/src/Magnum/Shaders/Test/LineGL_Test.cpp new file mode 100644 index 000000000..5c08b2182 --- /dev/null +++ b/src/Magnum/Shaders/Test/LineGL_Test.cpp @@ -0,0 +1,152 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include + +#include "Magnum/Shaders/LineGL.h" + +namespace Magnum { namespace Shaders { namespace Test { namespace { + +/* There's an underscore between GL and Test to disambiguate from GLTest, which + is a common suffix used to mark tests that need a GL context. Ugly, I know. */ +struct LineGL_Test: TestSuite::Tester { + explicit LineGL_Test(); + + template void constructConfigurationDefault(); + template void constructConfigurationSetters(); + + template void constructNoCreate(); + template void constructCopy(); + + void debugFlag(); + void debugFlags(); + void debugFlagsSupersets(); +}; + +LineGL_Test::LineGL_Test() { + addTests({&LineGL_Test::constructConfigurationDefault<2>, + &LineGL_Test::constructConfigurationDefault<3>, + &LineGL_Test::constructConfigurationSetters<2>, + &LineGL_Test::constructConfigurationSetters<3>, + + &LineGL_Test::constructNoCreate<2>, + &LineGL_Test::constructNoCreate<3>, + &LineGL_Test::constructCopy<2>, + &LineGL_Test::constructCopy<3>, + + &LineGL_Test::debugFlag, + &LineGL_Test::debugFlags, + &LineGL_Test::debugFlagsSupersets}); +} + +template void LineGL_Test::constructConfigurationDefault() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + typename LineGL::Configuration configuration; + CORRADE_COMPARE(configuration.flags(), typename LineGL::Flags{}); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_COMPARE(configuration.materialCount(), 1); + CORRADE_COMPARE(configuration.drawCount(), 1); + #endif +} + +template void LineGL_Test::constructConfigurationSetters() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + typename LineGL::Configuration configuration = typename LineGL::Configuration{} + .setFlags(LineGL::Flag::VertexColor) + #ifndef MAGNUM_TARGET_GLES2 + .setMaterialCount(17) + .setDrawCount(266) + #endif + ; + CORRADE_COMPARE(configuration.flags(), LineGL::Flag::VertexColor); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_COMPARE(configuration.materialCount(), 17); + CORRADE_COMPARE(configuration.drawCount(), 266); + #endif +} + +template void LineGL_Test::constructNoCreate() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + { + LineGL shader{NoCreate}; + CORRADE_COMPARE(shader.id(), 0); + CORRADE_COMPARE(shader.flags(), typename LineGL::Flags{}); + } + + CORRADE_VERIFY(true); +} + +template void LineGL_Test::constructCopy() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + CORRADE_VERIFY(!std::is_copy_constructible>{}); + CORRADE_VERIFY(!std::is_copy_assignable>{}); +} + +void LineGL_Test::debugFlag() { + std::ostringstream out; + + Debug{&out} << LineGL3D::Flag::VertexColor << LineGL3D::Flag(0xf00d); + CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::VertexColor Shaders::LineGL::Flag(0xf00d)\n"); +} + +void LineGL_Test::debugFlags() { + std::ostringstream out; + + Debug{&out} << (LineGL3D::Flag::VertexColor|LineGL3D::Flag::InstancedTransformation) << LineGL3D::Flags{}; + CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::VertexColor|Shaders::LineGL::Flag::InstancedTransformation Shaders::LineGL::Flags{}\n"); +} + +void LineGL_Test::debugFlagsSupersets() { + #ifndef MAGNUM_TARGET_GLES2 + /* InstancedObjectId is a superset of ObjectId so only one should be + printed */ + { + std::ostringstream out; + Debug{&out} << (LineGL3D::Flag::ObjectId|LineGL3D::Flag::InstancedObjectId); + CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::InstancedObjectId\n"); + } + #endif + + #ifndef MAGNUM_TARGET_GLES2 + /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + { + std::ostringstream out; + Debug{&out} << (LineGL3D::Flag::MultiDraw|LineGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::MultiDraw\n"); + } + #endif +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Shaders::Test::LineGL_Test) diff --git a/src/Magnum/Shaders/Test/LineTest.cpp b/src/Magnum/Shaders/Test/LineTest.cpp new file mode 100644 index 000000000..d91894925 --- /dev/null +++ b/src/Magnum/Shaders/Test/LineTest.cpp @@ -0,0 +1,196 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include + +#include "Magnum/Shaders/Line.h" + +namespace Magnum { namespace Shaders { namespace Test { namespace { + +struct LineTest: TestSuite::Tester { + explicit LineTest(); + + template void uniformSizeAlignment(); + + void drawUniformConstructDefault(); + void drawUniformConstructNoInit(); + void drawUniformSetters(); + void drawUniformMaterialIdPacking(); + + void materialUniformConstructDefault(); + void materialUniformConstructNoInit(); + void materialUniformSetters(); +}; + +LineTest::LineTest() { + addTests({&LineTest::uniformSizeAlignment, + &LineTest::uniformSizeAlignment, + + &LineTest::drawUniformConstructDefault, + &LineTest::drawUniformConstructNoInit, + &LineTest::drawUniformSetters, + &LineTest::drawUniformMaterialIdPacking, + + &LineTest::materialUniformConstructDefault, + &LineTest::materialUniformConstructNoInit, + &LineTest::materialUniformSetters}); +} + +using namespace Math::Literals; + +template struct UniformTraits; +template<> struct UniformTraits { + static const char* name() { return "LineDrawUniform"; } +}; +template<> struct UniformTraits { + static const char* name() { return "LineMaterialUniform"; } +}; + +template void LineTest::uniformSizeAlignment() { + setTestCaseTemplateName(UniformTraits::name()); + + CORRADE_FAIL_IF(sizeof(T) % sizeof(Vector4) != 0, sizeof(T) << "is not a multiple of vec4 for UBO alignment."); + + /* 48-byte structures are fine, we'll align them to 768 bytes and not + 256, but warn about that */ + CORRADE_FAIL_IF(768 % sizeof(T) != 0, sizeof(T) << "can't fit exactly into 768-byte UBO alignment."); + if(256 % sizeof(T) != 0) + CORRADE_WARN(sizeof(T) << "can't fit exactly into 256-byte UBO alignment, only 768."); + + CORRADE_COMPARE(alignof(T), 4); +} + +void LineTest::drawUniformConstructDefault() { + LineDrawUniform a; + LineDrawUniform b{DefaultInit}; + CORRADE_COMPARE(a.materialId, 0); + CORRADE_COMPARE(b.materialId, 0); + CORRADE_COMPARE(a.objectId, 0); + CORRADE_COMPARE(b.objectId, 0); + + constexpr LineDrawUniform ca; + constexpr LineDrawUniform cb{DefaultInit}; + CORRADE_COMPARE(ca.materialId, 0); + CORRADE_COMPARE(cb.materialId, 0); + CORRADE_COMPARE(ca.objectId, 0); + CORRADE_COMPARE(cb.objectId, 0); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void LineTest::drawUniformConstructNoInit() { + /* Testing only some fields, should be enough */ + LineDrawUniform a; + a.materialId = 5; + a.objectId = 7; + + new(&a) LineDrawUniform{NoInit}; + { + /* Explicitly check we're not on Clang because certain Clang-based IDEs + inherit __GNUC__ if GCC is used instead of leaving it at 4 like + Clang itself does */ + #if defined(CORRADE_TARGET_GCC) && !defined(CORRADE_TARGET_CLANG) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.materialId, 5); + CORRADE_COMPARE(a.objectId, 7); + } + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void LineTest::drawUniformSetters() { + LineDrawUniform a; + a.setMaterialId(5) + .setObjectId(7); + CORRADE_COMPARE(a.materialId, 5); + CORRADE_COMPARE(a.objectId, 7); +} + +void LineTest::drawUniformMaterialIdPacking() { + LineDrawUniform a; + a.setMaterialId(13765); + /* materialId should be right at the beginning, in the low 16 bits on both + LE and BE */ + CORRADE_COMPARE(reinterpret_cast(&a)[0] & 0xffff, 13765); +} + +void LineTest::materialUniformConstructDefault() { + LineMaterialUniform a; + LineMaterialUniform b{DefaultInit}; + CORRADE_COMPARE(a.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(b.color, 0xffffffff_rgbaf); + + constexpr LineMaterialUniform ca; + constexpr LineMaterialUniform cb{DefaultInit}; + CORRADE_COMPARE(ca.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(cb.color, 0xffffffff_rgbaf); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void LineTest::materialUniformConstructNoInit() { + /* Testing only some fields, should be enough */ + LineMaterialUniform a; + a.color = 0x354565fc_rgbaf; + + new(&a) LineMaterialUniform{NoInit}; + { + /* Explicitly check we're not on Clang because certain Clang-based IDEs + inherit __GNUC__ if GCC is used instead of leaving it at 4 like + Clang itself does */ + #if defined(CORRADE_TARGET_GCC) && !defined(CORRADE_TARGET_CLANG) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); + } + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void LineTest::materialUniformSetters() { + LineMaterialUniform a; + a.setColor(0x354565fc_rgbaf); + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Shaders::Test::LineTest) diff --git a/src/Magnum/Shaders/generic.glsl b/src/Magnum/Shaders/generic.glsl index 10a54b014..d70a60717 100644 --- a/src/Magnum/Shaders/generic.glsl +++ b/src/Magnum/Shaders/generic.glsl @@ -28,10 +28,12 @@ #define POSITION_ATTRIBUTE_LOCATION 0 #define TEXTURECOORDINATES_ATTRIBUTE_LOCATION 1 #define COLOR_ATTRIBUTE_LOCATION 2 -#define TANGENT_ATTRIBUTE_LOCATION 3 +#define TANGENT_ATTRIBUTE_LOCATION 3 /* also LineDirection */ +#define LINE_DIRECTION_ATTRIBUTE_LOCATION 3 /* also Tangent */ #define BITANGENT_ATTRIBUTE_LOCATION 4 /* also ObjectId */ #define OBJECT_ID_ATTRIBUTE_LOCATION 4 /* also Bitangent */ -#define NORMAL_ATTRIBUTE_LOCATION 5 +#define NORMAL_ATTRIBUTE_LOCATION 5 /* also LineNeighborDirection */ +#define LINE_NEIGHBOR_DIRECTION_ATTRIBUTE_LOCATION 5 /* also Normal */ #define TRANSFORMATION_MATRIX_ATTRIBUTE_LOCATION 8 #define NORMAL_MATRIX_ATTRIBUTE_LOCATION 12 diff --git a/src/Magnum/Shaders/resources-gl.conf b/src/Magnum/Shaders/resources-gl.conf index 57a1c346e..34de5a008 100644 --- a/src/Magnum/Shaders/resources-gl.conf +++ b/src/Magnum/Shaders/resources-gl.conf @@ -12,6 +12,12 @@ filename=FullScreenTriangle.glsl [file] filename=generic.glsl +[file] +filename=Line.vert + +[file] +filename=Line.frag + [file] filename=MeshVisualizer.vert