OpenSubdiv/opensubdiv/vtr/level.cpp
barfowl 7ebb04bdcb Fixed bugs arising from degenerate edges and triangles:
- avoid repeated edges within the same face on construction
    - corrected degenerate edge detection in tri-split refinement
2015-03-24 17:37:37 -07:00

1968 lines
73 KiB
C++

//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../sdc/types.h"
#include "../sdc/crease.h"
#include "../vtr/array.h"
#include "../vtr/level.h"
#include "../vtr/refinement.h"
#include "../vtr/fvarLevel.h"
#include <cassert>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
//
// Level:
// This is intended to be a fairly simple container of topology, sharpness and
// other information that is useful to retain for subdivision. It is intended to
// be constructed by other friend classes, i.e. factories and class specialized to
// contruct topology based on various splitting schemes. So its interface consists
// of simple methods for inspection, and low-level protected methods for populating
// it rather than high-level modifiers.
//
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr {
//
// Simple (for now) constructor and destructor:
//
Level::Level() :
_faceCount(0),
_edgeCount(0),
_vertCount(0),
_depth(0),
_maxEdgeFaces(0),
_maxValence(0) {
}
Level::~Level() {
for (int i = 0; i < (int)_fvarChannels.size(); ++i) {
delete _fvarChannels[i];
}
}
char const *
Level::getTopologyErrorString(TopologyError errCode) {
switch (errCode) {
case TOPOLOGY_MISSING_EDGE_FACES :
return "MISSING_EDGE_FACES";
case TOPOLOGY_MISSING_EDGE_VERTS :
return "MISSING_EDGE_VERTS";
case TOPOLOGY_MISSING_FACE_EDGES :
return "MISSING_FACE_EDGES";
case TOPOLOGY_MISSING_FACE_VERTS :
return "MISSING_FACE_VERTS";
case TOPOLOGY_MISSING_VERT_FACES :
return "MISSING_VERT_FACES";
case TOPOLOGY_MISSING_VERT_EDGES :
return "MISSING_VERT_EDGES";
case TOPOLOGY_FAILED_CORRELATION_EDGE_FACE :
return "FAILED_CORRELATION_EDGE_FACE";
case TOPOLOGY_FAILED_CORRELATION_FACE_VERT :
return "FAILED_CORRELATION_FACE_VERT";
case TOPOLOGY_FAILED_CORRELATION_FACE_EDGE :
return "FAILED_CORRELATION_FACE_EDGE";
case TOPOLOGY_FAILED_ORIENTATION_INCIDENT_EDGE :
return "FAILED_ORIENTATION_INCIDENT_EDGE";
case TOPOLOGY_FAILED_ORIENTATION_INCIDENT_FACE :
return "FAILED_ORIENTATION_INCIDENT_FACE";
case TOPOLOGY_FAILED_ORIENTATION_INCIDENT_FACES_EDGES :
return "FAILED_ORIENTATION_INCIDENT_FACES_EDGES";
case TOPOLOGY_DEGENERATE_EDGE :
return "DEGENERATE_EDGE";
case TOPOLOGY_NON_MANIFOLD_EDGE :
return "NON_MANIFOLD_EDGE";
case TOPOLOGY_INVALID_CREASE_EDGE :
return "INVALID_CREASE_EDGE";
case TOPOLOGY_INVALID_CREASE_VERT :
return "INVALID_CREASE_VERT";
default:
assert(0);
}
return 0;
}
//
// Debugging method to validate topology, i.e. verify appropriate symmetry
// between the relations, etc.
//
// Additions that need to be made in the near term:
// * verifying user-applied tags relating to topology:
// - non-manifold in particular (ordering above can be part of this)
// - face holes don't require anything
// - verifying orientation of components, particularly vert-edges and faces:
// - both need to be ordered correctly (when manifold)
// - both need to be in sync for an interior vertex
// ? is a rotation allowed for the interior case?
// - I don't see why not...
// ? verifying sharpness:
// - values < Smooth or > Infinite
// - sharpening of boundary edges (is this necessary, since we do it?)
// - it does ensure our work was not corrupted by client assignments
//
// Possibilities:
// - single validate() method, which will call all of:
// - validateTopology()
// - validateSharpness()
// - validateTagging()
// - consider using a mask/struct to choose what to validate, i.e.:
// - bool validate(ValidateOptions const& options) const;
//
#define REPORT(code, format, ...) \
if (callback) { \
char const * errStr = getTopologyErrorString(code); \
char msg[1024]; \
snprintf(msg, 1024, "%s - "format, errStr, ##__VA_ARGS__); \
callback(code, msg, clientData); \
}
bool
Level::validateTopology(ValidationCallback callback, void const * clientData) const {
//
// Verify internal topological consistency (eventually a Level method?):
// - each face-vert has corresponding vert-face (and child)
// - each face-edge has corresponding edge-face
// - each edge-vert has corresponding vert-edge (and child)
// The above three are enough for most cases, but it is still possible
// the latter relation in each above has no correspondent in the former,
// so apply the symmetric tests:
// - each edge-face has corresponding face-edge
// - each vert-face has corresponding face-vert
// - each vert-edge has corresponding edge-vert
// We are still left with the possibility of duplicate references in
// places we don't want them. Currently a component can exist multiple
// times in a component of higher dimension.
// - each vert-face <face,child> pair is unique
// - each vert-edge <edge,child> pair is unique
//
bool returnOnFirstError = true;
bool isValid = true;
// Verify each face-vert has corresponding vert-face and child:
if ((getNumFaceVerticesTotal() == 0) || (getNumVertexFacesTotal() == 0)) {
if (getNumFaceVerticesTotal() == 0) {
REPORT(TOPOLOGY_MISSING_FACE_VERTS, "missing face-verts");
}
if (getNumVertexFacesTotal() == 0) {
REPORT(TOPOLOGY_MISSING_VERT_FACES, "missing vert-faces");
}
return false;
}
for (int fIndex = 0; fIndex < getNumFaces(); ++fIndex) {
ConstIndexArray fVerts = getFaceVertices(fIndex);
int fVertCount = fVerts.size();
for (int i = 0; i < fVertCount; ++i) {
Index vIndex = fVerts[i];
ConstIndexArray vFaces = getVertexFaces(vIndex);
ConstLocalIndexArray vInFace = getVertexFaceLocalIndices(vIndex);
bool vertFaceOfFaceExists = false;
for (int j = 0; j < vFaces.size(); ++j) {
if ((vFaces[j] == fIndex) && (vInFace[j] == i)) {
vertFaceOfFaceExists = true;
break;
}
}
if (!vertFaceOfFaceExists) {
REPORT(TOPOLOGY_FAILED_CORRELATION_FACE_VERT,
"face %d correlation of vert %d failed", fIndex, i);
if (returnOnFirstError) return false;
isValid = false;
}
}
}
// Verify each face-edge has corresponding edge-face:
if ((getNumEdgeFacesTotal() == 0) || (getNumFaceEdgesTotal() == 0)) {
if (getNumEdgeFacesTotal() == 0) {
REPORT(TOPOLOGY_MISSING_EDGE_FACES, "missing edge-faces");
}
if (getNumFaceEdgesTotal() == 0) {
REPORT(TOPOLOGY_MISSING_FACE_EDGES, "missing face-edges");
}
return false;
}
for (int fIndex = 0; fIndex < getNumFaces(); ++fIndex) {
ConstIndexArray fEdges = getFaceEdges(fIndex);
int fEdgeCount = fEdges.size();
for (int i = 0; i < fEdgeCount; ++i) {
int eIndex = fEdges[i];
ConstIndexArray eFaces = getEdgeFaces(eIndex);
ConstLocalIndexArray eInFace = getEdgeFaceLocalIndices(eIndex);
bool edgeFaceOfFaceExists = false;
for (int j = 0; j < eFaces.size(); ++j) {
if ((eFaces[j] == fIndex) && (eInFace[j] == i)) {
edgeFaceOfFaceExists = true;
break;
}
}
if (!edgeFaceOfFaceExists) {
REPORT(TOPOLOGY_FAILED_CORRELATION_FACE_EDGE,
"face %d correlation of edge %d failed", fIndex, i);
if (returnOnFirstError) return false;
isValid = false;
}
}
}
// Verify each edge-vert has corresponding vert-edge and child:
if ((getNumEdgeVerticesTotal() == 0) || (getNumVertexEdgesTotal() == 0)) {
if (getNumEdgeVerticesTotal() == 0) {
REPORT(TOPOLOGY_MISSING_EDGE_VERTS, "missing edge-verts");
}
if (getNumVertexEdgesTotal() == 0) {
REPORT(TOPOLOGY_MISSING_VERT_EDGES, "missing vert-edges");
}
return false;
}
for (int eIndex = 0; eIndex < getNumEdges(); ++eIndex) {
ConstIndexArray eVerts = getEdgeVertices(eIndex);
for (int i = 0; i < 2; ++i) {
Index vIndex = eVerts[i];
ConstIndexArray vEdges = getVertexEdges(vIndex);
ConstLocalIndexArray vInEdge = getVertexEdgeLocalIndices(vIndex);
bool vertEdgeOfEdgeExists = false;
for (int j = 0; j < vEdges.size(); ++j) {
if ((vEdges[j] == eIndex) && (vInEdge[j] == i)) {
vertEdgeOfEdgeExists = true;
break;
}
}
if (!vertEdgeOfEdgeExists) {
REPORT(TOPOLOGY_FAILED_CORRELATION_FACE_VERT,
"edge %d correlation of vert %d failed", eIndex, i);
if (returnOnFirstError) return false;
isValid = false;
}
}
}
// Verify that vert-faces and vert-edges are properly ordered and in sync:
// - currently this requires the relations exactly match those that we construct from
// the ordering method, i.e. we do not allow rotations for interior vertices.
Index * indexBuffer = (Index*) alloca(2 * _maxValence * sizeof(Index));
for (int vIndex = 0; vIndex < getNumVertices(); ++vIndex) {
if (_vertTags[vIndex]._incomplete || _vertTags[vIndex]._nonManifold) continue;
ConstIndexArray vFaces = getVertexFaces(vIndex);
ConstIndexArray vEdges = getVertexEdges(vIndex);
Index * vFacesOrdered = indexBuffer;
Index * vEdgesOrdered = indexBuffer + vFaces.size();
if (!orderVertexFacesAndEdges(vIndex, vFacesOrdered, vEdgesOrdered)) {
REPORT(TOPOLOGY_FAILED_ORIENTATION_INCIDENT_FACES_EDGES,
"vertex %d cannot orient incident faces and edges", vIndex);
if (returnOnFirstError) return false;
isValid = false;
}
for (int i = 0; i < vFaces.size(); ++i) {
if (vFaces[i] != vFacesOrdered[i]) {
REPORT(TOPOLOGY_FAILED_ORIENTATION_INCIDENT_FACE,
"vertex %d orientation failure at incident face %d", vIndex, i);
if (returnOnFirstError) return false;
isValid = false;
break;
}
}
for (int i = 0; i < vEdges.size(); ++i) {
if (vEdges[i] != vEdgesOrdered[i]) {
REPORT(TOPOLOGY_FAILED_ORIENTATION_INCIDENT_EDGE,
"vertex %d orientation failure at incident edge %d", vIndex, i);
if (returnOnFirstError) return false;
isValid = false;
break;
}
}
}
// Verify non-manifold tags are appropriately assigned to edges and vertices:
// - note we have to validate orientation of vertex neighbors to do this rigorously
for (int eIndex = 0; eIndex < getNumEdges(); ++eIndex) {
Level::ETag const& eTag = _edgeTags[eIndex];
if (eTag._nonManifold) continue;
ConstIndexArray eVerts = getEdgeVertices(eIndex);
if (eVerts[0] == eVerts[1]) {
REPORT(TOPOLOGY_DEGENERATE_EDGE,
"Error in eIndex = %d: degenerate edge not tagged marked non-manifold", eIndex);
if (returnOnFirstError) return false;
isValid = false;
}
ConstIndexArray eFaces = getEdgeFaces(eIndex);
if ((eFaces.size() < 1) || (eFaces.size() > 2)) {
REPORT(TOPOLOGY_NON_MANIFOLD_EDGE,
"edge %d with %d incident faces not tagged non-manifold", eIndex, eFaces.size());
if (returnOnFirstError) return false;
isValid = false;
}
}
return isValid;
}
//
// Anonymous helper functions for debugging output -- yes, using printf(), this is not
// intended to serve anyone other than myself for now and I favor its formatting control
//
namespace {
template <typename INT_TYPE>
void
printIndexArray(ConstArray<INT_TYPE> const& array) {
printf("%d [%d", array.size(), array[0]);
for (int i = 1; i < array.size(); ++i) {
printf(" %d", array[i]);
}
printf("]\n");
}
const char*
ruleString(Sdc::Crease::Rule rule) {
switch (rule) {
case Sdc::Crease::RULE_UNKNOWN: return "<uninitialized>";
case Sdc::Crease::RULE_SMOOTH: return "Smooth";
case Sdc::Crease::RULE_DART: return "Dart";
case Sdc::Crease::RULE_CREASE: return "Crease";
case Sdc::Crease::RULE_CORNER: return "Corner";
default:
assert(0);
}
return 0;
}
}
void
Level::print(const Refinement* pRefinement) const {
bool printFaceVerts = true;
bool printFaceEdges = true;
bool printFaceChildVerts = false;
bool printFaceTags = true;
bool printEdgeVerts = true;
bool printEdgeFaces = true;
bool printEdgeChildVerts = true;
bool printEdgeSharpness = true;
bool printEdgeTags = true;
bool printVertFaces = true;
bool printVertEdges = true;
bool printVertChildVerts = false;
bool printVertSharpness = true;
bool printVertTags = true;
printf("Level (0x%p):\n", this);
printf(" Depth = %d\n", _depth);
printf(" Primary component counts:\n");
printf(" faces = %d\n", _faceCount);
printf(" edges = %d\n", _edgeCount);
printf(" verts = %d\n", _vertCount);
printf(" Topology relation sizes:\n");
printf(" Face relations:\n");
printf(" face-vert counts/offset = %lu\n", (unsigned long)_faceVertCountsAndOffsets.size());
printf(" face-vert indices = %lu\n", (unsigned long)_faceVertIndices.size());
if (_faceVertIndices.size()) {
for (int i = 0; printFaceVerts && i < getNumFaces(); ++i) {
printf(" face %4d verts: ", i);
printIndexArray(getFaceVertices(i));
}
}
printf(" face-edge indices = %lu\n", (unsigned long)_faceEdgeIndices.size());
if (_faceEdgeIndices.size()) {
for (int i = 0; printFaceEdges && i < getNumFaces(); ++i) {
printf(" face %4d edges: ", i);
printIndexArray(getFaceEdges(i));
}
}
printf(" face tags = %lu\n", (unsigned long)_faceTags.size());
for (int i = 0; printFaceTags && i < (int)_faceTags.size(); ++i) {
FTag const& fTag = _faceTags[i];
printf(" face %4d:", i);
printf(" hole = %d", (int)fTag._hole);
printf("\n");
}
if (pRefinement) {
printf(" face child-verts = %lu\n", (unsigned long)pRefinement->_faceChildVertIndex.size());
for (int i = 0; printFaceChildVerts && i < (int)pRefinement->_faceChildVertIndex.size(); ++i) {
printf(" face %4d child vert: %d\n", i, pRefinement->_faceChildVertIndex[i]);
}
}
printf(" Edge relations:\n");
printf(" edge-vert indices = %lu\n", (unsigned long)_edgeVertIndices.size());
if (_edgeVertIndices.size()) {
for (int i = 0; printEdgeVerts && i < getNumEdges(); ++i) {
printf(" edge %4d verts: ", i);
printIndexArray(getEdgeVertices(i));
}
}
printf(" edge-face counts/offset = %lu\n", (unsigned long)_edgeFaceCountsAndOffsets.size());
printf(" edge-face indices = %lu\n", (unsigned long)_edgeFaceIndices.size());
printf(" edge-face local-indices = %lu\n", (unsigned long)_edgeFaceLocalIndices.size());
if (_edgeFaceIndices.size()) {
for (int i = 0; printEdgeFaces && i < getNumEdges(); ++i) {
printf(" edge %4d faces: ", i);
printIndexArray(getEdgeFaces(i));
printf(" face-edges: ");
printIndexArray(getEdgeFaceLocalIndices(i));
}
}
if (pRefinement) {
printf(" edge child-verts = %lu\n", (unsigned long)pRefinement->_edgeChildVertIndex.size());
for (int i = 0; printEdgeChildVerts && i < (int)pRefinement->_edgeChildVertIndex.size(); ++i) {
printf(" edge %4d child vert: %d\n", i, pRefinement->_edgeChildVertIndex[i]);
}
}
printf(" edge sharpness = %lu\n", (unsigned long)_edgeSharpness.size());
for (int i = 0; printEdgeSharpness && i < (int)_edgeSharpness.size(); ++i) {
printf(" edge %4d sharpness: %f\n", i, _edgeSharpness[i]);
}
printf(" edge tags = %lu\n", (unsigned long)_edgeTags.size());
for (int i = 0; printEdgeTags && i < (int)_edgeTags.size(); ++i) {
ETag const& eTag = _edgeTags[i];
printf(" edge %4d:", i);
printf(" boundary = %d", (int)eTag._boundary);
printf(", nonManifold = %d", (int)eTag._nonManifold);
printf(", semiSharp = %d", (int)eTag._semiSharp);
printf(", infSharp = %d", (int)eTag._infSharp);
printf("\n");
}
printf(" Vert relations:\n");
printf(" vert-face counts/offset = %lu\n", (unsigned long)_vertFaceCountsAndOffsets.size());
printf(" vert-face indices = %lu\n", (unsigned long)_vertFaceIndices.size());
printf(" vert-face local-indices = %lu\n", (unsigned long)_vertFaceLocalIndices.size());
if (_vertFaceIndices.size()) {
for (int i = 0; printVertFaces && i < getNumVertices(); ++i) {
printf(" vert %4d faces: ", i);
printIndexArray(getVertexFaces(i));
printf(" face-verts: ");
printIndexArray(getVertexFaceLocalIndices(i));
}
}
printf(" vert-edge counts/offset = %lu\n", (unsigned long)_vertEdgeCountsAndOffsets.size());
printf(" vert-edge indices = %lu\n", (unsigned long)_vertEdgeIndices.size());
printf(" vert-edge local-indices = %lu\n", (unsigned long)_vertEdgeLocalIndices.size());
if (_vertEdgeIndices.size()) {
for (int i = 0; printVertEdges && i < getNumVertices(); ++i) {
printf(" vert %4d edges: ", i);
printIndexArray(getVertexEdges(i));
printf(" edge-verts: ");
printIndexArray(getVertexEdgeLocalIndices(i));
}
}
if (pRefinement) {
printf(" vert child-verts = %lu\n", (unsigned long)pRefinement->_vertChildVertIndex.size());
for (int i = 0; printVertChildVerts && i < (int)pRefinement->_vertChildVertIndex.size(); ++i) {
printf(" vert %4d child vert: %d\n", i, pRefinement->_vertChildVertIndex[i]);
}
}
printf(" vert sharpness = %lu\n", (unsigned long)_vertSharpness.size());
for (int i = 0; printVertSharpness && i < (int)_vertSharpness.size(); ++i) {
printf(" vert %4d sharpness: %f\n", i, _vertSharpness[i]);
}
printf(" vert tags = %lu\n", (unsigned long)_vertTags.size());
for (int i = 0; printVertTags && i < (int)_vertTags.size(); ++i) {
VTag const& vTag = _vertTags[i];
printf(" vert %4d:", i);
printf(" rule = %s", ruleString((Sdc::Crease::Rule)vTag._rule));
printf(", boundary = %d", (int)vTag._boundary);
printf(", corner = %d", (int)vTag._corner);
printf(", xordinary = %d", (int)vTag._xordinary);
printf(", nonManifold = %d", (int)vTag._nonManifold);
printf(", infSharp = %d", (int)vTag._infSharp);
printf(", semiSharp = %d", (int)vTag._semiSharp);
printf(", semiSharpEdges = %d", (int)vTag._semiSharpEdges);
printf("\n");
}
fflush(stdout);
}
namespace {
template <typename TAG_TYPE, typename INT_TYPE>
void
combineTags(TAG_TYPE& dstTag, TAG_TYPE const& srcTag) {
assert(sizeof(TAG_TYPE) == sizeof(INT_TYPE));
INT_TYPE const & srcInt = *(reinterpret_cast<INT_TYPE const*>(&srcTag));
INT_TYPE & dstInt = *(reinterpret_cast<INT_TYPE *> (&dstTag));
dstInt |= srcInt;
}
}
Level::VTag
Level::getFaceCompositeVTag(ConstIndexArray & faceVerts) const {
VTag compTag = _vertTags[faceVerts[0]];
for (int i = 1; i < faceVerts.size(); ++i) {
combineTags<VTag, VTag::VTagSize>(compTag, _vertTags[faceVerts[i]]);
}
return compTag;
}
Level::ETag
Level::getFaceCompositeETag(ConstIndexArray & faceEdges) const {
ETag compTag = _edgeTags[faceEdges[0]];
for (int i = 1; i < faceEdges.size(); ++i) {
combineTags<ETag, ETag::ETagSize>(compTag, _edgeTags[faceEdges[i]]);
}
return compTag;
}
//
// High-level topology gathering functions -- used mainly in patch construction. These
// may eventually be moved elsewhere, possibly to classes specialized for quad- and tri-
// patch identification and construction, but for now somewhere more accessible than the
// patch tables factory is preferable.
//
// Note a couple of nuisances...
// - debatable whether we should include the face's face-verts in the face functions
// - we refer to the result as a "patch" when we do
// - otherwise a "ring" of vertices is more appropriate
//
namespace {
template <typename INT_TYPE>
inline INT_TYPE fastMod4(INT_TYPE value) {
return (value & 0x3);
}
template <class ARRAY_OF_TYPE, class TYPE>
inline TYPE otherOfTwo(ARRAY_OF_TYPE const& arrayOfTwo, TYPE const& value) {
return arrayOfTwo[value == arrayOfTwo[0]];
}
}
//
// Gathering the one-ring of vertices from quads surrounding a vertex:
// - the neighborhood of the vertex is assumed to be quad-regular (manifold)
//
// Ordering of resulting vertices:
// The surrounding one-ring follows the ordering of the incident faces. For each
// incident quad, the two vertices in CCW order within that quad are added. If the
// vertex is on a boundary, a third vertex on the boundary edge will be contributed from
// the last face.
//
int
Level::gatherQuadRegularRingAroundVertex(
Index vIndex, int ringPoints[], int fvarChannel) const {
Level const& level = *this;
ConstIndexArray vEdges = level.getVertexEdges(vIndex);
ConstIndexArray vFaces = level.getVertexFaces(vIndex);
ConstLocalIndexArray vInFaces = level.getVertexFaceLocalIndices(vIndex);
bool isBoundary = (vEdges.size() > vFaces.size());
int ringIndex = 0;
for (int i = 0; i < vFaces.size(); ++i) {
//
// For every incident quad, we want the two vertices clockwise in each face, i.e.
// the vertex at the end of the leading edge and the vertex opposite this one:
//
ConstIndexArray fPoints = (fvarChannel < 0)
? level.getFaceVertices(vFaces[i])
: level.getFVarFaceValues(vFaces[i], fvarChannel);
int vInThisFace = vInFaces[i];
ringPoints[ringIndex++] = fPoints[fastMod4(vInThisFace + 1)];
ringPoints[ringIndex++] = fPoints[fastMod4(vInThisFace + 2)];
if (isBoundary && (i == (vFaces.size() - 1))) {
ringPoints[ringIndex++] = fPoints[fastMod4(vInThisFace + 3)];
}
}
return ringIndex;
}
//
// Gathering the 4 vertices of a quad:
//
// | |
// --0-----3--
// |x x|
// |x x|
// --1-----2--
// | |
//
int
Level::gatherQuadLinearPatchPoints(
Index thisFace, Index patchPoints[], int rotation, int fvarChannel) const {
Level const& level = *this;
assert((0 <= rotation) && (rotation < 4));
static int const rotationSequence[7] = { 0, 1, 2, 3, 0, 1, 2 };
int const * rotatedVerts = &rotationSequence[rotation];
ConstIndexArray facePoints = (fvarChannel < 0) ?
level.getFaceVertices(thisFace) :
level.getFVarFaceValues(thisFace, fvarChannel);
patchPoints[0] = facePoints[rotatedVerts[0]];
patchPoints[1] = facePoints[rotatedVerts[1]];
patchPoints[2] = facePoints[rotatedVerts[2]];
patchPoints[3] = facePoints[rotatedVerts[3]];
return 4;
}
//
// Gathering the 16 vertices of a quad-regular interior patch:
// - the neighborhood of the face is assumed to be quad-regular
//
// Ordering of resulting vertices:
// It was debatable whether to include the vertices of the original face for a complete
// "patch" or just the surrounding ring -- clearly we ended up with a function for the entire
// patch, but that may change.
// The latter ring of vertices around the face (potentially returned on its own) was
// oriented with respect to the face. The ring of 12 vertices is gathered as 4 groups of 3
// vertices -- one for each corner vertex, and each group forming the quad opposite each
// corner vertex when combined with that corner vertex. The four vertices of the face begin
// the patch.
//
// | | | |
// ---5-----4-----15----14---
// | | | |
// | | | |
// ---6-----0-----3-----13---
// | |x x| |
// | |x x| |
// ---7-----1-----2-----12---
// | | | |
// | | | |
// ---8-----9-----10----11---
// | | | |
//
int
Level::gatherQuadRegularInteriorPatchPoints(
Index thisFace, Index patchPoints[], int rotation, int fvarChannel) const {
Level const& level = *this;
assert((0 <= rotation) && (rotation < 4));
static int const rotationSequence[7] = { 0, 1, 2, 3, 0, 1, 2 };
int const * rotatedVerts = &rotationSequence[rotation];
ConstIndexArray thisFaceVerts = level.getFaceVertices(thisFace);
ConstIndexArray facePoints = (fvarChannel < 0) ? thisFaceVerts :
level.getFVarFaceValues(thisFace, fvarChannel);
patchPoints[0] = facePoints[rotatedVerts[0]];
patchPoints[1] = facePoints[rotatedVerts[1]];
patchPoints[2] = facePoints[rotatedVerts[2]];
patchPoints[3] = facePoints[rotatedVerts[3]];
//
// For each of the four corner vertices, there is a face diagonally opposite
// the given/central face. Each of these faces contains three points of the
// entire ring of points around that given/central face.
//
int pointIndex = 4;
for (int i = 0; i < 4; ++i) {
Index v = thisFaceVerts[rotatedVerts[i]];
ConstIndexArray vFaces = level.getVertexFaces(v);
ConstLocalIndexArray vInFaces = level.getVertexFaceLocalIndices(v);
int thisFaceInVFaces = vFaces.FindIndexIn4Tuple(thisFace);
int intFaceInVFaces = fastMod4(thisFaceInVFaces + 2);
Index intFace = vFaces[intFaceInVFaces];
int vInIntFace = vInFaces[intFaceInVFaces];
facePoints = (fvarChannel < 0) ? level.getFaceVertices(intFace) :
level.getFVarFaceValues(intFace, fvarChannel);
patchPoints[pointIndex++] = facePoints[fastMod4(vInIntFace + 1)];
patchPoints[pointIndex++] = facePoints[fastMod4(vInIntFace + 2)];
patchPoints[pointIndex++] = facePoints[fastMod4(vInIntFace + 3)];
}
assert(pointIndex == 16);
return 16;
}
//
// Gathering the 12 vertices of a quad-regular boundary patch:
// - the neighborhood of the face is assumed to be quad-regular
// - the single edge of the face that lies on the boundary is specified
// - only one edge of the face is a boundary edge
//
// Ordering of resulting vertices:
// It was debatable whether to include the vertices of the original face for a complete
// "patch" or just the surrounding ring -- clearly we ended up with a function for the entire
// patch, but that may change.
// The latter ring of vertices around the face (potentially returned on its own) was
// oriented beginning from the leading CCW boundary edge and ending at the trailing edge.
// The four vertices of the face begin the patch and are oriented similarly to this outer
// ring -- forming an inner ring that begins and ends in the same manner.
//
// ---4-----0-----3-----11---
// | |x x| |
// | |x x| |
// ---5-----1-----2-----10---
// | |v0 v1| |
// | | | |
// ---6-----7-----8-----9----
// | | | |
//
int
Level::gatherQuadRegularBoundaryPatchPoints(
Index face, Index patchPoints[], int boundaryEdgeInFace, int fvarChannel) const {
Level const& level = *this;
int interiorEdgeInFace = fastMod4(boundaryEdgeInFace + 2);
//
// V0 and V1 are the two interior vertices (opposite the boundary edge) around
// which we will gather most of the ring:
//
int intV0InFace = interiorEdgeInFace;
int intV1InFace = fastMod4(interiorEdgeInFace + 1);
ConstIndexArray faceVerts = level.getFaceVertices(face);
Index v0 = faceVerts[intV0InFace];
Index v1 = faceVerts[intV1InFace];
ConstIndexArray v0Faces = level.getVertexFaces(v0);
ConstIndexArray v1Faces = level.getVertexFaces(v1);
ConstLocalIndexArray v0InFaces = level.getVertexFaceLocalIndices(v0);
ConstLocalIndexArray v1InFaces = level.getVertexFaceLocalIndices(v1);
int boundaryFaceInV0Faces = -1;
int boundaryFaceInV1Faces = -1;
for (int i = 0; i < 4; ++i) {
if (face == v0Faces[i]) boundaryFaceInV0Faces = i;
if (face == v1Faces[i]) boundaryFaceInV1Faces = i;
}
assert((boundaryFaceInV0Faces >= 0) && (boundaryFaceInV1Faces >= 0));
// Identify the four faces of interest -- previous to and opposite V0 and
// opposite and next from V1 -- relative to V0 and V1:
int prevFaceInV0Faces = fastMod4(boundaryFaceInV0Faces + 1);
int intFaceInV0Faces = fastMod4(boundaryFaceInV0Faces + 2);
int intFaceInV1Faces = fastMod4(boundaryFaceInV1Faces + 2);
int nextFaceInV1Faces = fastMod4(boundaryFaceInV1Faces + 3);
// Identify the indices of the four faces:
Index prevFace = v0Faces[prevFaceInV0Faces];
Index intV0Face = v0Faces[intFaceInV0Faces];
Index intV1Face = v1Faces[intFaceInV1Faces];
Index nextFace = v1Faces[nextFaceInV1Faces];
// Identify V0 and V1 relative to these four faces:
LocalIndex v0InPrevFace = v0InFaces[prevFaceInV0Faces];
LocalIndex v0InIntFace = v0InFaces[intFaceInV0Faces];
LocalIndex v1InIntFace = v1InFaces[intFaceInV1Faces];
LocalIndex v1InNextFace = v1InFaces[nextFaceInV1Faces];
//
// Now that all faces of interest have been found, identify the point
// indices within each face (i.e. the vertex or fvar-value index arrays)
// and copy them into the patch points:
//
ConstIndexArray thisFacePoints,
prevFacePoints,
intV0FacePoints,
intV1FacePoints,
nextFacePoints;
if (fvarChannel < 0) {
thisFacePoints = faceVerts;
prevFacePoints = level.getFaceVertices(prevFace);
intV0FacePoints = level.getFaceVertices(intV0Face);
intV1FacePoints = level.getFaceVertices(intV1Face);
nextFacePoints = level.getFaceVertices(nextFace);
} else {
thisFacePoints = level.getFVarFaceValues(face, fvarChannel);
prevFacePoints = level.getFVarFaceValues(prevFace, fvarChannel);
intV0FacePoints = level.getFVarFaceValues(intV0Face, fvarChannel);
intV1FacePoints = level.getFVarFaceValues(intV1Face, fvarChannel);
nextFacePoints = level.getFVarFaceValues(nextFace, fvarChannel);
}
patchPoints[0] = thisFacePoints[fastMod4(boundaryEdgeInFace + 1)];
patchPoints[1] = thisFacePoints[fastMod4(boundaryEdgeInFace + 2)];
patchPoints[2] = thisFacePoints[fastMod4(boundaryEdgeInFace + 3)];
patchPoints[3] = thisFacePoints[ boundaryEdgeInFace];
patchPoints[4] = prevFacePoints[fastMod4(v0InPrevFace + 2)];
patchPoints[5] = intV0FacePoints[fastMod4(v0InIntFace + 1)];
patchPoints[6] = intV0FacePoints[fastMod4(v0InIntFace + 2)];
patchPoints[7] = intV0FacePoints[fastMod4(v0InIntFace + 3)];
patchPoints[8] = intV1FacePoints[fastMod4(v1InIntFace + 1)];
patchPoints[9] = intV1FacePoints[fastMod4(v1InIntFace + 2)];
patchPoints[10] = intV1FacePoints[fastMod4(v1InIntFace + 3)];
patchPoints[11] = nextFacePoints[fastMod4(v1InNextFace + 2)];
return 12;
}
//
// Gathering the 9 vertices of a quad-regular corner patch:
// - the neighborhood of the face is assumed to be quad-regular
// - the single corner vertex is specified
// - only one vertex of the face is a corner
//
// Ordering of resulting vertices:
// It was debatable whether to include the vertices of the original face for a complete
// "patch" or just the surrounding ring -- clearly we ended up with a function for the entire
// patch, but that may change.
// Like the boundary case, the latter ring of vertices around the face was oriented
// beginning from the leading CCW boundary edge and ending at the trailing edge. The four
// face vertices begin the patch, and begin with the corner vertex.
//
// 0-----3-----8---
// |x x| |
// |x x| |
// 1-----2-----7---
// | | |
// | | |
// 4-----5-----6---
// | | |
//
int
Level::gatherQuadRegularCornerPatchPoints(
Index face, Index patchPoints[], int cornerVertInFace, int fvarChannel) const {
Level const& level = *this;
int interiorFaceVert = fastMod4(cornerVertInFace + 2);
ConstIndexArray faceVerts = level.getFaceVertices(face);
Index intVert = faceVerts[interiorFaceVert];
ConstIndexArray intVertFaces = level.getVertexFaces(intVert);
ConstLocalIndexArray intVertInFaces = level.getVertexFaceLocalIndices(intVert);
int cornerFaceInIntVertFaces = -1;
for (int i = 0; i < intVertFaces.size(); ++i) {
if (face == intVertFaces[i]) {
cornerFaceInIntVertFaces = i;
break;
}
}
assert(cornerFaceInIntVertFaces >= 0);
// Identify the three faces relative to the interior vertex:
int prevFaceInIntVertFaces = fastMod4(cornerFaceInIntVertFaces + 1);
int intFaceInIntVertFaces = fastMod4(cornerFaceInIntVertFaces + 2);
int nextFaceInIntVertFaces = fastMod4(cornerFaceInIntVertFaces + 3);
// Identify the indices of the three other faces:
Index prevFace = intVertFaces[prevFaceInIntVertFaces];
Index intFace = intVertFaces[intFaceInIntVertFaces];
Index nextFace = intVertFaces[nextFaceInIntVertFaces];
// Identify the interior vertex relative to these three faces:
LocalIndex intVertInPrevFace = intVertInFaces[prevFaceInIntVertFaces];
LocalIndex intVertInIntFace = intVertInFaces[intFaceInIntVertFaces];
LocalIndex intVertInNextFace = intVertInFaces[nextFaceInIntVertFaces];
//
// Now that all faces of interest have been found, identify the point
// indices within each face (i.e. the vertex or fvar-value index arrays)
// and copy them into the patch points:
//
ConstIndexArray thisFacePoints,
prevFacePoints,
intFacePoints,
nextFacePoints;
if (fvarChannel < 0) {
thisFacePoints = faceVerts;
prevFacePoints = level.getFaceVertices(prevFace);
intFacePoints = level.getFaceVertices(intFace);
nextFacePoints = level.getFaceVertices(nextFace);
} else {
thisFacePoints = level.getFVarFaceValues(face);
prevFacePoints = level.getFVarFaceValues(prevFace);
intFacePoints = level.getFVarFaceValues(intFace);
nextFacePoints = level.getFVarFaceValues(nextFace);
}
patchPoints[0] = thisFacePoints[ cornerVertInFace];
patchPoints[1] = thisFacePoints[fastMod4(cornerVertInFace + 1)];
patchPoints[2] = thisFacePoints[fastMod4(cornerVertInFace + 2)];
patchPoints[3] = thisFacePoints[fastMod4(cornerVertInFace + 3)];
patchPoints[4] = prevFacePoints[fastMod4(intVertInPrevFace + 2)];
patchPoints[5] = intFacePoints[fastMod4(intVertInIntFace + 1)];
patchPoints[6] = intFacePoints[fastMod4(intVertInIntFace + 2)];
patchPoints[7] = intFacePoints[fastMod4(intVertInIntFace + 3)];
patchPoints[8] = nextFacePoints[fastMod4(intVertInNextFace + 2)];
return 9;
}
//
// Gathering the 12 vertices of a tri-regular interior patch:
// - the neighborhood of the face is assumed to be tri-regular
//
// Ordering of resulting vertices:
// The three vertices of the triangle begin the sequence, followed by counter-clockwise
// traversal of the outer ring of vertices -- beginning with the vertex incident V0 such
// that the ring is symmetric about the triangle.
/*
// 3 11
// X - - - - - X
// / \ / \
// / \ 0 / \
// 4 X - - - - - X - - - - - X 10
// / \ / * \ / \
// / \ / * * * \ / \
// 5 X - - - - - X - - - - - X - - - - - X 9
// \ / 1 \ / 2 \ /
// \ / \ / \ /
// X - - - - - X - - - - - X
// 6 7 8
*/
int
Level::gatherTriRegularInteriorPatchPoints(Index fIndex, Index points[12], int rotation) const
{
ConstIndexArray fVerts = getFaceVertices(fIndex);
ConstIndexArray fEdges = getFaceEdges(fIndex);
int index0 = 0;
int index1 = 1;
int index2 = 2;
if (rotation) {
index0 = rotation % 3;
index1 = (rotation + 1) % 3;
index2 = (rotation + 2) % 3;
}
Index v0 = fVerts[index0];
Index v1 = fVerts[index1];
Index v2 = fVerts[index2];
ConstIndexArray v0Edges = getVertexEdges(v0);
ConstIndexArray v1Edges = getVertexEdges(v1);
ConstIndexArray v2Edges = getVertexEdges(v2);
int e0InV0Edges = v0Edges.FindIndex(fEdges[index0]);
int e1InV1Edges = v1Edges.FindIndex(fEdges[index1]);
int e2InV2Edges = v2Edges.FindIndex(fEdges[index2]);
points[ 0] = v0;
points[ 1] = v1;
points[ 2] = v2;
points[11] = otherOfTwo(getEdgeVertices(v0Edges[(e0InV0Edges + 3) % 6]), v0);
points[ 3] = otherOfTwo(getEdgeVertices(v0Edges[(e0InV0Edges + 4) % 6]), v0);
points[ 4] = otherOfTwo(getEdgeVertices(v0Edges[(e0InV0Edges + 5) % 6]), v0);
points[ 5] = otherOfTwo(getEdgeVertices(v1Edges[(e1InV1Edges + 3) % 6]), v1);
points[ 6] = otherOfTwo(getEdgeVertices(v1Edges[(e1InV1Edges + 4) % 6]), v1);
points[ 7] = otherOfTwo(getEdgeVertices(v1Edges[(e1InV1Edges + 5) % 6]), v1);
points[ 8] = otherOfTwo(getEdgeVertices(v2Edges[(e2InV2Edges + 3) % 6]), v2);
points[ 9] = otherOfTwo(getEdgeVertices(v2Edges[(e2InV2Edges + 4) % 6]), v2);
points[10] = otherOfTwo(getEdgeVertices(v2Edges[(e2InV2Edges + 5) % 6]), v2);
return 12;
}
//
// Gathering the 9 vertices of a tri-regular "boundary edge" patch:
// - the neighborhood of the face is assumed to be tri-regular
// - referred to as "boundary edge" as the boundary occurs on the edge of the triangle
//
// Boundary edge:
//
/* 6 5
// X - - - - - X
// / \ / \
// / \ 2 / \
// 7 X - - - - - X - - - - - X 4
// / \ / * \ / \
// / \ / * * * \ / \
// 8 X - - - - - X - - - - - X - - - - - X 3
// 0 1
*/
int
Level::gatherTriRegularBoundaryEdgePatchPoints(Index fIndex, Index points[], int boundaryFaceEdge) const
{
ConstIndexArray fVerts = getFaceVertices(fIndex);
Index v0 = fVerts[boundaryFaceEdge];
Index v1 = fVerts[(boundaryFaceEdge + 1) % 3];
Index v2 = fVerts[(boundaryFaceEdge + 2) % 3];
ConstIndexArray v0Edges = getVertexEdges(v0);
ConstIndexArray v1Edges = getVertexEdges(v1);
ConstIndexArray v2Edges = getVertexEdges(v2);
int e1InV2Edges = v2Edges.FindIndex(v1Edges[2]);
points[0] = v0;
points[1] = v1;
points[2] = v2;
points[3] = otherOfTwo(getEdgeVertices(v1Edges[0]), v1);
points[4] = otherOfTwo(getEdgeVertices(v2Edges[(e1InV2Edges + 1) % 6]), v2);
points[5] = otherOfTwo(getEdgeVertices(v2Edges[(e1InV2Edges + 2) % 6]), v2);
points[6] = otherOfTwo(getEdgeVertices(v2Edges[(e1InV2Edges + 3) % 6]), v2);
points[7] = otherOfTwo(getEdgeVertices(v2Edges[(e1InV2Edges + 4) % 6]), v2);
points[8] = otherOfTwo(getEdgeVertices(v0Edges[3]), v0);
return 9;
}
//
// Gathering the 10 vertices of a tri-regular "boundary vertex" patch:
// - the neighborhood of the face is assumed to be tri-regular
// - referred to as "boundary vertex" as the boundary occurs on the vertex of the triangle
//
// Boundary vertex:
/*
// 0
// 3 X - - - - - X - - - - - X 9
// / \ / * \ / \
// / \ / * * * \ / \
// 4 X - - - - - X - - - - - X - - - - - X 8
// \ / 1 \ / 2 \ /
// \ / \ / \ /
// X - - - - - X - - - - - X
// 5 6 7
*/
int
Level::gatherTriRegularBoundaryVertexPatchPoints(Index fIndex, Index points[], int boundaryFaceVert) const
{
ConstIndexArray fVerts = getFaceVertices(fIndex);
ConstIndexArray fEdges = getFaceEdges(fIndex);
Index v0 = fVerts[boundaryFaceVert];
Index v1 = fVerts[(boundaryFaceVert + 1) % 3];
Index v2 = fVerts[(boundaryFaceVert + 2) % 3];
Index e1 = fEdges[boundaryFaceVert];
Index e2 = fEdges[(boundaryFaceVert + 2) % 3];
ConstIndexArray v1Edges = getVertexEdges(v1);
ConstIndexArray v2Edges = getVertexEdges(v2);
int e1InV1Edges = v1Edges.FindIndex(e1);
int e2InV2Edges = v2Edges.FindIndex(e2);
points[0] = v0;
points[1] = v1;
points[2] = v2;
points[3] = otherOfTwo(getEdgeVertices(v1Edges[(e1InV1Edges + 1) % 6]), v1);
points[4] = otherOfTwo(getEdgeVertices(v1Edges[(e1InV1Edges + 2) % 6]), v1);
points[5] = otherOfTwo(getEdgeVertices(v1Edges[(e1InV1Edges + 3) % 6]), v1);
points[6] = otherOfTwo(getEdgeVertices(v1Edges[(e1InV1Edges + 4) % 6]), v1);
points[7] = otherOfTwo(getEdgeVertices(v2Edges[(e2InV2Edges + 3) % 6]), v2);
points[8] = otherOfTwo(getEdgeVertices(v2Edges[(e2InV2Edges + 4) % 6]), v2);
points[9] = otherOfTwo(getEdgeVertices(v2Edges[(e2InV2Edges + 5) % 6]), v2);
return 10;
}
//
// Gathering the 6 vertices of a tri-regular "corner vertex" patch:
// - the neighborhood of the face is assumed to be tri-regular
// - referred to as "corner vertex" to disambiguate it from another corner case
// - another boundary case shares the edge with the corner triangle
//
// Corner vertex:
/*
// 0
// X
// / * \
// / * * * \
// X - - - - - X
// / 1 \ / 2 \
// / \ / \
// X - - - - - X - - - - - X
// 3 4 5
*/
int
Level::gatherTriRegularCornerVertexPatchPoints(Index fIndex, Index points[], int cornerFaceVert) const
{
ConstIndexArray fVerts = getFaceVertices(fIndex);
Index v0 = fVerts[cornerFaceVert];
Index v1 = fVerts[(cornerFaceVert + 1) % 3];
Index v2 = fVerts[(cornerFaceVert + 2) % 3];
ConstIndexArray v1Edges = getVertexEdges(v1);
ConstIndexArray v2Edges = getVertexEdges(v2);
points[0] = v0;
points[1] = v1;
points[2] = v2;
points[3] = otherOfTwo(getEdgeVertices(v1Edges[0]), v1);
points[4] = otherOfTwo(getEdgeVertices(v1Edges[1]), v1);
points[5] = otherOfTwo(getEdgeVertices(v2Edges[3]), v2);
return 6;
}
//
// Gathering the 8 vertices of a tri-regular "corner edge" patch:
// - the neighborhood of the face is assumed to be tri-regular
// - referred to as "corner edge" to disambiguate it from the vertex corner case
// - this faces shares the edge with a corner triangle
//
// Corner edge:
/*
// 6 5
// X - - - - - X
// / \ / \
// / \ 2 / \
// 7 X - - - - - X - - - - - X 4
// \ / * \ /
// \ / * * * \ /
// X - - - - - X
// 0 \ / 1
// \ /
// X
// 3
*/
int
Level::gatherTriRegularCornerEdgePatchPoints(Index fIndex, Index points[], int cornerFaceEdge) const
{
ConstIndexArray fVerts = getFaceVertices(fIndex);
Index v0 = fVerts[cornerFaceEdge];
Index v1 = fVerts[(cornerFaceEdge + 1) % 3];
Index v2 = fVerts[(cornerFaceEdge + 2) % 3];
ConstIndexArray v0Edges = getVertexEdges(v0);
ConstIndexArray v1Edges = getVertexEdges(v1);
points[0] = v0;
points[1] = v1;
points[2] = v2;
points[3] = otherOfTwo(getEdgeVertices(v1Edges[3]), v1);
points[4] = otherOfTwo(getEdgeVertices(v1Edges[0]), v1);
points[7] = otherOfTwo(getEdgeVertices(v0Edges[3]), v0);
ConstIndexArray v4Edges = getVertexEdges(points[4]);
ConstIndexArray v7Edges = getVertexEdges(points[7]);
points[5] = otherOfTwo(getEdgeVertices(v4Edges[v4Edges.size() - 3]), v1);
points[6] = otherOfTwo(getEdgeVertices(v7Edges[2]), v1);
return 8;
}
bool
Level::isSingleCreasePatch(Index face, float *sharpnessOut, int *rotationOut) const {
// Note: this function is called twice for the same patch, at topologyRefiner and patchTablesFactory.
// we may want to cache the result to improve the Far performance.
// To do so, FTag needs to be extended to store isSingleCrease(bool), sharpness(float) and rotation(0-3).
//
Vtr::ConstIndexArray fVerts = this->getFaceVertices(face);
// the face has to be quad
if (fVerts.size() != 4) return false;
// if there's any corner vertex, return false.
for (int i = 0; i < fVerts.size(); ++i) {
if (this->getVertexSharpness(fVerts[i]) > 0.0f)
return false;
}
// make sure there's only one edge with sharpness
Vtr::ConstIndexArray fEdges = this->getFaceEdges(face);
float sharpness = 0.0f;
int rotation = 0;
for (int i = 0; i < fEdges.size(); ++i) {
float s = this->getEdgeSharpness(fEdges[i]);
if (s > 0.0f) {
if (sharpness > 0.0f) {
// found more than one sharp edges.
return false;
}
sharpness = s;
rotation = i;
}
}
// rotation = 0
// | | | |
// ---5-----4-----15----14---
// | || | |
// | || | |
// ---6-----0-----3-----13---
// | || | |
// | || | |
// ---7-----1-----2-----12---
// | || | |
// | || | |
// ---8-----9-----10----11---
// | | | |
int v[4];
v[0] = fVerts[(0+rotation)%4]; // crease
v[1] = fVerts[(1+rotation)%4]; // crease
v[2] = fVerts[(2+rotation)%4]; // smooth
v[3] = fVerts[(3+rotation)%4]; // smooth
// check the edges around v[0], v[1]
for (int i = 0; i < 2; ++i) {
Vtr::ConstIndexArray vEdges = this->getVertexEdges(v[i]);
if (vEdges.size() != 4) return false;
int nSharpEdges = 0;
float sharpnesses[4];
for (int j = 0; j < 4; ++j) {
sharpnesses[j] = this->getEdgeSharpness(vEdges[j]);
if (sharpnesses[j] > 0.0f) {
if (++nSharpEdges == 3) return false;
}
}
// sharpnesses have to be [0, x, 0, x] or [x, 0, x, 0]
if (sharpnesses[0] != sharpnesses[2] or
sharpnesses[1] != sharpnesses[3]) {
return false;
}
}
// check the edges around v[2], v[3]
for (int i = 2; i < 4; ++i) {
Vtr::ConstIndexArray vEdges = this->getVertexEdges(v[i]);
if (vEdges.size() != 4) return false;
// all edges have to be smooth
for (int j = 0; j < 4; ++j) {
if (this->getEdgeSharpness(vEdges[j]) > 0.0f) return false;
}
}
if (sharpnessOut) *sharpnessOut = sharpness;
if (rotationOut) *rotationOut = rotation;
return true;
}
//
// What follows is an internal/anonymous class and protected methods to complete all
// topological relations when only the face-vertex relations is defined.
//
// In keeping with the original idea that Level is just data and relies on other
// classes to construct it, this functionality may be warranted elsewhere, but we are
// collectively unclear as to where that should be at present. In the meantime, the
// implementation is provided here so that we can test and make use of it.
//
namespace {
//
// This is an internal helper class to manage the assembly of the tological relations
// that do not have a predictable size, i.e. faces-per-edge, faces-per-vertex and
// edges-per-vertex. Level manages these with two vectors:
//
// - a vector of integer pairs for the "counts" and "offsets"
// - a vector of incident members accessed by the "offset" of each
//
// The "dynamic relation" allocates the latter vector of members based on a typical
// number of members per component, e.g. we expect valence 4 vertices in a typical
// quad-mesh, and so an "expected" number might be 6 to accomodate a few x-ordinary
// vertices. The member vector is allocated with this number per component and the
// counts and offsets initialized to refer to them -- but with the counts set to 0.
// The count will be incremented as members are identified and entered, and if any
// component "overflows" the expected number of members, the members are moved to a
// separate vector in an std::map for the component.
//
// Once all incident members have been added, the main vector is compressed and may
// need to merge entries from the map in the process.
//
typedef std::map<Index, IndexVector> IrregIndexMap;
class DynamicRelation {
public:
DynamicRelation(IndexVector& countAndOffsets, IndexVector& indices, int membersPerComp);
~DynamicRelation() { }
public:
// Methods dealing with the members for each component:
IndexArray getCompMembers(Index index);
void appendCompMember(Index index, Index member);
// Methods dealing with the components:
void appendComponent();
int compressMemberIndices();
public:
int _compCount;
int _memberCountPerComp;
IndexVector & _countsAndOffsets;
IndexVector & _regIndices;
IrregIndexMap _irregIndices;
};
inline
DynamicRelation::DynamicRelation(IndexVector& countAndOffsets, IndexVector& indices, int membersPerComp) :
_compCount(0),
_memberCountPerComp(membersPerComp),
_countsAndOffsets(countAndOffsets),
_regIndices(indices) {
_compCount = (int) _countsAndOffsets.size() / 2;
for (int i = 0; i < _compCount; ++i) {
_countsAndOffsets[2*i] = 0;
_countsAndOffsets[2*i+1] = i * _memberCountPerComp;
}
_regIndices.resize(_compCount * _memberCountPerComp);
}
inline IndexArray
DynamicRelation::getCompMembers(Index compIndex) {
int count = _countsAndOffsets[2*compIndex];
if (count > _memberCountPerComp) {
IndexVector & irregMembers = _irregIndices[compIndex];
return IndexArray(&irregMembers[0], (int)irregMembers.size());
} else {
int offset = _countsAndOffsets[2*compIndex+1];
return IndexArray(&_regIndices[offset], count);
}
}
inline void
DynamicRelation::appendCompMember(Index compIndex, Index memberValue) {
int count = _countsAndOffsets[2*compIndex];
int offset = _countsAndOffsets[2*compIndex+1];
if (count < _memberCountPerComp) {
_regIndices[offset + count] = memberValue;
} else {
IndexVector& irregMembers = _irregIndices[compIndex];
if (count > _memberCountPerComp) {
irregMembers.push_back(memberValue);
} else {
irregMembers.resize(_memberCountPerComp + 1);
std::memcpy(&irregMembers[0], &_regIndices[offset], sizeof(Index) * _memberCountPerComp);
irregMembers[_memberCountPerComp] = memberValue;
}
}
_countsAndOffsets[2*compIndex] ++;
}
inline void
DynamicRelation::appendComponent() {
_countsAndOffsets.push_back(0);
_countsAndOffsets.push_back(_compCount * _memberCountPerComp);
++ _compCount;
_regIndices.resize(_compCount * _memberCountPerComp);
}
int
DynamicRelation::compressMemberIndices() {
if (_irregIndices.size() == 0) {
int memberCount = _countsAndOffsets[0];
int memberMax = _countsAndOffsets[0];
for (int i = 1; i < _compCount; ++i) {
int count = _countsAndOffsets[2*i];
int offset = _countsAndOffsets[2*i + 1];
memmove(&_regIndices[memberCount], &_regIndices[offset], count * sizeof(Index));
_countsAndOffsets[2*i + 1] = memberCount;
memberCount += count;
memberMax = std::max(memberMax, count);
}
_regIndices.resize(memberCount);
return memberMax;
} else {
// Assign new offsets-per-component while determining if we can trivially compressed in place:
bool cannotBeCompressedInPlace = false;
int memberCount = _countsAndOffsets[0];
for (int i = 1; i < _compCount; ++i) {
_countsAndOffsets[2*i + 1] = memberCount;
cannotBeCompressedInPlace |= (memberCount > (_memberCountPerComp * i));
memberCount += _countsAndOffsets[2*i];
}
cannotBeCompressedInPlace |= (memberCount > (_memberCountPerComp * _compCount));
// Copy members into the original or temporary vector accordingly:
IndexVector tmpIndices;
if (cannotBeCompressedInPlace) {
tmpIndices.resize(memberCount);
}
IndexVector& dstIndices = cannotBeCompressedInPlace ? tmpIndices : _regIndices;
int memberMax = _memberCountPerComp;
for (int i = 0; i < _compCount; ++i) {
int count = _countsAndOffsets[2*i];
Index *dstMembers = &dstIndices[_countsAndOffsets[2*i + 1]];
Index *srcMembers = 0;
if (count <= _memberCountPerComp) {
srcMembers = &_regIndices[i * _memberCountPerComp];
} else {
srcMembers = &_irregIndices[i][0];
memberMax = std::max(memberMax, count);
}
memmove(dstMembers, srcMembers, count * sizeof(Index));
}
if (cannotBeCompressedInPlace) {
_regIndices.swap(tmpIndices);
} else {
_regIndices.resize(memberCount);
}
return memberMax;
}
}
}
//
// Methods to populate the missing topology relations of the Level:
//
inline Index
Level::findEdge(Index v0Index, Index v1Index, ConstIndexArray v0Edges) const {
if (v0Index != v1Index) {
for (int j = 0; j < v0Edges.size(); ++j) {
ConstIndexArray eVerts = this->getEdgeVertices(v0Edges[j]);
if ((eVerts[0] == v1Index) || (eVerts[1] == v1Index)) {
return v0Edges[j];
}
}
} else {
for (int j = 0; j < v0Edges.size(); ++j) {
ConstIndexArray eVerts = this->getEdgeVertices(v0Edges[j]);
if (eVerts[0] == eVerts[1]) {
return v0Edges[j];
}
}
}
return INDEX_INVALID;
}
Index
Level::findEdge(Index v0Index, Index v1Index) const {
return this->findEdge(v0Index, v1Index, this->getVertexEdges(v0Index));
}
bool
Level::completeTopologyFromFaceVertices() {
//
// Its assumed (a pre-condition) that face-vertices have been fully specified and that we
// are to construct the remaining relations: including the edge list. We may want to
// support the existence of the edge list too in future:
//
int vCount = this->getNumVertices();
int fCount = this->getNumFaces();
int eCount = this->getNumEdges();
assert((vCount > 0) && (fCount > 0) && (eCount == 0));
// May be unnecessary depending on how the vertices and faces were defined, but worth a
// call to ensure all data related to verts and faces is available -- this will be a
// harmless call if all has been taken care of).
//
// Remember to resize edges similarly after the edge list has been assembled...
this->resizeVertices(vCount);
this->resizeFaces(fCount);
this->resizeEdges(0);
//
// Resize face-edges to match face-verts and reserve for edges based on an estimate:
//
this->_faceEdgeIndices.resize(this->getNumFaceVerticesTotal());
int eCountEstimate = (vCount << 1);
this->_edgeVertIndices.reserve(eCountEstimate * 2);
this->_edgeFaceIndices.reserve(eCountEstimate * 2);
this->_edgeFaceCountsAndOffsets.reserve(eCountEstimate * 2);
//
// Create the dynamic relations to be populated (edge-faces will remain empty as reserved
// above since there are currently no edges) and iterate through the faces to do so:
//
const int avgSize = 6;
DynamicRelation dynEdgeFaces(this->_edgeFaceCountsAndOffsets, this->_edgeFaceIndices, 2);
DynamicRelation dynVertFaces(this->_vertFaceCountsAndOffsets, this->_vertFaceIndices, avgSize);
DynamicRelation dynVertEdges(this->_vertEdgeCountsAndOffsets, this->_vertEdgeIndices, avgSize);
// Inspect each edge created and identify those that are non-manifold as we go:
IndexVector nonManifoldEdges;
for (Index fIndex = 0; fIndex < fCount; ++fIndex) {
IndexArray fVerts = this->getFaceVertices(fIndex);
IndexArray fEdges = this->getFaceEdges(fIndex);
for (int i = 0; i < fVerts.size(); ++i) {
Index v0Index = fVerts[i];
Index v1Index = fVerts[(i+1) % fVerts.size()];
//
// If not degenerate, search for a previous occurrence of this edge [v0,v1]
// in v0's incident edge members. Otherwise, set the edge index as invalid
// to trigger creation of a new/unique instance of the degenerate edge:
//
Index eIndex;
if (v0Index != v1Index) {
eIndex = this->findEdge(v0Index, v1Index, dynVertEdges.getCompMembers(v0Index));
} else {
eIndex = INDEX_INVALID;
nonManifoldEdges.push_back(this->_edgeCount);
}
//
// If the edge already exists, see if is non-manifold, i.e. it has already been
// added to two faces, or this face has the edge in the same orientation as the
// first face (indicating opposite winding orders between the two faces).
//
// Otherwise, create a new edge, append the new vertex pair [v0,v1] and update
// the incidence relations for the edge and its end vertices and this face.
//
// Regardless of whether or not the edge was new, update the edge-faces, the
// face-edges and the vertex-faces for this vertex.
//
if (IndexIsValid(eIndex)) {
IndexArray eFaces = dynEdgeFaces.getCompMembers(eIndex);
if (eFaces[eFaces.size() - 1] == fIndex) {
// If the edge already occurs in this face, create a new instance:
nonManifoldEdges.push_back(eIndex);
nonManifoldEdges.push_back(this->_edgeCount);
eIndex = INDEX_INVALID;
} else if (eFaces.size() > 1) {
nonManifoldEdges.push_back(eIndex);
} else if (v0Index == this->getEdgeVertices(eIndex)[0]) {
nonManifoldEdges.push_back(eIndex);
}
}
if (!IndexIsValid(eIndex)) {
eIndex = (Index) this->_edgeCount;
this->_edgeCount ++;
this->_edgeVertIndices.push_back(v0Index);
this->_edgeVertIndices.push_back(v1Index);
dynEdgeFaces.appendComponent();
dynVertEdges.appendCompMember(v0Index, eIndex);
dynVertEdges.appendCompMember(v1Index, eIndex);
}
dynEdgeFaces.appendCompMember(eIndex, fIndex);
dynVertFaces.appendCompMember(v0Index, fIndex);
fEdges[i] = eIndex;
}
}
//
// Compress the incident member vectors while determining the maximum for each.
// Use these to set maximum relation count members and to test for valence or
// other incident member overflow: max edge-faces is simple, but for max-valence,
// remember it was first initialized with the maximum of face-verts, so use its
// existing value -- and some non-manifold cases can have #faces > #edges, so be
// sure to consider both.
//
int maxEdgeFaces = dynEdgeFaces.compressMemberIndices();
int maxVertFaces = dynVertFaces.compressMemberIndices();
int maxVertEdges = dynVertEdges.compressMemberIndices();
_maxEdgeFaces = maxEdgeFaces;
assert(_maxValence > 0);
_maxValence = std::max(maxVertFaces, _maxValence);
_maxValence = std::max(maxVertEdges, _maxValence);
// If max-edge-faces too large, max-valence must also be, so just need the one:
if (_maxValence > VALENCE_LIMIT) {
return false;
}
//
// At this point all incident members are associated with each component. We still
// need to populate the "local indices" for each and orient manifold components in
// counter-clockwise order. First mark tag non-manifold edges and their incident
// vertices so that we can trivially skip orienting these -- though some vertices
// will be determined non-manifold as a result of a failure to orient them (and
// will be marked accordingly when so detected).
//
// Finally, the local indices are assigned. This is trivial for manifold components
// as if component V is in component F, V will only occur once in F. For non-manifold
// cases V may occur multiple times in F -- we rely on such instances being successive
// based on their original assignment above, which simplifies the task.
//
// First resize edges to the new count to ensure anything related to edges is created:
eCount = this->getNumEdges();
this->resizeEdges(eCount);
for (int i = 0; i < (int)nonManifoldEdges.size(); ++i) {
Index eIndex = nonManifoldEdges[i];
_edgeTags[eIndex]._nonManifold = true;
IndexArray eVerts = getEdgeVertices(eIndex);
_vertTags[eVerts[0]]._nonManifold = true;
_vertTags[eVerts[1]]._nonManifold = true;
}
orientIncidentComponents();
populateLocalIndices();
//printf("Vertex topology completed...\n");
//this->print();
//printf(" validating vertex topology...\n");
//this->validateTopology();
//assert(this->validateTopology());
return true;
}
void
Level::populateLocalIndices() {
//
// We have three sets of local indices -- edge-faces, vert-faces and vert-edges:
//
int eCount = this->getNumEdges();
int vCount = this->getNumVertices();
this->_vertFaceLocalIndices.resize(this->_vertFaceIndices.size());
this->_vertEdgeLocalIndices.resize(this->_vertEdgeIndices.size());
this->_edgeFaceLocalIndices.resize(this->_edgeFaceIndices.size());
for (Index vIndex = 0; vIndex < vCount; ++vIndex) {
IndexArray vFaces = this->getVertexFaces(vIndex);
LocalIndexArray vInFaces = this->getVertexFaceLocalIndices(vIndex);
//
// We keep track of the last face during the iteration to detect when two
// (or more) successive faces are the same -- indicating a degenerate edge
// or other non-manifold situation. If so, we continue to search from the
// point of the last face's local index:
//
Index vFaceLast = INDEX_INVALID;
for (int i = 0; i < vFaces.size(); ++i) {
IndexArray fVerts = this->getFaceVertices(vFaces[i]);
int vStart = (vFaces[i] == vFaceLast) ? ((int)vInFaces[i-1] + 1) : 0;
int vInFaceIndex = (int)(std::find(fVerts.begin() + vStart, fVerts.end(), vIndex) - fVerts.begin());
vInFaces[i] = (LocalIndex) vInFaceIndex;
vFaceLast = vFaces[i];
}
}
for (Index vIndex = 0; vIndex < vCount; ++vIndex) {
IndexArray vEdges = this->getVertexEdges(vIndex);
LocalIndexArray vInEdges = this->getVertexEdgeLocalIndices(vIndex);
for (int i = 0; i < vEdges.size(); ++i) {
IndexArray eVerts = this->getEdgeVertices(vEdges[i]);
//
// For degenerate edges, the first occurrence of the edge (which
// are presumed successive) will get local index 0, the second 1.
//
if (eVerts[0] != eVerts[1]) {
vInEdges[i] = (vIndex == eVerts[1]);
} else {
vInEdges[i] = (i && (vEdges[i] == vEdges[i-1]));
}
}
_maxValence = std::max(_maxValence, vEdges.size());
}
for (Index eIndex = 0; eIndex < eCount; ++eIndex) {
IndexArray eFaces = this->getEdgeFaces(eIndex);
LocalIndexArray eInFaces = this->getEdgeFaceLocalIndices(eIndex);
//
// We keep track of the last face during the iteration to detect when two
// (or more) successive faces are the same -- indicating a degenerate edge
// or other non-manifold situation. If so, we continue to search from the
// point of the last face's local index:
//
Index eFaceLast = INDEX_INVALID;
for (int i = 0; i < eFaces.size(); ++i) {
IndexArray fEdges = this->getFaceEdges(eFaces[i]);
int eStart = (eFaces[i] == eFaceLast) ? ((int)eInFaces[i-1] + 1) : 0;
int eInFaceIndex = (int)(std::find(fEdges.begin() + eStart, fEdges.end(), eIndex) - fEdges.begin());
eInFaces[i] = (LocalIndex) eInFaceIndex;
eFaceLast = eFaces[i];
}
}
}
void
Level::orientIncidentComponents() {
int vCount = getNumVertices();
for (Index vIndex = 0; vIndex < vCount; ++vIndex) {
Level::VTag & vTag = _vertTags[vIndex];
if (!vTag._nonManifold) {
if (!orderVertexFacesAndEdges(vIndex)) {
vTag._nonManifold = true;
}
}
}
}
namespace {
inline int
findInArray(ConstIndexArray array, Index value) {
return (int)(std::find(array.begin(), array.end(), value) - array.begin());
}
}
bool
Level::orderVertexFacesAndEdges(Index vIndex, Index * vFacesOrdered, Index * vEdgesOrdered) const {
ConstIndexArray vEdges = this->getVertexEdges(vIndex);
ConstIndexArray vFaces = this->getVertexFaces(vIndex);
int fCount = vFaces.size();
int eCount = vEdges.size();
if ((fCount == 0) || (eCount < 2) || ((eCount - fCount) > 1)) return false;
//
// Note we have already eliminated the possibility of incident degenerate edges
// and other bad edges earlier -- marking its vertices non-manifold as a result
// and explicitly avoiding this method:
//
Index fStart = INDEX_INVALID;
Index eStart = INDEX_INVALID;
int fvStart = 0;
if (eCount == fCount) {
// Interior case -- start with the first face
fStart = vFaces[0];
fvStart = findInArray(this->getFaceVertices(fStart), vIndex);
eStart = this->getFaceEdges(fStart)[fvStart];
} else {
// Boundary case -- start with (identify) the leading of two boundary edges:
for (int i = 0; i < eCount; ++i) {
ConstIndexArray eFaces = this->getEdgeFaces(vEdges[i]);
if (eFaces.size() == 1) {
eStart = vEdges[i];
fStart = eFaces[0];
fvStart = findInArray(this->getFaceVertices(fStart), vIndex);
// Singular edge -- look for forward edge to this vertex:
if (eStart == (this->getFaceEdges(fStart)[fvStart])) {
break;
}
}
}
}
//
// We have identified a starting face, face-vert and leading edge from
// which to walk counter clockwise to identify manifold neighbors. If
// this vertex is really locally manifold, we will end up back at the
// starting edge or at the other singular edge of a boundary:
//
int eCountOrdered = 1;
int fCountOrdered = 1;
vFacesOrdered[0] = fStart;
vEdgesOrdered[0] = eStart;
Index eFirst = eStart;
while (eCountOrdered < eCount) {
//
// Find the next edge, i.e. the one counter-clockwise to the last:
//
ConstIndexArray fVerts = this->getFaceVertices(fStart);
ConstIndexArray fEdges = this->getFaceEdges(fStart);
int feStart = fvStart;
int feNext = feStart ? (feStart - 1) : (fVerts.size() - 1);
Index eNext = fEdges[feNext];
// Two non-manifold situations detected:
// - two subsequent edges the same, i.e. a "repeated edge" in a face
// - back at the start before all edges processed
if ((eNext == eStart) || (eNext == eFirst)) return false;
//
// Add the next edge and if more faces to visit (not at the end of
// a boundary) look to its opposite face:
//
vEdgesOrdered[eCountOrdered++] = eNext;
if (fCountOrdered < fCount) {
ConstIndexArray eFaces = this->getEdgeFaces(eNext);
if (eFaces.size() == 0) return false;
if ((eFaces.size() == 1) && (eFaces[0] == fStart)) return false;
fStart = eFaces[eFaces[0] == fStart];
fvStart = findInArray(this->getFaceEdges(fStart), eNext);
vFacesOrdered[fCountOrdered++] = fStart;
}
eStart = eNext;
}
assert(eCountOrdered == eCount);
assert(fCountOrdered == fCount);
return true;
}
bool
Level::orderVertexFacesAndEdges(Index vIndex) {
IndexArray vFaces = this->getVertexFaces(vIndex);
IndexArray vEdges = this->getVertexEdges(vIndex);
Index * vFacesOrdered = (Index *)alloca((vFaces.size() + vEdges.size()) * sizeof(Index));
Index * vEdgesOrdered = vFacesOrdered + vFaces.size();
if (orderVertexFacesAndEdges(vIndex, vFacesOrdered, vEdgesOrdered)) {
std::memcpy(&vFaces[0], vFacesOrdered, vFaces.size() * sizeof(Index));
std::memcpy(&vEdges[0], vEdgesOrdered, vEdges.size() * sizeof(Index));
return true;
}
return false;
}
//
// In development -- methods for accessing face-varying data channels...
//
int
Level::createFVarChannel(int fvarValueCount, Sdc::Options const& fvarOptions) {
FVarLevel* fvarLevel = new FVarLevel(*this);
fvarLevel->setOptions(fvarOptions);
fvarLevel->resizeValues(fvarValueCount);
fvarLevel->resizeComponents();
_fvarChannels.push_back(fvarLevel);
return (int)_fvarChannels.size() - 1;
}
void
Level::destroyFVarChannel(int channel) {
delete _fvarChannels[channel];
_fvarChannels.erase(_fvarChannels.begin() + channel);
}
int
Level::getNumFVarValues(int channel) const {
return _fvarChannels[channel]->getNumValues();
}
Sdc::Options
Level::getFVarOptions(int channel) const {
return _fvarChannels[channel]->_options;
}
ConstIndexArray
Level::getFVarFaceValues(Index faceIndex, int channel) const {
return _fvarChannels[channel]->getFaceValues(faceIndex);
}
IndexArray
Level::getFVarFaceValues(Index faceIndex, int channel) {
return _fvarChannels[channel]->getFaceValues(faceIndex);
}
void
Level::completeFVarChannelTopology(int channel, int regBoundaryValence) {
return _fvarChannels[channel]->completeTopologyFromFaceValues(regBoundaryValence);
}
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv