Browse Source

MeshTools: explicit generateSmoothNormals() overloads for each index type.

The templated version had the unfortunate "feature" of not being able to
figure out the type when an array view or a C array got passed to it.
That led to worse-than-ideal UX and even though it's now a bit more
verbose on the implementation side, it's the preferred solution.
pull/371/head
Vladimír Vondruš 6 years ago
parent
commit
29f0fdb188
  1. 2
      doc/snippets/MagnumMeshTools-stl.cpp
  2. 2
      src/Magnum/MeshTools/Compile.cpp
  3. 46
      src/Magnum/MeshTools/GenerateNormals.cpp
  4. 36
      src/Magnum/MeshTools/GenerateNormals.h
  5. 33
      src/Magnum/MeshTools/Test/GenerateNormalsTest.cpp

2
doc/snippets/MagnumMeshTools-stl.cpp

@ -58,7 +58,7 @@ std::vector<UnsignedInt> indices;
std::vector<Vector3> positions; std::vector<Vector3> positions;
std::vector<Vector3> normals{positions.size()}; std::vector<Vector3> normals{positions.size()};
MeshTools::generateSmoothNormalsInto<UnsignedInt>(indices, positions, normals); MeshTools::generateSmoothNormalsInto(indices, positions, normals);
/* [generateSmoothNormalsInto] */ /* [generateSmoothNormalsInto] */
} }

2
src/Magnum/MeshTools/Compile.cpp

@ -199,7 +199,7 @@ GL::Mesh compile(const Trade::MeshData3D& meshData, CompileFlags flags) {
normalStorage = generateFlatNormals(positions); normalStorage = generateFlatNormals(positions);
useIndices = false; useIndices = false;
} else { } else {
normalStorage = generateSmoothNormals<UnsignedInt>(meshData.indices(), positions); normalStorage = generateSmoothNormals(meshData.indices(), positions);
useIndices = true; useIndices = true;
} }

46
src/Magnum/MeshTools/GenerateNormals.cpp

@ -86,6 +86,8 @@ std::pair<std::vector<UnsignedInt>, std::vector<Vector3>> generateFlatNormals(co
} }
#endif #endif
namespace {
#if defined(CORRADE_MSVC2019_COMPATIBILITY) && !defined(CORRADE_MSVC2017_COMPATIBILITY) #if defined(CORRADE_MSVC2019_COMPATIBILITY) && !defined(CORRADE_MSVC2017_COMPATIBILITY)
/* When using /permissive- with MSVC2019, using namespace inside the function /* When using /permissive- with MSVC2019, using namespace inside the function
below FOR SOME REASON gets lost when instantiating the template. That's below FOR SOME REASON gets lost when instantiating the template. That's
@ -94,7 +96,7 @@ std::pair<std::vector<UnsignedInt>, std::vector<Vector3>> generateFlatNormals(co
using namespace Math::Literals; using namespace Math::Literals;
#endif #endif
template<class T> void generateSmoothNormalsInto(const Containers::StridedArrayView1D<const T>& indices, const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<Vector3>& normals) { template<class T> inline void generateSmoothNormalsIntoImplementation(const Containers::StridedArrayView1D<const T>& indices, const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<Vector3>& normals) {
CORRADE_ASSERT(indices.size() % 3 == 0, CORRADE_ASSERT(indices.size() % 3 == 0,
"MeshTools::generateSmoothNormalsInto(): index count not divisible by 3", ); "MeshTools::generateSmoothNormalsInto(): index count not divisible by 3", );
CORRADE_ASSERT(normals.size() == positions.size(), CORRADE_ASSERT(normals.size() == positions.size(),
@ -217,22 +219,42 @@ template<class T> void generateSmoothNormalsInto(const Containers::StridedArrayV
} }
} }
#ifndef DOXYGEN_GENERATING_OUTPUT }
template void generateSmoothNormalsInto<UnsignedByte>(const Containers::StridedArrayView1D<const UnsignedByte>&, const Containers::StridedArrayView1D<const Vector3>&, const Containers::StridedArrayView1D<Vector3>&);
template void generateSmoothNormalsInto<UnsignedShort>(const Containers::StridedArrayView1D<const UnsignedShort>&, const Containers::StridedArrayView1D<const Vector3>&, const Containers::StridedArrayView1D<Vector3>&); /* If not done this way but with templates instead, C++ wouldn't be able to
template void generateSmoothNormalsInto<UnsignedInt>(const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const Vector3>&, const Containers::StridedArrayView1D<Vector3>&); figure out on its own which overload to use when indices are not already a
#endif strided arrray view */
void generateSmoothNormalsInto(const Containers::StridedArrayView1D<const UnsignedByte>& indices, const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<Vector3>& normals) {
generateSmoothNormalsIntoImplementation(indices, positions, normals);
}
void generateSmoothNormalsInto(const Containers::StridedArrayView1D<const UnsignedShort>& indices, const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<Vector3>& normals) {
generateSmoothNormalsIntoImplementation(indices, positions, normals);
}
void generateSmoothNormalsInto(const Containers::StridedArrayView1D<const UnsignedInt>& indices, const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<Vector3>& normals) {
generateSmoothNormalsIntoImplementation(indices, positions, normals);
}
template<class T> Containers::Array<Vector3> generateSmoothNormals(const Containers::StridedArrayView1D<const T>& indices, const Containers::StridedArrayView1D<const Vector3>& positions) { namespace {
template<class T> inline Containers::Array<Vector3> generateSmoothNormalsImplementation(const Containers::StridedArrayView1D<const T>& indices, const Containers::StridedArrayView1D<const Vector3>& positions) {
Containers::Array<Vector3> out{Containers::NoInit, positions.size()}; Containers::Array<Vector3> out{Containers::NoInit, positions.size()};
generateSmoothNormalsInto(indices, positions, out); generateSmoothNormalsInto(indices, positions, out);
return out; return out;
} }
#ifndef DOXYGEN_GENERATING_OUTPUT }
template Containers::Array<Vector3> generateSmoothNormals<UnsignedByte>(const Containers::StridedArrayView1D<const UnsignedByte>&, const Containers::StridedArrayView1D<const Vector3>&);
template Containers::Array<Vector3> generateSmoothNormals<UnsignedShort>(const Containers::StridedArrayView1D<const UnsignedShort>&, const Containers::StridedArrayView1D<const Vector3>&); /* If not done this way but with templates instead, C++ wouldn't be able to
template Containers::Array<Vector3> generateSmoothNormals<UnsignedInt>(const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const Vector3>&); figure out on its own which overload to use when indices are not already a
#endif strided arrray view */
Containers::Array<Vector3> generateSmoothNormals(const Containers::StridedArrayView1D<const UnsignedByte>& indices, const Containers::StridedArrayView1D<const Vector3>& positions) {
return generateSmoothNormalsImplementation(indices, positions);
}
Containers::Array<Vector3> generateSmoothNormals(const Containers::StridedArrayView1D<const UnsignedShort>& indices, const Containers::StridedArrayView1D<const Vector3>& positions) {
return generateSmoothNormalsImplementation(indices, positions);
}
Containers::Array<Vector3> generateSmoothNormals(const Containers::StridedArrayView1D<const UnsignedInt>& indices, const Containers::StridedArrayView1D<const Vector3>& positions) {
return generateSmoothNormalsImplementation(indices, positions);
}
}} }}

36
src/Magnum/MeshTools/GenerateNormals.h

@ -118,13 +118,19 @@ Martijn Buijs.
@see @ref generateSmoothNormalsInto(), @ref generateFlatNormals(), @see @ref generateSmoothNormalsInto(), @ref generateFlatNormals(),
@ref MeshTools::CompileFlag::GenerateSmoothNormals @ref MeshTools::CompileFlag::GenerateSmoothNormals
*/ */
template<class T> MAGNUM_MESHTOOLS_EXPORT Containers::Array<Vector3> generateSmoothNormals(const Containers::StridedArrayView1D<const T>& indices, const Containers::StridedArrayView1D<const Vector3>& positions); MAGNUM_MESHTOOLS_EXPORT Containers::Array<Vector3> generateSmoothNormals(const Containers::StridedArrayView1D<const UnsignedInt>& indices, const Containers::StridedArrayView1D<const Vector3>& positions);
#if defined(CORRADE_TARGET_WINDOWS) && !defined(__MINGW32__) /**
extern template MAGNUM_MESHTOOLS_EXPORT Containers::Array<Vector3> generateSmoothNormals<UnsignedByte>(const Containers::StridedArrayView1D<const UnsignedByte>&, const Containers::StridedArrayView1D<const Vector3>&); * @overload
extern template MAGNUM_MESHTOOLS_EXPORT Containers::Array<Vector3> generateSmoothNormals<UnsignedShort>(const Containers::StridedArrayView1D<const UnsignedShort>&, const Containers::StridedArrayView1D<const Vector3>&); * @m_since{2019,10}
extern template MAGNUM_MESHTOOLS_EXPORT Containers::Array<Vector3> generateSmoothNormals<UnsignedInt>(const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const Vector3>&); */
#endif MAGNUM_MESHTOOLS_EXPORT Containers::Array<Vector3> generateSmoothNormals(const Containers::StridedArrayView1D<const UnsignedShort>& indices, const Containers::StridedArrayView1D<const Vector3>& positions);
/**
* @overload
* @m_since{2019,10}
*/
MAGNUM_MESHTOOLS_EXPORT Containers::Array<Vector3> generateSmoothNormals(const Containers::StridedArrayView1D<const UnsignedByte>& indices, const Containers::StridedArrayView1D<const Vector3>& positions);
/** /**
@brief Generate smooth normals into an existing array @brief Generate smooth normals into an existing array
@ -147,13 +153,19 @@ conversions:
@see @ref generateFlatNormalsInto() @see @ref generateFlatNormalsInto()
*/ */
template<class T> MAGNUM_MESHTOOLS_EXPORT void generateSmoothNormalsInto(const Containers::StridedArrayView1D<const T>& indices, const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<Vector3>& normals); MAGNUM_MESHTOOLS_EXPORT void generateSmoothNormalsInto(const Containers::StridedArrayView1D<const UnsignedInt>& indices, const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<Vector3>& normals);
#if defined(CORRADE_TARGET_WINDOWS) && !defined(__MINGW32__) /**
extern template MAGNUM_MESHTOOLS_EXPORT void generateSmoothNormalsInto<UnsignedByte>(const Containers::StridedArrayView1D<const UnsignedByte>&, const Containers::StridedArrayView1D<const Vector3>&, const Containers::StridedArrayView1D<Vector3>&); * @overload
extern template MAGNUM_MESHTOOLS_EXPORT void generateSmoothNormalsInto<UnsignedShort>(const Containers::StridedArrayView1D<const UnsignedShort>&, const Containers::StridedArrayView1D<const Vector3>&, const Containers::StridedArrayView1D<Vector3>&); * @m_since{2019,10}
extern template MAGNUM_MESHTOOLS_EXPORT void generateSmoothNormalsInto<UnsignedInt>(const Containers::StridedArrayView1D<const UnsignedInt>&, const Containers::StridedArrayView1D<const Vector3>&, const Containers::StridedArrayView1D<Vector3>&); */
#endif MAGNUM_MESHTOOLS_EXPORT void generateSmoothNormalsInto(const Containers::StridedArrayView1D<const UnsignedShort>& indices, const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<Vector3>& normals);
/**
* @overload
* @m_since{2019,10}
*/
MAGNUM_MESHTOOLS_EXPORT void generateSmoothNormalsInto(const Containers::StridedArrayView1D<const UnsignedByte>& indices, const Containers::StridedArrayView1D<const Vector3>& positions, const Containers::StridedArrayView1D<Vector3>& normals);
}} }}

33
src/Magnum/MeshTools/Test/GenerateNormalsTest.cpp

@ -51,7 +51,7 @@ struct GenerateNormalsTest: TestSuite::Tester {
void flatWrongCount(); void flatWrongCount();
void flatIntoWrongSize(); void flatIntoWrongSize();
void smoothTwoTriangles(); template<class T> void smoothTwoTriangles();
void smoothCube(); void smoothCube();
void smoothBeveledCube(); void smoothBeveledCube();
void smoothCylinder(); void smoothCylinder();
@ -72,7 +72,9 @@ GenerateNormalsTest::GenerateNormalsTest() {
&GenerateNormalsTest::flatWrongCount, &GenerateNormalsTest::flatWrongCount,
&GenerateNormalsTest::flatIntoWrongSize, &GenerateNormalsTest::flatIntoWrongSize,
&GenerateNormalsTest::smoothTwoTriangles, &GenerateNormalsTest::smoothTwoTriangles<UnsignedByte>,
&GenerateNormalsTest::smoothTwoTriangles<UnsignedShort>,
&GenerateNormalsTest::smoothTwoTriangles<UnsignedInt>,
&GenerateNormalsTest::smoothCube, &GenerateNormalsTest::smoothCube,
&GenerateNormalsTest::smoothBeveledCube, &GenerateNormalsTest::smoothBeveledCube,
&GenerateNormalsTest::smoothCylinder, &GenerateNormalsTest::smoothCylinder,
@ -155,12 +157,14 @@ void GenerateNormalsTest::flatIntoWrongSize() {
CORRADE_COMPARE(out.str(), "MeshTools::generateFlatNormalsInto(): bad output size, expected 6 but got 7\n"); CORRADE_COMPARE(out.str(), "MeshTools::generateFlatNormalsInto(): bad output size, expected 6 but got 7\n");
} }
void GenerateNormalsTest::smoothTwoTriangles() { template<class T> void GenerateNormalsTest::smoothTwoTriangles() {
const UnsignedInt indices[]{0, 1, 2, 3, 4, 5}; setTestCaseTemplateName(Math::TypeTraits<T>::name());
const T indices[]{0, 1, 2, 3, 4, 5};
/* Should generate the same output as flat normals */ /* Should generate the same output as flat normals */
CORRADE_COMPARE_AS( CORRADE_COMPARE_AS(
generateSmoothNormals(Containers::stridedArrayView(indices), TwoTriangles), generateSmoothNormals(indices, TwoTriangles),
(Containers::Array<Vector3>{Containers::InPlaceInit, { (Containers::Array<Vector3>{Containers::InPlaceInit, {
Vector3::zAxis(), Vector3::zAxis(),
Vector3::zAxis(), Vector3::zAxis(),
@ -194,7 +198,7 @@ void GenerateNormalsTest::smoothCube() {
/* Normals should be the same as positions, only normalized */ /* Normals should be the same as positions, only normalized */
CORRADE_COMPARE_AS( CORRADE_COMPARE_AS(
generateSmoothNormals(Containers::stridedArrayView(indices), positions), generateSmoothNormals(indices, positions),
(Containers::Array<Vector3>{Containers::InPlaceInit, { (Containers::Array<Vector3>{Containers::InPlaceInit, {
positions[0]/Constants::sqrt3(), positions[0]/Constants::sqrt3(),
positions[1]/Constants::sqrt3(), positions[1]/Constants::sqrt3(),
@ -207,7 +211,6 @@ void GenerateNormalsTest::smoothCube() {
}}), TestSuite::Compare::Container); }}), TestSuite::Compare::Container);
} }
constexpr Vector3 BeveledCubePositions[] { constexpr Vector3 BeveledCubePositions[] {
{-1.0f, -0.6f, 1.1f}, {-1.0f, -0.6f, 1.1f},
{ 1.0f, -0.6f, 1.1f}, { 1.0f, -0.6f, 1.1f},
@ -284,7 +287,7 @@ void GenerateNormalsTest::smoothBeveledCube() {
Vector3 x{0.996072f, 0.0754969f, 0.0462723f}; Vector3 x{0.996072f, 0.0754969f, 0.0462723f};
Vector3 y{0.0467958f, 0.997808f, 0.0467958f}; Vector3 y{0.0467958f, 0.997808f, 0.0467958f};
CORRADE_COMPARE_AS(generateSmoothNormals( CORRADE_COMPARE_AS(generateSmoothNormals(
Containers::stridedArrayView(BeveledCubeIndices), BeveledCubePositions), BeveledCubeIndices, BeveledCubePositions),
(Containers::Array<Vector3>{Containers::InPlaceInit, { (Containers::Array<Vector3>{Containers::InPlaceInit, {
z*Math::sign(BeveledCubePositions[ 0]), z*Math::sign(BeveledCubePositions[ 0]),
z*Math::sign(BeveledCubePositions[ 1]), z*Math::sign(BeveledCubePositions[ 1]),
@ -341,8 +344,7 @@ void GenerateNormalsTest::smoothZeroAreaTriangle() {
0, 1, 2, 1, 2, 1 0, 1, 2, 1, 2, 1
}; };
CORRADE_COMPARE_AS(generateSmoothNormals( CORRADE_COMPARE_AS(generateSmoothNormals(indices, positions),
Containers::stridedArrayView(indices), positions),
(Containers::Array<Vector3>{Containers::InPlaceInit, { (Containers::Array<Vector3>{Containers::InPlaceInit, {
Vector3::zAxis(), Vector3::zAxis(),
Vector3::zAxis(), Vector3::zAxis(),
@ -364,8 +366,7 @@ void GenerateNormalsTest::smoothNanPosition() {
0, 1, 2, 1, 2, 1 0, 1, 2, 1, 2, 1
}; };
Containers::Array<Vector3> generated = generateSmoothNormals( Containers::Array<Vector3> generated = generateSmoothNormals(indices, positions);
Containers::stridedArrayView(indices), positions);
CORRADE_COMPARE_AS(generated.prefix(3), CORRADE_COMPARE_AS(generated.prefix(3),
(Containers::Array<Vector3>{Containers::InPlaceInit, { (Containers::Array<Vector3>{Containers::InPlaceInit, {
Vector3::zAxis(), Vector3::zAxis(),
@ -381,7 +382,7 @@ void GenerateNormalsTest::smoothWrongCount() {
const UnsignedByte indices[7]{}; const UnsignedByte indices[7]{};
const Vector3 positions[1]; const Vector3 positions[1];
generateSmoothNormals(Containers::stridedArrayView(indices), positions); generateSmoothNormals(indices, positions);
CORRADE_COMPARE(out.str(), "MeshTools::generateSmoothNormalsInto(): index count not divisible by 3\n"); CORRADE_COMPARE(out.str(), "MeshTools::generateSmoothNormalsInto(): index count not divisible by 3\n");
} }
@ -392,7 +393,7 @@ void GenerateNormalsTest::smoothIntoWrongSize() {
const UnsignedByte indices[6]{}; const UnsignedByte indices[6]{};
const Vector3 positions[3]; const Vector3 positions[3];
Vector3 normals[4]; Vector3 normals[4];
generateSmoothNormalsInto(Containers::stridedArrayView(indices), positions, normals); generateSmoothNormalsInto(indices, positions, normals);
CORRADE_COMPARE(out.str(), "MeshTools::generateSmoothNormalsInto(): bad output size, expected 3 but got 4\n"); CORRADE_COMPARE(out.str(), "MeshTools::generateSmoothNormalsInto(): bad output size, expected 3 but got 4\n");
} }
@ -412,9 +413,7 @@ void GenerateNormalsTest::benchmarkFlat() {
void GenerateNormalsTest::benchmarkSmooth() { void GenerateNormalsTest::benchmarkSmooth() {
Containers::Array<Vector3> normals{Containers::NoInit, Containers::arraySize(BeveledCubePositions)}; Containers::Array<Vector3> normals{Containers::NoInit, Containers::arraySize(BeveledCubePositions)};
CORRADE_BENCHMARK(10) { CORRADE_BENCHMARK(10) {
generateSmoothNormalsInto( generateSmoothNormalsInto(BeveledCubeIndices, BeveledCubePositions, normals);
Containers::stridedArrayView(BeveledCubeIndices),
BeveledCubePositions, normals);
} }
CORRADE_COMPARE(Math::min(normals), (Vector3{-0.996072f, -0.997808f, -0.996072f})); CORRADE_COMPARE(Math::min(normals), (Vector3{-0.996072f, -0.997808f, -0.996072f}));

Loading…
Cancel
Save