From defc3b3ac955fe1e0b1ea4fbc3ba1ea876e19c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 14 Oct 2022 14:56:51 +0200 Subject: [PATCH] [wip] --- src/Magnum/MeshTools/Test/CMakeLists.txt | 1 + .../MeshTools/Test/GenerateLinesTest.cpp | 39 +++ src/Magnum/Shaders/Line.frag | 31 +- src/Magnum/Shaders/Line.vert | 79 +++--- src/Magnum/Shaders/LineGL.cpp | 12 +- src/Magnum/Shaders/LineGL.h | 31 +- src/Magnum/Shaders/Test/LineGLTest.cpp | 264 +++++++++++++----- .../Shaders/Test/LineTestFiles/defaults2D.tga | Bin 0 -> 1328 bytes .../LineTestFiles/joint-angles-obtuse.tga | Bin 0 -> 1066 bytes src/Magnum/Shaders/generic.glsl | 8 +- 10 files changed, 348 insertions(+), 117 deletions(-) create mode 100644 src/Magnum/MeshTools/Test/GenerateLinesTest.cpp create mode 100644 src/Magnum/Shaders/Test/LineTestFiles/defaults2D.tga create mode 100644 src/Magnum/Shaders/Test/LineTestFiles/joint-angles-obtuse.tga diff --git a/src/Magnum/MeshTools/Test/CMakeLists.txt b/src/Magnum/MeshTools/Test/CMakeLists.txt index 5281f7976..3eb90d0b5 100644 --- a/src/Magnum/MeshTools/Test/CMakeLists.txt +++ b/src/Magnum/MeshTools/Test/CMakeLists.txt @@ -35,6 +35,7 @@ corrade_add_test(MeshToolsDuplicateTest DuplicateTest.cpp LIBRARIES MagnumMeshTo corrade_add_test(MeshToolsFilterAttributesTest FilterAttributesTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsFlipNormalsTest FlipNormalsTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsGenerateIndicesTest GenerateIndicesTest.cpp LIBRARIES MagnumMeshToolsTestLib) +corrade_add_test(MeshToolsGenerateLinesTest GenerateLinesTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsGenerateNormalsTest GenerateNormalsTest.cpp LIBRARIES MagnumMeshToolsTestLib MagnumPrimitives) corrade_add_test(MeshToolsInterleaveTest InterleaveTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsReferenceTest ReferenceTest.cpp LIBRARIES MagnumMeshToolsTestLib MagnumPrimitives) diff --git a/src/Magnum/MeshTools/Test/GenerateLinesTest.cpp b/src/Magnum/MeshTools/Test/GenerateLinesTest.cpp new file mode 100644 index 000000000..0baeae421 --- /dev/null +++ b/src/Magnum/MeshTools/Test/GenerateLinesTest.cpp @@ -0,0 +1,39 @@ +/* + 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 + +namespace Magnum { namespace MeshTools { namespace Test { namespace { + +struct GenerateLinesTest: TestSuite::Tester { + explicit GenerateLinesTest(); +}; + +GenerateLinesTest::GenerateLinesTest() { +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::MeshTools::Test::GenerateLinesTest) diff --git a/src/Magnum/Shaders/Line.frag b/src/Magnum/Shaders/Line.frag index 2ab435f05..aa507406a 100644 --- a/src/Magnum/Shaders/Line.frag +++ b/src/Magnum/Shaders/Line.frag @@ -133,6 +133,7 @@ layout(std140 in highp vec2 centerDistanceSigned; in highp float halfSegmentLength; +in lowp float hasCap; #ifdef VERTEX_COLOR in lowp vec4 interpolatedVertexColor; @@ -179,17 +180,39 @@ void main() { 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 = smoothstep( // TODO CSE +// edge - vec2(smoothness), +// edge + vec2(smoothness), abs(centerDistanceSigned)); // lowp const vec2 factor = step(edge, abs(centerDistanceSigned)); + // TODO better names ffs + highp vec2 distance_ = vec2(max(abs(centerDistanceSigned.x) - halfSegmentLength, 0.0), abs(centerDistanceSigned.y)); + if(hasCap < 0.0) distance_.x = 0.0; + + #ifdef CAP_STYLE_SQUARE + const highp float distanceS = max(distance_.x, distance_.y); + #elif defined(CAP_STYLE_ROUND) + const highp float distanceS = length(distance_); + #elif defined(CAP_STYLE_TRIANGLE) + const highp float distanceS = distance_.x + distance_.y; + #else + #error + #endif + + const highp float factor = smoothstep(width*0.5 - smoothness, width*0.5 + smoothness, distanceS); + + #if 1 + const highp float factorX = distance(abs(centerDistanceSigned), vec2(halfSegmentLength, 0.0)); + #else + const highp float factorX = factor.x; + #endif + // fragmentColor.ba = vec2(0.0); fragmentColor = mix( #ifdef VERTEX_COLOR interpolatedVertexColor* #endif - color, backgroundColor, max(factor.x, factor.y)); // TODO huh*/ + color, backgroundColor, factor); #ifdef OBJECT_ID // TODO how to handle smoothness here? diff --git a/src/Magnum/Shaders/Line.vert b/src/Magnum/Shaders/Line.vert index 6aeac513e..5f0a00d09 100644 --- a/src/Magnum/Shaders/Line.vert +++ b/src/Magnum/Shaders/Line.vert @@ -144,23 +144,23 @@ in highp vec4 position; #endif #ifdef EXPLICIT_ATTRIB_LOCATION -layout(location = LINE_DIRECTION_ATTRIBUTE_LOCATION) +layout(location = LINE_PREVIOUS_POSITION_ATTRIBUTE_LOCATION) #endif #ifdef TWO_DIMENSIONS -in highp vec2 direction_; +in highp vec2 previousPosition; #elif defined(THREE_DIMENSIONS) -in highp vec3 direction_; +in highp vec3 previousPosition; #else #error #endif #ifdef EXPLICIT_ATTRIB_LOCATION -layout(location = LINE_NEIGHBOR_DIRECTION_ATTRIBUTE_LOCATION) +layout(location = LINE_NEXT_POSITION_ATTRIBUTE_LOCATION) #endif #ifdef TWO_DIMENSIONS -in highp vec2 neighborDirection_; +in highp vec2 nextPosition; #elif defined(THREE_DIMENSIONS) -in highp vec3 neighborDirection_; +in highp vec3 nextPosition; #else #error #endif @@ -196,6 +196,7 @@ in highp mat4 instancedTransformationMatrix; out highp vec2 centerDistanceSigned; out highp float halfSegmentLength; +out lowp float hasCap; #ifdef VERTEX_COLOR out lowp vec4 interpolatedVertexColor; @@ -237,38 +238,45 @@ void main() { #endif #endif + // TODO look at the precision qualifiers, same for *.frag #ifdef TWO_DIMENSIONS - const vec2 lineCenterPosition = (transformationProjectionMatrix* + highp const vec2 transformedPosition = (transformationProjectionMatrix* #ifdef INSTANCED_TRANSFORMATION instancedTransformationMatrix* #endif vec3(position, 1.0)).xy; - // TODO take prev/next positions, this makes no sense - const vec2 direction = (transformationProjectionMatrix* + highp const vec2 transformedPreviousPosition = (transformationProjectionMatrix* #ifdef INSTANCED_TRANSFORMATION instancedTransformationMatrix* #endif - vec3(position + direction_, 1.0)).xy - lineCenterPosition; + vec3(previousPosition, 1.0)).xy; - const vec2 neighborDirection = (transformationProjectionMatrix* + highp const vec2 transformedNextPosition = (transformationProjectionMatrix* #ifdef INSTANCED_TRANSFORMATION instancedTransformationMatrix* #endif - vec3(position + neighborDirection_, 1.0)).xy - lineCenterPosition; + vec3(nextPosition, 1.0)).xy; - const float directionLength = length(direction); + highp const vec2 direction = (gl_VertexID & 2) == 0 ? + transformedNextPosition - transformedPosition : + transformedPosition - transformedPreviousPosition; + highp const vec2 neighborDirection = (gl_VertexID & 2) == 0 ? + transformedPosition - transformedPreviousPosition : + transformedNextPosition - transformedPosition; + + highp 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; + highp 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; + highp 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` @@ -288,21 +296,26 @@ void main() { 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; + highp const float edgeSign = (gl_VertexID & 1) == 0 ? 1.0 : -1.0; + highp const float capSign = (gl_VertexID & 2) == 0 ? -1.0 : 1.0; + highp const float edgeDistanceSigned = edgeDistance*edgeSign; // vec2 edgeDirection = perpendicular(directionNormalized)*edgeDistanceSigned*2.0/viewportSize; // float centerDirection - vec2 edgeDirection; + highp 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 + where `abs(centerDirection) <= [d+w,w]` is inside the line. + + On the left is shown a line segment that has caps on both sides, thus + has + 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. + TODO what the hell, this is never 0, this is all wrong 0------------------2 0-----------------------------2 [0,+w]-------------[0,+w] [-d-w,+w]------------------[+d+w,+w] | | | | | | @@ -310,7 +323,6 @@ void main() { | | | | | | [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 -- @@ -325,12 +337,14 @@ void main() { 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))) { + // TODO make the 0.7 configurable + if(all(isnan(neighborDirection)) || dot(normalize(direction), normalize(neighborDirection)) < -0.7) { edgeDirection = (directionNormalized*capSign + perpendicular(directionNormalized)*edgeSign)*edgeDistance*2.0/viewportSize ; centerDistanceSigned.x = (edgeDistance + halfSegmentLength)*capSign; + hasCap = 1.0; // TODO uhhhhhhh some extra offset for the cap here?? /* Otherwise we need to create a tight joint with the neighboring line segment, as shown with the points 2 and 3. Given normalized direction @@ -361,26 +375,27 @@ void main() { } 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 highp vec2 averageDirection = capSign*(directionNormalized + normalize(neighborDirection)); + const highp float averageDirectionLength = length(averageDirection); + const highp float j = 2.0*edgeDistance/averageDirectionLength; + edgeDirection = (normalize(perpendicular(averageDirection))*capSign*edgeSign*j)*2.0/viewportSize; // const float ex = sqrt((4.0*edgeDistance*edgeDistance/(averageDirectionLength*averageDirectionLength)) - edgeDistance*edgeDistance); - const float ex = sqrt(dot(edgeDirection, edgeDirection) - edgeDistance*edgeDistance); + const highp float ex = sqrt(j*j - 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 the ex should be included in here, why it isn't?? + centerDistanceSigned.x = halfSegmentLength*capSign;// + ex*sign(dot(direction*capSign, (perpendicular(averageDirection))*capSign*edgeSign)); - // TODO cap ends / joints, depending on neighborDirection size + hasCap = -1.0; // TODO uhhh some extra offset here so 0 is in the center? + } - gl_Position.xyzw = vec4(lineCenterPosition + edgeDirection, 0.0, 1.0); + gl_Position.xyzw = vec4(transformedPosition + edgeDirection, 0.0, 1.0); #elif defined(THREE_DIMENSIONS) // TODO 3D, how to handle perspective? multiply edgeDistance with w? + // TODO also how to handle depth? gl_Position = transformationProjectionMatrix* #ifdef INSTANCED_TRANSFORMATION instancedTransformationMatrix* diff --git a/src/Magnum/Shaders/LineGL.cpp b/src/Magnum/Shaders/LineGL.cpp index ad776868f..bcc2d3610 100644 --- a/src/Magnum/Shaders/LineGL.cpp +++ b/src/Magnum/Shaders/LineGL.cpp @@ -130,6 +130,13 @@ template typename LineGL::CompileState LineG .addSource(configuration.flags() >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") #endif ; + if(configuration.capStyle() == CapStyle::Square) + frag.addSource("#define CAP_STYLE_SQUARE\n"); + else if(configuration.capStyle() == CapStyle::Round) + frag.addSource("#define CAP_STYLE_ROUND\n"); + else if(configuration.capStyle() == CapStyle::Triangle) + frag.addSource("#define CAP_STYLE_TRIANGLE\n"); + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { frag.addSource(Utility::formatString( @@ -149,6 +156,7 @@ template typename LineGL::CompileState LineG LineGL out{NoInit}; out._flags = configuration.flags(); + out._capStyle = configuration.capStyle(); #ifndef MAGNUM_TARGET_GLES2 out._materialCount = configuration.materialCount(); out._drawCount = configuration.drawCount(); @@ -164,8 +172,8 @@ template typename LineGL::CompileState LineG #endif { out.bindAttributeLocation(Position::Location, "position"); - out.bindAttributeLocation(LineDirection::Location, "direction"); - out.bindAttributeLocation(LineNeighborDirection::Location, "neighborDirection"); + out.bindAttributeLocation(PreviousPosition::Location, "direction"); + out.bindAttributeLocation(NextPosition::Location, "neighborDirection"); if(configuration.flags() & Flag::VertexColor) out.bindAttributeLocation(Color3::Location, "vertexColor"); /* Color4 is the same */ #ifndef MAGNUM_TARGET_GLES2 diff --git a/src/Magnum/Shaders/LineGL.h b/src/Magnum/Shaders/LineGL.h index d0353fbad..6e33332a4 100644 --- a/src/Magnum/Shaders/LineGL.h +++ b/src/Magnum/Shaders/LineGL.h @@ -47,6 +47,13 @@ namespace Implementation { #endif }; typedef Containers::EnumSet LineGLFlags; + + // TODO this is GL-independent probably, what to do? put in Line.h? + enum class LineGLCapStyle: UnsignedByte { + Square, + Round, + Triangle + }; } template class MAGNUM_SHADERS_EXPORT LineGL: public GL::AbstractShaderProgram { @@ -57,8 +64,8 @@ template class MAGNUM_SHADERS_EXPORT LineGL: public GL:: typedef typename GenericGL::Position Position; // TODO move to Generic - typedef GL::Attribute<3, VectorTypeFor> LineDirection; - typedef GL::Attribute<5, VectorTypeFor> LineNeighborDirection; + typedef GL::Attribute<3, VectorTypeFor> PreviousPosition; + typedef GL::Attribute<5, VectorTypeFor> NextPosition; typedef typename GenericGL::Color3 Color3; @@ -103,11 +110,20 @@ template class MAGNUM_SHADERS_EXPORT LineGL: public GL:: * @see @ref flags() */ typedef Containers::EnumSet Flags; + + enum class CapStyle: UnsignedByte { + Square, + Round, + Triangle + }; #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; + + // + typedef Implementation::LineGLCapStyle CapStyle; #endif static CompileState compile(const Configuration& configuration = Configuration{}); @@ -177,6 +193,7 @@ template class MAGNUM_SHADERS_EXPORT LineGL: public GL:: explicit LineGL(NoInitT); Flags _flags; + CapStyle _capStyle; #ifndef MAGNUM_TARGET_GLES2 UnsignedInt _materialCount{}, _drawCount{}; #endif @@ -209,6 +226,15 @@ template class LineGL::Configuration { Flags flags() const { return _flags; } + Configuration& setCapStyle(CapStyle style) { + _capStyle = style; + return *this; + } + + CapStyle capStyle() const { return _capStyle; } + + // TODO joint style + #ifndef MAGNUM_TARGET_GLES2 Configuration& setMaterialCount(UnsignedInt count) { _materialCount = count; @@ -227,6 +253,7 @@ template class LineGL::Configuration { private: Flags _flags; + CapStyle _capStyle = CapStyle::Square; #ifndef MAGNUM_TARGET_GLES2 UnsignedInt _materialCount{1}; UnsignedInt _drawCount{1}; diff --git a/src/Magnum/Shaders/Test/LineGLTest.cpp b/src/Magnum/Shaders/Test/LineGLTest.cpp index 289da6e8b..13db421d0 100644 --- a/src/Magnum/Shaders/Test/LineGLTest.cpp +++ b/src/Magnum/Shaders/Test/LineGLTest.cpp @@ -114,7 +114,6 @@ struct LineGLTest: GL::OpenGLTester { private: PluginManager::Manager _manager{"nonexistent"}; - Containers::String _testDir; GL::Renderbuffer _color{NoCreate}; #ifndef MAGNUM_TARGET_GLES2 @@ -163,7 +162,7 @@ const struct { // }; #ifndef MAGNUM_TARGET_GLES2 -constexpr struct { +const struct { const char* name; LineGL2D::Flags flags; UnsignedInt materialCount, drawCount; @@ -176,6 +175,34 @@ constexpr struct { }; #endif +const struct { + const char* name; + Containers::Array lineSegments; + Float width; + Float smoothness; + const char* expected; +} Render2DData[]{ + // TODO cap variants, short & long + {"joint angles, obtuse", {InPlaceInit, { + { 0.2f, 0.8f}, {0.2f, 0.4f}, {0.2f, 0.4f}, {0.8f, 0.4f}, + {-0.4f, 0.4f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.8f, 0.0f}, + {-0.8f, -0.0f}, {0.0f, -0.4f}, {0.0f, -0.4f}, {0.8f, -0.4f}, + {-0.8f, -0.8f}, {0.0f, -0.8f}, {0.0f, -0.8f}, {0.8f, -0.8f}, + }}, 10.0f, 0.0f, "joint-angles-obtuse.tga"}, + // TODO cap variants here also + {"joint angles, acute", {InPlaceInit, { + { 0.4f, 0.8f}, {0.0f, 0.4f}, {0.0f, 0.4f}, {0.8f, 0.4f}, + { 0.8f, 0.0f}, {0.0f, -0.4f}, {0.0f, -0.4f}, {0.8f, -0.4f}, + { 0.8f, -0.8f}, {0.0f, -0.8f}, {0.0f, -0.8f}, {0.8f, -0.8f}, + }}, 10.0f, 0.0f, "joint-angles-acute.tga"}, + // TODO cap variants here also + {"joint angles, acute, short", {InPlaceInit, { + { -0.25f, 0.45f}, {-0.3f, 0.4f}, {-0.3f, 0.4f}, {0.6f, 0.4f}, + { -0.25f, -0.45f}, {-0.3f, -0.4f}, {-0.3f, -0.4f}, {0.6f, -0.4f}, + }}, 20.0f, 0.0f, "joint-angles-acute-short.tga"}, + // TODO cap variants here also +}; + LineGLTest::LineGLTest() { addInstancedTests({ &LineGLTest::construct<2>, @@ -253,6 +280,17 @@ LineGLTest::LineGLTest() { &LineGLTest::renderSetup, &LineGLTest::renderTeardown); + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ + &LineGLTest::render2D, + #ifndef MAGNUM_TARGET_GLES2 + // &LineGLTest::render2D, + #endif + }, + Containers::arraySize(Render2DData), + &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 @@ -617,6 +655,88 @@ void LineGLTest::renderTeardown() { _color = GL::Renderbuffer{NoCreate}; } +template GL::Mesh generateLineMesh(Containers::ArrayView> lineSegments) { + struct Vertex { + VectorTypeFor previousPosition; + VectorTypeFor position; + VectorTypeFor nextPosition; + }; + + CORRADE_INTERNAL_ASSERT(lineSegments.size() % 2 == 0); + Containers::Array vertices{NoInit, lineSegments.size()*2}; + for(std::size_t i = 0; i != lineSegments.size(); ++i) + vertices[i*2 + 0].position = vertices[i*2 + 1].position = lineSegments[i]; + + /* Mark prev/next positions with NaN if it's the beginning, the end or the + segments are disjoint */ + vertices[0].previousPosition = + vertices[1].previousPosition = + vertices[vertices.size() - 2].nextPosition = + vertices[vertices.size() - 1].nextPosition = + VectorTypeFor{Constants::nan()}; + for(std::size_t i = 4; i < vertices.size(); i += 4) { + if(vertices[i - 2].position == vertices[i].position) continue; + vertices[i - 2].nextPosition = + vertices[i - 1].nextPosition = + vertices[i + 0].previousPosition = + vertices[i + 1].previousPosition = + VectorTypeFor{Constants::nan()}; + } + + /* Prev positions for first vertices */ + for(std::size_t i = 2; i < Containers::arraySize(vertices); i += 4) { + if(Math::isNan(vertices[i].previousPosition)) continue; + vertices[i + 0].previousPosition = vertices[i + 1].previousPosition = + vertices[i - 2].position; + } + /* Prev positions for last vertices */ + for(std::size_t i = 4; i < Containers::arraySize(vertices); i += 4) { + if(Math::isNan(vertices[i].previousPosition)) continue; + vertices[i + 0].previousPosition = vertices[i + 1].previousPosition = + vertices[i - 4].position; + } + /* Next positions for first vertices */ + for(std::size_t i = 0; i < Containers::arraySize(vertices) - 2; i += 4) { + if(Math::isNan(vertices[i].nextPosition)) continue; + vertices[i + 0].nextPosition = vertices[i + 1].nextPosition = + vertices[i + 2].position; + } + /* Next positions for last vertices */ + for(std::size_t i = 2; i < Containers::arraySize(vertices) - 4; i += 4) { + if(Math::isNan(vertices[i].nextPosition)) continue; + vertices[i + 0].nextPosition = vertices[i + 1].nextPosition = + vertices[i + 4].position; + } + + Containers::Array indices{NoInit, lineSegments.size()*6/2}; + for(std::size_t i = 0; i != lineSegments.size()/2; ++i) { + indices[i*6 + 0] = i*4 + 0; + indices[i*6 + 1] = i*4 + 1; + indices[i*6 + 2] = i*4 + 2; + indices[i*6 + 3] = i*4 + 2; + indices[i*6 + 4] = i*4 + 1; + indices[i*6 + 5] = i*4 + 3; + } + + GL::Mesh mesh; + mesh.addVertexBuffer(GL::Buffer{vertices}, 0, + LineGL2D::PreviousPosition{}, + LineGL2D::Position{}, + LineGL2D::NextPosition{}) + .setIndexBuffer(GL::Buffer{indices}, 0, GL::MeshIndexType::UnsignedInt) + .setCount(indices.size()); + + return mesh; +} + +GL::Mesh generateLineMesh(std::initializer_list lineSegments) { + return generateLineMesh<2>(Containers::arrayView(lineSegments)); +} + +// GL::Mesh generateLineMesh(std::initializer_list lineSegments) { +// return generateLineMesh<3>(Containers::arrayView(lineSegments)); +// } + template void LineGLTest::renderDefaults2D() { #ifndef MAGNUM_TARGET_GLES2 if(flag == LineGL2D::Flag::UniformBuffers) { @@ -629,89 +749,88 @@ template void LineGLTest::renderDefaults2D() { } #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}, {}}, - }; + GL::Mesh lines = generateLineMesh({ + /* A / line from the top to bottom */ + {-0.0f, 0.5f}, {-0.5f, -0.5f}, + /* A / line from the bottom to top */ + {-0.5f, -0.5f}, {0.5f, -0.25f}, + /* A | line from the bottom to top */ + {-0.75f, -0.25f}, {-0.75f, 0.75f}, + /* A _ line from the left to right */ + {-0.25f, -0.75f}, {0.75f, -0.75f}, + /* A zero-size line that should be visible as a point */ + {0.5f, 0.5f}, {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; + LineGL2D shader{LineGL2D::Configuration{} + .setFlags(flag)}; + shader.setViewportSize(Vector2{RenderSize}); + + if(flag == LineGL2D::Flag{}) { + shader.draw(lines); } - /* 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; + #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(); - !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); + MAGNUM_VERIFY_NO_GL_ERROR(); - 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 - }; + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); - 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)); + 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(SHADERS_TEST_DIR, "LineTestFiles/defaults2D.tga"), + (DebugTools::CompareImageToFile{_manager})); +} + +template void LineGLTest::render2D() { + auto&& data = Render2DData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #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 + + GL::Mesh lines = generateLineMesh<2>(data.lineSegments); LineGL2D shader{LineGL2D::Configuration{} .setFlags(flag)}; shader.setViewportSize(Vector2{RenderSize}); shader - .setWidth(5.0f) - .setSmoothness(1.0f) + .setWidth(data.width) + .setSmoothness(data.smoothness) // .setBackgroundColor(0xff0000ff_rgbaf) // .setColor(0x557766_rgbf) ; + // TODO test with blending -- there should be no self-intersection + if(flag == LineGL2D::Flag{}) { shader.draw(lines); } @@ -744,9 +863,8 @@ template void LineGLTest::renderDefaults2D() { 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})); + Utility::Path::join({SHADERS_TEST_DIR, "LineTestFiles", data.expected}), + (DebugTools::CompareImageToFile{_manager})); } }}}} diff --git a/src/Magnum/Shaders/Test/LineTestFiles/defaults2D.tga b/src/Magnum/Shaders/Test/LineTestFiles/defaults2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..deb0179c1a07049da1b33417cc719487ad75425e GIT binary patch literal 1328 zcmd5*%T5A85FD=D_U32tO9T!Y1Vse_B^*d}QSk){iD&M7(o&U_neIb}J+euqck3}b zJ=>d7GyPw!)JhlWP!z=}Ji}8~RdrRv%eC%+2K*?0z&+5x<4*UI+M+k84}j>d=r2!S zihf5O0dn_5zk2#c^b&Op$jyy$Cks#0LHY^x5s-T-niO9z`a0jvepvtI_ys6Du54WlEokm08a4-!c8f3jYti=f!7?GR!7^X|wSD Ld>#+jN*x>l05H9N literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/LineTestFiles/joint-angles-obtuse.tga b/src/Magnum/Shaders/Test/LineTestFiles/joint-angles-obtuse.tga new file mode 100644 index 0000000000000000000000000000000000000000..a63a9773986d01ac382c4e637a41e81fc7f9c562 GIT binary patch literal 1066 zcmd6kSxN&z6h-UXzy8azvZc(%jFlKiM1o{CMn%Le_?0EK=fZsuT!7fn+{5YWu2;8H zI!kd?Q#Do6u_%hT>+pA3mdd~6*#CDfm}BXAp5V244sXqm@U!^=elg#}ujX6$-Fyvy zm}l^(c?y4-GdzP=R->*U%1#${e{b3r~blee!Z*yPTc^f{=#Lr yhf{yyw1;|8f8jKXdQpGjG;<55{=(@@4V?DZH||KCxd!JGr!&X%|IA0`jQa;En$64r literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/generic.glsl b/src/Magnum/Shaders/generic.glsl index d70a60717..d2e280bc7 100644 --- a/src/Magnum/Shaders/generic.glsl +++ b/src/Magnum/Shaders/generic.glsl @@ -28,12 +28,12 @@ #define POSITION_ATTRIBUTE_LOCATION 0 #define TEXTURECOORDINATES_ATTRIBUTE_LOCATION 1 #define COLOR_ATTRIBUTE_LOCATION 2 -#define TANGENT_ATTRIBUTE_LOCATION 3 /* also LineDirection */ -#define LINE_DIRECTION_ATTRIBUTE_LOCATION 3 /* also Tangent */ +#define TANGENT_ATTRIBUTE_LOCATION 3 /* also LinePreviousPosition */ +#define LINE_PREVIOUS_POSITION_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 /* also LineNeighborDirection */ -#define LINE_NEIGHBOR_DIRECTION_ATTRIBUTE_LOCATION 5 /* also Normal */ +#define NORMAL_ATTRIBUTE_LOCATION 5 /* also LineNextPosition */ +#define LINE_NEXT_POSITION_ATTRIBUTE_LOCATION 5 /* also Normal */ #define TRANSFORMATION_MATRIX_ATTRIBUTE_LOCATION 8 #define NORMAL_MATRIX_ATTRIBUTE_LOCATION 12