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.pull/279/head
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