mirror of https://github.com/mosra/magnum.git
Browse Source
Algorithm used: 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 Also created new shared MeshTools library.vectorfields
8 changed files with 429 additions and 1 deletions
@ -0,0 +1,144 @@
|
||||
/*
|
||||
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
This file is part of Magnum. |
||||
|
||||
Magnum is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU Lesser General Public License version 3 |
||||
only, as published by the Free Software Foundation. |
||||
|
||||
Magnum is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Lesser General Public License version 3 for more details. |
||||
*/ |
||||
|
||||
#include "TipsifyTest.h" |
||||
|
||||
#include <QtTest/QTest> |
||||
|
||||
#include "MeshBuilder.h" |
||||
#include "MeshTools/Tipsify.h" |
||||
|
||||
QTEST_APPLESS_MAIN(Magnum::MeshTools::Test::TipsifyTest) |
||||
|
||||
using namespace std; |
||||
|
||||
namespace Magnum { namespace MeshTools { namespace Test { |
||||
|
||||
/*
|
||||
|
||||
0 ----- 1 ----- 2 ----- 3 |
||||
\ 0 / \ 7 / \ 2 / \
|
||||
\ / 11 \ / 13 \ / 12 \
|
||||
4 ----- 5 ----- 6 ----- 7 |
||||
/ \ 3 / \ 8 / \ 5 / |
||||
/ 14 \ / 9 \ / 15 \ / |
||||
8 ----- 9 ---- 10 ---- 11 18 ---- 17 |
||||
\ 4 / \ 1 / \ 17 / \ \ 18 / |
||||
\ / 16 \ / 10 \ / 6 \ \ / |
||||
12 ---- 13 ---- 14 ---- 15 16 |
||||
|
||||
*/ |
||||
|
||||
TipsifyTest::TipsifyTest(QObject* parent): QObject(parent) { |
||||
unsigned int vertices[19]; /* who cares */ |
||||
static const unsigned int indices[] = { |
||||
4, 1, 0, |
||||
10, 9, 13, |
||||
6, 3, 2, |
||||
9, 5, 4, |
||||
12, 9, 8, |
||||
11, 7, 6, |
||||
|
||||
14, 15, 11, |
||||
2, 1, 5, |
||||
10, 6, 5, |
||||
10, 5, 9, |
||||
13, 14, 10, |
||||
1, 4, 5, |
||||
|
||||
7, 3, 6, |
||||
6, 2, 5, |
||||
9, 4, 8, |
||||
6, 10, 11, |
||||
13, 9, 12, |
||||
14, 11, 10, |
||||
|
||||
16, 17, 18 |
||||
}; |
||||
builder.setData(vertices, indices, 19, 19*3); |
||||
} |
||||
|
||||
void TipsifyTest::buildAdjacency() { |
||||
vector<unsigned int> liveTriangleCount, neighborOffset, neighbors; |
||||
Tipsify(builder).buildAdjacency(liveTriangleCount, neighborOffset, neighbors); |
||||
|
||||
QVERIFY((liveTriangleCount == vector<unsigned int>{ |
||||
1, 3, 3, 2, |
||||
4, 6, 6, 2, |
||||
2, 6, 6, 4, |
||||
2, 3, 3, 1, |
||||
1, 1, 1 |
||||
})); |
||||
|
||||
QVERIFY((neighborOffset == vector<unsigned int>{ |
||||
0, 1, 4, 7, |
||||
9, 13, 19, 25, |
||||
27, 29, 35, 41, |
||||
45, 47, 50, 53, |
||||
54, 55, 56, 57 |
||||
})); |
||||
|
||||
QVERIFY((neighbors == vector<unsigned int>{ |
||||
0, |
||||
0, 7, 11, |
||||
2, 7, 13, |
||||
2, 12, |
||||
|
||||
0, 3, 11, 14, |
||||
3, 7, 8, 9, 11, 13, |
||||
2, 5, 8, 12, 13, 15, |
||||
5, 12, |
||||
|
||||
4, 14, |
||||
1, 3, 4, 9, 14, 16, |
||||
1, 8, 9, 10, 15, 17, |
||||
5, 6, 15, 17, |
||||
|
||||
4, 16, |
||||
1, 10, 16, |
||||
6, 10, 17, |
||||
6, |
||||
|
||||
18, 18, 18 |
||||
})); |
||||
} |
||||
|
||||
void TipsifyTest::tipsify() { |
||||
MeshTools::tipsify(builder, 3); |
||||
|
||||
QVERIFY((builder.indices() == vector<unsigned int>{ |
||||
4, 1, 0, |
||||
9, 5, 4, |
||||
1, 4, 5, |
||||
9, 4, 8, |
||||
12, 9, 8, |
||||
13, 9, 12, |
||||
10, 9, 13, |
||||
13, 14, 10, |
||||
10, 6, 5, |
||||
10, 5, 9, |
||||
6, 10, 11, |
||||
14, 11, 10, |
||||
6, 3, 2, |
||||
11, 7, 6, |
||||
7, 3, 6, |
||||
6, 2, 5, |
||||
2, 1, 5, |
||||
14, 15, 11, /* from dead-end vertex stack */ |
||||
16, 17, 18 /* arbitrary vertex */ |
||||
})); |
||||
} |
||||
|
||||
}}} |
||||
@ -0,0 +1,40 @@
|
||||
#ifndef Magnum_MeshTools_Test_TipsifyTest_h |
||||
#define Magnum_MeshTools_Test_TipsifyTest_h |
||||
/*
|
||||
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
This file is part of Magnum. |
||||
|
||||
Magnum is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU Lesser General Public License version 3 |
||||
only, as published by the Free Software Foundation. |
||||
|
||||
Magnum is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Lesser General Public License version 3 for more details. |
||||
*/ |
||||
|
||||
#include <QtCore/QObject> |
||||
|
||||
#include "MeshBuilder.h" |
||||
|
||||
namespace Magnum { namespace MeshTools { namespace Test { |
||||
|
||||
class TipsifyTest: public QObject { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
explicit TipsifyTest(QObject* parent = nullptr); |
||||
|
||||
private slots: |
||||
void buildAdjacency(); |
||||
void tipsify(); |
||||
|
||||
private: |
||||
MeshBuilder<unsigned int> builder; |
||||
}; |
||||
|
||||
}}} |
||||
|
||||
#endif |
||||
@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
This file is part of Magnum. |
||||
|
||||
Magnum is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU Lesser General Public License version 3 |
||||
only, as published by the Free Software Foundation. |
||||
|
||||
Magnum is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Lesser General Public License version 3 for more details. |
||||
*/ |
||||
|
||||
#include "Tipsify.h" |
||||
|
||||
#include <stack> |
||||
|
||||
namespace Magnum { namespace MeshTools { |
||||
|
||||
void Tipsify::run(size_t cacheSize) { |
||||
/* Neighboring triangles for each vertex, per-vertex live triangle count */ |
||||
std::vector<unsigned int> liveTriangleCount, neighborPosition, neighbors; |
||||
buildAdjacency(liveTriangleCount, neighborPosition, neighbors); |
||||
|
||||
/* Global time, per-vertex caching timestamps, per-triangle emmited flag */ |
||||
unsigned int time = cacheSize+1; |
||||
std::vector<unsigned int> timestamp(vertexCount); |
||||
std::vector<bool> emitted(indices.size()/3); |
||||
|
||||
/* Dead-end vertex stack */ |
||||
std::stack<unsigned int> deadEndStack; |
||||
|
||||
/* Output index buffer */ |
||||
std::vector<unsigned int> outputIndices; |
||||
outputIndices.reserve(indices.size()); |
||||
|
||||
/* Starting vertex for fanning, cursor */ |
||||
unsigned int fanningVertex = 0; |
||||
unsigned int i = 0; |
||||
while(fanningVertex != 0xFFFFFFFFu) { |
||||
/* Array with candidates for next fanning vertex (in 1-ring around
|
||||
fanning vertex) */ |
||||
std::vector<unsigned int> candidates; |
||||
|
||||
/* For all neighbors of fanning vertex */ |
||||
for(unsigned int ti = neighborPosition[fanningVertex], t = neighbors[ti]; ti != neighborPosition[fanningVertex+1]; t = neighbors[++ti]) { |
||||
/* Continue if already emitted */ |
||||
if(emitted[t]) continue; |
||||
emitted[t] = true; |
||||
|
||||
/* Write all vertices of the triangle to output buffer */ |
||||
for(unsigned int vi = 0, v = indices[t*3]; vi != 3; v = indices[++vi+t*3]) { |
||||
outputIndices.push_back(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); |
||||
|
||||
/* Decrease live triangle count */ |
||||
--liveTriangleCount[v]; |
||||
|
||||
/* If not in cache, set timestamp */ |
||||
if(time-timestamp[v] > cacheSize) |
||||
timestamp[v] = time++; |
||||
} |
||||
} |
||||
|
||||
/* Get next fanning vertex */ |
||||
fanningVertex = 0xFFFFFFFFu; |
||||
|
||||
/* Go through candidates in 1-ring around fanning vertex */ |
||||
int candidatePriority = -1; |
||||
for(unsigned int v: candidates) { |
||||
/* Skip if it doesn't have any live triangles */ |
||||
if(!liveTriangleCount[v]) continue; |
||||
|
||||
/* Get most fresh candidate which will still be in cache even
|
||||
after fanning. Every fanned triangle will generate at most |
||||
two cache misses, thus 2*liveTriangleCount */ |
||||
int priority = 0; |
||||
if(time-timestamp[v]+2*liveTriangleCount[v] <= cacheSize) |
||||
priority = time-timestamp[v]; |
||||
if(priority > candidatePriority) { |
||||
fanningVertex = v; |
||||
candidatePriority = priority; |
||||
} |
||||
} |
||||
|
||||
/* On dead-end */ |
||||
if(fanningVertex == 0xFFFFFFFFu) { |
||||
/* Find vertex with live triangles in dead-end stack */ |
||||
while(!deadEndStack.empty()) { |
||||
unsigned int d = deadEndStack.top(); |
||||
deadEndStack.pop(); |
||||
|
||||
if(!liveTriangleCount[d]) continue; |
||||
fanningVertex = d; |
||||
break; |
||||
} |
||||
|
||||
/* If not found, find next artbitrary vertex with live
|
||||
triangles */ |
||||
while(++i < vertexCount) { |
||||
if(!liveTriangleCount[i]) continue; |
||||
|
||||
fanningVertex = i; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* Swap original index buffer with optimized */ |
||||
std::swap(indices, outputIndices); |
||||
} |
||||
|
||||
void Tipsify::buildAdjacency(std::vector<unsigned int>& liveTriangleCount, std::vector<unsigned int>& neighborOffset, std::vector<unsigned int>& neighbors) const { |
||||
/* How many times is each vertex referenced == count of neighboring
|
||||
triangles for each vertex */ |
||||
liveTriangleCount.clear(); |
||||
liveTriangleCount.resize(vertexCount); |
||||
for(unsigned int i = 0; i != indices.size(); ++i) |
||||
++liveTriangleCount[indices[i]]; |
||||
|
||||
/* Building offset array from counts. Neighbors for i-th vertex will at
|
||||
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); |
||||
unsigned int sum = 0; |
||||
for(unsigned int i = 0; i != vertexCount; ++i) { |
||||
neighborOffset.push_back(sum); |
||||
sum += liveTriangleCount[i]; |
||||
} |
||||
|
||||
/* Array of neighbors, using (and changing) neighborOffset array for
|
||||
positioning */ |
||||
neighbors.clear(); |
||||
neighbors.resize(sum); |
||||
for(unsigned int i = 0; i != indices.size(); ++i) |
||||
neighbors[neighborOffset[indices[i]+1]++] = i/3; |
||||
} |
||||
|
||||
}} |
||||
@ -0,0 +1,82 @@
|
||||
#ifndef Magnum_MeshTools_Tipsify_h |
||||
#define Magnum_MeshTools_Tipsify_h |
||||
/*
|
||||
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
This file is part of Magnum. |
||||
|
||||
Magnum is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU Lesser General Public License version 3 |
||||
only, as published by the Free Software Foundation. |
||||
|
||||
Magnum is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Lesser General Public License version 3 for more details. |
||||
*/ |
||||
|
||||
/** @file
|
||||
* @brief Class Magnum::MeshTools::Tipsify |
||||
*/ |
||||
|
||||
#include "AbstractTool.h" |
||||
|
||||
namespace Magnum { namespace MeshTools { |
||||
|
||||
/**
|
||||
@brief %Mesh tipsifier implementation |
||||
|
||||
See tipsify() for full documentation. |
||||
*/ |
||||
class Tipsify: public AbstractIndexTool { |
||||
public: |
||||
/** @copydoc AbstractIndexTool::AbstractIndexTool() */ |
||||
template<class Vertex> inline Tipsify(MeshBuilder<Vertex>& builder): AbstractIndexTool(builder) {} |
||||
|
||||
/**
|
||||
* @brief Functor |
||||
* |
||||
* See tipsify() for full documentation. |
||||
*/ |
||||
void run(size_t cacheSize); |
||||
|
||||
/**
|
||||
* @brief Build vertex-triangle adjacency |
||||
* |
||||
* Computes count and indices of adjacent triangles for each vertex |
||||
* (used internally). |
||||
*/ |
||||
void buildAdjacency(std::vector<unsigned int>& liveTriangleCount, std::vector<unsigned int>& neighborOffset, std::vector<unsigned int>& neighbors) const; |
||||
}; |
||||
|
||||
/**
|
||||
@brief %Tipsify the mesh |
||||
@tparam Vertex Vertex data type (the same as in MeshBuilder) |
||||
@param builder %Mesh builder to operate on |
||||
@param 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: |
||||
<em>Pedro V. Sander, Diego Nehab, and Joshua Barczak, |
||||
<a href="http://gfx.cs.princeton.edu/pubs/Sander_2007_%3ETR/index.php">Fast |
||||
Triangle Reordering for Vertex Locality and Reduced Overdraw</a>, SIGGRAPH |
||||
2007</em>. |
||||
|
||||
This is convenience function supplementing direct usage of Tipsify class, |
||||
instead of |
||||
@code |
||||
MeshBuilder<T> builder; |
||||
MeshTools::Tipsify(builder).run(cacheSize); |
||||
@endcode |
||||
you can just write |
||||
@code |
||||
MeshTools::tipsify(builder, cacheSize); |
||||
@endcode |
||||
*/ |
||||
template<class Vertex> inline void tipsify(MeshBuilder<Vertex>& builder, size_t cacheSize) { |
||||
Tipsify(builder).run(cacheSize); |
||||
} |
||||
|
||||
}} |
||||
|
||||
#endif |
||||
Loading…
Reference in new issue