Browse Source

MeshTools: make generateLines() public.

This is unfortunately a breaking change to compileLines(), which now
takes the output of generateLines() instead of a line mesh. There's a
new assertion that'll blow up if the code is used the previous way,
sorry for the breakage.

What's however very useful about this change is that now it's possible
to take those generated line meshes and concatenate() them together,
achieving what's otherwise not implemented yet, such as drawing several
disconnected line strips or loops together.

It's all still partially private (the custom mesh attribute names are)
and I'm marking both APIs as experimental now to hint that it's not in
the final form/functionality yet. In particular, the data layout
optimizations described in the shader docs aren't used by these tools
yet, and if/once the line-specific vertex attributes become builtin,
compileLines() will not need to exist anymore as compile() will handle
that directly.
pull/617/head
Vladimír Vondruš 3 years ago
parent
commit
ad8aca370c
  1. 3
      doc/generated/shaders.cpp
  2. 3
      doc/snippets/MagnumShaders-gl.cpp
  3. 2
      src/Magnum/MeshTools/CMakeLists.txt
  4. 51
      src/Magnum/MeshTools/CompileLines.cpp
  5. 29
      src/Magnum/MeshTools/CompileLines.h
  6. 37
      src/Magnum/MeshTools/GenerateLines.cpp
  7. 89
      src/Magnum/MeshTools/GenerateLines.h
  8. 9
      src/Magnum/MeshTools/Test/CMakeLists.txt
  9. 54
      src/Magnum/MeshTools/Test/CompileLinesGLTest.cpp
  10. 185
      src/Magnum/MeshTools/Test/GenerateLinesTest.cpp
  11. 5
      src/Magnum/Shaders/LineGL.h

3
doc/generated/shaders.cpp

@ -56,6 +56,7 @@
#include <Magnum/Math/Matrix4.h> #include <Magnum/Math/Matrix4.h>
#include <Magnum/MeshTools/Compile.h> #include <Magnum/MeshTools/Compile.h>
#include <Magnum/MeshTools/CompileLines.h> #include <Magnum/MeshTools/CompileLines.h>
#include <Magnum/MeshTools/GenerateLines.h>
#include <Magnum/MeshTools/Interleave.h> #include <Magnum/MeshTools/Interleave.h>
#include <Magnum/Primitives/Square.h> #include <Magnum/Primitives/Square.h>
#include <Magnum/Primitives/Circle.h> #include <Magnum/Primitives/Circle.h>
@ -211,7 +212,7 @@ Containers::StringView ShaderVisualizer::line() {
.setViewportSize(Vector2{ImageSize}) .setViewportSize(Vector2{ImageSize})
.setWidth(5.0f) .setWidth(5.0f)
.setSmoothness(1.0f) .setSmoothness(1.0f)
.draw(MeshTools::compileLines(mesh)); .draw(MeshTools::compileLines(MeshTools::generateLines(mesh)));
return "line.png"; return "line.png";
} }

3
doc/snippets/MagnumShaders-gl.cpp

@ -52,6 +52,7 @@
#include "Magnum/Math/FunctionsBatch.h" #include "Magnum/Math/FunctionsBatch.h"
#include "Magnum/MeshTools/Compile.h" #include "Magnum/MeshTools/Compile.h"
#include "Magnum/MeshTools/Duplicate.h" #include "Magnum/MeshTools/Duplicate.h"
#include "Magnum/MeshTools/GenerateLines.h"
#include "Magnum/Shaders/DistanceFieldVectorGL.h" #include "Magnum/Shaders/DistanceFieldVectorGL.h"
#include "Magnum/Shaders/FlatGL.h" #include "Magnum/Shaders/FlatGL.h"
#include "Magnum/Shaders/MeshVisualizerGL.h" #include "Magnum/Shaders/MeshVisualizerGL.h"
@ -703,7 +704,7 @@ vert.addSource(Utility::format(
{ {
/* [LineGL-usage] */ /* [LineGL-usage] */
Trade::MeshData circle = Primitives::circle2DWireframe(16); Trade::MeshData circle = Primitives::circle2DWireframe(16);
GL::Mesh mesh = MeshTools::compileLines(circle); GL::Mesh mesh = MeshTools::compileLines(MeshTools::generateLines(circle));
/* [LineGL-usage] */ /* [LineGL-usage] */
} }

2
src/Magnum/MeshTools/CMakeLists.txt

@ -41,6 +41,7 @@ set(MagnumMeshTools_GracefulAssert_SRCS
FilterAttributes.cpp FilterAttributes.cpp
FlipNormals.cpp FlipNormals.cpp
GenerateIndices.cpp GenerateIndices.cpp
GenerateLines.cpp
GenerateNormals.cpp GenerateNormals.cpp
Interleave.cpp Interleave.cpp
Reference.cpp Reference.cpp
@ -56,6 +57,7 @@ set(MagnumMeshTools_HEADERS
FilterAttributes.h FilterAttributes.h
FlipNormals.h FlipNormals.h
GenerateIndices.h GenerateIndices.h
GenerateLines.h
GenerateNormals.h GenerateNormals.h
Interleave.h Interleave.h
InterleaveFlags.h InterleaveFlags.h

51
src/Magnum/MeshTools/CompileLines.cpp

@ -25,10 +25,12 @@
#include "CompileLines.h" #include "CompileLines.h"
#include <Corrade/Containers/Optional.h>
#include "Magnum/GL/Buffer.h" #include "Magnum/GL/Buffer.h"
#include "Magnum/GL/Mesh.h" #include "Magnum/GL/Mesh.h"
#include "Magnum/MeshTools/Compile.h" #include "Magnum/MeshTools/Compile.h"
#include "Magnum/MeshTools/Implementation/GenerateLines.h" #include "Magnum/MeshTools/GenerateLines.h"
/* This header is included only privately and doesn't introduce any linker /* This header is included only privately and doesn't introduce any linker
dependency (taking just the PreviousPosition, NextPosition and Annotation dependency (taking just the PreviousPosition, NextPosition and Annotation
@ -38,13 +40,16 @@
namespace Magnum { namespace MeshTools { namespace Magnum { namespace MeshTools {
GL::Mesh compileLines(const Trade::MeshData& lineMesh) { GL::Mesh compileLines(const Trade::MeshData& mesh) {
Trade::MeshData mesh = Implementation::generateLines(lineMesh); /* The assertion checks might be a bit excessive but the custom attributes
*may* conflict with some other user-defined ones so better rule that out
#ifdef CORRADE_GRACEFUL_ASSERT as much as possible */
/* If it asserted inside, bail */ CORRADE_ASSERT(
if(!mesh.attributeCount()) return GL::Mesh{}; mesh.primitive() == MeshPrimitive::Triangles &&
#endif mesh.hasAttribute(Implementation::LineMeshAttributePreviousPosition) &&
mesh.hasAttribute(Implementation::LineMeshAttributeNextPosition) &&
mesh.hasAttribute(Implementation::LineMeshAttributeAnnotation),
"MeshTools::compileLines(): the mesh wasn't produced with generateLines()", GL::Mesh{});
/* Upload the buffers, bind the line-specific attributes manually */ /* Upload the buffers, bind the line-specific attributes manually */
GL::Buffer indices{GL::Buffer::TargetHint::ElementArray, mesh.indexData()}; GL::Buffer indices{GL::Buffer::TargetHint::ElementArray, mesh.indexData()};
@ -56,28 +61,28 @@ GL::Mesh compileLines(const Trade::MeshData& lineMesh) {
static_assert(Shaders::GenericGL3D::TextureCoordinates::Location == UnsignedInt(Shaders::LineGL3D::Annotation::Location), ""); static_assert(Shaders::GenericGL3D::TextureCoordinates::Location == UnsignedInt(Shaders::LineGL3D::Annotation::Location), "");
static_assert(Shaders::GenericGL3D::Tangent::Location == UnsignedInt(Shaders::LineGL3D::PreviousPosition::Location), ""); static_assert(Shaders::GenericGL3D::Tangent::Location == UnsignedInt(Shaders::LineGL3D::PreviousPosition::Location), "");
static_assert(Shaders::GenericGL3D::Normal::Location == UnsignedInt(Shaders::LineGL3D::NextPosition::Location), ""); static_assert(Shaders::GenericGL3D::Normal::Location == UnsignedInt(Shaders::LineGL3D::NextPosition::Location), "");
if(const Containers::Optional<UnsignedInt> id = lineMesh.findAttributeId(Trade::MeshAttribute::TextureCoordinates)) if(const Containers::Optional<UnsignedInt> id = mesh.findAttributeId(Trade::MeshAttribute::TextureCoordinates))
Warning{} << "MeshTools::compileLines():" << lineMesh.attributeName(*id) << "conflicts with line annotation attribute, ignoring"; Warning{} << "MeshTools::compileLines():" << mesh.attributeName(*id) << "conflicts with line annotation attribute, ignoring";
if(const Containers::Optional<UnsignedInt> id = lineMesh.findAttributeId(Trade::MeshAttribute::Tangent)) if(const Containers::Optional<UnsignedInt> id = mesh.findAttributeId(Trade::MeshAttribute::Tangent))
Warning{} << "MeshTools::compileLines():" << lineMesh.attributeName(*id) << "conflicts with line previous position attribute, ignoring"; Warning{} << "MeshTools::compileLines():" << mesh.attributeName(*id) << "conflicts with line previous position attribute, ignoring";
if(const Containers::Optional<UnsignedInt> id = lineMesh.findAttributeId(Trade::MeshAttribute::Normal)) if(const Containers::Optional<UnsignedInt> id = mesh.findAttributeId(Trade::MeshAttribute::Normal))
Warning{} << "MeshTools::compileLines():" << lineMesh.attributeName(*id) << "conflicts with line next position attribute, ignoring"; Warning{} << "MeshTools::compileLines():" << mesh.attributeName(*id) << "conflicts with line next position attribute, ignoring";
/* PreviousPosition / NextPosition are bound to the same location in both /* PreviousPosition / NextPosition are bound to the same location in both
2D and 3D, using the 3D variant so it can be trimmed to just two 2D and 3D, using the 3D variant so it can be trimmed to just two
components in 2D (which wouldn't be possible the other way around) */ components in 2D (which wouldn't be possible the other way around) */
out.addVertexBuffer(vertices, out.addVertexBuffer(vertices,
mesh.attributeOffset(Implementation::MeshAttributePreviousPosition), mesh.attributeOffset(Implementation::LineMeshAttributePreviousPosition),
mesh.attributeStride(Implementation::MeshAttributePreviousPosition), mesh.attributeStride(Implementation::LineMeshAttributePreviousPosition),
GL::DynamicAttribute{Shaders::LineGL3D::PreviousPosition{}, mesh.attributeFormat(Implementation::MeshAttributePreviousPosition)}); GL::DynamicAttribute{Shaders::LineGL3D::PreviousPosition{}, mesh.attributeFormat(Implementation::LineMeshAttributePreviousPosition)});
out.addVertexBuffer(vertices, out.addVertexBuffer(vertices,
mesh.attributeOffset(Implementation::MeshAttributeNextPosition), mesh.attributeOffset(Implementation::LineMeshAttributeNextPosition),
mesh.attributeStride(Implementation::MeshAttributeNextPosition), mesh.attributeStride(Implementation::LineMeshAttributeNextPosition),
GL::DynamicAttribute{Shaders::LineGL3D::NextPosition{}, mesh.attributeFormat(Implementation::MeshAttributeNextPosition)}); GL::DynamicAttribute{Shaders::LineGL3D::NextPosition{}, mesh.attributeFormat(Implementation::LineMeshAttributeNextPosition)});
out.addVertexBuffer(std::move(vertices), out.addVertexBuffer(std::move(vertices),
mesh.attributeOffset(Implementation::MeshAttributeAnnotation), mesh.attributeOffset(Implementation::LineMeshAttributeAnnotation),
mesh.attributeStride(Implementation::MeshAttributeAnnotation), mesh.attributeStride(Implementation::LineMeshAttributeAnnotation),
GL::DynamicAttribute{Shaders::LineGL3D::Annotation{}, mesh.attributeFormat(Implementation::MeshAttributeAnnotation)}); GL::DynamicAttribute{Shaders::LineGL3D::Annotation{}, mesh.attributeFormat(Implementation::LineMeshAttributeAnnotation)});
return out; return out;
} }

29
src/Magnum/MeshTools/CompileLines.h

@ -45,31 +45,10 @@ namespace Magnum { namespace MeshTools {
@brief Compile a line mesh for use with @ref Shaders::LineGL @brief Compile a line mesh for use with @ref Shaders::LineGL
@m_since_latest @m_since_latest
Returns a @ref MeshPrimitive::Triangles mesh with Expects that the @p mesh is returned from @ref generateLines(), see its
@ref MeshIndexType::UnsignedInt indices, all input attributes preserved in documentation for more information.
their original format, and additionally with
@ref Shaders::LineGL::PreviousPosition and @ref Shaders::LineGL::NextPosition
attributes added in the same format as the input
@ref Trade::MeshAttribute::Position, and the @ref Shaders::LineGL::Annotation
attribute as @ref VertexFormat::UnsignedInt, according to the
@ref Shaders-LineGL-mesh-representation documentation of the shader.
Each line segment in the input vertices is converted to a quad, with first two @experimental
vertices inheriting vertex data from the first point of the segment and second
two vertices inheriting data from the second point of the segment. If the input
mesh is indexed, it's deindexed first. Neighbor information from a
@ref MeshPrimitive::LineStrip or @ref MeshPrimitive::LineLoop mesh is used to
form a single contiguous strip or a loop, @ref MeshPrimitive::Lines is treated
as loose segments.
For compatibility with shaders other than @ref Shaders::LineGL, the output mesh
can be also interpreted as indexed @ref MeshPrimitive::Lines --- out of every
six indices forming a quad, two will form a line segment between the two
original points, and the remaining four collapse into two degenerate line
segments.
Expects that the mesh contains at least a @ref Trade::MeshAttribute::Position
and is a line @relativeref{Magnum,MeshPrimitive}.
@note This function is available only if Magnum is compiled with @note This function is available only if Magnum is compiled with
@ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features @ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features
@ -82,7 +61,7 @@ and is a line @relativeref{Magnum,MeshPrimitive}.
is not available in WebGL 1.0, thus neither this function is defined in is not available in WebGL 1.0, thus neither this function is defined in
WebGL 1.0 builds. WebGL 1.0 builds.
*/ */
MAGNUM_MESHTOOLS_EXPORT GL::Mesh compileLines(const Trade::MeshData& lineMesh); MAGNUM_MESHTOOLS_EXPORT GL::Mesh compileLines(const Trade::MeshData& mesh);
}} }}
#else #else

37
src/Magnum/MeshTools/Implementation/GenerateLines.h → src/Magnum/MeshTools/GenerateLines.cpp

@ -1,5 +1,3 @@
#ifndef Magnum_MeshTools_Implementation_GenerateLines_h
#define Magnum_MeshTools_Implementation_GenerateLines_h
/* /*
This file is part of Magnum. This file is part of Magnum.
@ -25,6 +23,8 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include "GenerateLines.h"
#include <Corrade/Containers/GrowableArray.h> #include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/Optional.h> #include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/StridedArrayView.h> #include <Corrade/Containers/StridedArrayView.h>
@ -32,30 +32,19 @@
#include "Magnum/MeshTools/Duplicate.h" #include "Magnum/MeshTools/Duplicate.h"
#include "Magnum/MeshTools/GenerateIndices.h" #include "Magnum/MeshTools/GenerateIndices.h"
#include "Magnum/Trade/MeshData.h"
/* This header is included only privately and doesn't introduce any linker /* This header is included only privately and doesn't introduce any linker
dependency (taking just the LineVertexAnnotations enum), thus it's dependency (taking just the LineVertexAnnotations enum), thus it's
completely safe to not link to the Shaders library */ completely safe to not link to the Shaders library */
#include "Magnum/Shaders/Line.h" #include "Magnum/Shaders/Line.h"
namespace Magnum { namespace MeshTools { namespace Implementation { namespace { namespace Magnum { namespace MeshTools {
/* This code is used internally by compileLines() and returns a MeshData
instance in order to be testable. The intention is to eventually make this
API public and the custom attributes builtin in order to make it possible to
(for example) produce glTF files that are already directly renderable as
wide line meshes. */
constexpr Trade::MeshAttribute MeshAttributePreviousPosition = Trade::meshAttributeCustom(32765);
constexpr Trade::MeshAttribute MeshAttributeNextPosition = Trade::meshAttributeCustom(32766);
constexpr Trade::MeshAttribute MeshAttributeAnnotation = Trade::meshAttributeCustom(32767);
Trade::MeshData generateLines(const Trade::MeshData& lineMesh) { Trade::MeshData generateLines(const Trade::MeshData& lineMesh) {
CORRADE_ASSERT(lineMesh.primitive() == MeshPrimitive::Lines || CORRADE_ASSERT(lineMesh.primitive() == MeshPrimitive::Lines ||
lineMesh.primitive() == MeshPrimitive::LineStrip || lineMesh.primitive() == MeshPrimitive::LineStrip ||
lineMesh.primitive() == MeshPrimitive::LineLoop, lineMesh.primitive() == MeshPrimitive::LineLoop,
"Trade::MeshTools::compileLines(): expected a line primitive, got" << lineMesh.primitive(), (Trade::MeshData{MeshPrimitive::Triangles, 0})); "Trade::MeshTools::generateLines(): expected a line primitive, got" << lineMesh.primitive(), (Trade::MeshData{MeshPrimitive::Triangles, 0}));
/** @todo this will assert if the count in MeshData is wrong, check here /** @todo this will assert if the count in MeshData is wrong, check here
already */ already */
@ -97,19 +86,19 @@ Trade::MeshData generateLines(const Trade::MeshData& lineMesh) {
/* Position is required, everything else is optional */ /* Position is required, everything else is optional */
const Containers::Optional<UnsignedInt> positionAttributeId = lineMesh.findAttributeId(Trade::MeshAttribute::Position); const Containers::Optional<UnsignedInt> positionAttributeId = lineMesh.findAttributeId(Trade::MeshAttribute::Position);
CORRADE_ASSERT(positionAttributeId, CORRADE_ASSERT(positionAttributeId,
"Trade::MeshTools::compileLines(): the mesh has no positions", (Trade::MeshData{MeshPrimitive::Triangles, 0})); "Trade::MeshTools::generateLines(): the mesh has no positions", (Trade::MeshData{MeshPrimitive::Triangles, 0}));
/* Duplicate the input mesh to have each input line segment turned into /* Duplicate the input mesh to have each input line segment turned into
four vertices for a quad. Allocate space for the additional attributes four vertices for a quad. Allocate space for the additional attributes
as well. */ as well. */
Trade::MeshData mesh = duplicate(Trade::MeshData{MeshPrimitive::Triangles, {}, pointDuplicationIndices, Trade::MeshIndexData{pointDuplicationIndices}, {}, lineMesh.vertexData(), Trade::meshAttributeDataNonOwningArray(lineMesh.attributeData()), lineMesh.vertexCount()}, { Trade::MeshData mesh = duplicate(Trade::MeshData{MeshPrimitive::Triangles, {}, pointDuplicationIndices, Trade::MeshIndexData{pointDuplicationIndices}, {}, lineMesh.vertexData(), Trade::meshAttributeDataNonOwningArray(lineMesh.attributeData()), lineMesh.vertexCount()}, {
Trade::MeshAttributeData{MeshAttributePreviousPosition, Trade::MeshAttributeData{Implementation::LineMeshAttributePreviousPosition,
lineMesh.attributeFormat(*positionAttributeId), nullptr}, lineMesh.attributeFormat(*positionAttributeId), nullptr},
Trade::MeshAttributeData{MeshAttributeNextPosition, Trade::MeshAttributeData{Implementation::LineMeshAttributeNextPosition,
lineMesh.attributeFormat(*positionAttributeId), nullptr}, lineMesh.attributeFormat(*positionAttributeId), nullptr},
/** @todo use a 8-bit type and make the attribute non-interleaved to /** @todo use a 8-bit type and make the attribute non-interleaved to
save space? */ save space? */
Trade::MeshAttributeData{MeshAttributeAnnotation, Trade::MeshAttributeData{Implementation::LineMeshAttributeAnnotation,
VertexFormat::UnsignedInt, nullptr}, VertexFormat::UnsignedInt, nullptr},
}); });
@ -123,12 +112,12 @@ Trade::MeshData generateLines(const Trade::MeshData& lineMesh) {
{positions.size()[0]/2, 2, positions.size()[1]}, {positions.size()[0]/2, 2, positions.size()[1]},
{positions.stride()[0]*2, positions.stride()[0], positions.stride()[1]}}; {positions.stride()[0]*2, positions.stride()[0], positions.stride()[1]}};
const Containers::StridedArrayView2D<char> previousPositions = mesh.mutableAttribute(MeshAttributePreviousPosition); const Containers::StridedArrayView2D<char> previousPositions = mesh.mutableAttribute(Implementation::LineMeshAttributePreviousPosition);
const Containers::StridedArrayView3D<char> previousPositions3{mesh.mutableVertexData(), static_cast<char*>(previousPositions.data()), const Containers::StridedArrayView3D<char> previousPositions3{mesh.mutableVertexData(), static_cast<char*>(previousPositions.data()),
{previousPositions.size()[0]/2, 2, previousPositions.size()[1]}, {previousPositions.size()[0]/2, 2, previousPositions.size()[1]},
{previousPositions.stride()[0]*2, previousPositions.stride()[0], previousPositions.stride()[1]}}; {previousPositions.stride()[0]*2, previousPositions.stride()[0], previousPositions.stride()[1]}};
const Containers::StridedArrayView2D<char> nextPositions = mesh.mutableAttribute(MeshAttributeNextPosition); const Containers::StridedArrayView2D<char> nextPositions = mesh.mutableAttribute(Implementation::LineMeshAttributeNextPosition);
const Containers::StridedArrayView3D<char> nextPositions3{mesh.mutableVertexData(), static_cast<char*>(nextPositions.data()), const Containers::StridedArrayView3D<char> nextPositions3{mesh.mutableVertexData(), static_cast<char*>(nextPositions.data()),
{nextPositions.size()[0]/2, 2, nextPositions.size()[1]}, {nextPositions.size()[0]/2, 2, nextPositions.size()[1]},
{nextPositions.stride()[0]*2, nextPositions.stride()[0], nextPositions.stride()[1]}}; {nextPositions.stride()[0]*2, nextPositions.stride()[0], nextPositions.stride()[1]}};
@ -180,7 +169,7 @@ Trade::MeshData generateLines(const Trade::MeshData& lineMesh) {
} }
/* Fill in point annotation */ /* Fill in point annotation */
const Containers::StridedArrayView1D<Shaders::LineVertexAnnotations> annotations = Containers::arrayCast<Shaders::LineVertexAnnotations>(mesh.mutableAttribute<UnsignedInt>(MeshAttributeAnnotation)); const Containers::StridedArrayView1D<Shaders::LineVertexAnnotations> annotations = Containers::arrayCast<Shaders::LineVertexAnnotations>(mesh.mutableAttribute<UnsignedInt>(Implementation::LineMeshAttributeAnnotation));
for(UnsignedInt i = 0; i != quadCount; ++i) { for(UnsignedInt i = 0; i != quadCount; ++i) {
annotations[i*4 + 0] = Shaders::LineVertexAnnotation::Up|Shaders::LineVertexAnnotation::Begin; annotations[i*4 + 0] = Shaders::LineVertexAnnotation::Up|Shaders::LineVertexAnnotation::Begin;
annotations[i*4 + 1] = Shaders::LineVertexAnnotation::Begin; annotations[i*4 + 1] = Shaders::LineVertexAnnotation::Begin;
@ -282,6 +271,4 @@ Trade::MeshData generateLines(const Trade::MeshData& lineMesh) {
mesh.releaseVertexData(), mesh.releaseAttributeData()}; mesh.releaseVertexData(), mesh.releaseAttributeData()};
} }
}}}} }}
#endif

89
src/Magnum/MeshTools/GenerateLines.h

@ -0,0 +1,89 @@
#ifndef Magnum_MeshTools_GenerateLines_h
#define Magnum_MeshTools_GenerateLines_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
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 Function @ref Magnum::MeshTools::generateLines()
* @m_since_latest
*/
#include "Magnum/MeshTools/visibility.h"
#include "Magnum/Trade/MeshData.h" /* needs meshAttributeCustom() for now */
namespace Magnum { namespace MeshTools {
namespace Implementation {
/** @todo make these builtin, and once they are, deprecate compileLines() in
favor of compile() handling these extra attributes as well */
constexpr Trade::MeshAttribute LineMeshAttributePreviousPosition = Trade::meshAttributeCustom(32765);
constexpr Trade::MeshAttribute LineMeshAttributeNextPosition = Trade::meshAttributeCustom(32766);
constexpr Trade::MeshAttribute LineMeshAttributeAnnotation = Trade::meshAttributeCustom(32767);
}
/**
@brief Generate a line mesh for use with @ref Shaders::LineGL
@m_since_latest
Creates a @ref MeshPrimitive::Triangles mesh with
@ref MeshIndexType::UnsignedInt indices, all input attributes preserved in
their original format, and additionally with custom attributes corresponding to
@ref Shaders::LineGL::PreviousPosition and @ref Shaders::LineGL::NextPosition
added in the same format as the input @ref Trade::MeshAttribute::Position, and
a custom attribute corresponding to the @ref Shaders::LineGL::Annotation
attribute as @ref VertexFormat::UnsignedInt. See
@ref Shaders-LineGL-mesh-representation "documentation of the shader" for
details about the internal representation.
Each line segment in the input vertices is converted to a quad, with first two
vertices inheriting vertex data from the first point of the segment and second
two vertices inheriting data from the second point of the segment. If the input
mesh is indexed, it's deindexed first. Neighbor information from a
@ref MeshPrimitive::LineStrip or @ref MeshPrimitive::LineLoop mesh is used to
form a single contiguous strip or a loop, @ref MeshPrimitive::Lines is treated
as loose segments.
For compatibility with shaders other than @ref Shaders::LineGL, the output mesh
can be also interpreted as indexed @ref MeshPrimitive::Lines --- out of every
six indices forming a quad, two will form a line segment between the two
original points, and the remaining four collapse into two degenerate line
segments.
Expects that the mesh contains at least a @ref Trade::MeshAttribute::Position
and is a line @relativeref{Magnum,MeshPrimitive}.
The returned @ref Trade::MeshData instance is meant to be passed to
@ref compileLines() for use with the shader. It can however be also processed
with other @ref MeshTools first, such as @ref compressIndices(const Trade::MeshData&, MeshIndexType) or @ref concatenate().
@experimental
*/
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData generateLines(const Trade::MeshData& lineMesh);
}}
#endif

9
src/Magnum/MeshTools/Test/CMakeLists.txt

@ -35,6 +35,9 @@ corrade_add_test(MeshToolsDuplicateTest DuplicateTest.cpp LIBRARIES MagnumMeshTo
corrade_add_test(MeshToolsFilterAttributesTest FilterAttributesTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsFilterAttributesTest FilterAttributesTest.cpp LIBRARIES MagnumMeshToolsTestLib)
corrade_add_test(MeshToolsFlipNormalsTest FlipNormalsTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsFlipNormalsTest FlipNormalsTest.cpp LIBRARIES MagnumMeshToolsTestLib)
corrade_add_test(MeshToolsGenerateIndicesTest GenerateIndicesTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsGenerateIndicesTest GenerateIndicesTest.cpp LIBRARIES MagnumMeshToolsTestLib)
corrade_add_test(MeshToolsGenerateLinesTest GenerateLinesTest.cpp
# Needs to link to Shaders for debug output for LineVertexAnnotations
LIBRARIES MagnumMeshToolsTestLib MagnumShaders)
corrade_add_test(MeshToolsGenerateNormalsTest GenerateNormalsTest.cpp LIBRARIES MagnumMeshToolsTestLib MagnumPrimitives) corrade_add_test(MeshToolsGenerateNormalsTest GenerateNormalsTest.cpp LIBRARIES MagnumMeshToolsTestLib MagnumPrimitives)
corrade_add_test(MeshToolsInterleaveTest InterleaveTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsInterleaveTest InterleaveTest.cpp LIBRARIES MagnumMeshToolsTestLib)
corrade_add_test(MeshToolsReferenceTest ReferenceTest.cpp LIBRARIES MagnumMeshToolsTestLib MagnumPrimitives) corrade_add_test(MeshToolsReferenceTest ReferenceTest.cpp LIBRARIES MagnumMeshToolsTestLib MagnumPrimitives)
@ -43,12 +46,6 @@ corrade_add_test(MeshToolsSubdivideTest SubdivideTest.cpp LIBRARIES Magnum Magnu
corrade_add_test(MeshToolsTipsifyTest TipsifyTest.cpp LIBRARIES MagnumMeshTools) corrade_add_test(MeshToolsTipsifyTest TipsifyTest.cpp LIBRARIES MagnumMeshTools)
corrade_add_test(MeshToolsTransformTest TransformTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsTransformTest TransformTest.cpp LIBRARIES MagnumMeshToolsTestLib)
if(NOT MAGNUM_TARGET_GLES2)
corrade_add_test(MeshToolsCompileLinesTest CompileLinesTest.cpp
# Needs to link to Shaders for debug output for LineVertexAnnotations
LIBRARIES MagnumMeshToolsTestLib MagnumShaders)
endif()
# Graceful assert for testing # Graceful assert for testing
set_property(TARGET set_property(TARGET
MeshToolsConcatenateTest MeshToolsConcatenateTest

54
src/Magnum/MeshTools/Test/CompileLinesGLTest.cpp

@ -45,6 +45,7 @@
#include "Magnum/Math/Matrix4.h" #include "Magnum/Math/Matrix4.h"
#include "Magnum/MeshTools/Compile.h" #include "Magnum/MeshTools/Compile.h"
#include "Magnum/MeshTools/CompileLines.h" #include "Magnum/MeshTools/CompileLines.h"
#include "Magnum/MeshTools/GenerateLines.h"
#include "Magnum/Shaders/FlatGL.h" #include "Magnum/Shaders/FlatGL.h"
#include "Magnum/Shaders/Line.h" #include "Magnum/Shaders/Line.h"
#include "Magnum/Shaders/LineGL.h" #include "Magnum/Shaders/LineGL.h"
@ -68,7 +69,7 @@ struct CompileLinesGLTest: GL::OpenGLTester {
void emptyMesh(); void emptyMesh();
void notLines(); void notGeneratedLineMesh();
void noAttributes(); void noAttributes();
void noPositionAttribute(); void noPositionAttribute();
@ -128,9 +129,7 @@ CompileLinesGLTest::CompileLinesGLTest() {
addTests({&CompileLinesGLTest::emptyMesh, addTests({&CompileLinesGLTest::emptyMesh,
&CompileLinesGLTest::notLines, &CompileLinesGLTest::notGeneratedLineMesh});
&CompileLinesGLTest::noAttributes,
&CompileLinesGLTest::noPositionAttribute});
/* Load the plugins directly from the build tree. Otherwise they're either /* Load the plugins directly from the build tree. Otherwise they're either
static and already loaded or not present in the build tree */ static and already loaded or not present in the build tree */
@ -174,7 +173,7 @@ void CompileLinesGLTest::twoDimensions() {
if(data.colors) if(data.colors)
arrayAppend(attributes, InPlaceInit, Trade::MeshAttribute::Color, vertices.slice(&Vertex::color)); arrayAppend(attributes, InPlaceInit, Trade::MeshAttribute::Color, vertices.slice(&Vertex::color));
GL::Mesh mesh = compileLines(Trade::MeshData{MeshPrimitive::LineLoop, {}, vertexData, std::move(attributes)}); GL::Mesh mesh = compileLines(generateLines(Trade::MeshData{MeshPrimitive::LineLoop, {}, vertexData, std::move(attributes)}));
Shaders::LineGL2D shader{Shaders::LineGL2D::Configuration{} Shaders::LineGL2D shader{Shaders::LineGL2D::Configuration{}
.setFlags(data.colors ? Shaders::LineGL2D::Flag::VertexColor : Shaders::LineGL2D::Flags{}) .setFlags(data.colors ? Shaders::LineGL2D::Flag::VertexColor : Shaders::LineGL2D::Flags{})
@ -206,9 +205,9 @@ void CompileLinesGLTest::threeDimensions() {
{-1.0f, +1.0f, -0.5f}, {-1.0f, +1.0f, -0.5f},
}; };
GL::Mesh mesh = compileLines(Trade::MeshData{MeshPrimitive::LineLoop, {}, positions, { GL::Mesh mesh = compileLines(generateLines(Trade::MeshData{MeshPrimitive::LineLoop, {}, positions, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(positions)} Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(positions)}
}}); }}));
Shaders::LineGL3D{} Shaders::LineGL3D{}
.setViewportSize({32, 32}) .setViewportSize({32, 32})
@ -268,7 +267,7 @@ void CompileLinesGLTest::linePrimitiveCompatibility() {
/* Render the line mesh with the primitive set back to lines. The index /* Render the line mesh with the primitive set back to lines. The index
buffer layout should be compatible with it, and produce the same buffer layout should be compatible with it, and produce the same
result. */ result. */
shader.draw(compileLines(lineMeshData).setPrimitive(MeshPrimitive::Lines)); shader.draw(compileLines(generateLines(lineMeshData)).setPrimitive(MeshPrimitive::Lines));
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE_WITH( CORRADE_COMPARE_WITH(
_framebuffer.read({{}, {32, 32}}, {PixelFormat::RGBA8Unorm}), _framebuffer.read({{}, {32, 32}}, {PixelFormat::RGBA8Unorm}),
@ -298,10 +297,10 @@ void CompileLinesGLTest::conflictingAttributes() {
}; };
auto vertices = Containers::stridedArrayView(vertexData); auto vertices = Containers::stridedArrayView(vertexData);
Trade::MeshData lineMesh{MeshPrimitive::LineLoop, {}, vertexData, { Trade::MeshData lineMesh = generateLines(Trade::MeshData{MeshPrimitive::LineLoop, {}, vertexData, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, vertices.slice(&Vertex::position)}, Trade::MeshAttributeData{Trade::MeshAttribute::Position, vertices.slice(&Vertex::position)},
Trade::MeshAttributeData{data.attribute, data.format, vertices.slice(&Vertex::extra)}, Trade::MeshAttributeData{data.attribute, data.format, vertices.slice(&Vertex::extra)},
}}; }});
std::ostringstream out; std::ostringstream out;
GL::Mesh mesh{NoCreate}; GL::Mesh mesh{NoCreate};
@ -325,48 +324,31 @@ void CompileLinesGLTest::conflictingAttributes() {
} }
void CompileLinesGLTest::emptyMesh() { void CompileLinesGLTest::emptyMesh() {
GL::Mesh mesh = compileLines(Trade::MeshData{MeshPrimitive::LineLoop, {}, nullptr, { GL::Mesh mesh = compileLines(generateLines(Trade::MeshData{MeshPrimitive::LineLoop, {}, nullptr, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, VertexFormat::Vector2, nullptr} Trade::MeshAttributeData{Trade::MeshAttribute::Position, VertexFormat::Vector2, nullptr}
}}); }}));
CORRADE_COMPARE(mesh.primitive(), GL::MeshPrimitive::Triangles); CORRADE_COMPARE(mesh.primitive(), GL::MeshPrimitive::Triangles);
CORRADE_VERIFY(mesh.isIndexed()); CORRADE_VERIFY(mesh.isIndexed());
CORRADE_COMPARE(mesh.count(), 0); CORRADE_COMPARE(mesh.count(), 0);
} }
void CompileLinesGLTest::notLines() { void CompileLinesGLTest::notGeneratedLineMesh() {
CORRADE_SKIP_IF_NO_ASSERT(); CORRADE_SKIP_IF_NO_ASSERT();
Vector3 positions[3]{}; Vector3 positions[3]{};
std::ostringstream out; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};
compileLines(Trade::MeshData{MeshPrimitive::TriangleFan, {}, positions, { compileLines(Trade::MeshData{MeshPrimitive::Lines, {}, positions, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(positions)} Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(positions)}
}}); }});
CORRADE_COMPARE(out.str(), "Trade::MeshTools::compileLines(): expected a line primitive, got MeshPrimitive::TriangleFan\n"); compileLines(Trade::MeshData{MeshPrimitive::Triangles, {}, positions, {
} Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(positions)}
void CompileLinesGLTest::noAttributes() {
CORRADE_SKIP_IF_NO_ASSERT();
std::ostringstream out;
Error redirectError{&out};
compileLines(Trade::MeshData{MeshPrimitive::Lines, 12});
CORRADE_COMPARE(out.str(), "Trade::MeshTools::compileLines(): the mesh has no positions\n");
}
void CompileLinesGLTest::noPositionAttribute() {
CORRADE_SKIP_IF_NO_ASSERT();
Vector3 colors[2]{};
std::ostringstream out;
Error redirectError{&out};
compileLines(Trade::MeshData{MeshPrimitive::Lines, {}, colors, {
Trade::MeshAttributeData{Trade::MeshAttribute::Color, Containers::stridedArrayView(colors)}
}}); }});
CORRADE_COMPARE(out.str(), "Trade::MeshTools::compileLines(): the mesh has no positions\n"); CORRADE_COMPARE(out.str(),
"MeshTools::compileLines(): the mesh wasn't produced with generateLines()\n"
"MeshTools::compileLines(): the mesh wasn't produced with generateLines()\n");
} }
}}}} }}}}

185
src/Magnum/MeshTools/Test/CompileLinesTest.cpp → src/Magnum/MeshTools/Test/GenerateLinesTest.cpp

@ -26,14 +26,17 @@
#include <sstream> #include <sstream>
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h> #include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/Utility/DebugStl.h>
#include "Magnum/Math/Color.h"
#include "Magnum/Math/PackingBatch.h" #include "Magnum/Math/PackingBatch.h"
#include "Magnum/MeshTools/Implementation/GenerateLines.h" #include "Magnum/MeshTools/GenerateLines.h"
#include "Magnum/Shaders/Line.h"
namespace Magnum { namespace MeshTools { namespace Test { namespace { namespace Magnum { namespace MeshTools { namespace Test { namespace {
struct CompileLinesTest: TestSuite::Tester { struct GenerateLinesTest: TestSuite::Tester {
explicit CompileLinesTest(); explicit GenerateLinesTest();
template<class T> void oneLoop(); template<class T> void oneLoop();
@ -41,9 +44,10 @@ struct CompileLinesTest: TestSuite::Tester {
void zeroVertices(); void zeroVertices();
void twoVerticesStrip(); void twoVerticesStrip();
void twoVerticesLoop(); void twoVerticesLoop();
/* Non-line primitives and absence of position attribute tested in
CompileLinesGLTest, to verify it's not accessed earlier than the void notLines();
assertion */ void noAttributes();
void noPositionAttribute();
}; };
using namespace Math::Literals; using namespace Math::Literals;
@ -103,20 +107,24 @@ const struct {
/** @todo closed (indexed) strip, once arbitrary index buffer looping is supported */ /** @todo closed (indexed) strip, once arbitrary index buffer looping is supported */
}; };
CompileLinesTest::CompileLinesTest() { GenerateLinesTest::GenerateLinesTest() {
addInstancedTests<CompileLinesTest>({ addInstancedTests<GenerateLinesTest>({
&CompileLinesTest::oneLoop<UnsignedInt>, &GenerateLinesTest::oneLoop<UnsignedInt>,
&CompileLinesTest::oneLoop<UnsignedShort>, &GenerateLinesTest::oneLoop<UnsignedShort>,
&CompileLinesTest::oneLoop<UnsignedByte>}, &GenerateLinesTest::oneLoop<UnsignedByte>},
Containers::arraySize(OneLoopData)); Containers::arraySize(OneLoopData));
addTests({&CompileLinesTest::extraAttributes, addTests({&GenerateLinesTest::extraAttributes,
&CompileLinesTest::zeroVertices, &GenerateLinesTest::zeroVertices,
&CompileLinesTest::twoVerticesStrip, &GenerateLinesTest::twoVerticesStrip,
&CompileLinesTest::twoVerticesLoop}); &GenerateLinesTest::twoVerticesLoop,
&GenerateLinesTest::notLines,
&GenerateLinesTest::noAttributes,
&GenerateLinesTest::noPositionAttribute});
} }
template<class T> void CompileLinesTest::oneLoop() { template<class T> void GenerateLinesTest::oneLoop() {
auto&& data = OneLoopData[testCaseInstanceId()]; auto&& data = OneLoopData[testCaseInstanceId()];
setTestCaseTemplateName(Math::TypeTraits<T>::name()); setTestCaseTemplateName(Math::TypeTraits<T>::name());
setTestCaseDescription(data.name); setTestCaseDescription(data.name);
@ -130,7 +138,7 @@ template<class T> void CompileLinesTest::oneLoop() {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, stridedArrayView(data.positions)} Trade::MeshAttributeData{Trade::MeshAttribute::Position, stridedArrayView(data.positions)}
}}; }};
Trade::MeshData mesh = Implementation::generateLines(lineMesh); Trade::MeshData mesh = generateLines(lineMesh);
CORRADE_COMPARE(mesh.primitive(), MeshPrimitive::Triangles); CORRADE_COMPARE(mesh.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(mesh.attributeCount(), 4); CORRADE_COMPARE(mesh.attributeCount(), 4);
@ -179,14 +187,14 @@ template<class T> void CompileLinesTest::oneLoop() {
{-1.0f, -1.0f}, {-1.0f, -1.0f} {-1.0f, -1.0f}, {-1.0f, -1.0f}
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition)); CORRADE_VERIFY(mesh.hasAttribute(Implementation::LineMeshAttributePreviousPosition));
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributeNextPosition)); CORRADE_VERIFY(mesh.hasAttribute(Implementation::LineMeshAttributeNextPosition));
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributeAnnotation)); CORRADE_VERIFY(mesh.hasAttribute(Implementation::LineMeshAttributeAnnotation));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributePreviousPosition), VertexFormat::Vector2); CORRADE_COMPARE(mesh.attributeFormat(Implementation::LineMeshAttributePreviousPosition), VertexFormat::Vector2);
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeNextPosition), VertexFormat::Vector2); CORRADE_COMPARE(mesh.attributeFormat(Implementation::LineMeshAttributeNextPosition), VertexFormat::Vector2);
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeAnnotation), VertexFormat::UnsignedInt); CORRADE_COMPARE(mesh.attributeFormat(Implementation::LineMeshAttributeAnnotation), VertexFormat::UnsignedInt);
if(data.expectedJoins && data.expectedJoinsFirstLast) { if(data.expectedJoins && data.expectedJoinsFirstLast) {
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributePreviousPosition), Containers::arrayView<Vector2>({ CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::LineMeshAttributePreviousPosition), Containers::arrayView<Vector2>({
positions[12], positions[12], positions[12], positions[12],
positions[0], positions[0], positions[0], positions[0],
positions[0], positions[0], positions[0], positions[0],
@ -196,7 +204,7 @@ template<class T> void CompileLinesTest::oneLoop() {
positions[8], positions[8], positions[8], positions[8],
positions[12], positions[12], positions[12], positions[12],
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributeNextPosition), Containers::arrayView<Vector2>({ CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::LineMeshAttributeNextPosition), Containers::arrayView<Vector2>({
positions[2], positions[2], positions[2], positions[2],
positions[6], positions[6], positions[6], positions[6],
positions[6], positions[6], positions[6], positions[6],
@ -206,7 +214,7 @@ template<class T> void CompileLinesTest::oneLoop() {
positions[14], positions[14], positions[14], positions[14],
positions[2], positions[2], positions[2], positions[2],
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::MeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({ CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::LineMeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({
Shaders::LineVertexAnnotation::Up| Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin| Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join, Shaders::LineVertexAnnotation::Join,
@ -241,7 +249,7 @@ template<class T> void CompileLinesTest::oneLoop() {
Shaders::LineVertexAnnotation::Join, Shaders::LineVertexAnnotation::Join,
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
} else if(data.expectedJoins) { } else if(data.expectedJoins) {
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributePreviousPosition), Containers::arrayView<Vector2>({ CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::LineMeshAttributePreviousPosition), Containers::arrayView<Vector2>({
{}, {}, {}, {},
positions[0], positions[0], positions[0], positions[0],
positions[0], positions[0], positions[0], positions[0],
@ -251,7 +259,7 @@ template<class T> void CompileLinesTest::oneLoop() {
positions[8], positions[8], positions[8], positions[8],
positions[12], positions[12], positions[12], positions[12],
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributeNextPosition), Containers::arrayView<Vector2>({ CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::LineMeshAttributeNextPosition), Containers::arrayView<Vector2>({
positions[2], positions[2], positions[2], positions[2],
positions[6], positions[6], positions[6], positions[6],
positions[6], positions[6], positions[6], positions[6],
@ -261,7 +269,7 @@ template<class T> void CompileLinesTest::oneLoop() {
positions[14], positions[14], positions[14], positions[14],
{}, {} {}, {}
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::MeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({ CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::LineMeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({
Shaders::LineVertexAnnotation::Up| Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin, Shaders::LineVertexAnnotation::Begin,
Shaders::LineVertexAnnotation::Begin, Shaders::LineVertexAnnotation::Begin,
@ -293,7 +301,7 @@ template<class T> void CompileLinesTest::oneLoop() {
{}, {},
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
} else { } else {
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributePreviousPosition), Containers::arrayView<Vector2>({ CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::LineMeshAttributePreviousPosition), Containers::arrayView<Vector2>({
{}, {}, {}, {},
positions[0], positions[0], positions[0], positions[0],
{}, {}, {}, {},
@ -303,7 +311,7 @@ template<class T> void CompileLinesTest::oneLoop() {
{}, {}, {}, {},
positions[12], positions[12], positions[12], positions[12],
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributeNextPosition), Containers::arrayView<Vector2>({ CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::LineMeshAttributeNextPosition), Containers::arrayView<Vector2>({
positions[2], positions[2], positions[2], positions[2],
{}, {}, {}, {},
positions[6], positions[6], positions[6], positions[6],
@ -313,7 +321,7 @@ template<class T> void CompileLinesTest::oneLoop() {
positions[14], positions[14], positions[14], positions[14],
{}, {}, {}, {},
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::MeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({ CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::LineMeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({
Shaders::LineVertexAnnotation::Up| Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin, Shaders::LineVertexAnnotation::Begin,
Shaders::LineVertexAnnotation::Begin, Shaders::LineVertexAnnotation::Begin,
@ -338,7 +346,7 @@ template<class T> void CompileLinesTest::oneLoop() {
} }
} }
void CompileLinesTest::extraAttributes() { void GenerateLinesTest::extraAttributes() {
const struct Vertex { const struct Vertex {
Color3ub color; Color3ub color;
Vector3b position; Vector3b position;
@ -360,7 +368,7 @@ void CompileLinesTest::extraAttributes() {
Trade::MeshAttributeData{Trade::MeshAttribute::ObjectId, vertices.slice(&Vertex::objectId)}, Trade::MeshAttributeData{Trade::MeshAttribute::ObjectId, vertices.slice(&Vertex::objectId)},
}}; }};
Trade::MeshData mesh = Implementation::generateLines(lineMesh); Trade::MeshData mesh = generateLines(lineMesh);
CORRADE_COMPARE(mesh.primitive(), MeshPrimitive::Triangles); CORRADE_COMPARE(mesh.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(mesh.attributeCount(), 6); CORRADE_COMPARE(mesh.attributeCount(), 6);
@ -417,14 +425,14 @@ void CompileLinesTest::extraAttributes() {
156, 156, 156, 156,
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition)); CORRADE_VERIFY(mesh.hasAttribute(Implementation::LineMeshAttributePreviousPosition));
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributeNextPosition)); CORRADE_VERIFY(mesh.hasAttribute(Implementation::LineMeshAttributeNextPosition));
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributeAnnotation)); CORRADE_VERIFY(mesh.hasAttribute(Implementation::LineMeshAttributeAnnotation));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributePreviousPosition), VertexFormat::Vector3b); CORRADE_COMPARE(mesh.attributeFormat(Implementation::LineMeshAttributePreviousPosition), VertexFormat::Vector3b);
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeNextPosition), VertexFormat::Vector3b); CORRADE_COMPARE(mesh.attributeFormat(Implementation::LineMeshAttributeNextPosition), VertexFormat::Vector3b);
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeAnnotation), VertexFormat::UnsignedInt); CORRADE_COMPARE(mesh.attributeFormat(Implementation::LineMeshAttributeAnnotation), VertexFormat::UnsignedInt);
CORRADE_COMPARE_AS(mesh.attribute<Vector3b>(Implementation::MeshAttributePreviousPosition), Containers::arrayView<Vector3b>({ CORRADE_COMPARE_AS(mesh.attribute<Vector3b>(Implementation::LineMeshAttributePreviousPosition), Containers::arrayView<Vector3b>({
positions[12], positions[12], positions[12], positions[12],
positions[0], positions[0], positions[0], positions[0],
positions[0], positions[0], positions[0], positions[0],
@ -434,7 +442,7 @@ void CompileLinesTest::extraAttributes() {
positions[8], positions[8], positions[8], positions[8],
positions[12], positions[12], positions[12], positions[12],
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(mesh.attribute<Vector3b>(Implementation::MeshAttributeNextPosition), Containers::arrayView<Vector3b>({ CORRADE_COMPARE_AS(mesh.attribute<Vector3b>(Implementation::LineMeshAttributeNextPosition), Containers::arrayView<Vector3b>({
positions[2], positions[2], positions[2], positions[2],
positions[6], positions[6], positions[6], positions[6],
positions[6], positions[6], positions[6], positions[6],
@ -444,7 +452,7 @@ void CompileLinesTest::extraAttributes() {
positions[14], positions[14], positions[14], positions[14],
positions[2], positions[2], positions[2], positions[2],
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::MeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({ CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::LineMeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({
Shaders::LineVertexAnnotation::Up| Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin| Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join, Shaders::LineVertexAnnotation::Join,
@ -480,23 +488,23 @@ void CompileLinesTest::extraAttributes() {
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
} }
void CompileLinesTest::zeroVertices() { void GenerateLinesTest::zeroVertices() {
Trade::MeshData lineMesh{MeshPrimitive::LineLoop, nullptr, { Trade::MeshData lineMesh{MeshPrimitive::LineLoop, nullptr, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, VertexFormat::Vector3usNormalized, nullptr} Trade::MeshAttributeData{Trade::MeshAttribute::Position, VertexFormat::Vector3usNormalized, nullptr}
}}; }};
Trade::MeshData mesh = Implementation::generateLines(lineMesh); Trade::MeshData mesh = generateLines(lineMesh);
CORRADE_COMPARE(mesh.primitive(), MeshPrimitive::Triangles); CORRADE_COMPARE(mesh.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(mesh.attributeCount(), 4); CORRADE_COMPARE(mesh.attributeCount(), 4);
CORRADE_COMPARE(mesh.vertexCount(), 0); CORRADE_COMPARE(mesh.vertexCount(), 0);
CORRADE_VERIFY(mesh.hasAttribute(Trade::MeshAttribute::Position)); CORRADE_VERIFY(mesh.hasAttribute(Trade::MeshAttribute::Position));
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition)); CORRADE_VERIFY(mesh.hasAttribute(Implementation::LineMeshAttributePreviousPosition));
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition)); CORRADE_VERIFY(mesh.hasAttribute(Implementation::LineMeshAttributePreviousPosition));
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributeAnnotation)); CORRADE_VERIFY(mesh.hasAttribute(Implementation::LineMeshAttributeAnnotation));
} }
void CompileLinesTest::twoVerticesStrip() { void GenerateLinesTest::twoVerticesStrip() {
Vector2 positionData[]{ Vector2 positionData[]{
{-1.0f, 0.0f}, {-1.0f, 0.0f},
{+1.0f, 0.0f} {+1.0f, 0.0f}
@ -506,7 +514,7 @@ void CompileLinesTest::twoVerticesStrip() {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(positionData)} Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(positionData)}
}}; }};
Trade::MeshData mesh = Implementation::generateLines(lineMesh); Trade::MeshData mesh = generateLines(lineMesh);
CORRADE_COMPARE(mesh.primitive(), MeshPrimitive::Triangles); CORRADE_COMPARE(mesh.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(mesh.attributeCount(), 4); CORRADE_COMPARE(mesh.attributeCount(), 4);
@ -524,23 +532,23 @@ void CompileLinesTest::twoVerticesStrip() {
{+1.0f, 0.0f}, {+1.0f, 0.0f}, {+1.0f, 0.0f}, {+1.0f, 0.0f},
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition)); CORRADE_VERIFY(mesh.hasAttribute(Implementation::LineMeshAttributePreviousPosition));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributePreviousPosition), VertexFormat::Vector2); CORRADE_COMPARE(mesh.attributeFormat(Implementation::LineMeshAttributePreviousPosition), VertexFormat::Vector2);
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributePreviousPosition), Containers::arrayView<Vector2>({ CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::LineMeshAttributePreviousPosition), Containers::arrayView<Vector2>({
{}, {}, {}, {},
positions[0], positions[0], positions[0], positions[0],
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition)); CORRADE_VERIFY(mesh.hasAttribute(Implementation::LineMeshAttributePreviousPosition));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeNextPosition), VertexFormat::Vector2); CORRADE_COMPARE(mesh.attributeFormat(Implementation::LineMeshAttributeNextPosition), VertexFormat::Vector2);
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributeNextPosition), Containers::arrayView<Vector2>({ CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::LineMeshAttributeNextPosition), Containers::arrayView<Vector2>({
positions[2], positions[2], positions[2], positions[2],
{}, {}, {}, {},
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributeAnnotation)); CORRADE_VERIFY(mesh.hasAttribute(Implementation::LineMeshAttributeAnnotation));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeAnnotation), VertexFormat::UnsignedInt); CORRADE_COMPARE(mesh.attributeFormat(Implementation::LineMeshAttributeAnnotation), VertexFormat::UnsignedInt);
CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::MeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({ CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::LineMeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({
Shaders::LineVertexAnnotation::Up| Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin, Shaders::LineVertexAnnotation::Begin,
Shaders::LineVertexAnnotation::Begin, Shaders::LineVertexAnnotation::Begin,
@ -549,7 +557,7 @@ void CompileLinesTest::twoVerticesStrip() {
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
} }
void CompileLinesTest::twoVerticesLoop() { void GenerateLinesTest::twoVerticesLoop() {
Vector2 positionData[]{ Vector2 positionData[]{
{-1.0f, 0.0f}, {-1.0f, 0.0f},
{+1.0f, 0.0f} {+1.0f, 0.0f}
@ -559,7 +567,7 @@ void CompileLinesTest::twoVerticesLoop() {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(positionData)} Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(positionData)}
}}; }};
Trade::MeshData mesh = Implementation::generateLines(lineMesh); Trade::MeshData mesh = generateLines(lineMesh);
CORRADE_COMPARE(mesh.primitive(), MeshPrimitive::Triangles); CORRADE_COMPARE(mesh.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(mesh.attributeCount(), 4); CORRADE_COMPARE(mesh.attributeCount(), 4);
@ -582,27 +590,27 @@ void CompileLinesTest::twoVerticesLoop() {
{-1.0f, 0.0f}, {-1.0f, 0.0f}, {-1.0f, 0.0f}, {-1.0f, 0.0f},
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition)); CORRADE_VERIFY(mesh.hasAttribute(Implementation::LineMeshAttributePreviousPosition));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributePreviousPosition), VertexFormat::Vector2); CORRADE_COMPARE(mesh.attributeFormat(Implementation::LineMeshAttributePreviousPosition), VertexFormat::Vector2);
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributePreviousPosition), Containers::arrayView<Vector2>({ CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::LineMeshAttributePreviousPosition), Containers::arrayView<Vector2>({
positions[4], positions[4], positions[4], positions[4],
positions[0], positions[0], positions[0], positions[0],
positions[0], positions[0], positions[0], positions[0],
positions[4], positions[4], positions[4], positions[4],
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition)); CORRADE_VERIFY(mesh.hasAttribute(Implementation::LineMeshAttributePreviousPosition));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeNextPosition), VertexFormat::Vector2); CORRADE_COMPARE(mesh.attributeFormat(Implementation::LineMeshAttributeNextPosition), VertexFormat::Vector2);
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributeNextPosition), Containers::arrayView<Vector2>({ CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::LineMeshAttributeNextPosition), Containers::arrayView<Vector2>({
positions[2], positions[2], positions[2], positions[2],
positions[6], positions[6], positions[6], positions[6],
positions[6], positions[6], positions[6], positions[6],
positions[2], positions[2], positions[2], positions[2],
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributeAnnotation)); CORRADE_VERIFY(mesh.hasAttribute(Implementation::LineMeshAttributeAnnotation));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeAnnotation), VertexFormat::UnsignedInt); CORRADE_COMPARE(mesh.attributeFormat(Implementation::LineMeshAttributeAnnotation), VertexFormat::UnsignedInt);
CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::MeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({ CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::LineMeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({
Shaders::LineVertexAnnotation::Up| Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin| Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join, Shaders::LineVertexAnnotation::Join,
@ -622,6 +630,41 @@ void CompileLinesTest::twoVerticesLoop() {
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
} }
void GenerateLinesTest::notLines() {
CORRADE_SKIP_IF_NO_ASSERT();
Vector3 positions[3]{};
std::ostringstream out;
Error redirectError{&out};
generateLines(Trade::MeshData{MeshPrimitive::TriangleFan, {}, positions, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(positions)}
}});
CORRADE_COMPARE(out.str(), "Trade::MeshTools::generateLines(): expected a line primitive, got MeshPrimitive::TriangleFan\n");
}
void GenerateLinesTest::noAttributes() {
CORRADE_SKIP_IF_NO_ASSERT();
std::ostringstream out;
Error redirectError{&out};
generateLines(Trade::MeshData{MeshPrimitive::Lines, 12});
CORRADE_COMPARE(out.str(), "Trade::MeshTools::generateLines(): the mesh has no positions\n");
}
void GenerateLinesTest::noPositionAttribute() {
CORRADE_SKIP_IF_NO_ASSERT();
Vector3 colors[2]{};
std::ostringstream out;
Error redirectError{&out};
generateLines(Trade::MeshData{MeshPrimitive::Lines, {}, colors, {
Trade::MeshAttributeData{Trade::MeshAttribute::Color, Containers::stridedArrayView(colors)}
}});
CORRADE_COMPARE(out.str(), "Trade::MeshTools::generateLines(): the mesh has no positions\n");
}
}}}} }}}}
CORRADE_TEST_MAIN(Magnum::MeshTools::Test::CompileLinesTest) CORRADE_TEST_MAIN(Magnum::MeshTools::Test::GenerateLinesTest)

5
src/Magnum/Shaders/LineGL.h

@ -88,8 +88,9 @@ describing point wíth their surroundings, and @ref Annotation with
point-specific annotation such as whether given point is a line cap or a join point-specific annotation such as whether given point is a line cap or a join
with neighboring segment. The data layout is described in detail in with neighboring segment. The data layout is described in detail in
@ref Shaders-LineGL-mesh-representation below, however in practice it's easiest @ref Shaders-LineGL-mesh-representation below, however in practice it's easiest
to compile an existing line @ref Trade::MeshData to the desired form using to convert an existing line @ref Trade::MeshData to a form accepted by this
@ref MeshTools::compileLines(): shader with @ref MeshTools::generateLines() and then compile it to a
@ref GL::Mesh with @ref MeshTools::compileLines():
@snippet MagnumShaders-gl.cpp LineGL-usage @snippet MagnumShaders-gl.cpp LineGL-usage

Loading…
Cancel
Save