Browse Source

MeshTools: deSTLify flipNormals(), flipFaceWinding() and tipsify().

And rename them to *InPlace(), since that's what they do. The original
STL variants are now deprecated wrappers over the new names. Not
adapting the test yet in order to test everything is alright.
pull/371/head
Vladimír Vondruš 6 years ago
parent
commit
b589f15640
  1. 17
      doc/changelog.dox
  2. 37
      src/Magnum/MeshTools/FlipNormals.cpp
  3. 113
      src/Magnum/MeshTools/FlipNormals.h
  4. 18
      src/Magnum/MeshTools/Implementation/Tipsify.h
  5. 23
      src/Magnum/MeshTools/Test/TipsifyTest.cpp
  6. 58
      src/Magnum/MeshTools/Tipsify.cpp
  7. 41
      src/Magnum/MeshTools/Tipsify.h

17
doc/changelog.dox

@ -219,6 +219,13 @@ See also:
@ref MeshTools::removeDuplicatesInPlace() variant
- Added @ref MeshTools::duplicateInto() variants that take type-erased
2D strided array views
- @ref MeshTools::flipNormalsInPlace() and @ref MeshTools::flipFaceWindingInPlace()
were renamed for clarity and now accept a
@ref Corrade::Containers::StridedArrayView instead of a @ref std::vector,
additionally working on 8- and 16-byte index types as well
- @ref MeshTools::tipsifyInPlace() was renamed for clarity and now accepts a
@ref Corrade::Containers::StridedArrayView instead of a @ref std::vector,
additionally working on 8- and 16-byte index types as well
- Added @ref MeshTools::generateSmoothNormals() and
@ref MeshTools::generateSmoothNormalsInto() variants taking type-erased
index arrays
@ -313,8 +320,8 @@ See also:
- It was not possible to override DPI scaling using
@ref Platform::Sdl2Application::Configuration as command-line arguments
always got a priority (see [mosra/magnum#416](https://github.com/mosra/magnum/issues/416))
- Fixed an otherwise harmless OOB access in @ref MeshTools::tipsify() that
could trigger ASan or debug iterator errors
- Fixed an otherwise harmless OOB access in @ref MeshTools::tipsifyInPlace()
that could trigger ASan or debug iterator errors
- With @ref Corrade/Utility/DebugStl.h not being included, @ref std::string
instances could get accidentally printed as @ref ResourceKey instances.
Added and explicit header dependency to avoid such cases.
@ -356,6 +363,12 @@ See also:
@ref Text::FontConverterFeatures, @ref Trade::ImporterFeatures,
@ref Trade::ImageConverterFeatures enum sets and their corresponding enums
placed directly in the namespace to have them shorter and unambiguous
- @cpp MeshTools::flipNormals() @ce and @cpp MeshTools::flipFaceWinding() @ce
and @cpp MeshTools::tipsify() @ce are deprecated in favor of
@ref MeshTools::flipNormalsInPlace(),
@ref MeshTools::flipFaceWindingInPlace() and @ref MeshTools::tipsifyInPlace()
that accept a @ref Corrade::Containers::StridedArrayView instead of a
@ref std::vector and work with 8- and 16-byte index types as well.
@subsection changelog-latest-compatibility Potential compatibility breakages, removed APIs

37
src/Magnum/MeshTools/FlipNormals.cpp

@ -25,11 +25,16 @@
#include "FlipNormals.h"
#include <vector>
#include <Corrade/Containers/StridedArrayView.h>
#include "Magnum/Math/Vector3.h"
namespace Magnum { namespace MeshTools {
void flipFaceWinding(std::vector<UnsignedInt>& indices) {
namespace {
template<class T> inline void flipFaceWindingInPlaceImplementation(const Containers::StridedArrayView1D<T>& indices) {
CORRADE_ASSERT(!(indices.size()%3), "MeshTools::flipNormals(): index count is not divisible by 3!", );
using std::swap;
@ -37,9 +42,37 @@ void flipFaceWinding(std::vector<UnsignedInt>& indices) {
swap(indices[i+1], indices[i+2]);
}
void flipNormals(std::vector<Vector3>& normals) {
}
void flipFaceWindingInPlace(const Containers::StridedArrayView1D<UnsignedInt>& indices) {
flipFaceWindingInPlaceImplementation(indices);
}
void flipFaceWindingInPlace(const Containers::StridedArrayView1D<UnsignedShort>& indices) {
flipFaceWindingInPlaceImplementation(indices);
}
void flipFaceWindingInPlace(const Containers::StridedArrayView1D<UnsignedByte>& indices) {
flipFaceWindingInPlaceImplementation(indices);
}
void flipNormalsInPlace(const Containers::StridedArrayView1D<Vector3>& normals) {
for(Vector3& normal: normals)
normal = -normal;
}
#ifdef MAGNUM_BUILD_DEPRECATED
void flipNormals(std::vector<UnsignedInt>& indices, std::vector<Vector3>& normals) {
flipNormalsInPlace(indices, normals);
}
void flipFaceWinding(std::vector<UnsignedInt>& indices) {
flipFaceWindingInPlace(indices);
}
void flipNormals(std::vector<Vector3>& normals) {
flipNormalsInPlace(normals);
}
#endif
}}

113
src/Magnum/MeshTools/FlipNormals.h

@ -26,52 +26,119 @@
*/
/** @file
* @brief Function @ref Magnum::MeshTools::flipFaceWinding(), @ref Magnum::MeshTools::flipNormals()
* @brief Function @ref Magnum::MeshTools::flipFaceWindingInPlace(), @ref Magnum::MeshTools::flipNormalsInPlace()
*/
#include <vector>
#include <Corrade/Containers/Containers.h>
#include "Magnum/Magnum.h"
#include "Magnum/MeshTools/visibility.h"
#ifdef MAGNUM_BUILD_DEPRECATED
#include <Corrade/Utility/StlForwardVector.h>
#include <Corrade/Containers/ArrayViewStl.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Utility/Macros.h>
#endif
namespace Magnum { namespace MeshTools {
/**
@brief Flip face winding
@brief Flip mesh normals and face winding in-place
@param[in,out] indices Index array to operate on
@param[in,out] normals Normal array to operate on
The same as @ref flipNormals(std::vector<UnsignedInt>&, std::vector<Vector3>&),
but flips only face winding.
@attention The function requires the mesh to have triangle faces, thus index
count must be divisible by 3.
Flips normal vectors and face winding in index array for face culling to work
properly too. See also @ref flipNormalsInPlace(const Containers::StridedArrayView1D<Vector3>&)
and @ref flipFaceWindingInPlace(), which flip normals or face winding only.
Expects a triangle mesh, thus the index count has to be divisible by 3.
*/
void MAGNUM_MESHTOOLS_EXPORT flipFaceWinding(std::vector<UnsignedInt>& indices);
void flipNormalsInPlace(const Containers::StridedArrayView1D<UnsignedInt>& indices, const Containers::StridedArrayView1D<Vector3>& normals);
/**
@brief Flip mesh normals
@param[in,out] normals Normal array to operate on
* @overload
* @m_since_latest
*/
void flipNormalsInPlace(const Containers::StridedArrayView1D<UnsignedShort>& indices, const Containers::StridedArrayView1D<Vector3>& normals);
The same as @ref flipNormals(std::vector<UnsignedInt>&, std::vector<Vector3>&),
but flips only normals, not face winding.
/**
* @overload
* @m_since_latest
*/
void flipNormalsInPlace(const Containers::StridedArrayView1D<UnsignedByte>& indices, const Containers::StridedArrayView1D<Vector3>& normals);
#ifdef MAGNUM_BUILD_DEPRECATED
/**
@brief @copybrief flipNormalsInPlace(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector3>&)
@m_deprecated_since_latest Use @ref flipNormalsInPlace(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector3>&)
instead.
*/
void MAGNUM_MESHTOOLS_EXPORT flipNormals(std::vector<Vector3>& normals);
CORRADE_DEPRECATED("use flipNormalsInPlace() instead") MAGNUM_MESHTOOLS_EXPORT void flipNormals(std::vector<UnsignedInt>& indices, std::vector<Vector3>& normals);
#endif
/**
@brief Flip mesh normals and face winding
@brief Flip face winding in-place
@param[in,out] indices Index array to operate on
@m_since_latest
Same as @ref flipNormalsInPlace(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector3>&),
but flips only face winding. Expects a triangle mesh, thus the index count has
to be divisible by 3.
*/
void MAGNUM_MESHTOOLS_EXPORT flipFaceWindingInPlace(const Containers::StridedArrayView1D<UnsignedInt>& indices);
/**
* @overload
* @m_since_latest
*/
void MAGNUM_MESHTOOLS_EXPORT flipFaceWindingInPlace(const Containers::StridedArrayView1D<UnsignedShort>& indices);
/**
* @overload
* @m_since_latest
*/
void MAGNUM_MESHTOOLS_EXPORT flipFaceWindingInPlace(const Containers::StridedArrayView1D<UnsignedByte>& indices);
#ifdef MAGNUM_BUILD_DEPRECATED
/**
@brief @copybrief flipFaceWindingInPlace(const Containers::StridedArrayView1D<UnsignedInt>&)
@m_deprecated_since_latest Use @ref flipFaceWindingInPlace(const Containers::StridedArrayView1D<UnsignedInt>&)
instead.
*/
CORRADE_DEPRECATED("use flipFaceWindingInPlace() instead") MAGNUM_MESHTOOLS_EXPORT void flipFaceWinding(std::vector<UnsignedInt>& indices);
#endif
/**
@brief Flip mesh normals in-place
@param[in,out] normals Normal array to operate on
Flips normal vectors and face winding in index array for face culling to work
properly too. See also @ref flipNormals(std::vector<Vector3>&) and
@ref flipFaceWinding(), which flip normals or face winding only.
Same as @ref flipNormalsInPlace(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView1D<Vector3>&),
but flips only normals, not face winding.
*/
void MAGNUM_MESHTOOLS_EXPORT flipNormalsInPlace(const Containers::StridedArrayView1D<Vector3>& normals);
@attention The function requires the mesh to have triangle faces, thus index
count must be divisible by 3.
#ifdef MAGNUM_BUILD_DEPRECATED
/**
@copybrief flipNormalsInPlace(const Containers::StridedArrayView1D<Vector3>&)
@m_deprecated_since_latest Use @ref flipNormalsInPlace(const Containers::StridedArrayView1D<Vector3>&)
instead.
*/
inline void flipNormals(std::vector<UnsignedInt>& indices, std::vector<Vector3>& normals) {
flipFaceWinding(indices);
flipNormals(normals);
CORRADE_DEPRECATED("use flipNormalsInPlace() instead") MAGNUM_MESHTOOLS_EXPORT void flipNormals(std::vector<Vector3>& normals);
#endif
inline void flipNormalsInPlace(const Containers::StridedArrayView1D<UnsignedInt>& indices, const Containers::StridedArrayView1D<Vector3>& normals) {
flipFaceWindingInPlace(indices);
flipNormalsInPlace(normals);
}
inline void flipNormalsInPlace(const Containers::StridedArrayView1D<UnsignedShort>& indices, const Containers::StridedArrayView1D<Vector3>& normals) {
flipFaceWindingInPlace(indices);
flipNormalsInPlace(normals);
}
inline void flipNormalsInPlace(const Containers::StridedArrayView1D<UnsignedByte>& indices, const Containers::StridedArrayView1D<Vector3>& normals) {
flipFaceWindingInPlace(indices);
flipNormalsInPlace(normals);
}
}}

18
src/Magnum/MeshTools/Implementation/Tipsify.h

@ -25,7 +25,8 @@
DEALINGS IN THE SOFTWARE.
*/
#include <vector>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/StridedArrayView.h>
#include "Magnum/Magnum.h"
@ -33,11 +34,10 @@ namespace Magnum { namespace MeshTools { namespace Implementation { namespace {
/* Vertex-triangle adjacency. Computes count and indices of adjacent triangles
for each vertex (used internally by tipsify()) */
void buildAdjacency(const std::vector<UnsignedInt>& indices, const UnsignedInt vertexCount, std::vector<UnsignedInt>& liveTriangleCount, std::vector<UnsignedInt>& neighborOffset, std::vector<UnsignedInt>& neighbors) {
template<class T> void buildAdjacency(const Containers::StridedArrayView1D<const T>& indices, const UnsignedInt vertexCount, Containers::Array<UnsignedInt>& liveTriangleCount, Containers::Array<UnsignedInt>& neighborOffset, Containers::Array<UnsignedInt>& neighbors) {
/* How many times is each vertex referenced == count of neighboring
triangles for each vertex */
liveTriangleCount.clear();
liveTriangleCount.resize(vertexCount);
liveTriangleCount = Containers::Array<UnsignedInt>{vertexCount};
for(std::size_t i = 0; i != indices.size(); ++i)
++liveTriangleCount[indices[i]];
@ -45,19 +45,17 @@ void buildAdjacency(const std::vector<UnsignedInt>& indices, const UnsignedInt v
the end be in interval neighbors[neighborOffset[i]] ;
neighbors[neighborOffset[i+1]]. Currently the values are shifted to
right, because the next loop will shift them back left. */
neighborOffset.clear();
neighborOffset.reserve(vertexCount+1);
neighborOffset.push_back(0);
neighborOffset = Containers::Array<UnsignedInt>{Containers::NoInit, vertexCount + 1};
neighborOffset[0] = 0;
UnsignedInt sum = 0;
for(std::size_t i = 0; i != vertexCount; ++i) {
neighborOffset.push_back(sum);
neighborOffset[i + 1] = sum;
sum += liveTriangleCount[i];
}
/* Array of neighbors, using (and changing) neighborOffset array for
positioning */
neighbors.clear();
neighbors.resize(sum);
neighbors = Containers::Array<UnsignedInt>{Containers::NoInit, sum};
for(std::size_t i = 0; i != indices.size(); ++i)
neighbors[neighborOffset[indices[i]+1]++] = i/3;
}

23
src/Magnum/MeshTools/Test/TipsifyTest.cpp

@ -23,7 +23,9 @@
DEALINGS IN THE SOFTWARE.
*/
#include <Corrade/Containers/Array.h>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h>
#include "Magnum/Magnum.h"
#include "Magnum/MeshTools/Tipsify.h"
@ -54,7 +56,7 @@ struct TipsifyTest: TestSuite::Tester {
*/
const std::vector<UnsignedInt> Indices{
constexpr UnsignedInt Indices[]{
4, 1, 0,
10, 9, 13,
6, 3, 2,
@ -88,27 +90,26 @@ TipsifyTest::TipsifyTest() {
}
void TipsifyTest::buildAdjacency() {
std::vector<UnsignedInt> indices = Indices;
std::vector<UnsignedInt> liveTriangleCount, neighborOffset, neighbors;
Implementation::buildAdjacency(indices, VertexCount, liveTriangleCount, neighborOffset, neighbors);
Containers::Array<UnsignedInt> liveTriangleCount, neighborOffset, neighbors;
Implementation::buildAdjacency(Containers::stridedArrayView(Indices), VertexCount, liveTriangleCount, neighborOffset, neighbors);
CORRADE_COMPARE(liveTriangleCount, (std::vector<UnsignedInt>{
CORRADE_COMPARE_AS(liveTriangleCount, Containers::arrayView<UnsignedInt>({
1, 3, 3, 2,
4, 6, 6, 2,
2, 6, 6, 4,
2, 3, 3, 1,
1, 1, 1
}));
}), TestSuite::Compare::Container);
CORRADE_COMPARE(neighborOffset, (std::vector<UnsignedInt>{
CORRADE_COMPARE_AS(neighborOffset, Containers::arrayView<UnsignedInt>({
0, 1, 4, 7,
9, 13, 19, 25,
27, 29, 35, 41,
45, 47, 50, 53,
54, 55, 56, 57
}));
}), TestSuite::Compare::Container);
CORRADE_COMPARE(neighbors, (std::vector<UnsignedInt>{
CORRADE_COMPARE_AS(neighbors, Containers::arrayView<UnsignedInt>({
0,
0, 7, 11,
2, 7, 13,
@ -130,11 +131,11 @@ void TipsifyTest::buildAdjacency() {
6,
18, 18, 18
}));
}), TestSuite::Compare::Container);
}
void TipsifyTest::tipsify() {
std::vector<UnsignedInt> indices = Indices;
std::vector<UnsignedInt> indices{std::begin(Indices), std::end(Indices)};
MeshTools::tipsify(indices, VertexCount, 3);
CORRADE_COMPARE(indices, (std::vector<UnsignedInt>{

58
src/Magnum/MeshTools/Tipsify.cpp

@ -25,36 +25,43 @@
#include "Tipsify.h"
#include <stack>
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Utility/Algorithms.h>
#include "Magnum/MeshTools/Implementation/Tipsify.h"
namespace Magnum { namespace MeshTools {
void tipsify(std::vector<UnsignedInt>& indices, const UnsignedInt vertexCount, const std::size_t cacheSize) {
namespace {
template<class T> void tipsifyInPlaceImplementation(const Containers::StridedArrayView1D<T>& indices, const UnsignedInt vertexCount, const std::size_t cacheSize) {
/* Neighboring triangles for each vertex, per-vertex live triangle count */
std::vector<UnsignedInt> liveTriangleCount, neighborOffset, neighbors;
Implementation::buildAdjacency(indices, vertexCount, liveTriangleCount, neighborOffset, neighbors);
Containers::Array<UnsignedInt> liveTriangleCount, neighborOffset, neighbors;
Implementation::buildAdjacency<T>(indices, vertexCount, liveTriangleCount, neighborOffset, neighbors);
/* Global time, per-vertex caching timestamps, per-triangle emmited flag */
UnsignedInt time = cacheSize+1;
std::vector<UnsignedInt> timestamp(vertexCount);
std::vector<bool> emitted(indices.size()/3);
Containers::Array<UnsignedInt> timestamp{vertexCount};
/** @todo Have some bitset/staticbitset class for this */
Containers::Array<bool> emitted{indices.size()/3};
/* Dead-end vertex stack */
std::stack<UnsignedInt> deadEndStack;
Containers::Array<UnsignedInt> deadEndStack;
/* Output index buffer */
std::vector<UnsignedInt> outputIndices;
outputIndices.reserve(indices.size());
Containers::Array<T> outputIndices{Containers::NoInit, indices.size()};
std::size_t outputIndex = 0;
/* Array with candidates for next fanning vertex (in 1-ring around
fanning vertex) */
Containers::Array<UnsignedInt> candidates;
/* Starting vertex for fanning, cursor */
UnsignedInt fanningVertex = 0;
UnsignedInt i = 0;
while(fanningVertex != 0xFFFFFFFFu) {
/* Array with candidates for next fanning vertex (in 1-ring around
fanning vertex) */
std::vector<UnsignedInt> candidates;
/* Reset the candidates for this vertex */
arrayResize(candidates, 0);
/* For all neighbors of fanning vertex */
for(UnsignedInt ti = neighborOffset[fanningVertex]; ti != neighborOffset[fanningVertex+1]; ++ti) {
@ -68,12 +75,12 @@ void tipsify(std::vector<UnsignedInt>& indices, const UnsignedInt vertexCount, c
for(UnsignedInt vi = 0; vi != 3; ++vi) {
const UnsignedInt v = indices[vi + t*3];
outputIndices.push_back(v);
outputIndices[outputIndex++] = v;
/* Add to dead end stack and candidates array */
/** @todo Limit size of dead end stack to cache size */
deadEndStack.push(v);
candidates.push_back(v);
arrayAppend(deadEndStack, v);
arrayAppend(candidates, v);
/* Decrease live triangle count */
--liveTriangleCount[v];
@ -109,8 +116,8 @@ void tipsify(std::vector<UnsignedInt>& indices, const UnsignedInt vertexCount, c
if(fanningVertex == 0xFFFFFFFFu) {
/* Find vertex with live triangles in dead-end stack */
while(!deadEndStack.empty()) {
unsigned int d = deadEndStack.top();
deadEndStack.pop();
UnsignedInt d = deadEndStack.back();
arrayRemoveSuffix(deadEndStack);
if(!liveTriangleCount[d]) continue;
fanningVertex = d;
@ -129,8 +136,21 @@ void tipsify(std::vector<UnsignedInt>& indices, const UnsignedInt vertexCount, c
}
/* Swap original index buffer with optimized */
using std::swap;
swap(indices, outputIndices);
Utility::copy(outputIndices, indices);
}
}
void tipsifyInPlace(const Containers::StridedArrayView1D<UnsignedInt>& indices, const UnsignedInt vertexCount, const std::size_t cacheSize) {
tipsifyInPlaceImplementation(indices, vertexCount, cacheSize);
}
void tipsifyInPlace(const Containers::StridedArrayView1D<UnsignedShort>& indices, const UnsignedInt vertexCount, const std::size_t cacheSize) {
tipsifyInPlaceImplementation(indices, vertexCount, cacheSize);
}
void tipsifyInPlace(const Containers::StridedArrayView1D<UnsignedByte>& indices, const UnsignedInt vertexCount, const std::size_t cacheSize) {
tipsifyInPlaceImplementation(indices, vertexCount, cacheSize);
}
}}

41
src/Magnum/MeshTools/Tipsify.h

@ -26,30 +26,59 @@
*/
/** @file
* @brief Function @ref Magnum::MeshTools::tipsify()
* @brief Function @ref Magnum::MeshTools::tipsifyInPlace()
*/
#include <vector>
#include <Corrade/Containers/Containers.h>
#include "Magnum/Types.h"
#include "Magnum/Magnum.h"
#include "Magnum/MeshTools/visibility.h"
#ifdef MAGNUM_BUILD_DEPRECATED
#include <vector>
#include <Corrade/Containers/ArrayViewStl.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Utility/Macros.h>
#endif
namespace Magnum { namespace MeshTools {
/**
@brief Tipsify the mesh
@brief Tipsify the mesh in-place
@param[in,out] indices Indices array to operate on
@param[in] vertexCount Vertex count
@param[in] cacheSize Post-transform vertex cache size
Optimizes the mesh for vertex-bound applications by rearranging its index
array for beter usage of post-transform vertex cache. Algorithm used:
*Pedro V. Sander, Diego Nehab, and Joshua Barczak --- Fast Triangle Reordering
* *Pedro V. Sander, Diego Nehab, and Joshua Barczak --- Fast Triangle Reordering
for Vertex Locality and Reduced Overdraw, SIGGRAPH 2007,
http://gfx.cs.princeton.edu/pubs/Sander_2007_%3ETR/index.php*.
@todo Ability to compute vertex count automatically
*/
MAGNUM_MESHTOOLS_EXPORT void tipsify(std::vector<UnsignedInt>& indices, UnsignedInt vertexCount, std::size_t cacheSize);
MAGNUM_MESHTOOLS_EXPORT void tipsifyInPlace(const Containers::StridedArrayView1D<UnsignedInt>& indices, UnsignedInt vertexCount, std::size_t cacheSize);
/**
* @overload
* @m_since_latest
*/
MAGNUM_MESHTOOLS_EXPORT void tipsifyInPlace(const Containers::StridedArrayView1D<UnsignedShort>& indices, UnsignedInt vertexCount, std::size_t cacheSize);
/**
* @overload
* @m_since_latest
*/
MAGNUM_MESHTOOLS_EXPORT void tipsifyInPlace(const Containers::StridedArrayView1D<UnsignedByte>& indices, UnsignedInt vertexCount, std::size_t cacheSize);
#ifdef MAGNUM_BUILD_DEPRECATED
/**
* @brief @copybrief tipsifyInPlace()
* @m_deprecated_since_latest Use @ref tipsifyInPlace() instead.
*/
inline CORRADE_DEPRECATED("use tipsifyInPlace() instead") void tipsify(std::vector<UnsignedInt>& indices, UnsignedInt vertexCount, std::size_t cacheSize) {
tipsifyInPlace(indices, vertexCount, cacheSize);
}
#endif
}}

Loading…
Cancel
Save