Commit 1f5afa7b authored by Christopher Tenter's avatar Christopher Tenter
Browse files

new algorithm for forcing unshared per face vertices after triangulation

parent a88b1ad3
......@@ -733,10 +733,6 @@ void MeshCompiler::splitVertices()
const int fsize = getFaceSize(i);
for (int k = 0; k < fsize; ++k)
{
// get vertex position id before splitting
int vertex[16];
getInputFaceVertex_Welded(i, k, vertex);
// get interleaved vertex id for (i, k) after splitting
int idx = getInputIndexSplit(i, k);
......@@ -752,6 +748,11 @@ void MeshCompiler::splitVertices()
}
bool MeshCompiler_forceUnsharedFaceVertex_InnerValenceSorter( const std::pair<int, int>& a, const std::pair<int, int>& b )
{
return a.second > b.second;
}
void MeshCompiler::forceUnsharedFaceVertex()
{
// ==============================================
......@@ -759,11 +760,6 @@ void MeshCompiler::forceUnsharedFaceVertex()
// make sure that each triangle has at least one unique unshared vertex
// this vertex can store per face attributes when needed
// the used algorithm is optimal (min split count) for any n-poly mesh
// however, this step should be done before triangulation
// and faces have to be triangulated in fans to ensure
// that each triangle makes use of the face-specific vertex
// sharedVertex[i] = 1 iff the vertex id of corner i is shared with any neighboring face
// sharedVertex is computed on-the-fly for each face
......@@ -774,17 +770,245 @@ void MeshCompiler::forceUnsharedFaceVertex()
std::vector<int> tmpFaceVerts; // used for face rotation-swap
tmpFaceVerts.resize(maxFaceSize_);
// number of ccw index rotations of the face to make the first corner unshared
faceRotCount_.resize(numFaces_, 0);
int numInitialVerts = numDrawVerts_;
char* VertexUsed = new char[numDrawVerts_]; // marks vertices which are not shared with any neighboring face
memset(VertexUsed, 0, sizeof(char) * numDrawVerts_);
std::vector<int> VertexUsed(numDrawVerts_, -1); // marks vertices which are not shared with any neighboring face
// process all n-polygons first
/*
new and better algorithm: O(n^2)
for each face:
trisCovered = 0;
for (int faceID = 0; faceID < numFaces_; ++faceID)
while (trisCovered < faceSize)
{
compute inner valence of all corners in the remaining polygon
add 'best' corner: - highest inner valence and unused by other tris
for each triangle affected by this corner
rotate triIndexBuffer entries of the tri
remove tri from the remaining triangle list
++ trisCovered
}
*/
int triCounter = 0;
int fids[] = { 276, 284 };
std::vector<int> faces[2];
std::vector<int> tris[2];
for (int i = 0; i < 2; ++i)
{
int faceID = fids[i];
int fsize = getFaceSize(fids[i]);
for (int k = 0; k < fsize; ++k)
faces[i].push_back(getInputIndexSplit(faceID, k));
}
for (int sortFaceID = 0; sortFaceID < numFaces_; ++sortFaceID)
{
// get original face id
const int faceID = faceSortMap_.empty() ? sortFaceID : faceSortMap_[sortFaceID];
int fsize = getFaceSize(faceID);
int faceNo = -1;
if (faceID == fids[0])
faceNo = 0;
else if (faceID == fids[1])
faceNo = 1;
if (0 <= faceNo && faceNo <= 1)
{
for (int i = 0; i < fsize - 2; ++i)
{
tris[faceNo].push_back(triIndexBuffer_[(triCounter + i) * 3]);
tris[faceNo].push_back(triIndexBuffer_[(triCounter + i) * 3 + 1]);
tris[faceNo].push_back(triIndexBuffer_[(triCounter + i) * 3 + 2]);
}
}
triCounter += fsize - 2;
}
triCounter = 0;
if (1)
{
for (int sortFaceID = 0; sortFaceID < numFaces_; ++sortFaceID)
{
// get original face id
const int faceID = faceSortMap_.empty() ? sortFaceID : faceSortMap_[sortFaceID];
const int faceSize = getFaceSize(faceID);
if (faceSize > 3)
{
// which corners of the polygon have been duplicated
// std::vector<int> duplicatedVerts;
// vertexPriorities[priority] = pair(cornerID, valence)
std::vector< std::pair<int, int> > vertexPriorities(faceSize);
// linked ring list for all the triangles in the uncovered triangulation
// ie. nextTri = remainingTris[currentTri];
const int faceTris = faceSize - 2;
struct RingTriangle
{
RingTriangle() {}
RingTriangle(int i, RingTriangle* p, RingTriangle* n) : id(i), prev(p), next(n) {}
int id; // local index of the triangle within a polygon [0, ... faceSize-3]
RingTriangle* prev; // prev triangle in the ring
RingTriangle* next; // next triangle in the ring
};
std::vector<RingTriangle> remainingTris(faceTris);
for (int i = 0; i < faceTris; ++i)
remainingTris[i] = RingTriangle(i, &remainingTris[(i + faceTris - 1) % faceTris], &remainingTris[(i + 1) % faceTris]);
RingTriangle* currentTri = &remainingTris[0];
int numTrisCovered = 0;
while (numTrisCovered < faceTris)
{
// compute valence of vertices within the remaining triangulation
for (int k = 0; k < faceSize; ++k)
vertexPriorities[k] = std::pair<int, int>(k, 0);
RingTriangle* startTri = currentTri;
int numRemainingTris = faceTris - numTrisCovered;
for (int t = 0; t < numRemainingTris; ++t)
{
for (int k = 0; k < 3; ++k)
{
int cornerID = -1 -triIndexBuffer_[(triCounter + currentTri->id) * 3 + k];
++vertexPriorities[cornerID].second;
}
currentTri = currentTri->next;
}
assert(currentTri == startTri);
// sort by valence
std::sort(vertexPriorities.begin(), vertexPriorities.end(), MeshCompiler_forceUnsharedFaceVertex_InnerValenceSorter);
// find a good corner
int goodCorner = -1;
int goodVertexID = -1;
int bestValence = -1;
for (int k = 0; k < faceSize && vertexPriorities[k].second; ++k)
{
int cornerID = vertexPriorities[k].first;
int vertexID = getInputIndexSplit(faceID, cornerID);
int valence = vertexPriorities[k].second;
if (vertexID >= numInitialVerts || (VertexUsed[vertexID] == faceID))
{
// best case, this vertex is already owned by the polygon
// stop the search
goodCorner = cornerID;
goodVertexID = vertexID;
bestValence = valence;
break;
}
else if (VertexUsed[vertexID] < 0 && bestValence < valence)
{
goodCorner = cornerID; // best for now, but continue the search
goodVertexID = vertexID;
bestValence = valence;
}
}
// maybe add a new vertex
if (goodCorner < 0)
{
// have to add a new vertex
// use the one with highest inner valence
goodCorner = vertexPriorities[0].first; // polygon corner
// duplicatedVerts.push_back(goodCorner);
// add new vertex at the end of the buffer
goodVertexID = numDrawVerts_;
setInputIndexSplit(faceID, goodCorner, goodVertexID);
}
else
{
// mark the polygon as owner of the vertex
VertexUsed[goodVertexID] = faceID;
}
// process tris
for (int t = 0; t < numRemainingTris; ++t)
{
// check if the triangle references the good corner by testing the 3 vertices of the triangulation
bool triSkipped = true;
for (int k = 0; k < 3; ++k)
{
int cornerID = -1 - triIndexBuffer_[(triCounter + currentTri->id) * 3 + k];
if (cornerID == goodCorner)
{
// rotate the triangle such that the first corner of the triangle references the good corner
int rotCount = 3 - k;
// make a temp copy of current triangle
int tmpTriVerts[3] =
{
triIndexBuffer_[(triCounter + currentTri->id) * 3],
triIndexBuffer_[(triCounter + currentTri->id) * 3 + 1],
triIndexBuffer_[(triCounter + currentTri->id) * 3 + 2],
};
// apply rotation
for (int i = 0; i < 3; ++i)
triIndexBuffer_[(triCounter + currentTri->id) * 3 + (i + rotCount) % 3] = tmpTriVerts[i];
++numTrisCovered;
triSkipped = false;
// remove triangle from the ring list
currentTri->prev->next = currentTri->next;
currentTri->next->prev = currentTri->prev;
break;
}
}
currentTri = currentTri->next;
}
}
}
triCounter += faceSize - 2;
}
}
// process all triangles now
numInitialVerts = VertexUsed.size();
triCounter = 0;
for (int sortFaceID = 0; sortFaceID < numFaces_; ++sortFaceID)
{
int faceID = faceSortMap_.empty() ? sortFaceID : faceSortMap_[sortFaceID];
const int numCorners = getFaceSize(faceID);
if (numCorners == 3)
{
// reset shared list
memset(&sharedVertices[0], 0, sizeof(int) * maxFaceSize_);
int numShared = 0;
......@@ -797,13 +1021,16 @@ void MeshCompiler::forceUnsharedFaceVertex()
const int vertexID0 = getInputIndexSplit(faceID, v0);
if (vertexID0 >= numInitialVerts || VertexUsed[vertexID0])
// EDIT:
// vertexID0 >= numInitialVerts || (...) seemed wrong
if (vertexID0 < numInitialVerts && (VertexUsed[vertexID0] >= 0 && VertexUsed[vertexID0] != faceID))
{
sharedVertices[v0] = true;
++numShared;
}
}
int rotCount = 0;
if (numShared == numCorners)
{
......@@ -831,13 +1058,13 @@ void MeshCompiler::forceUnsharedFaceVertex()
// rotation order: i -> i+1
// find # rotations needed
int rotCount = 1;
rotCount = 1;
for (; rotCount < numCorners; ++rotCount)
{
if (!sharedVertices[rotCount % numCorners])
{
if (tmpFaceVerts[rotCount] < numInitialVerts)
VertexUsed[tmpFaceVerts[rotCount]] = 1;
VertexUsed[tmpFaceVerts[rotCount]] = faceID;
break;
}
}
......@@ -847,23 +1074,26 @@ void MeshCompiler::forceUnsharedFaceVertex()
// rotate: i -> i+rotCount
rotCount = numCorners - rotCount;
faceRotCount_[faceID] = rotCount;
for (int i = 0; i < numCorners; ++i)
setInputIndexSplit(faceID, (i + rotCount)%numCorners, tmpFaceVerts[i]);
{
// setInputIndexSplit(faceID, i, tmpFaceVerts[(i + numCorners - rotCount) % numCorners]);
triIndexBuffer_[triCounter * 3 + i] = tmpFaceVerts[(i + numCorners - rotCount) % numCorners];
}
}
else
{
// best-case: unshared vertex at corner 0
const int idx = getInputIndexSplit(faceID, 0);
if (idx < numInitialVerts)
VertexUsed[idx] = 1;
VertexUsed[idx] = faceID;
}
}
delete [] VertexUsed;
triCounter += numCorners - 2;
}
std::cout << "force unshared num added: " << (numDrawVerts_ - numInitialVerts) << std::endl;
}
void MeshCompiler::getInputFaceVertex( const int _face, const int _corner, int* _out ) const
......@@ -965,25 +1195,14 @@ MeshCompiler::~MeshCompiler()
}
int MeshCompiler::getInputIndexOffset( const int _face, const int _corner, const bool _rotation ) const
int MeshCompiler::getInputIndexOffset( const int _face, const int _corner ) const
{
assert(_face >= 0);
assert(_face < numFaces_);
// baseIdx: offset to first index of the (face, corner) pair
const int baseIdx = int(faceStart_.empty() ? maxFaceSize_ * _face : faceStart_[_face]);
const int fsize = getFaceSize(_face);
// rotate the vertices of the face if necessary (if constraint: first vertex of each face is exclusive to that face)
const int rotCount = faceRotCount_.empty() && _rotation ? 0 : faceRotCount_[_face];
assert(baseIdx >= 0);
assert(baseIdx <= numIndices_);
assert(fsize > 0);
return baseIdx + (_corner + rotCount) % fsize;
return baseIdx + _corner;
}
......@@ -1249,38 +1468,90 @@ void MeshCompiler::triangulate()
// convert polygon into triangle fan
// NOTE: all triangles must use the first face-vertex here!
for (int k = 0; k < 3; ++k)
triIndexBuffer_[indexCounter++] = getInputIndexSplit(faceID, k);
triIndexBuffer_[indexCounter++] = -1 - k;
for (int k = 3; k < faceSize; ++k)
{
// added tri belongs to current face
triToSortFaceMap_[triCounter++] = sortFaceID;
triIndexBuffer_[indexCounter++] = getInputIndexSplit(faceID, 0);
triIndexBuffer_[indexCounter++] = getInputIndexSplit(faceID, k-1);
triIndexBuffer_[indexCounter++] = getInputIndexSplit(faceID, k);
triIndexBuffer_[indexCounter++] = -1;
triIndexBuffer_[indexCounter++] = -1 - (k-1);
triIndexBuffer_[indexCounter++] = -1 - k;
}
}
// rotate tris such that the unshared face vertex is at the wanted provoking position of each triangle
if (provokingVertex_ >= 0)
{
for (int i = 0; i < numTris_; ++i)
{
for (int k = 0; k < 3 - provokingVertex_; ++k)
{
const int tmp = triIndexBuffer_[i*3];
triIndexBuffer_[i*3] = triIndexBuffer_[i*3 + 1];
triIndexBuffer_[i*3 + 1] = triIndexBuffer_[i*3 + 2];
triIndexBuffer_[i*3 + 2] = tmp;
}
}
}
// for (int sortFaceID = 0; sortFaceID < numFaces_; ++sortFaceID)
// {
// // get original face id
// const int faceID = faceSortMap_.empty() ? sortFaceID : faceSortMap_[sortFaceID];
//
// const int faceSize = getFaceSize(faceID);
//
// if (faceSize < 4)
// {
// // save face index mapping
// triToSortFaceMap_[triCounter++] = sortFaceID;
//
// for (int k = 0; k < 3; ++k)
// triIndexBuffer_[indexCounter++] = getInputIndexSplit(faceID, k);
// }
// else
// {
// // use ACG::Triangulator to process complex polygons
// std::vector<Vec3f> poly(faceSize);
// for (int k = 0; k < faceSize; ++k)
// {
// VertexElement posElement;
// posElement.type_ = GL_FLOAT;
// posElement.numElements_ = 3;
// posElement.usage_ = VERTEX_USAGE_POSITION;
// posElement.pointer_ = 0;
// posElement.shaderInputName_ = 0;
// posElement.divisor_ = 0;
// posElement.vbo_ = 0;
// int posID = getInputIndexSplit(faceID, k);
// input_[inputIDPos_].getElementData( posID, &poly[k], &posElement);
// }
// Triangulator tris(poly);
//
// if (1 || tris.convex())
// {
// // best case: convert polygon into triangle fan
// // NOTE: all triangles must use the first face-vertex here!
// triToSortFaceMap_[triCounter++] = sortFaceID;
// for (int k = 0; k < 3; ++k)
// triIndexBuffer_[indexCounter++] = getInputIndexSplit(faceID, k);
//
// for (int k = 3; k < faceSize; ++k)
// {
// // added tri belongs to current face
// triToSortFaceMap_[triCounter++] = sortFaceID;
//
// triIndexBuffer_[indexCounter++] = getInputIndexSplit(faceID, 0);
// triIndexBuffer_[indexCounter++] = getInputIndexSplit(faceID, k - 1);
// triIndexBuffer_[indexCounter++] = getInputIndexSplit(faceID, k);
// }
// }
// else
// {
// // concave polygon
// // enforcing an unshared vertex gets ugly now
//
// for (int i = 0; i < tris.numTriangles(); ++i)
// {
// triToSortFaceMap_[triCounter++] = sortFaceID;
// for (int k = 0; k < 3; ++k)
// {
// int cornerID = tris.index(i * 3 + k);
//
// triIndexBuffer_[indexCounter++] = getInputIndexSplit(faceID, cornerID);
// }
// }
// }
// }
//
// ---------------
// fill out missing subset info:
......@@ -1315,6 +1586,47 @@ void MeshCompiler::triangulate()
}
void MeshCompiler::resolveTriangulation()
{
// rotate tris such that the unshared face vertex is at the wanted provoking position of each triangle
if (provokingVertex_ >= 0)
{
for (int i = 0; i < numTris_; ++i)
{
for (int k = 0; k < 3 - provokingVertex_; ++k)
{
const int tmp = triIndexBuffer_[i*3];
triIndexBuffer_[i*3] = triIndexBuffer_[i*3 + 1];
triIndexBuffer_[i*3 + 1] = triIndexBuffer_[i*3 + 2];
triIndexBuffer_[i*3 + 2] = tmp;
}
}
}
// resolve triangulation to indices
for (int drawTriID = 0; drawTriID < numTris_; ++drawTriID)
{
if (triIndexBuffer_[drawTriID * 3] < 0)
{
// triIndexBuffer stores the corner ids of the triangulations as:
// triIndexBuffer[idx] = -cornerID - 1
// get original face id
const int sortFaceID = triToSortFaceMap_[drawTriID];
const int faceID = faceSortMap_.empty() ? sortFaceID : faceSortMap_[sortFaceID];
for (int k = 0; k < 3; ++k)
{
int negCornerID = triIndexBuffer_[drawTriID * 3 + k];
int cornerID = -1 - negCornerID;
int vertexID = triIndexBuffer_[drawTriID * 3 + k] = getInputIndexSplit(faceID, cornerID);
assert(vertexID >= 0);
}
}
}
}
void MeshCompiler::sortFacesByGroup()
{
// sort faces based on their group id
......@@ -1425,20 +1737,15 @@ void MeshCompiler::optimize()
for (int i = 0; i < numFaces_; ++i)
{
const int frot = faceRotCount_.empty() ? 0 : faceRotCount_[i];
const int fsize = getFaceSize(i);
for (int k = 0; k < fsize; ++k)
{
// undo face rotation
int rotIdx = k - frot;
if (rotIdx < 0) rotIdx += fsize;
int oldVertex = getInputIndexSplit(i, rotIdx);
int oldVertex = getInputIndexSplit(i, k);
int newVertex = vertexOptMap[oldVertex];
setInputIndexSplit(i, rotIdx, newVertex);
setInputIndexSplit(i, k, newVertex);
}
}
......@@ -1510,24 +1817,6 @@ void MeshCompiler::build(bool _weldVertices, bool _optimizeVCache, bool _needPer
}
}
if (_needPerFaceAttribute)
{
if (dbg_MemProfiling)
std::cout << "force unshared vertex.., memusage = " << (getMemoryUsage() /(1024 * 1024)) << std::endl;
// The provoking vertex of each face shall not be referenced by any other face.
// This vertex can then be used to store per-face data
// default provoking position 2
if (provokingVertex_ < 0)
provokingVertex_ = 2;
provokingVertex_ = provokingVertex_ % 3;
// Adjacency info needed here
forceUnsharedFaceVertex();
}
/*
2. step
......@@ -1543,6 +1832,8 @@ void MeshCompiler::build(bool _weldVertices, bool _optimizeVCache, bool _needPer
sortFacesByGroup();
/*
3. step
......@@ -1572,6 +1863,27 @@ void MeshCompiler::build(bool _weldVertices, bool _optimizeVCache, bool _needPer
triangulate();
if (_needPerFaceAttribute)
{
if (dbg_MemProfiling)
std::cout << "force unshared vertex.., memusage = " << (getMemoryUsage() / (1024 * 1024)) << std::endl;
// The provoking vertex of each face shall not be referenced by any other face.
// This vertex can then be used to store per-face data
// default provoking position 2
if (provokingVertex_ < 0)
provokingVertex_ = 2;
provokingVertex_ = provokingVertex_ % 3;
// Adjacency info needed here
forceUnsharedFaceVertex();
}
resolveTriangulation();
/*
4. step