OpenSubdiv/opensubdiv/bfr/faceVertex.cpp
Barry Fowler a1c7be7c8e Addition of Bfr interface (1 of 4): opensubdiv/bfr
This set of commits includes the addition of a new evaluation interface
that treats a subdivision mesh more like a piecewise parametric surface
primitive.  The new interface was placed in namespace "Bfr" for "Base
Face Representation" as all concepts and classes relate to a single face
of the base mesh.
2022-08-02 20:38:17 -07:00

904 lines
30 KiB
C++

//
// Copyright 2021 Pixar
//
// 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 "../bfr/faceVertex.h"
#include "../sdc/crease.h"
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <map>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// Main initialize and finalize methods used to bracket the assignment
// by clients to the VertexDescriptor member:
//
void
FaceVertex::Initialize(int faceSize, int regFaceSize) {
_commonFaceSize = (short) faceSize;
_regFaceSize = (unsigned char) regFaceSize;
_numFaceVerts = 0;
_isExpInfSharp = false;
_isExpSemiSharp = false;
_isImpInfSharp = false;
_isImpSemiSharp = false;
_vDesc._isValid = false;
_vDesc._isInitialized = false;
}
void
FaceVertex::Finalize(int faceInVertex) {
assert(_vDesc._isFinalized);
_faceInRing = (short) faceInVertex;
//
// Initialize members from the VertexDescriptor:
//
if (!_vDesc.HasIncidentFaceSizes()) {
// Common face size was previously initialized to the face size
_numFaceVerts = _vDesc._numFaces * _commonFaceSize;
} else {
_commonFaceSize = 0;
// Recall face sizes are available as differences between offsets:
_numFaceVerts = _vDesc._faceSizeOffsets[_vDesc._numFaces];
}
// Vertex sharpness:
_isExpInfSharp = Sdc::Crease::IsInfinite(_vDesc._vertSharpness);
_isExpSemiSharp = Sdc::Crease::IsSemiSharp(_vDesc._vertSharpness);
//
// Initialize tags from VertexDescriptor and other members
//
// Note that not all tags can be assigned at this point if the vertex
// is defined by a set of unordered faces. In such cases, the tags
// will be assigned later when the connectivity between incident faces
// is determined. Those that can be assigned regardless of ordering
// are set here -- splitting the assignment of those remaining between
// ordered and unordered cases.
//
_tag.Clear();
_tag._unCommonFaceSizes = _vDesc.HasIncidentFaceSizes();
_tag._irregularFaceSizes = (_commonFaceSize != _regFaceSize);
_tag._infSharpVerts = _isExpInfSharp;
_tag._semiSharpVerts = _isExpSemiSharp;
_tag._unOrderedFaces = !_vDesc.IsManifold();
if (_vDesc.IsManifold()) {
finalizeOrderedTags();
}
}
void
FaceVertex::finalizeOrderedTags() {
//
// A vertex with a set of ordered faces is required to be manifold:
//
_tag._unOrderedFaces = false;
_tag._nonManifoldVerts = false;
_tag._boundaryVerts = _vDesc.IsBoundary();
_tag._boundaryNonSharp = _vDesc.IsBoundary();
//
// Assign tags (and other members) affected by edge sharpness:
//
if (_vDesc.HasEdgeSharpness()) {
float const * sharpness = &_vDesc._faceEdgeSharpness[0];
// Detect unsharpened boundary edges:
bool isBoundary = _tag._boundaryVerts;
if (isBoundary) {
int last = 2 * _vDesc._numFaces - 1;
_tag._boundaryNonSharp =
!Sdc::Crease::IsInfinite(sharpness[0]) ||
!Sdc::Crease::IsInfinite(sharpness[last]);
}
// Detect interior inf-sharp and semi-sharp edges:
int numInfSharpEdges = 0;
int numSemiSharpEdges = 0;
for (int i = isBoundary; i < _vDesc._numFaces; ++i ) {
if (Sdc::Crease::IsInfinite(sharpness[2*i])) {
++ numInfSharpEdges;
} else if (Sdc::Crease::IsSharp(sharpness[2*i])) {
++ numSemiSharpEdges;
}
}
// Mark the presence of interior sharp edges:
_tag._infSharpEdges = (numInfSharpEdges > 0);
_tag._semiSharpEdges = (numSemiSharpEdges > 0);
_tag._infSharpDarts = (numInfSharpEdges == 1) && !isBoundary;
// Detect edges effectively making the vertex sharp -- note that
// a vertex can be both explicitly and implicitly sharp (e.g. low
// semi-sharp vertex value with a higher semi-sharp edge):
int numInfSharpTotal = numInfSharpEdges + isBoundary * 2;
if (numInfSharpTotal > 2) {
_isImpInfSharp = true;
} else if ((numInfSharpTotal + numSemiSharpEdges) > 2) {
_isImpSemiSharp = true;
}
// Mark the vertex inf-sharp if implicitly inf-sharp:
if (!_isExpInfSharp && _isImpInfSharp) {
_tag._infSharpVerts = true;
_tag._semiSharpVerts = false;
}
}
}
bool
FaceVertex::HasImplicitVertexSharpness() const {
return _isImpInfSharp || _isImpSemiSharp;
}
float
FaceVertex::GetImplicitVertexSharpness() const {
if (_isImpInfSharp) {
return Sdc::Crease::SHARPNESS_INFINITE;
}
assert(_isImpSemiSharp);
//
// Since this will be applied at an inf-sharp crease, there will be
// two inf-sharp edges in addition to the semi-sharp, so we only
// need find the max of the semi-sharp edges and whatever explicit
// vertex sharpness may have been assigned. Iterate through all
// faces and inspect the sharpness of each leading interior edge:
//
float sharpness = GetVertexSharpness();
for (int i = 0; i < GetNumFaces(); ++i) {
if (GetFacePrevious(i) >= 0) {
sharpness = std::max(sharpness, GetFaceEdgeSharpness(2*i));
}
}
return sharpness;
}
//
// Methods to initialize and/or find subsets of the corner's topology:
//
int
FaceVertex::initCompleteSubset(Subset * subsetPtr) const {
Subset & subset = *subsetPtr;
//
// Initialize with tags and assign the extent:
//
int numFaces = GetNumFaces();
subset.Initialize(GetTag());
subset._numFacesTotal = (short) numFaces;
if (isInterior()) {
subset._numFacesBefore = 0;
subset._numFacesAfter = (short)(numFaces - 1);
} else if (isOrdered()) {
subset._numFacesBefore = _faceInRing;
subset._numFacesAfter = (short)(numFaces - 1 - subset._numFacesBefore);
} else {
// Unordered faces -- boundary needs to identify its orientation:
subset._numFacesAfter = 0;
for (int f = GetFaceNext(_faceInRing); f >= 0; f = GetFaceNext(f)) {
++ subset._numFacesAfter;
}
subset._numFacesBefore = (short)(numFaces - 1 - subset._numFacesAfter);
}
return subset._numFacesTotal;
}
int
FaceVertex::findConnectedSubsetExtent(Subset * subsetPtr) const {
Subset & subset = *subsetPtr;
//
// Initialize with tags and mark manifold:
//
subset.Initialize(GetTag());
subset._tag._nonManifoldVerts = false;
// Add faces to the dflt single face extent by seeking forward/backward:
int fStart = _faceInRing;
for (int f = GetFaceNext(fStart); f >= 0; f = GetFaceNext(f)) {
if (f == fStart) {
// Periodic -- tag as such and return:
subset.SetBoundary(false);
return subset._numFacesTotal;
}
subset._numFacesAfter ++;
subset._numFacesTotal ++;
}
for (int f = GetFacePrevious(fStart); f >= 0; f = GetFacePrevious(f)) {
subset._numFacesBefore ++;
subset._numFacesTotal ++;
}
subset.SetBoundary(true);
return subset._numFacesTotal;
}
int
FaceVertex::GetVertexSubset(Subset * subsetPtr) const {
//
// The subset from a manifold vertex is trivially complete (ordered
// or not), but for non-manifold cases we need to search and update
// the tags according to the content of the subset:
//
if (isManifold()) {
initCompleteSubset(subsetPtr);
} else {
findConnectedSubsetExtent(subsetPtr);
adjustSubsetTags(subsetPtr);
// And if on a non-manifold crease, test for implicit sharpness:
if (!subsetPtr->IsSharp() && HasImplicitVertexSharpness()) {
SharpenSubset(subsetPtr, GetImplicitVertexSharpness());
}
}
return subsetPtr->_numFacesTotal;
}
int
FaceVertex::findFVarSubsetExtent(Subset const & vtxSub,
Subset * fvarSubsetPtr,
Index const fvarIndices[]) const {
Subset & fvarSub = *fvarSubsetPtr;
//
// Initialize with tags and declare as a boundary to start:
//
fvarSub.Initialize(vtxSub._tag);
fvarSub.SetBoundary(true);
if (vtxSub._numFacesTotal == 1) return 1;
//
// Inspect/gather faces "after" (counter-clockwise order from) the
// corner face. If we arrive back at the corner face, a periodic
// set is complete, but check the continuity of the seam and apply
// before returning:
//
int cornerFace = _faceInRing;
int numFacesAfterToVisit = vtxSub._numFacesAfter;
if (numFacesAfterToVisit) {
int thisFace = cornerFace;
int nextFace = GetFaceNext(thisFace);
for (int i = 0; i < numFacesAfterToVisit; ++i) {
if (!FaceIndicesMatchAcrossEdge(thisFace, nextFace, fvarIndices)) {
break;
}
++ fvarSub._numFacesAfter;
++ fvarSub._numFacesTotal;
thisFace = nextFace;
nextFace = GetFaceNext(thisFace);
}
if (nextFace == cornerFace) {
assert(vtxSub._numFacesBefore == 0);
if (FaceIndicesMatchAtEdgeEnd(thisFace, cornerFace, fvarIndices)) {
fvarSub.SetBoundary(false);
}
return fvarSub._numFacesTotal;
}
}
//
// Inspect/gather faces "before" (clockwise order from) the corner
// face. Include any faces "after" in the case of a periodic vertex
// that was interrupted by a discontinuity above:
//
int numFacesBeforeToVisit = vtxSub._numFacesBefore;
if (!vtxSub.IsBoundary()) {
numFacesBeforeToVisit += vtxSub._numFacesAfter - fvarSub._numFacesAfter;
}
if (numFacesBeforeToVisit) {
int thisFace = cornerFace;
int prevFace = GetFacePrevious(thisFace);
for (int i = 0; i < numFacesBeforeToVisit; ++i) {
if (!FaceIndicesMatchAcrossEdge(prevFace, thisFace, fvarIndices)) {
break;
}
++ fvarSub._numFacesBefore;
++ fvarSub._numFacesTotal;
thisFace = prevFace;
prevFace = GetFacePrevious(thisFace);
}
}
return fvarSub._numFacesTotal;
}
int
FaceVertex::FindFaceVaryingSubset(Subset * fvarSubsetPtr,
Index const fvarIndices[],
Subset const & vtxSub) const {
Subset & fvarSub = *fvarSubsetPtr;
//
// Find the face-varying extent and update the tags if its topology
// is a true subset of the vertex. Also reset the sharpness in this
// case as the rules for the FVar interpolation options (applied
// later) take precedence over those of the vertex:
//
findFVarSubsetExtent(vtxSub, &fvarSub, fvarIndices);
bool fvarTopologyMatchesVertex = fvarSub.ExtentMatchesSuperset(vtxSub);
if (!fvarTopologyMatchesVertex) {
if (fvarSub.IsSharp()) {
UnSharpenSubset(&fvarSub);
}
adjustSubsetTags(&fvarSub, &vtxSub);
}
// Sharpen if the vertex is non-manifold:
if (!fvarSub.IsSharp() && !isManifold()) {
SharpenSubset(&fvarSub);
}
// Sharpen if the face-varying value is non-manifold, i.e. if there
// are any occurrences of the corner FVar index outside the subset:
if (!fvarSub.IsSharp() && (fvarSub.GetNumFaces() < vtxSub.GetNumFaces())) {
Index fvarMatch = GetFaceIndexAtCorner(fvarIndices);
int numMatches = 0;
for (int i = 0; i < GetNumFaces(); ++i) {
numMatches += (GetFaceIndexAtCorner(i, fvarIndices) == fvarMatch);
if (numMatches > fvarSub.GetNumFaces()) {
SharpenSubset(&fvarSub);
break;
}
}
}
return fvarSub.GetNumFaces();
}
//
// Method to revise the tags for a subset of the corner, which may no
// longer include properties that trigger exceptional behavior:
//
void
FaceVertex::SharpenSubset(Subset * subset) const {
// Mark the subset sharp and ensure any related tags are also
// updated accordingly:
subset->_tag._infSharpVerts = true;
subset->_tag._semiSharpVerts = false;
}
void
FaceVertex::UnSharpenSubset(Subset * subset) const {
// Restore subset sharpness based on actual sharpness assignment:
subset->_tag._infSharpVerts = _isExpInfSharp;
subset->_tag._semiSharpVerts = _isExpSemiSharp;
}
void
FaceVertex::SharpenSubset(Subset * subset, float sharpness) const {
// Mark the subset according to sharpness value
if (sharpness > subset->_localSharpness) {
subset->_localSharpness = sharpness;
subset->_tag._infSharpVerts = Sdc::Crease::IsInfinite(sharpness);
subset->_tag._semiSharpVerts = Sdc::Crease::IsSemiSharp(sharpness);
}
}
bool
FaceVertex::subsetHasIrregularFaces(Subset const & subset) const {
assert(_tag.HasIrregularFaceSizes());
if (!_tag._unCommonFaceSizes) return true;
int f = GetFaceFirst(subset);
for (int i = 0; i < subset.GetNumFaces(); ++i, f = GetFaceNext(f)) {
if (GetFaceSize(f) != _regFaceSize) return true;
}
return false;
}
bool
FaceVertex::subsetHasInfSharpEdges(Subset const & subset) const {
assert(_tag.HasInfSharpEdges());
int n = subset.GetNumFaces();
if (n > 1) {
int f = GetFaceFirst(subset);
// Reduce number of faces to visit when inspecting trailing edges:
for (int i = subset.IsBoundary(); i < n; ++i, f = GetFaceNext(f)) {
if (IsFaceEdgeInfSharp(f, 1)) return true;
}
}
return false;
}
bool
FaceVertex::subsetHasSemiSharpEdges(Subset const & subset) const {
assert(_tag.HasSemiSharpEdges());
int n = subset.GetNumFaces();
if (n > 1) {
int f = GetFaceFirst(subset);
// Reduce number of faces to visit when inspecting trailing edges:
for (int i = subset.IsBoundary(); i < n; ++i, f = GetFaceNext(f)) {
if (IsFaceEdgeSemiSharp(f, 1)) return true;
}
}
return false;
}
void
FaceVertex::adjustSubsetTags(Subset * subset,
Subset const * superset) const {
VertexTag & subsetTag = subset->_tag;
// Adjust any tags related to boundary or sharpness status:
if (subsetTag.IsBoundary()) {
subsetTag._infSharpDarts = false;
}
if (subsetTag.IsInfSharp()) {
subsetTag._semiSharpVerts = false;
}
// Adjust for the presence of irregular faces or sharp edges if the
// subset is actually a proper subset of this entire corner or the
// optionally provided superset:
int numSuperFaces = superset ? superset->GetNumFaces() : GetNumFaces();
bool superBoundary = superset ? superset->IsBoundary() : isBoundary();
if ((subset->GetNumFaces() < numSuperFaces) ||
(subset->IsBoundary() != superBoundary)) {
if (subsetTag._irregularFaceSizes) {
subsetTag._irregularFaceSizes = subsetHasIrregularFaces(*subset);
}
if (subsetTag._infSharpEdges) {
subsetTag._infSharpEdges = subsetHasInfSharpEdges(*subset);
if (subsetTag._infSharpEdges && subset->IsBoundary()) {
SharpenSubset(subset);
}
}
if (subsetTag._semiSharpEdges) {
subsetTag._semiSharpEdges = subsetHasSemiSharpEdges(*subset);
}
}
}
//
// Main and supporting internal datatypes and methods to connect unordered
// faces and allow for topological traversals of the incident faces:
//
// The fundamental element of this process is the following definition of
// an Edge. It is lightweight and only stores a state (boundary, interior,
// or non-manifold) along with the one or two faces for a manifold edge.
// It is initialized as a boundary when first created and is then modified
// by adding additional incident faces.
//
struct FaceVertex::Edge {
// Empty constructor intentional since we over-allocate what we need:
Edge() { }
void clear() { std::memset(this, 0, sizeof(*this)); }
void Initialize(Index vtx) { clear(), endVertex = vtx; }
// Transition of state as incident faces are added:
void SetBoundary() { boundary = 1; }
void SetInterior() { boundary = 0, interior = 1; }
void SetNonManifold() { boundary = 0, interior = 0, nonManifold = 1; }
// Special cases forcing non-manifold
void SetDegenerate() { SetNonManifold(), degenerate = 1; }
void SetDuplicate() { SetNonManifold(), duplicate = 1; }
void SetSharpness(float sharpness) {
if (sharpness > 0.0f) {
if (Sdc::Crease::IsInfinite(sharpness)) {
infSharp = true;
} else {
semiSharp = true;
}
}
}
void SetFace(int newFace, bool newTrailing) {
trailing = newTrailing;
*(trailing ? &prevFace : &nextFace) = (short) newFace;
}
void AddFace(int newFace, bool newTrailing) {
// Update the state of the Edge based on the added incident face:
if (boundary) {
if (newTrailing == trailing) {
// Edge is reversed
SetNonManifold();
} else if (newFace == (trailing ? prevFace : nextFace)) {
// Edge is repeated in the face
SetNonManifold();
} else {
// Edge is manifold thus far -- promote to interior
SetInterior();
SetFace(newFace, newTrailing);
}
} else if (interior) {
// More than two incident faces -- make non-manifold
SetNonManifold();
}
}
Index endVertex;
unsigned short boundary : 1;
unsigned short interior : 1;
unsigned short nonManifold : 1;
unsigned short trailing : 1;
unsigned short degenerate : 1;
unsigned short duplicate : 1;
unsigned short infSharp : 1;
unsigned short semiSharp : 1;
short prevFace, nextFace;
};
void
FaceVertex::ConnectUnOrderedFaces(Index const fvIndices[]) {
//
// There are two transient sets of data needed here: a set of Edges
// that connect adjoining faces, and a set of indices (one for each
// of the 2*N face-edges) to identify the Edge for each face-edge.
//
// IMPORTANT -- since these later edge indices are of the same type
// and size as the internal face-edge neighbors, we'll use that array
// to avoid a separate declaration (and possible allocation) and will
// update it in place later.
//
int numFaceEdges = GetNumFaces() * 2;
_faceEdgeNeighbors.SetSize(numFaceEdges);
// Allocate and populate the edges and indices referring to them.
// Initialization fails to detect some "duplicate" edges in a face,
// so post-process to catch these before continuing:
Vtr::internal::StackBuffer<Edge,32,true> edges(numFaceEdges);
short * feEdges = &_faceEdgeNeighbors[0];
int numEdges = createUnOrderedEdges(edges, feEdges, fvIndices);
markDuplicateEdges(edges, feEdges, fvIndices);
// Use the connecting edges to assign neighboring faces (overwriting
// our edge indices) and finish initializing the tags retaining the
// properties of the corner:
assignUnOrderedFaceNeighbors(edges, feEdges);
finalizeUnOrderedTags(edges, numEdges);
}
//
// Identify a set of shared edges between unordered faces so that we can
// establish connections between them.
//
// The "face-edge edges" are really just half-edges that refer (by index)
// to potentially shared Edges. As Edges are created, these half-edges
// are made to refer to them, after which the state of the edge may change
// due to the presence or orientation of additional incident faces.
//
int
FaceVertex::createUnOrderedEdges(Edge edges[],
short feEdges[],
Index const fvIndices[]) const {
// Optional map to help construction for high valence:
typedef std::map<Index,int> EdgeMap;
EdgeMap edgeMap;
bool useMap = (GetNumFaces() > 16);
//
// Iterate through the face-edge pairs to find connecting edges:
//
Index vCorner = GetFaceIndexAtCorner(0, fvIndices);
int numFaceEdges = 2 * GetNumFaces();
int numEdges = 0;
// Don't rely on the tag yet to determine presence of sharpness:
bool hasSharpness = _vDesc.HasEdgeSharpness();
for (int feIndex = 0; feIndex < numFaceEdges; ++feIndex) {
Index vIndex = (feIndex & 1) ?
GetFaceIndexTrailing((feIndex >> 1), fvIndices) :
GetFaceIndexLeading( (feIndex >> 1), fvIndices);
int eIndex = -1;
if (vIndex != vCorner) {
if (useMap) {
EdgeMap::iterator eFound = edgeMap.find(vIndex);
if (eFound != edgeMap.end()) {
eIndex = eFound->second;
} else {
// Make sure to create the new edge below at this index
edgeMap[vIndex] = numEdges;
}
} else {
for (int j = 0; j < numEdges; ++j) {
if (edges[j].endVertex == vIndex) {
eIndex = j;
break;
}
}
}
// Update an existing edge or create a new one
if (eIndex >= 0) {
edges[eIndex].AddFace(feIndex >> 1, feIndex & 1);
} else {
// Index of the new (pre-allocated) edge:
eIndex = numEdges ++;
// Initialize a new edge as boundary (manifold)
Edge & E = edges[eIndex];
E.Initialize(vIndex);
E.SetBoundary();
E.SetFace(feIndex >> 1, feIndex & 1);
if (hasSharpness) {
E.SetSharpness(GetFaceEdgeSharpness(feIndex));
}
}
} else {
// If degenerate, create unique edge (non-manifold)
eIndex = numEdges++;
edges[eIndex].Initialize(vIndex);
edges[eIndex].SetDegenerate();
}
assert(eIndex >= 0);
feEdges[feIndex] = (short) eIndex;
}
return numEdges;
}
void
FaceVertex::markDuplicateEdges(Edge edges[],
short const feEdges[],
Index const fvIndices[]) const {
//
// The edge assignment thus far does not correctly detect the presence
// of all edges repeated or duplicated in the same face, e.g. for quad
// with vertices {A, B, A, C} the edge AB occurs both as AB and BA.
// When the face is oriented relative to corner B, we have {B, A, C, A}
// and edge BA will be detected as non-manifold -- but not from corner
// A or C.
//
// So look for repeated instances of the corner vertex in the face and
// inspect its neighbors to see if they match the leading or trailing
// edges.
//
// This is a trivial test for a quad: if the opposite vertex matches
// the corner vertex, both the leading and trailing edges will be
// duplicated and so can immediately be marked non-manifold. So deal
// with the common case of all neighboring quads separately.
//
if (_commonFaceSize == 3) return;
Index vCorner = fvIndices[0];
int numFaces = GetNumFaces();
if (_commonFaceSize == 4) {
Index const * fvOpposite = fvIndices + 2;
for (int face = 0; face < numFaces; ++face, fvOpposite += 4) {
if (*fvOpposite == vCorner) {
edges[feEdges[2*face ]].SetDuplicate();
edges[feEdges[2*face+1]].SetDuplicate();
}
}
} else {
Index const * fv = fvIndices;
for (int face = 0; face < numFaces; ++face) {
int faceSize = GetFaceSize(face);
if (faceSize == 4) {
if (fv[2] == vCorner) {
edges[feEdges[2*face ]].SetDuplicate();
edges[feEdges[2*face+1]].SetDuplicate();
}
} else {
for (int j = 2; j < (faceSize - 2); ++j) {
if (fv[j] == vCorner) {
if (fv[j-1] == fv[1])
edges[feEdges[2*face ]].SetDuplicate();
if (fv[j+1] == fv[faceSize-1])
edges[feEdges[2*face+1]].SetDuplicate();
}
}
}
fv += faceSize;
}
}
}
void
FaceVertex::assignUnOrderedFaceNeighbors(Edge const edges[],
short const feEdges[]) {
int numFaceEdges = 2 * GetNumFaces();
for (int i = 0; i < numFaceEdges; ++i) {
assert(feEdges[i] >= 0);
Edge const & E = edges[feEdges[i]];
bool edgeIsSingular = E.nonManifold || E.boundary;
if (edgeIsSingular) {
_faceEdgeNeighbors[i] = -1;
} else {
_faceEdgeNeighbors[i] = (i & 1) ? E.nextFace : E.prevFace;
}
}
}
void
FaceVertex::finalizeUnOrderedTags(Edge const edges[], int numEdges) {
//
// Summarize properties of the corner given the number and nature of
// the edges around its vertex and initialize remaining members or
// tags that depend on them.
//
// First, take inventory of relevant properties from the edges:
//
int numNonManifoldEdges = 0;
int numInfSharpEdges = 0;
int numSemiSharpEdges = 0;
int numSingularEdges = 0;
bool hasBoundaryEdges = false;
bool hasBoundaryEdgesNotSharp = false;
bool hasDegenerateEdges = false;
bool hasDuplicateEdges = false;
for (int i = 0; i < numEdges; ++i) {
Edge const & E = edges[i];
if (E.interior) {
numInfSharpEdges += E.infSharp;
numSemiSharpEdges += E.semiSharp;
} else if (E.boundary) {
hasBoundaryEdges = true;
hasBoundaryEdgesNotSharp |= !E.infSharp;
} else {
++ numNonManifoldEdges;
hasDegenerateEdges |= E.degenerate;
hasDuplicateEdges |= E.duplicate;
}
// Singular edges include all that are effectively inf-sharp:
numSingularEdges += E.nonManifold || E.boundary || E.infSharp;
}
//
// Next determine whether manifold or not. Some obvious tests quickly
// indicate if the corner is non-manifold, but ultimately it will be
// necessary to traverse the faces to confirm that they form a single
// connected set (e.g. two cones sharing their apex vertex may appear
// manifold to this point but as two connected sets are non-manifold).
//
bool isNonManifold = false;
bool isNonManifoldCrease = false;
if (numNonManifoldEdges) {
isNonManifold = true;
if (!hasDegenerateEdges && !hasDuplicateEdges && !hasBoundaryEdges) {
// Special crease case that avoids sharpening: two interior
// non-manifold edges radiating more than two sets of faces:
isNonManifoldCrease = (numNonManifoldEdges == 2) &&
(GetNumFaces() > numEdges);
}
} else {
// Mismatch between number of incident faces and edges:
isNonManifold = ((numEdges - GetNumFaces()) != (int)hasBoundaryEdges);
if (!isNonManifold) {
// If all faces are not connected, the set is non-manifold:
Subset subset;
int numFacesInSubset = findConnectedSubsetExtent(&subset);
if (numFacesInSubset < GetNumFaces()) {
isNonManifold = true;
}
}
}
//
// Assign tags and other members related to the inventory of edges
// (boundary status is relevant if non-manifold as it can affect
// the presence of the limit surface):
//
_tag._nonManifoldVerts = isNonManifold;
_tag._boundaryVerts = hasBoundaryEdges;
_tag._boundaryNonSharp = hasBoundaryEdgesNotSharp;
_tag._infSharpEdges = (numInfSharpEdges > 0);
_tag._semiSharpEdges = (numSemiSharpEdges > 0);
_tag._infSharpDarts = (numInfSharpEdges == 1) && !hasBoundaryEdges;
// Conditions effectively making the vertex sharp, include the usual
// excess of inf-sharp edges plus some non-manifold cases:
if ((numSingularEdges > 2) || (isNonManifold && !isNonManifoldCrease)) {
_isImpInfSharp = true;
} else if ((numSingularEdges + numSemiSharpEdges) > 2) {
_isImpSemiSharp = true;
}
// Mark the vertex inf-sharp if implicitly inf-sharp:
if (!_isExpInfSharp && _isImpInfSharp) {
_tag._infSharpVerts = true;
_tag._semiSharpVerts = false;
}
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv