Add Chromium-only Blender WebEngine parity work
This commit is contained in:
988
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceVertex.cpp
vendored
Normal file
988
blender-5.2.0/extern/opensubdiv-source/opensubdiv/bfr/faceVertex.cpp
vendored
Normal file
@@ -0,0 +1,988 @@
|
||||
//
|
||||
// Copyright 2021 Pixar
|
||||
//
|
||||
// Licensed under the terms set forth in the LICENSE.txt file available at
|
||||
// https://opensubdiv.org/license.
|
||||
//
|
||||
|
||||
#include "../bfr/faceVertex.h"
|
||||
#include "../sdc/crease.h"
|
||||
#include "../vtr/array.h"
|
||||
#include "../vtr/stackBuffer.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, fvIndices);
|
||||
}
|
||||
|
||||
//
|
||||
// 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,
|
||||
Index const fvIndices[]) {
|
||||
|
||||
//
|
||||
// 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.
|
||||
// This requires closer inspection to confirm, so make use of
|
||||
// as many pre-conditions available here to avoid doing so:
|
||||
isNonManifoldCrease = !_isExpInfSharp &&
|
||||
(numNonManifoldEdges == 2) && (GetNumFaces() > numEdges) &&
|
||||
testNonManifoldCrease(edges, numEdges, fvIndices);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
FaceVertex::testNonManifoldCrease(Edge const edges[], int numEdges,
|
||||
Index const fvIndices[]) const {
|
||||
//
|
||||
// Local struct that keeps track of face-corners remaining around
|
||||
// a vertex as those visited as part of connected manifold subsets
|
||||
// are removed during inspection:
|
||||
//
|
||||
struct FaceCornerArray {
|
||||
typedef Vtr::internal::StackBuffer<int,32,true> MemberArray;
|
||||
typedef Vtr::ConstArray<int> SearchArray;
|
||||
|
||||
MemberArray leading;
|
||||
MemberArray trailing;
|
||||
int size;
|
||||
|
||||
FaceCornerArray(int n) : leading(n), trailing(n), size(n) { }
|
||||
|
||||
int FindLeading(int vertex) const {
|
||||
return SearchArray(leading, size).FindIndex(vertex);
|
||||
}
|
||||
int FindTrailing(int vertex) const {
|
||||
return SearchArray(trailing, size).FindIndex(vertex);
|
||||
}
|
||||
void RemoveFace(int index) {
|
||||
std::swap(leading[index], leading[size - 1]);
|
||||
std::swap(trailing[index], trailing[size - 1]);
|
||||
-- size;
|
||||
}
|
||||
int RemoveManifoldSubset(int startVertex, int endVertex) {
|
||||
assert(startVertex != endVertex);
|
||||
|
||||
for (int nextVertex = startVertex; nextVertex != endVertex; ) {
|
||||
int faceIndex = FindLeading(nextVertex);
|
||||
if (faceIndex < 0) return (nextVertex == startVertex) ? 0 : -1;
|
||||
|
||||
nextVertex = trailing[faceIndex];
|
||||
if (nextVertex == startVertex) return -1;
|
||||
|
||||
RemoveFace(faceIndex);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Identify the vertices at the ends of the two non-manifold edges
|
||||
// of the potential crease:
|
||||
//
|
||||
int creaseEnd[2] = { -1, -1 };
|
||||
|
||||
for (int i = 0; i < numEdges; ++i) {
|
||||
if (edges[i].nonManifold) {
|
||||
creaseEnd[creaseEnd[0] >= 0] = edges[i].endVertex;
|
||||
}
|
||||
}
|
||||
assert((creaseEnd[0] >= 0) && (creaseEnd[1] >= 0));
|
||||
|
||||
//
|
||||
// Use the locally defined set of face-edge pairs to determine if
|
||||
// the collection of faces around the vertex forms a non-manifold
|
||||
// crease.
|
||||
//
|
||||
// First initialize the FaceCornerArray from the face vertices:
|
||||
//
|
||||
int numFaces = GetNumFaces();
|
||||
|
||||
FaceCornerArray faceCorners(numFaces);
|
||||
|
||||
for (int i = 0; i < numFaces; ++i) {
|
||||
faceCorners.leading[i] = GetFaceIndexLeading( i, fvIndices);
|
||||
faceCorners.trailing[i] = GetFaceIndexTrailing(i, fvIndices);
|
||||
}
|
||||
|
||||
// Remove faces for manifold subsets in one direction:
|
||||
int removed = 0;
|
||||
do {
|
||||
removed = faceCorners.RemoveManifoldSubset(creaseEnd[0], creaseEnd[1]);
|
||||
if (removed < 0) {
|
||||
return false;
|
||||
}
|
||||
} while (removed);
|
||||
|
||||
if (faceCorners.size == 0) return true;
|
||||
|
||||
// Remove faces for manifold subsets in the other direction:
|
||||
do {
|
||||
removed = faceCorners.RemoveManifoldSubset(creaseEnd[1], creaseEnd[0]);
|
||||
if (removed < 0) {
|
||||
return false;
|
||||
}
|
||||
} while (removed);
|
||||
|
||||
return (faceCorners.size == 0);
|
||||
}
|
||||
|
||||
} // end namespace Bfr
|
||||
|
||||
} // end namespace OPENSUBDIV_VERSION
|
||||
} // end namespace OpenSubdiv
|
||||
Reference in New Issue
Block a user