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. 111
      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
}}

111
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.
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 flipNormalsInPlace(const Containers::StridedArrayView1D<UnsignedInt>& indices, const Containers::StridedArrayView1D<Vector3>& normals);
@attention The function requires the mesh to have triangle faces, thus index
count must be divisible by 3.
/**
* @overload
* @m_since_latest
*/
void MAGNUM_MESHTOOLS_EXPORT flipFaceWinding(std::vector<UnsignedInt>& indices);
void flipNormalsInPlace(const Containers::StridedArrayView1D<UnsignedShort>& 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<UnsignedByte>& indices, const Containers::StridedArrayView1D<Vector3>& normals);
The same as @ref flipNormals(std::vector<UnsignedInt>&, std::vector<Vector3>&),
but flips only normals, not face winding.
#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