Skip to content
This repository has been archived by the owner on Sep 6, 2023. It is now read-only.

Commit

Permalink
Implement mesh optimizer
Browse files Browse the repository at this point in the history
  • Loading branch information
tenko committed Nov 3, 2012
1 parent 65fd73b commit de727fa
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 7 deletions.
4 changes: 4 additions & 0 deletions LICENSE.txt
Expand Up @@ -9,6 +9,10 @@ Code based on freecad :
http://sourceforge.net/projects/free-cad/
License - GPL v2, several authors (Check site)

Mesh optimize code based on Horde3D :
http://http://www.horde3d.org/
License - Eclipse Public License v1.0, several authors (Check site)

Truetype code:
stb_truetype.h - v0.6c - public domain
authored from 2009-2012 by Sean Barrett / RAD Game Tools
Expand Down
222 changes: 222 additions & 0 deletions occmodel/@src/OCCModel.cpp
Expand Up @@ -234,4 +234,226 @@ int OCCMesh::extractFaceMesh(const TopoDS_Face& face, bool qualityNormals = fals
}

return 1;
}

void OCCMesh::optimize() {
//printf("calcCacheEfficiency1 = %f\n", MeshOptimizer::calcCacheEfficiency(this));
MeshOptimizer::optimizeIndexOrder(this);
//printf("calcCacheEfficiency2 = %f\n\n", MeshOptimizer::calcCacheEfficiency(this));
}

void OptVertex::updateScore(int cacheIndex)
{
if(faces.empty())
{
score = 0;
return;
}

// The constants used here are coming from the paper
if(cacheIndex < 0) score = 0; // Not in cache
else if(cacheIndex < 3) score = 0.75f; // Among three most recent vertices
else score = pow(1.0f - ((cacheIndex - 3) / MeshOptimizer::maxCacheSize), 1.5f);

score += 2.0f * pow((float)faces.size(), -0.5f);
}

void MeshOptimizer::optimizeIndexOrder(OCCMesh *mesh)
{
// Implementation of Linear-Speed Vertex Cache Optimisation by Tom Forsyth
// (see http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html)
const size_t nvertices = mesh->vertices.size();
const size_t nindices = mesh->triangles.size();
if(nindices == 0) return;

std::vector<OptVertex> verts(nvertices);
std::set<OptFace *> faces;
std::list<OptVertex *> cache;

// Build vertex and triangle structures
for(unsigned int i = 0; i < nindices; ++i)
{
std::set<OptFace *>::iterator itr1 = faces.insert(faces.begin(), new OptFace());
OptFace *face = (*itr1);

const OCCStruct3I *tri = &mesh->triangles[i];
face->verts[0] = &verts[tri->i];
face->verts[1] = &verts[tri->j];
face->verts[2] = &verts[tri->k];
face->verts[0]->faces.insert(face);
face->verts[1]->faces.insert(face);
face->verts[2]->faces.insert(face);
}
for( unsigned int i = 0; i < nvertices; ++i )
{
verts[i].index = i;
verts[i].updateScore( -1 );
}

// Main loop of algorithm
unsigned int curIndex = 0;

while( !faces.empty() )
{
OptFace *bestFace = 0x0;
float bestScore = -1.0f;

// Try to find best scoring face in cache
std::list<OptVertex *>::iterator itr1 = cache.begin();
while(itr1 != cache.end())
{
std::set<OptFace *>::iterator itr2 = (*itr1)->faces.begin();
while(itr2 != (*itr1)->faces.end())
{
if((*itr2)->getScore() > bestScore)
{
bestFace = *itr2;
bestScore = bestFace->getScore();
}
++itr2;
}
++itr1;
}

// If that didn't work find it in the complete list of triangles
if(bestFace == 0x0)
{
std::set<OptFace *>::iterator itr2 = faces.begin();
while(itr2 != faces.end())
{
if((*itr2)->getScore() > bestScore)
{
bestFace = (*itr2);
bestScore = bestFace->getScore();
}
++itr2;
}
}

// Process vertices of best face
OCCStruct3I *tri = &mesh->triangles[curIndex++];
tri->i = bestFace->verts[0]->index;
tri->j = bestFace->verts[1]->index;
tri->k = bestFace->verts[2]->index;

for( unsigned int i = 0; i < 3; ++i )
{
// Move vertex to head of cache
itr1 = find(cache.begin(), cache.end(), bestFace->verts[i]);
if(itr1 != cache.end() ) cache.erase( itr1);
cache.push_front(bestFace->verts[i]);

// Remove face from vertex lists
bestFace->verts[i]->faces.erase(bestFace);
}

// Remove best face
faces.erase(faces.find(bestFace));
delete bestFace;

// Update scores of vertices in cache
unsigned int cacheIndex = 0;
for(itr1 = cache.begin(); itr1 != cache.end(); ++itr1)
{
(*itr1)->updateScore( cacheIndex++ );
}

// Trim cache
for(unsigned int i = cache.size(); i > maxCacheSize; --i)
{
cache.pop_back();
}
}

// Remap vertices to make access to them as linear as possible
std::map<unsigned int, unsigned int> mapping;
unsigned int curVertex = 0;

for(unsigned int i = 0; i < nindices; ++i)
{
OCCStruct3I *tri = &mesh->triangles[i];

std::map<unsigned int, unsigned int>::iterator itr1 = mapping.find(tri->i);
if(itr1 == mapping.end())
{
mapping[tri->i] = curVertex;
tri->i = curVertex++;
}
else
{
tri->i = itr1->second;
}

std::map<unsigned int, unsigned int>::iterator itr2 = mapping.find(tri->j);
if(itr2 == mapping.end())
{
mapping[tri->j] = curVertex;
tri->j = curVertex++;
}
else
{
tri->j = itr2->second;
}

std::map<unsigned int, unsigned int>::iterator itr3 = mapping.find(tri->k);
if(itr3 == mapping.end())
{
mapping[tri->k] = curVertex;
tri->k = curVertex++;
}
else
{
tri->k = itr3->second;
}
}

std::vector<OCCStruct3f> oldVertices(mesh->vertices.begin(), mesh->vertices.end());
std::vector<OCCStruct3f> oldNormals(mesh->normals.begin(), mesh->normals.end());
for(std::map<unsigned int, unsigned int>::iterator itr1 = mapping.begin();
itr1 != mapping.end(); ++itr1 )
{
mesh->vertices[itr1->second] = oldVertices[itr1->first];
mesh->normals[itr1->second] = oldNormals[itr1->first];
}

for(unsigned int i = 0; i < mesh->edgeindices.size(); ++i)
{
mesh->edgeindices[i] = mapping[mesh->edgeindices[i]];
}
}

float MeshOptimizer::calcCacheEfficiency(OCCMesh *mesh,
const unsigned int cacheSize)
{
// Measure efficiency of index array regarding post-transform vertex cache
unsigned int misses = 0;
const unsigned int nindices = mesh->triangles.size();
std::list<unsigned int> testCache;
for(unsigned int i = 0; i < nindices; ++i)
{
OCCStruct3I *tri = &mesh->triangles[i];
if(std::find(testCache.begin(), testCache.end(), tri->i) == testCache.end())
{
testCache.push_back(tri->i);
if(testCache.size() > cacheSize) testCache.erase(testCache.begin());
++misses;
}
if(std::find(testCache.begin(), testCache.end(), tri->j) == testCache.end())
{
testCache.push_back(tri->j);
if(testCache.size() > cacheSize) testCache.erase(testCache.begin());
++misses;
}
if(std::find(testCache.begin(), testCache.end(), tri->k) == testCache.end())
{
testCache.push_back(tri->k);
if(testCache.size() > cacheSize) testCache.erase(testCache.begin());
++misses;
}
}

// Average transform to vertex ratio (ATVR)
// 1.0 is theoretical optimum, meaning that each vertex is just transformed exactly one time
float atvr = (float)(3*nindices + misses) / (3*nindices);
return atvr;
}
39 changes: 34 additions & 5 deletions occmodel/@src/OCCModel.h
Expand Up @@ -3,11 +3,18 @@
#ifndef OCCMODEL_H
#define OCCMODEL_H
#include "OCCIncludes.h"
#include <sstream>
#include <math.h>
#include <limits>
#include <vector>
#include <set>
#include <sstream>
#include <limits>
#include <map>
#include <list>
#include <algorithm>

typedef std::vector<float> FVec;
typedef std::vector<double> DVec;
typedef std::vector<int> IVec;

struct OCCStruct3d {
double x;
Expand All @@ -27,9 +34,21 @@ struct OCCStruct3I {
unsigned int k;
};

typedef std::vector<float> FVec;
typedef std::vector<double> DVec;
typedef std::vector<int> IVec;
struct OptFace;

struct OptVertex
{
unsigned int index; // Index in vertex array
float score;
std::set<OptFace *> faces; // Faces that are using this vertex
void updateScore(int cacheIndex);
};

struct OptFace
{
OptVertex *verts[3];
float getScore() {return verts[0]->score + verts[1]->score + verts[2]->score;}
};

enum BoolOpType {BOOL_FUSE, BOOL_CUT, BOOL_COMMON};

Expand All @@ -56,6 +75,16 @@ class OCCMesh {
std::vector<int> edgehash;
OCCMesh() { ; }
int extractFaceMesh(const TopoDS_Face& face, bool qualityNormals);
void optimize();
};

class MeshOptimizer
{
public:
static const unsigned int maxCacheSize = 16;
static float calcCacheEfficiency(OCCMesh *mesh,
const unsigned int cacheSize = maxCacheSize);
static void optimizeIndexOrder(OCCMesh *mesh);
};

unsigned int decutf8(unsigned int* state, unsigned int* codep, unsigned int byte);
Expand Down
1 change: 1 addition & 0 deletions occmodel/@src/OCCModelLib.pxd
Expand Up @@ -47,6 +47,7 @@ cdef extern from "OCCModel.h":
vector[int] edgehash

c_OCCMesh()
void optimize()

cdef enum c_BoolOpType "BoolOpType":
BOOL_FUSE
Expand Down
9 changes: 8 additions & 1 deletion occmodel/occmodel.pyx
Expand Up @@ -123,7 +123,14 @@ cdef class Mesh:
cdef c_OCCMesh *occ = <c_OCCMesh *>self.thisptr
return occ.vertices.size() > 0 and occ.normals.size() > 0 and \
occ.triangles.size() > 0


cpdef optimize(self):
'''
Vertex Cache Optimisation
'''
cdef c_OCCMesh *occ = <c_OCCMesh *>self.thisptr
occ.optimize()

cdef setArrays(self):
cdef c_OCCMesh *occ = <c_OCCMesh *>self.thisptr

Expand Down
4 changes: 3 additions & 1 deletion occmodel/occmodelviewer.py
Expand Up @@ -191,7 +191,9 @@ def addObject(self, obj, color = None):
mesh = obj.createMesh()
if not mesh.isValid():
return False


mesh.optimize()

# update bounding box
bbox = obj.boundingBox()
self.bbox.addPoint(bbox.min)
Expand Down

0 comments on commit de727fa

Please sign in to comment.