OpenSubdiv: Work on better vert edge/face orientation code
Previous version of code didn't handle cases like hourglass connectivity with loose edge. The new code is supposed to handle all this cases.
This commit is contained in:
@@ -49,22 +49,51 @@ inline int findInArray(T array, int value)
|
|||||||
return (int)(std::find(array.begin(), array.end(), value) - array.begin());
|
return (int)(std::find(array.begin(), array.end(), value) - array.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace */
|
#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
|
||||||
|
inline void check_oriented_vert_connectivity(const int num_vert_edges,
|
||||||
|
const int num_vert_faces,
|
||||||
|
const int *vert_edges,
|
||||||
|
const int *vert_faces,
|
||||||
|
const int *dst_vert_edges,
|
||||||
|
const int *dst_vert_faces)
|
||||||
|
{
|
||||||
|
# ifndef NDEBUG
|
||||||
|
for (int i = 0; i < num_vert_faces; ++i) {
|
||||||
|
bool found = false;
|
||||||
|
for (int j = 0; j < num_vert_faces; ++j) {
|
||||||
|
if (vert_faces[i] == dst_vert_faces[j]) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
assert(!"vert-faces connectivity ruined");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < num_vert_edges; ++i) {
|
||||||
|
bool found = false;
|
||||||
|
for (int j = 0; j < num_vert_edges; ++j) {
|
||||||
|
if (vert_edges[i] == dst_vert_edges[j]) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
assert(!"vert-edges connectivity ruined");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
(void)num_vert_edges;
|
||||||
|
(void)num_vert_faces;
|
||||||
|
(void)vert_edges;
|
||||||
|
(void)vert_faces;
|
||||||
|
(void)dst_vert_edges;
|
||||||
|
(void)dst_vert_faces;
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
struct StackElem {
|
} /* namespace */
|
||||||
StackElem(int face_start,
|
|
||||||
int edge_start,
|
|
||||||
int face_vert_start,
|
|
||||||
bool append_start_edge = true)
|
|
||||||
: face_start(face_start),
|
|
||||||
edge_start(edge_start),
|
|
||||||
face_vert_start(face_vert_start),
|
|
||||||
append_start_edge(append_start_edge){}
|
|
||||||
int face_start;
|
|
||||||
int edge_start;
|
|
||||||
int face_vert_start;
|
|
||||||
bool append_start_edge;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline bool TopologyRefinerFactory<OpenSubdiv_Converter>::resizeComponentTopology(
|
inline bool TopologyRefinerFactory<OpenSubdiv_Converter>::resizeComponentTopology(
|
||||||
@@ -123,7 +152,12 @@ inline bool TopologyRefinerFactory<OpenSubdiv_Converter>::assignComponentTopolog
|
|||||||
}
|
}
|
||||||
/* Vertex relations */
|
/* Vertex relations */
|
||||||
const int num_verts = conv.get_num_verts(&conv);
|
const int num_verts = conv.get_num_verts(&conv);
|
||||||
|
#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
|
||||||
|
/* Flags used to see if the face was traversed already or not. */
|
||||||
|
bool *face_used = new bool[num_faces];
|
||||||
|
#endif
|
||||||
for (int vert = 0; vert < num_verts; ++vert) {
|
for (int vert = 0; vert < num_verts; ++vert) {
|
||||||
|
|
||||||
/* Vert-Faces */
|
/* Vert-Faces */
|
||||||
IndexArray dst_vert_faces = getBaseVertexFaces(refiner, vert);
|
IndexArray dst_vert_faces = getBaseVertexFaces(refiner, vert);
|
||||||
int num_vert_faces = conv.get_num_vert_faces(&conv, vert);
|
int num_vert_faces = conv.get_num_vert_faces(&conv, vert);
|
||||||
@@ -135,56 +169,101 @@ inline bool TopologyRefinerFactory<OpenSubdiv_Converter>::assignComponentTopolog
|
|||||||
int *vert_edges = new int[num_vert_edges];
|
int *vert_edges = new int[num_vert_edges];
|
||||||
conv.get_vert_edges(&conv, vert, vert_edges);
|
conv.get_vert_edges(&conv, vert, vert_edges);
|
||||||
#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
|
#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
|
||||||
/* Order vertex edges and faces in a CCW order. */
|
/* ** Order vertex edges and faces in a CCW order. ** */
|
||||||
/* TODO(sergey): Look into possible optimizations here. */
|
|
||||||
bool *face_used = new bool[num_faces];
|
|
||||||
memset(face_used, 0, sizeof(bool) * num_faces);
|
memset(face_used, 0, sizeof(bool) * num_faces);
|
||||||
std::stack<StackElem> stack;
|
/* Number of edges and faces added to the ordered array. */
|
||||||
int edge_count_ordered = 0, face_count_ordered = 0;
|
int edge_count_ordered = 0, face_count_ordered = 0;
|
||||||
if (num_vert_edges == num_vert_faces) {
|
/* Add loose edges straight into the edges array. */
|
||||||
/* Manifold vertex, start with any face and perform traversal. */
|
bool has_fan_connections = false;
|
||||||
int face_start = vert_faces[0];
|
for (int i = 0; i < num_vert_edges; ++i) {
|
||||||
int face_vert_start = findInArray(getBaseFaceVertices(refiner, face_start), vert);
|
IndexArray edge_faces = getBaseEdgeFaces(refiner, vert_edges[i]);
|
||||||
int edge_start = getBaseFaceEdges(refiner, face_start)[face_vert_start];
|
if (edge_faces.size() == 0) {
|
||||||
stack.push(StackElem(face_start, edge_start, face_vert_start));
|
dst_vert_edges[edge_count_ordered++] = vert_edges[i];
|
||||||
|
}
|
||||||
|
else if (edge_faces.size() > 2) {
|
||||||
|
has_fan_connections = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
if (has_fan_connections) {
|
||||||
/* ** Non-manifold vertex. Special handle here. ** */
|
/* OpenSubdiv currently doesn't give us clues how to handle
|
||||||
/* Add all loose edges adjacent to the vertex. */
|
* fan face connections. and since handling such connections
|
||||||
for (int i = 0; i < num_vert_edges; ++i) {
|
* complicates the loop below we simply don't do special
|
||||||
IndexArray edge_faces = getBaseEdgeFaces(refiner, vert_edges[i]);
|
* orientation for them.
|
||||||
if (edge_faces.size() == 0) {
|
*/
|
||||||
/* Can't really orient loose edges, just add then straight
|
memcpy(&dst_vert_edges[0], vert_edges, sizeof(int) * num_vert_edges);
|
||||||
* to the vert-edges array.
|
memcpy(&dst_vert_faces[0], vert_faces, sizeof(int) * num_vert_faces);
|
||||||
*/
|
delete [] vert_edges;
|
||||||
dst_vert_edges[edge_count_ordered++] = vert_edges[i];
|
delete [] vert_faces;
|
||||||
}
|
continue;
|
||||||
else if (edge_faces.size() == 1) {
|
}
|
||||||
int edge_start = vert_edges[i];
|
/* Perform at max numbder of vert-edges iteration and try to avoid
|
||||||
int face_start = edge_faces[0];
|
* deadlock here for malformed mesh.
|
||||||
int face_vert_start = findInArray(getBaseFaceVertices(refiner, face_start), vert);
|
*/
|
||||||
if (edge_start == (getBaseFaceEdges(refiner, face_start)[face_vert_start])) {
|
for (int global_iter = 0; global_iter < num_vert_edges; ++global_iter) {
|
||||||
stack.push(StackElem(face_start, edge_start, face_vert_start));
|
/* Numbr of edges and faces which are still to be ordered. */
|
||||||
face_used[face_start] = true;
|
int num_vert_edges_remained = num_vert_edges - edge_count_ordered,
|
||||||
|
num_vert_faces_remained = num_vert_faces - face_count_ordered;
|
||||||
|
if (num_vert_edges_remained == 0 && num_vert_faces_remained == 0) {
|
||||||
|
/* All done, nothing to do anymore. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Face, edge and face-vertex inndex to start traversal from. */
|
||||||
|
int face_start = -1, edge_start = -1, face_vert_start = -1;
|
||||||
|
if (num_vert_edges_remained == num_vert_faces_remained) {
|
||||||
|
/* Vertex is eitehr complete manifold or is connected to seevral
|
||||||
|
* manifold islands (hourglass-like configuration), can pick up
|
||||||
|
* random edge unused and start from it.
|
||||||
|
*/
|
||||||
|
/* TODO(sergey): Start from previous edge from which traversal
|
||||||
|
* began at previous iteration.
|
||||||
|
*/
|
||||||
|
for (int i = 0; i < num_vert_edges; ++i) {
|
||||||
|
face_start = vert_faces[i];
|
||||||
|
if (!face_used[face_start]) {
|
||||||
|
ConstIndexArray
|
||||||
|
face_verts = getBaseFaceVertices(refiner, face_start),
|
||||||
|
face_edges = getBaseFaceEdges(refiner, face_start);
|
||||||
|
face_vert_start = findInArray(face_verts, vert);
|
||||||
|
edge_start = face_edges[face_vert_start];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
while (!stack.empty()) {
|
/* Special handle of non-manifold vertex. */
|
||||||
StackElem& top = stack.top();
|
for (int i = 0; i < num_vert_edges; ++i) {
|
||||||
int edge_start = top.edge_start;
|
bool start_found = false;
|
||||||
int face_start = top.face_start;
|
edge_start = vert_edges[i];
|
||||||
int face_vert_start = top.face_vert_start;
|
IndexArray edge_faces = getBaseEdgeFaces(refiner, edge_start);
|
||||||
bool append_start_edge = top.append_start_edge;
|
for (int j = 0; j < edge_faces.size(); ++j) {
|
||||||
stack.pop();
|
face_start = edge_faces[j];
|
||||||
Index edge_first = edge_start;
|
if (!face_used[face_start]) {
|
||||||
|
ConstIndexArray
|
||||||
dst_vert_faces[face_count_ordered++] = face_start;
|
face_verts = getBaseFaceVertices(refiner, face_start),
|
||||||
if (append_start_edge) {
|
face_edges = getBaseFaceEdges(refiner, face_start);
|
||||||
dst_vert_edges[edge_count_ordered++] = edge_start;
|
face_vert_start = findInArray(face_verts, vert);
|
||||||
|
if (edge_start == face_edges[face_vert_start]) {
|
||||||
|
start_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (start_found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Reset indices for sanity check below. */
|
||||||
|
face_start = edge_start = face_vert_start = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
/* Sanity check. */
|
||||||
|
assert(face_start != -1 &&
|
||||||
|
edge_start != -1 &&
|
||||||
|
face_vert_start != -1);
|
||||||
|
/* Traverse faces starting from the current one. */
|
||||||
|
int edge_first = edge_start;
|
||||||
|
dst_vert_faces[face_count_ordered++] = face_start;
|
||||||
|
dst_vert_edges[edge_count_ordered++] = edge_start;
|
||||||
face_used[face_start] = true;
|
face_used[face_start] = true;
|
||||||
|
|
||||||
while (edge_count_ordered < num_vert_edges) {
|
while (edge_count_ordered < num_vert_edges) {
|
||||||
IndexArray face_verts = getBaseFaceVertices(refiner, face_start);
|
IndexArray face_verts = getBaseFaceVertices(refiner, face_start);
|
||||||
IndexArray face_edges = getBaseFaceEdges(refiner, face_start);
|
IndexArray face_edges = getBaseFaceEdges(refiner, face_start);
|
||||||
@@ -192,24 +271,9 @@ inline bool TopologyRefinerFactory<OpenSubdiv_Converter>::assignComponentTopolog
|
|||||||
int face_edge_next = (face_edge_start > 0) ? (face_edge_start - 1) : (face_verts.size() - 1);
|
int face_edge_next = (face_edge_start > 0) ? (face_edge_start - 1) : (face_verts.size() - 1);
|
||||||
Index edge_next = face_edges[face_edge_next];
|
Index edge_next = face_edges[face_edge_next];
|
||||||
if (edge_next == edge_first) {
|
if (edge_next == edge_first) {
|
||||||
/* TODO(sergey): Find more generic solution so non-manifold
|
/* Multiple manifolds found, stop for now and handle rest
|
||||||
* edges combined with some manifold adjacent geometry is
|
* in the next iteration.
|
||||||
* handled correct.
|
|
||||||
*/
|
*/
|
||||||
if (num_vert_edges == num_vert_faces &&
|
|
||||||
edge_count_ordered != num_vert_edges)
|
|
||||||
{
|
|
||||||
IndexArray edge_faces = getBaseEdgeFaces(refiner, edge_next);
|
|
||||||
for (int i = 0; i < num_vert_faces; ++i) {
|
|
||||||
int face_start = edge_faces[i];
|
|
||||||
if (!face_used[face_start]) {
|
|
||||||
int edge_start = edge_next;
|
|
||||||
int face_vert_start = findInArray(getBaseFaceVertices(refiner, face_start), vert);
|
|
||||||
stack.push(StackElem(face_start, edge_start, face_vert_start, false));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dst_vert_edges[edge_count_ordered++] = edge_next;
|
dst_vert_edges[edge_count_ordered++] = edge_next;
|
||||||
@@ -221,16 +285,6 @@ inline bool TopologyRefinerFactory<OpenSubdiv_Converter>::assignComponentTopolog
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (edge_faces.size() != 2) {
|
else if (edge_faces.size() != 2) {
|
||||||
for (int i = 0; i < edge_faces.size(); ++i) {
|
|
||||||
if (edge_faces[i] != face_start) {
|
|
||||||
int face_start = edge_faces[i];
|
|
||||||
if (!face_used[face_start]) {
|
|
||||||
int edge_start = edge_next;
|
|
||||||
int face_vert_start = findInArray(getBaseFaceVertices(refiner, face_start), vert);
|
|
||||||
stack.push(StackElem(face_start, edge_start, face_vert_start, false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assert(edge_faces.size() == 2);
|
assert(edge_faces.size() == 2);
|
||||||
@@ -242,45 +296,36 @@ inline bool TopologyRefinerFactory<OpenSubdiv_Converter>::assignComponentTopolog
|
|||||||
edge_start = edge_next;
|
edge_start = edge_next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete [] face_used;
|
|
||||||
|
|
||||||
/* Verify ordering doesn't ruin connectivity information. */
|
/* Verify ordering doesn't ruin connectivity information. */
|
||||||
assert(face_count_ordered == num_vert_faces);
|
assert(face_count_ordered == num_vert_faces);
|
||||||
assert(edge_count_ordered == num_vert_edges);
|
assert(edge_count_ordered == num_vert_edges);
|
||||||
#ifndef NDEBUG
|
check_oriented_vert_connectivity(num_vert_edges,
|
||||||
for (int i = 0; i < num_vert_faces; ++i) {
|
num_vert_faces,
|
||||||
bool found = false;
|
vert_edges,
|
||||||
for (int j = 0; j < num_vert_faces; ++j) {
|
vert_faces,
|
||||||
if (vert_faces[i] == dst_vert_faces[j]) {
|
&dst_vert_edges[0],
|
||||||
found = true;
|
&dst_vert_faces[0]);
|
||||||
break;
|
/* For the release builds we're failing mesh construction so instead
|
||||||
}
|
* of nasty bugs the unsupported mesh will simply disappear from the
|
||||||
}
|
* viewport.
|
||||||
if (!found) {
|
*/
|
||||||
assert(!"vert-faces connectivity ruined");
|
if (face_count_ordered != num_vert_faces ||
|
||||||
}
|
edge_count_ordered != num_vert_edges)
|
||||||
|
{
|
||||||
|
delete [] vert_edges;
|
||||||
|
delete [] vert_faces;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < num_vert_edges; ++i) {
|
|
||||||
bool found = false;
|
|
||||||
for (int j = 0; j < num_vert_edges; ++j) {
|
|
||||||
if (vert_edges[i] == dst_vert_edges[j]) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
assert(!"vert-edges connectivity ruined");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#else /* OPENSUBDIV_ORIENT_TOPOLOGY */
|
#else /* OPENSUBDIV_ORIENT_TOPOLOGY */
|
||||||
memcpy(&dst_vert_edges[0], vert_edges, sizeof(int) * num_vert_edges);
|
memcpy(&dst_vert_edges[0], vert_edges, sizeof(int) * num_vert_edges);
|
||||||
memcpy(&dst_vert_faces[0], vert_faces, sizeof(int) * num_vert_faces);
|
memcpy(&dst_vert_faces[0], vert_faces, sizeof(int) * num_vert_faces);
|
||||||
#endif /* OPENSUBDIV_ORIENT_TOPOLOGY */
|
#endif /* OPENSUBDIV_ORIENT_TOPOLOGY */
|
||||||
|
|
||||||
delete [] vert_edges;
|
delete [] vert_edges;
|
||||||
delete [] vert_faces;
|
delete [] vert_faces;
|
||||||
}
|
}
|
||||||
|
#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
|
||||||
|
delete [] face_used;
|
||||||
|
#endif
|
||||||
populateBaseLocalIndices(refiner);
|
populateBaseLocalIndices(refiner);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user