Browse Source

MeshTools: implementation of Tipsify algorithm.

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
Vladimír Vondruš 15 years ago
parent
commit
0c67d17148
  1. 1
      CMakeLists.txt
  2. 4
      modules/FindMagnum.cmake
  3. 9
      src/MeshTools/CMakeLists.txt
  4. 2
      src/MeshTools/Test/CMakeLists.txt
  5. 144
      src/MeshTools/Test/TipsifyTest.cpp
  6. 40
      src/MeshTools/Test/TipsifyTest.h
  7. 148
      src/MeshTools/Tipsify.cpp
  8. 82
      src/MeshTools/Tipsify.h

1
CMakeLists.txt

@ -20,6 +20,7 @@ find_package(Corrade REQUIRED)
set_parent_scope(MAGNUM_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
set_parent_scope(MAGNUM_LIBRARY Magnum)
set_parent_scope(MAGNUM_PRIMITIVES_LIBRARY MagnumPrimitives)
set_parent_scope(MAGNUM_MESHTOOLS_LIBRARY MagnumMeshTools)
include(FindMagnum)
add_subdirectory(modules)

4
modules/FindMagnum.cmake

@ -16,7 +16,7 @@ find_package(Corrade REQUIRED)
find_package(OpenGL REQUIRED)
find_package(GLEW REQUIRED)
if (MAGNUM_INCLUDE_DIR AND MAGNUM_LIBRARY AND MAGNUM_PRIMITIVES_LIBRARY)
if (MAGNUM_INCLUDE_DIR AND MAGNUM_LIBRARY AND MAGNUM_PRIMITIVES_LIBRARY AND MAGNUM_MESHTOOLS_LIBRARY)
# Already in cache
set(MAGNUM_FOUND TRUE)
@ -25,6 +25,7 @@ else()
# Libraries
find_library(MAGNUM_LIBRARY Magnum)
find_library(MAGNUM_PRIMITIVES_LIBRARY MagnumPrimitives)
find_library(MAGNUM_MESHTOOLS_LIBRARY MagnumMeshTools)
# Paths
find_path(MAGNUM_INCLUDE_DIR
@ -37,6 +38,7 @@ else()
MAGNUM_INCLUDE_DIR
MAGNUM_LIBRARY
MAGNUM_PRIMITIVES_LIBRARY
MAGNUM_MESHTOOLS_LIBRARY
)
endif()

9
src/MeshTools/CMakeLists.txt

@ -1,3 +1,12 @@
set(MagnumMeshTools_SRCS
Tipsify.cpp
)
add_library(MagnumMeshTools SHARED ${MagnumMeshTools_SRCS})
target_link_libraries(MagnumMeshTools ${MAGNUM_LIBRARY})
install(TARGETS MagnumMeshTools DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR})
if(BUILD_TESTS)
enable_testing()
add_subdirectory(Test)

2
src/MeshTools/Test/CMakeLists.txt

@ -2,3 +2,5 @@ corrade_add_test(CleanTest CleanTest.h CleanTest.cpp)
corrade_add_test(SubdivideTest SubdivideTest.h SubdivideTest.cpp)
corrade_add_test(SubdivideCleanBenchmark SubdivideCleanBenchmark.h SubdivideCleanBenchmark.cpp)
target_link_libraries(SubdivideCleanBenchmark ${MAGNUM_PRIMITIVES_LIBRARY})
corrade_add_test(TipsifyTest TipsifyTest.h TipsifyTest.cpp)
target_link_libraries(TipsifyTest ${MAGNUM_MESHTOOLS_LIBRARY})

144
src/MeshTools/Test/TipsifyTest.cpp

@ -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 */
}));
}
}}}

40
src/MeshTools/Test/TipsifyTest.h

@ -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

148
src/MeshTools/Tipsify.cpp

@ -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;
}
}}

82
src/MeshTools/Tipsify.h

@ -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…
Cancel
Save