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() ...@@ -733,10 +733,6 @@ void MeshCompiler::splitVertices()
const int fsize = getFaceSize(i); const int fsize = getFaceSize(i);
for (int k = 0; k < fsize; ++k) 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 // get interleaved vertex id for (i, k) after splitting
int idx = getInputIndexSplit(i, k); int idx = getInputIndexSplit(i, k);
...@@ -752,6 +748,11 @@ void MeshCompiler::splitVertices() ...@@ -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() void MeshCompiler::forceUnsharedFaceVertex()
{ {
// ============================================== // ==============================================
...@@ -759,11 +760,6 @@ void MeshCompiler::forceUnsharedFaceVertex() ...@@ -759,11 +760,6 @@ void MeshCompiler::forceUnsharedFaceVertex()
// make sure that each triangle has at least one unique unshared vertex // make sure that each triangle has at least one unique unshared vertex
// this vertex can store per face attributes when needed // 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[i] = 1 iff the vertex id of corner i is shared with any neighboring face
// sharedVertex is computed on-the-fly for each face // sharedVertex is computed on-the-fly for each face
...@@ -774,96 +770,330 @@ void MeshCompiler::forceUnsharedFaceVertex() ...@@ -774,96 +770,330 @@ void MeshCompiler::forceUnsharedFaceVertex()
std::vector<int> tmpFaceVerts; // used for face rotation-swap std::vector<int> tmpFaceVerts; // used for face rotation-swap
tmpFaceVerts.resize(maxFaceSize_); tmpFaceVerts.resize(maxFaceSize_);
// number of ccw index rotations of the face to make the first corner unshared
faceRotCount_.resize(numFaces_, 0);
int numInitialVerts = numDrawVerts_; int numInitialVerts = numDrawVerts_;
char* VertexUsed = new char[numDrawVerts_]; // marks vertices which are not shared with any neighboring face std::vector<int> VertexUsed(numDrawVerts_, -1); // marks vertices which are not shared with any neighboring face
memset(VertexUsed, 0, sizeof(char) * numDrawVerts_);
// process all n-polygons first
/*
new and better algorithm: O(n^2)
for (int faceID = 0; faceID < numFaces_; ++faceID) for each face:
trisCovered = 0;
while (trisCovered < faceSize)
{ {
const int numCorners = getFaceSize(faceID); compute inner valence of all corners in the remaining polygon
// reset shared list
memset(&sharedVertices[0], 0, sizeof(int) * maxFaceSize_);
int numShared = 0;
// find shared list (corners of this face, that are shared with the neighbors) add 'best' corner: - highest inner valence and unused by other tris
for (int v0 = 0; v0 < numCorners && numShared < numCorners; ++v0)
{ for each triangle affected by this corner
if (sharedVertices[v0]) rotate triIndexBuffer entries of the tri
continue; remove tri from the remaining triangle list
++ trisCovered
}
*/
int triCounter = 0;
const int vertexID0 = getInputIndexSplit(faceID, v0);
if (vertexID0 >= numInitialVerts || VertexUsed[vertexID0]) 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)
{ {
sharedVertices[v0] = true; tris[faceNo].push_back(triIndexBuffer_[(triCounter + i) * 3]);
++numShared; tris[faceNo].push_back(triIndexBuffer_[(triCounter + i) * 3 + 1]);
tris[faceNo].push_back(triIndexBuffer_[(triCounter + i) * 3 + 2]);
} }
} }
if (numShared == numCorners) triCounter += fsize - 2;
}
triCounter = 0;
if (1)
{
for (int sortFaceID = 0; sortFaceID < numFaces_; ++sortFaceID)
{ {
// worst-case: all vertices shared with neighbors // 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;
}
}
// add split vertex to end of vertex buffer, which is used exclusively by the current face
// current vertex count is stored in numDrawVerts_
setInputIndexSplit(faceID, 0, numDrawVerts_); }
triCounter += faceSize - 2;
} }
else if (sharedVertices[0]) }
{
// validation code
int x = 0;
for (int i = 0; i < numCorners; ++i)
x += sharedVertices[i];
assert(x < numCorners);
// we have to make sure that an unshared vertex is the first referenced face vertex // process all triangles now
// this is currently not the case, so rotate the face indices until this is true numInitialVerts = VertexUsed.size();
triCounter = 0;
// make copy of current face splitVertexID for (int sortFaceID = 0; sortFaceID < numFaces_; ++sortFaceID)
for (int i = 0; i < numCorners; ++i) {
tmpFaceVerts[i] = getInputIndexSplit(faceID, i); 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;
// rotation order: i -> i+1 // find shared list (corners of this face, that are shared with the neighbors)
// find # rotations needed for (int v0 = 0; v0 < numCorners && numShared < numCorners; ++v0)
int rotCount = 1;
for (; rotCount < numCorners; ++rotCount)
{ {
if (!sharedVertices[rotCount % numCorners]) if (sharedVertices[v0])
continue;
const int vertexID0 = getInputIndexSplit(faceID, v0);
// EDIT:
// vertexID0 >= numInitialVerts || (...) seemed wrong
if (vertexID0 < numInitialVerts && (VertexUsed[vertexID0] >= 0 && VertexUsed[vertexID0] != faceID))
{ {
if (tmpFaceVerts[rotCount] < numInitialVerts) sharedVertices[v0] = true;
VertexUsed[tmpFaceVerts[rotCount]] = 1; ++numShared;
break;
} }
} }
assert(rotCount < numCorners); int rotCount = 0;
// rotate: i -> i+rotCount if (numShared == numCorners)
rotCount = numCorners - rotCount; {
// worst-case: all vertices shared with neighbors
faceRotCount_[faceID] = rotCount; // add split vertex to end of vertex buffer, which is used exclusively by the current face
// current vertex count is stored in numDrawVerts_
for (int i = 0; i < numCorners; ++i)
setInputIndexSplit(faceID, (i + rotCount)%numCorners, tmpFaceVerts[i]); setInputIndexSplit(faceID, 0, numDrawVerts_);
} }
else else if (sharedVertices[0])
{ {
// best-case: unshared vertex at corner 0 // validation code
const int idx = getInputIndexSplit(faceID, 0); int x = 0;
if (idx < numInitialVerts) for (int i = 0; i < numCorners; ++i)
VertexUsed[idx] = 1; x += sharedVertices[i];
assert(x < numCorners);
// we have to make sure that an unshared vertex is the first referenced face vertex
// this is currently not the case, so rotate the face indices until this is true
// make copy of current face splitVertexID
for (int i = 0; i < numCorners; ++i)
tmpFaceVerts[i] = getInputIndexSplit(faceID, i);
// rotation order: i -> i+1
// find # rotations needed
rotCount = 1;
for (; rotCount < numCorners; ++rotCount)
{
if (!sharedVertices[rotCount % numCorners])
{
if (tmpFaceVerts[rotCount] < numInitialVerts)
VertexUsed[tmpFaceVerts[rotCount]] = faceID;
break;
}
}
assert(rotCount < numCorners);
// rotate: i -> i+rotCount
rotCount = numCorners - rotCount;
for (int i = 0; i < numCorners; ++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] = faceID;
}
} }
triCounter += numCorners - 2;
} }
delete [] VertexUsed; std::cout << "force unshared num added: " << (numDrawVerts_ - numInitialVerts) << std::endl;
} }
void MeshCompiler::getInputFaceVertex( const int _face, const int _corner, int* _out ) const void MeshCompiler::getInputFaceVertex( const int _face, const int _corner, int* _out ) const
...@@ -965,25 +1195,14 @@ MeshCompiler::~MeshCompiler() ...@@ -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 >= 0);
assert(_face < numFaces_); assert(_face < numFaces_);
// baseIdx: offset to first index of the (face, corner) pair // baseIdx: offset to first index of the (face, corner) pair
const int baseIdx = int(faceStart_.empty() ? maxFaceSize_ * _face : faceStart_[_face]); const int baseIdx = int(faceStart_.empty() ? maxFaceSize_ * _face : faceStart_[_face]);
const int fsize = getFaceSize(_face); return baseIdx + _corner;
// 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;
} }
...@@ -1249,38 +1468,90 @@ void MeshCompiler::triangulate() ...@@ -1249,38 +1468,90 @@ void MeshCompiler::triangulate()
// convert polygon into triangle fan // convert polygon into triangle fan
// NOTE: all triangles must use the first face-vertex here! // NOTE: all triangles must use the first face-vertex here!
for (int k = 0; k < 3; ++k) for (int k = 0; k < 3; ++k)
triIndexBuffer_[indexCounter++] = getInputIndexSplit(faceID, k); triIndexBuffer_[indexCounter++] = -1 - k;
for (int k = 3; k < faceSize; ++k) for (int k = 3; k < faceSize; ++k)
{ {
// added tri belongs to current face // added tri belongs to current face
triToSortFaceMap_[triCounter++] = sortFaceID; triToSortFaceMap_[triCounter++] = sortFaceID;
triIndexBuffer_[indexCounter++] = getInputIndexSplit(faceID, 0); triIndexBuffer_[indexCounter++] = -1;
triIndexBuffer_[indexCounter++] = getInputIndexSplit(faceID, k-1); triIndexBuffer_[indexCounter++] = -1 - (k-1);
triIndexBuffer_[indexCounter++] = getInputIndexSplit(faceID, k); triIndexBuffer_[indexCounter++] = -1 - k;
} }
} }
// for (int sortFaceID = 0; sortFaceID < numFaces_; ++sortFaceID)
// rotate tris such that the unshared face vertex is at the wanted provoking position of each triangle // {
// // get original face id
if (provokingVertex_ >= 0) // const int faceID = faceSortMap_.empty() ? sortFaceID : faceSortMap_[sortFaceID];
{ //
for (int i = 0; i < numTris_; ++i) // const int faceSize = getFaceSize(faceID);
{ //
for (int k = 0; k < 3 - provokingVertex_; ++k) // if (faceSize < 4)
{ // {
const int tmp = triIndexBuffer_[i*3]; // // save face index mapping
triIndexBuffer_[i*3] = triIndexBuffer_[i*3 + 1]; // triToSortFaceMap_[triCounter++] = sortFaceID;
triIndexBuffer_[i*3 + 1] = triIndexBuffer_[i*3 + 2]; //
triIndexBuffer_[i*3 + 2] = tmp; // 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: // fill out missing subset info:
...@@ -1315,6 +1586,47 @@ void MeshCompiler::triangulate() ...@@ -1315,6 +1586,47 @@ void MeshCompiler::triangulate()
} }