Added more functionality missing from stubs for Sdc limit masks:

- modified main LimitMask queries to include subdivision Rule
    - split internal mask assignments into corner, crease and smooth cases
    - adapted all three schemes to the new structure
    - completed limit tangents for Loop
    - update TopologyRefiner Limit methods to pass newly required Rule
This commit is contained in:
barfowl 2015-04-10 12:29:23 -07:00
parent f5c1617b0f
commit 6ba910cd95
6 changed files with 520 additions and 115 deletions

View File

@ -37,6 +37,7 @@
#include "../vtr/fvarRefinement.h"
#include "../vtr/maskInterfaces.h"
#include "../far/types.h"
#include "../far/error.h"
#include <vector>
#include <cassert>
@ -1418,7 +1419,11 @@ template <class T, class U>
inline void
TopologyRefiner::Limit(T const & src, U * dst) const {
assert(GetMaxLevel() > 0);
if (getLevel(GetMaxLevel()).getNumVertexEdgesTotal() == 0) {
Error(FAR_RUNTIME_ERROR,
"Cannot compute limit points -- last level of refinement does not include full topology.");
return;
}
switch (_subdivType) {
case Sdc::SCHEME_CATMARK:
@ -1461,6 +1466,18 @@ TopologyRefiner::limit(T const & src, U * dst) const {
for (int vert = 0; vert < level.getNumVertices(); ++vert) {
ConstIndexArray vEdges = level.getVertexEdges(vert);
// Incomplete vertices (present in sparse refinement) do not have their full
// topological neighborhood to determine a proper limit -- just leave the
// vertex at the refined location and continue to the next:
//
if (level._vertTags[vert]._incomplete || (vEdges.size() == 0)) {
dst[vert].Clear();
dst[vert].AddWithWeight(src[vert], 1.0);
continue;
}
// Assign the mask weights to the common buffer and compute the mask:
//
float * vWeights = weightBuffer,
* eWeights = vWeights + 1,
* fWeights = eWeights + vEdges.size();
@ -1470,7 +1487,7 @@ TopologyRefiner::limit(T const & src, U * dst) const {
// This is a bit obscure -- child vertex index will be ignored here
vHood.SetIndex(vert, vert);
scheme.ComputeVertexLimitMask(vHood, vMask);
scheme.ComputeVertexLimitMask(vHood, vMask, level.getVertexRule(vert));
// Apply the weights to the vertex, the vertices opposite its incident
// edges, and the opposite vertices of its incident faces:
@ -1512,7 +1529,11 @@ template <class T, class U>
inline void
TopologyRefiner::LimitFaceVarying(T const & src, U * dst, int channel) const {
assert(GetMaxLevel() > 0);
if (getLevel(GetMaxLevel()).getNumVertexEdgesTotal() == 0) {
Error(FAR_RUNTIME_ERROR,
"Cannot compute limit points -- last level of refinement does not include full topology.");
return;
}
switch (_subdivType) {
case Sdc::SCHEME_CATMARK:
@ -1546,21 +1567,31 @@ TopologyRefiner::faceVaryingLimit(T const & src, U * dst, int channel) const {
for (int vert = 0; vert < level.getNumVertices(); ++vert) {
ConstIndexArray vEdges = level.getVertexEdges(vert);
ConstIndexArray vValues = fvarChannel.getVertexValues(vert);
// Incomplete vertices (present in sparse refinement) do not have their full
// topological neighborhood to determine a proper limit -- just leave the
// values (perhaps more than one per vertex) at the refined location.
//
// The same can be done if the face-varying channel is purely linear.
//
bool isIncomplete = (level._vertTags[vert]._incomplete || (vEdges.size() == 0));
if (isIncomplete || fvarChannel._isLinear) {
for (int i = 0; i < vValues.size(); ++i) {
Vtr::Index vValue = vValues[i];
dst[vValue].Clear();
dst[vValue].AddWithWeight(src[vValue], 1.0f);
}
continue;
}
bool fvarVertMatchesVertex = fvarChannel.valueTopologyMatches(vValues[0]);
if (fvarChannel._isLinear && fvarVertMatchesVertex) {
Vtr::Index srcValueIndex = fvarChannel.getVertexValue(vert);
Vtr::Index dstValueIndex = vValues[0];
if (fvarVertMatchesVertex) {
dst[dstValueIndex].Clear();
dst[dstValueIndex].AddWithWeight(src[srcValueIndex], 1.0f);
} else if (fvarVertMatchesVertex) {
// Assign the mask weights to the common buffer and compute the mask:
//
// Compute the limit mask based on vertex topology:
//
ConstIndexArray vEdges = level.getVertexEdges(vert);
float * vWeights = weightBuffer,
* eWeights = vWeights + 1,
* fWeights = eWeights + vEdges.size();
@ -1569,7 +1600,7 @@ TopologyRefiner::faceVaryingLimit(T const & src, U * dst, int channel) const {
vHood.SetIndex(vert, vert);
scheme.ComputeVertexLimitMask(vHood, vMask);
scheme.ComputeVertexLimitMask(vHood, vMask, level.getVertexRule(vert));
//
// Apply mask to corresponding FVar values for neighboring vertices:

View File

@ -76,13 +76,12 @@ Scheme<SCHEME_BILINEAR>::ComputeVertexVertexMask(VERTEX const& vertex, MASK& mas
//
// Limit masks for any bilinear vertex are the vertex itself, with all tangents being
// zero for now as tangents are not unique (what did Hbr do?):
// Limit masks for position -- the limit position of all vertices is the refined vertex.
//
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_BILINEAR>::assignInteriorLimitMask(VERTEX const& /* vertex */, MASK& posMask) const {
Scheme<SCHEME_BILINEAR>::assignCornerLimitMask(VERTEX const& /* vertex */, MASK& posMask) const {
posMask.SetNumVertexWeights(1);
posMask.SetNumEdgeWeights(0);
@ -95,38 +94,65 @@ Scheme<SCHEME_BILINEAR>::assignInteriorLimitMask(VERTEX const& /* vertex */, MAS
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_BILINEAR>::assignBoundaryLimitMask(VERTEX const& vertex, MASK& posMask) const {
Scheme<SCHEME_BILINEAR>::assignCreaseLimitMask(VERTEX const& vertex, MASK& posMask,
int const /* creaseEnds */[2]) const {
assignInteriorLimitMask(vertex, posMask);
assignCornerLimitMask(vertex, posMask);
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_BILINEAR>::assignInteriorLimitTangentMasks(VERTEX const& /* vertex */,
Scheme<SCHEME_BILINEAR>::assignSmoothLimitMask(VERTEX const& vertex, MASK& posMask) const {
assignCornerLimitMask(vertex, posMask);
}
//
// Limit masks for tangents -- these are ambibuous around all vertices. Provide
// the tangents based on the incident edges of the first face.
//
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_BILINEAR>::assignCornerLimitTangentMasks(VERTEX const& /* vertex */,
MASK& tan1Mask, MASK& tan2Mask) const {
tan1Mask.SetNumVertexWeights(1);
tan1Mask.SetNumEdgeWeights(0);
tan1Mask.SetNumEdgeWeights(2);
tan1Mask.SetNumFaceWeights(0);
tan1Mask.SetFaceWeightsForFaceCenters(false);
tan2Mask.SetNumVertexWeights(1);
tan2Mask.SetNumEdgeWeights(0);
tan2Mask.SetNumEdgeWeights(2);
tan2Mask.SetNumFaceWeights(0);
tan2Mask.SetFaceWeightsForFaceCenters(false);
tan1Mask.VertexWeight(0) = 0.0f;
tan2Mask.VertexWeight(0) = 0.0f;
tan1Mask.VertexWeight(0) = -1.0f;
tan1Mask.EdgeWeight(0) = 1.0f;
tan1Mask.EdgeWeight(1) = 0.0f;
tan2Mask.VertexWeight(0) = -1.0f;
tan2Mask.EdgeWeight(0) = 0.0f;
tan2Mask.EdgeWeight(1) = 1.0f;
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_BILINEAR>::assignBoundaryLimitTangentMasks(VERTEX const& vertex,
Scheme<SCHEME_BILINEAR>::assignCreaseLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask, int const /* creaseEnds */[2]) const {
assignCornerLimitTangentMasks(vertex, tan1Mask, tan2Mask);
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_BILINEAR>::assignSmoothLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask) const {
assignInteriorLimitTangentMasks(vertex, tan1Mask, tan2Mask);
assignCornerLimitTangentMasks(vertex, tan1Mask, tan2Mask);
}
} // end namespace sdc

View File

@ -145,8 +145,8 @@ Scheme<SCHEME_CATMARK>::assignSmoothMaskForEdge(EDGE const& edge, MASK& mask) co
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignCreaseMaskForVertex(VERTEX const& vertex, MASK& mask, float const edgeSharpness[]) const {
Scheme<SCHEME_CATMARK>::assignCreaseMaskForVertex(VERTEX const& vertex, MASK& mask,
int const creaseEnds[2]) const {
typedef typename MASK::Weight Weight;
int valence = vertex.GetNumEdges();
@ -160,25 +160,11 @@ Scheme<SCHEME_CATMARK>::assignCreaseMaskForVertex(VERTEX const& vertex, MASK& ma
Weight eWeight = 0.125f;
mask.VertexWeight(0) = vWeight;
//
// NOTE -- at some point the sharpness vector was optional, and topology would be used
// to identify a boundary crease. We are currently no longer passing a null sharpness
// vector and may not support it in future, in which case this test can be removed:
//
if (edgeSharpness != 0) {
// Use the sharpness values to identify the crease edges:
for (int i = 0; i < valence; ++i) {
mask.EdgeWeight(i) = (edgeSharpness[i] > 0.0f) ? eWeight : 0.0f;
}
} else {
// Use the boundary edges (first and last) as the crease edges:
mask.EdgeWeight(0) = eWeight;
for (int i = 1; i < (valence - 1); ++i) {
mask.EdgeWeight(i) = 0.0f;
}
mask.EdgeWeight(valence-1) = eWeight;
for (int i = 0; i < valence; ++i) {
mask.EdgeWeight(i) = 0.0f;
}
mask.EdgeWeight(creaseEnds[0]) = eWeight;
mask.EdgeWeight(creaseEnds[1]) = eWeight;
}
template <>
@ -225,7 +211,21 @@ Scheme<SCHEME_CATMARK>::assignSmoothMaskForVertex(VERTEX const& vertex, MASK& ma
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignBoundaryLimitMask(VERTEX const& vertex, MASK& posMask) const {
Scheme<SCHEME_CATMARK>::assignCornerLimitMask(VERTEX const& /* vertex */, MASK& posMask) const {
posMask.SetNumVertexWeights(1);
posMask.SetNumEdgeWeights(0);
posMask.SetNumFaceWeights(0);
posMask.SetFaceWeightsForFaceCenters(false);
posMask.VertexWeight(0) = 1.0f;
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignCreaseLimitMask(VERTEX const& vertex, MASK& posMask,
int const creaseEnds[2]) const {
typedef typename MASK::Weight Weight;
@ -240,17 +240,17 @@ Scheme<SCHEME_CATMARK>::assignBoundaryLimitMask(VERTEX const& vertex, MASK& posM
Weight eWeight = 1.0f / 6.0f;
posMask.VertexWeight(0) = vWeight;
posMask.EdgeWeight(0) = eWeight;
for (int i = 1; i < valence - 1; ++i) {
for (int i = 0; i < valence; ++i) {
posMask.EdgeWeight(i) = 0.0f;
}
posMask.EdgeWeight(valence - 1) = eWeight;
posMask.EdgeWeight(creaseEnds[0]) = eWeight;
posMask.EdgeWeight(creaseEnds[1]) = eWeight;
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignInteriorLimitMask(VERTEX const& vertex, MASK& posMask) const {
Scheme<SCHEME_CATMARK>::assignSmoothLimitMask(VERTEX const& vertex, MASK& posMask) const {
typedef typename MASK::Weight Weight;
@ -262,11 +262,16 @@ Scheme<SCHEME_CATMARK>::assignInteriorLimitMask(VERTEX const& vertex, MASK& posM
posMask.SetNumFaceWeights(valence);
posMask.SetFaceWeightsForFaceCenters(false);
// Probably a good idea to test for and assign the regular case as a special case:
// Specialize for the regular case:
Weight fWeight = 1.0f / 36.0f;
Weight eWeight = 1.0f / 9.0f;
Weight vWeight = 4.0f / 9.0f;
Weight fWeight = 1.0f / (Weight)(valence * (valence + 5.0f));
Weight eWeight = 4.0f * fWeight;
Weight vWeight = (Weight)(1.0f - valence * (eWeight + fWeight));
if (valence != 4) {
fWeight = 1.0f / (Weight)(valence * (valence + 5.0f));
eWeight = 4.0f * fWeight;
vWeight = (Weight)(1.0f - valence * (eWeight + fWeight));
}
posMask.VertexWeight(0) = vWeight;
for (int i = 0; i < valence; ++i) {
@ -282,26 +287,91 @@ Scheme<SCHEME_CATMARK>::assignInteriorLimitMask(VERTEX const& vertex, MASK& posM
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignBoundaryLimitTangentMasks(VERTEX const& /* vertex */,
Scheme<SCHEME_CATMARK>::assignCornerLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask) const {
int valence = vertex.GetNumEdges();
tan1Mask.SetNumVertexWeights(1);
tan1Mask.SetNumEdgeWeights(0);
tan1Mask.SetNumEdgeWeights(valence);
tan1Mask.SetNumFaceWeights(0);
tan1Mask.SetFaceWeightsForFaceCenters(false);
tan1Mask.VertexWeight(0) = 0.0f;
tan2Mask.SetNumVertexWeights(1);
tan2Mask.SetNumEdgeWeights(0);
tan2Mask.SetNumEdgeWeights(valence);
tan2Mask.SetNumFaceWeights(0);
tan2Mask.SetFaceWeightsForFaceCenters(false);
tan2Mask.VertexWeight(0) = 0.0f;
// Should be at least 2 edges -- be sure to clear weights for any more:
tan1Mask.VertexWeight(0) = -1.0f;
tan1Mask.EdgeWeight(0) = 1.0f;
tan1Mask.EdgeWeight(1) = 0.0f;
tan2Mask.VertexWeight(0) = -1.0f;
tan2Mask.EdgeWeight(0) = 0.0f;
tan2Mask.EdgeWeight(1) = 1.0f;
for (int i = 2; i < valence; ++i) {
tan1Mask.EdgeWeight(i) = 0.0f;
tan2Mask.EdgeWeight(i) = 0.0f;
}
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignInteriorLimitTangentMasks(VERTEX const& vertex,
Scheme<SCHEME_CATMARK>::assignCreaseLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask, int const creaseEnds[2]) const {
int valence = vertex.GetNumEdges();
tan1Mask.SetNumVertexWeights(1);
tan1Mask.SetNumEdgeWeights(valence);
tan1Mask.SetNumFaceWeights(0);
tan1Mask.SetFaceWeightsForFaceCenters(false);
tan2Mask.SetNumVertexWeights(1);
tan2Mask.SetNumEdgeWeights(valence);
tan2Mask.SetNumFaceWeights(0);
tan2Mask.SetFaceWeightsForFaceCenters(false);
// Specialize for the regular (boundary) case:
bool isRegular = (vertex.GetNumEdges() == 3);
if (isRegular) {
tan1Mask.VertexWeight(0) = 0.0f;
tan1Mask.EdgeWeight(0) = 1.0f;
tan1Mask.EdgeWeight(1) = 0.0f;
tan1Mask.EdgeWeight(2) = -1.0f;
tan2Mask.VertexWeight(0) = -1.0f;
tan2Mask.EdgeWeight(0) = 0.0f;
tan2Mask.EdgeWeight(1) = 1.0f;
tan2Mask.EdgeWeight(2) = 0.0f;
} else {
// First, the tangent along the crease:
tan1Mask.VertexWeight(0) = 0.0f;
for (int i = 0; i < valence; ++i) {
tan1Mask.EdgeWeight(i) = 0.0f;
}
tan1Mask.EdgeWeight(creaseEnds[0]) = 1.0f;
tan1Mask.EdgeWeight(creaseEnds[1]) = -1.0f;
// Second, the tangent across the interior faces:
// - just using an interior edge for now
// - ultimately need regular and extra-ordinary cases here:
//
tan2Mask.VertexWeight(0) = -1.0f;
for (int i = 0; i < valence; ++i) {
tan2Mask.EdgeWeight(i) = 0.0f;
}
tan2Mask.EdgeWeight((creaseEnds[0] + creaseEnds[1]) >> 1) = 1.0f;
}
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignSmoothLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask) const {
typedef typename MASK::Weight Weight;

View File

@ -165,6 +165,10 @@ public:
float const* incidentEdgeSharpness,
float const* childEdgesSharpness) const;
void GetSharpEdgePairOfCrease(float const * incidentEdgeSharpness,
int incidentEdgeCount,
int sharpEdgePair[2]) const;
// Would these really help? Maybe only need Rules for the vertex-vertex case...
//
// Rule DetermineEdgeVertexRule(float parentEdgeSharpness) const;
@ -220,6 +224,20 @@ Crease::SubdivideVertexSharpness(float vertexSharpness) const {
return decrementSharpness(vertexSharpness);
}
inline void
Crease::GetSharpEdgePairOfCrease(float const * incidentEdgeSharpness, int incidentEdgeCount,
int sharpEdgePair[2]) const {
// Only to be called when a crease is present at a vertex -- exactly two sharp
// edges are expected here:
//
sharpEdgePair[0] = 0;
while (IsSmooth(incidentEdgeSharpness[sharpEdgePair[0]])) ++ sharpEdgePair[0];
sharpEdgePair[1] = incidentEdgeCount - 1;
while (IsSmooth(incidentEdgeSharpness[sharpEdgePair[1]])) -- sharpEdgePair[1];
}
} // end namespace sdc
} // end namespace OPENSUBDIV_VERSION

View File

@ -151,8 +151,8 @@ Scheme<SCHEME_LOOP>::assignCornerMaskForVertex(VERTEX const&, MASK& mask) const
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignCreaseMaskForVertex(VERTEX const& vertex, MASK& mask, float const edgeSharpness[]) const
{
Scheme<SCHEME_LOOP>::assignCreaseMaskForVertex(VERTEX const& vertex, MASK& mask,
int const creaseEnds[2]) const {
typedef typename MASK::Weight Weight;
int valence = vertex.GetNumEdges();
@ -166,10 +166,11 @@ Scheme<SCHEME_LOOP>::assignCreaseMaskForVertex(VERTEX const& vertex, MASK& mask,
Weight eWeight = 0.125f;
mask.VertexWeight(0) = vWeight;
for (int i = 0; i < valence; ++i) {
mask.EdgeWeight(i) = (edgeSharpness[i] > 0.0f) ? eWeight : 0.0f;
mask.EdgeWeight(i) = 0.0f;
}
mask.EdgeWeight(creaseEnds[0]) = eWeight;
mask.EdgeWeight(creaseEnds[1]) = eWeight;
}
template <>
@ -215,7 +216,21 @@ Scheme<SCHEME_LOOP>::assignSmoothMaskForVertex(VERTEX const& vertex, MASK& mask)
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignBoundaryLimitMask(VERTEX const& vertex, MASK& posMask) const {
Scheme<SCHEME_LOOP>::assignCornerLimitMask(VERTEX const& /* vertex */, MASK& posMask) const {
posMask.SetNumVertexWeights(1);
posMask.SetNumEdgeWeights(0);
posMask.SetNumFaceWeights(0);
posMask.SetFaceWeightsForFaceCenters(false);
posMask.VertexWeight(0) = 1.0f;
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignCreaseLimitMask(VERTEX const& vertex, MASK& posMask,
int const creaseEnds[2]) const {
typedef typename MASK::Weight Weight;
@ -226,21 +241,32 @@ Scheme<SCHEME_LOOP>::assignBoundaryLimitMask(VERTEX const& vertex, MASK& posMask
posMask.SetNumFaceWeights(0);
posMask.SetFaceWeightsForFaceCenters(false);
//
// The refinement mask for a crease vertex is (1/8, 3/4, 1/8) and for a crease
// edge is (1/2, 1/2) -- producing a uniform B-spline curve along the crease
// (boundary) whether the vertex or its crease is regular or not. The limit
// mask is therefore (1/6, 2/3, 1/6) for ALL cases.
//
// An alternative limit mask (1/5, 3/5, 1/5) is often published for use either
// for irregular crease vertices or for all crease/boundary vertices, but this
// is based on an alternate refinement mask for the edge -- (3/8, 5/8) versus
// the usual (1/2, 1/2) -- and will not produce the B-spline curve desired.
//
Weight vWeight = 4.0f / 6.0f;
Weight eWeight = 1.0f / 6.0f;
posMask.VertexWeight(0) = vWeight;
posMask.EdgeWeight(0) = eWeight;
for (int i = 1; i < valence - 1; ++i) {
for (int i = 0; i < valence; ++i) {
posMask.EdgeWeight(i) = 0.0f;
}
posMask.EdgeWeight(valence - 1) = eWeight;
posMask.EdgeWeight(creaseEnds[0]) = eWeight;
posMask.EdgeWeight(creaseEnds[1]) = eWeight;
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignInteriorLimitMask(VERTEX const& vertex, MASK& posMask) const {
Scheme<SCHEME_LOOP>::assignSmoothLimitMask(VERTEX const& vertex, MASK& posMask) const {
typedef typename MASK::Weight Weight;
@ -275,31 +301,221 @@ Scheme<SCHEME_LOOP>::assignInteriorLimitMask(VERTEX const& vertex, MASK& posMask
//
// Limit masks for tangents:
//
// A note on tangent magnitudes:
//
// Several formulae exist for limit tangents at a vertex to accomodate the
// different topological configurations around the vertex. While these produce
// the desired direction, there is inconsistency in the resulting magnitudes.
// Ideally a regular mesh of uniformly shaped triangles with similar edge lengths
// should produce tangents of similar magnitudes throughout -- including corners
// and boundaries. So some of the common formulae for these are adjusted with
// scale factors.
//
// For uses where magnitude does not matter, this scaling should be irrelevant.
// But just as with patches, where the magnitudes of partial derivates are
// consistent between similar patches, the magnitudes of limit tangents should
// also be similar.
//
// The reference tangents, in terms of magnitudes, are those produced by the
// limit tangent mask for smooth interior vertices, for which well established
// sin/cos formulae apply -- these remain unscaled. Formulae for the other
// crease/boundary, corner tangents and irregular cases are scaled to be more
// consistent with these.
//
// The crease/boundary tangents for the regular case can be viewed as derived
// from the smooth interior masks with two "phantom" points extrapolated across
// the regular boundary:
//
// v3 v2
// X - - - - - X
// / \ / \
// / \ / \
// v4 X - - - - - X - - - - - X v1
// . . 0 . .
// . . . .
// . . . .
// (v5) (v6)
//
// where v5 = v0 + (v4 - v3) and v6 = v0 + v1 - v2.
//
// When the standard limit tangent mask is applied, the cosines of increments
// of pi/3 gives us coefficients that are mutliples of 1/2, leading to the first
// tangent T1 = 3/2 * (v1 - v4), rather than the widely used T1 = v1 - v4. So
// this scale factor of 3/2 is applied to insure tangents along the boundaries
// are of similar magnitude as tangents in the immediate interior (which may be
// parallel).
//
// Tangents at corners are essentially a form of boundary tangent, and so its
// simple difference formula is scaled to be consistent with adjoining boundary
// tangents -- not just with the 3/2 factor from above, but with an additional
// 2.0 to compensate for the fact that the difference of only side of the vertex
// is considered here. The resulting scale factor of 3.0 for the regular corner
// is what similarly arises by extrapolating an interior region around the
// vertex and using the interior mask for the first tangent.
//
// The cross-tangent formula for the regular crease/boundary is similarly found
// from the above construction of the boundary, but the commonly used weights of
// +/- 1 and 2 result from omitting the common factor of sqrt(3)/2 (arising from
// the sines of increments of pi/3). With that scale factor close to one, it has
// less impact than the irregular cases, which are analogous to corner tangents
// in that differences on only one side of the vertex are considered. While a
// scaling of 3.0 is similarly understandable for the valence 2 and 3 cases, it is
// less obvious in the irregular formula for valence > 4, but similarly effective.
//
// The end result of these adjustments should be a set of limit tangents that are
// of similar magnitude over a regular mesh including boundaries and corners.
//
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignBoundaryLimitTangentMasks(VERTEX const& /* vertex */,
Scheme<SCHEME_LOOP>::assignCornerLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask) const {
// Need to dig up formulae for this case...
int valence = vertex.GetNumEdges();
tan1Mask.SetNumVertexWeights(1);
tan1Mask.SetNumEdgeWeights(0);
tan1Mask.SetNumEdgeWeights(valence);
tan1Mask.SetNumFaceWeights(0);
tan1Mask.SetFaceWeightsForFaceCenters(false);
tan1Mask.VertexWeight(0) = 0.0f;
tan2Mask.SetNumVertexWeights(1);
tan2Mask.SetNumEdgeWeights(0);
tan2Mask.SetNumEdgeWeights(valence);
tan2Mask.SetNumFaceWeights(0);
tan2Mask.SetFaceWeightsForFaceCenters(false);
tan2Mask.VertexWeight(0) = 0.0f;
// See note above regarding scale factor of 3.0:
tan1Mask.VertexWeight(0) = -3.0f;
tan1Mask.EdgeWeight(0) = 3.0f;
tan1Mask.EdgeWeight(1) = 0.0f;
tan2Mask.VertexWeight(0) = -3.0f;
tan2Mask.EdgeWeight(0) = 0.0f;
tan2Mask.EdgeWeight(1) = 3.0f;
// Should be at least 2 edges -- be sure to clear weights for any more:
for (int i = 2; i < valence; ++i) {
tan1Mask.EdgeWeight(i) = 0.0f;
tan2Mask.EdgeWeight(i) = 0.0f;
}
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignInteriorLimitTangentMasks(VERTEX const& vertex,
Scheme<SCHEME_LOOP>::assignCreaseLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask, int const creaseEnds[2]) const {
typedef typename MASK::Weight Weight;
//
// First, the tangent along the crease:
// The first crease edge is considered the "leading" edge of the span
// of surface for which we are evaluating tangents and the second edge the
// "trailing edge". By convention, the tangent along the crease is oriented
// in the direction of the leading edge.
//
int valence = vertex.GetNumEdges();
tan1Mask.SetNumVertexWeights(1);
tan1Mask.SetNumEdgeWeights(valence);
tan1Mask.SetNumFaceWeights(0);
tan1Mask.SetFaceWeightsForFaceCenters(false);
tan1Mask.VertexWeight(0) = 0.0f;
for (int i = 0; i < valence; ++i) {
tan1Mask.EdgeWeight(i) = 0.0f;
}
// See the note above regarding scale factor of 1.5:
tan1Mask.EdgeWeight(creaseEnds[0]) = 1.5f;
tan1Mask.EdgeWeight(creaseEnds[1]) = -1.5f;
//
// Second, the tangent across the interior faces:
// Note this is ambigous for an interior vertex. We currently return
// the tangent for the surface in the counter-clockwise span between the
// leading and trailing edges that form the crease. Given the expected
// computation of a surface normal as Tan1 X Tan2, this tangent should be
// oriented "inward" from the crease/boundary -- across the surface rather
// than outward and away from it.
//
// There is inconsistency in the orientation of this tangent in commonly
// published results: the general formula provided for arbitrary valence
// has the tangent pointing across the crease and "outward" from the surface,
// while the special cases for regular valence and lower have the tangent
// pointing across the surface and "inward" from the crease. So if we are
// to consistently orient the first tangent along the crease, regardless of
// the interior topology, we have to correct this. With the first tangent
// following the direction of the leading crease edge, we want the second
// tangent pointing inward/across the surface -- so we flip the result of
// the general formula.
//
tan2Mask.SetNumVertexWeights(1);
tan2Mask.SetNumEdgeWeights(valence);
tan2Mask.SetNumFaceWeights(0);
tan2Mask.SetFaceWeightsForFaceCenters(false);
for (int i = 0; i < creaseEnds[0]; ++i) {
tan2Mask.EdgeWeight(i) = 0.0f;
}
int interiorEdgeCount = creaseEnds[1] - creaseEnds[0] - 1;
if (interiorEdgeCount == 2) {
// See note above regarding scale factor of (sin(60 degs) == sqrt(3)/2:
static Weight const Root3 = (Weight) 1.73205080756887729352f;
static Weight const Root3by2 = (Weight) (Root3 * 0.5);
tan2Mask.VertexWeight(0) = -Root3;
tan2Mask.EdgeWeight(creaseEnds[0]) = -Root3by2;
tan2Mask.EdgeWeight(creaseEnds[1]) = -Root3by2;
tan2Mask.EdgeWeight(creaseEnds[0] + 1) = Root3;
tan2Mask.EdgeWeight(creaseEnds[0] + 2) = Root3;
} else if (interiorEdgeCount > 2) {
// See notes above regarding scale factor of -3.0 (-1 for orientation,
// 2.0 for considering the region as a half-disk, and 1.5 in keeping
// with the crease tangent):
double theta = M_PI / (interiorEdgeCount + 1);
Weight cWeight = -3.0f * std::sin(theta);
Weight eWeightCoeff = -3.0f * (2.0f * std::cos(theta) - 2.0f);
tan2Mask.VertexWeight(0) = 0.0f;
tan2Mask.EdgeWeight(creaseEnds[0]) = cWeight;
tan2Mask.EdgeWeight(creaseEnds[1]) = cWeight;
for (int i = 1; i <= interiorEdgeCount; ++i) {
tan2Mask.EdgeWeight(creaseEnds[0] + i) = eWeightCoeff * std::sin(i * theta);
}
} else if (interiorEdgeCount == 1) {
// See notes above regarding scale factor of 3.0:
tan2Mask.VertexWeight(0) = -3.0f;
tan2Mask.EdgeWeight(creaseEnds[0]) = 0.0f;
tan2Mask.EdgeWeight(creaseEnds[1]) = 0.0f;
tan2Mask.EdgeWeight(creaseEnds[0] + 1) = 3.0f;
} else {
// See notes above regarding scale factor of 3.0:
tan2Mask.VertexWeight(0) = -6.0f;
tan2Mask.EdgeWeight(creaseEnds[0]) = 3.0f;
tan2Mask.EdgeWeight(creaseEnds[1]) = 3.0f;
}
for (int i = creaseEnds[1] + 1; i < valence; ++i) {
tan2Mask.EdgeWeight(i) = 0.0f;
}
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignSmoothLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask) const {
typedef typename MASK::Weight Weight;
@ -320,11 +536,29 @@ Scheme<SCHEME_LOOP>::assignInteriorLimitTangentMasks(VERTEX const& vertex,
tan1Mask.VertexWeight(0) = 0.0f;
tan2Mask.VertexWeight(0) = 0.0f;
Weight alpha = (Weight) (2.0f * M_PI / valence);
for (int i = 0; i < valence; ++i) {
double alphaI = alpha * i;
tan1Mask.EdgeWeight(i) = std::cos(alphaI);
tan2Mask.EdgeWeight(i) = std::sin(alphaI);
if (valence == 6) {
static Weight const Root3by2 = (Weight)(0.5f * 1.73205080756887729352f);
tan1Mask.EdgeWeight(0) = 1.0f;
tan1Mask.EdgeWeight(1) = 0.5f;
tan1Mask.EdgeWeight(2) = -0.5f;
tan1Mask.EdgeWeight(3) = -1.0f;
tan1Mask.EdgeWeight(4) = -0.5f;
tan1Mask.EdgeWeight(5) = 0.5f;
tan2Mask.EdgeWeight(0) = 0.0f;
tan2Mask.EdgeWeight(1) = Root3by2;
tan2Mask.EdgeWeight(2) = Root3by2;
tan2Mask.EdgeWeight(3) = 0.0f;
tan2Mask.EdgeWeight(4) = -Root3by2;
tan2Mask.EdgeWeight(5) = -Root3by2;
} else {
Weight alpha = (Weight) (2.0f * M_PI / valence);
for (int i = 0; i < valence; ++i) {
double alphaI = alpha * i;
tan1Mask.EdgeWeight(i) = std::cos(alphaI);
tan2Mask.EdgeWeight(i) = std::sin(alphaI);
}
}
}

View File

@ -115,21 +115,22 @@ public:
Crease::Rule childRule = Crease::RULE_UNKNOWN) const;
///
/// \brief IN PROGRESS -- NOT YET FULLY FUNCTIONAL...
///
/// Masks for limit points and tangents -- note that these require the vertex be
/// suitably isolated such that its limit is well-defined.
///
/// These are stubs that are still being completed. The position masks are now
/// supported but tangent masks need work.
/// \brief Limit masks for vertices -- position and tangents
/// These presume that a vertex is suitably isolated for its limit to be well-defined
/// and, unlike the refinement masks, the subdivision Rule for the vertex (presumably at
/// its last level of refinement) is required rather than being optional. In the
/// presence of semi-sharp creasing that has not decayed to zero, the limit is unknown
/// and it is up to the caller to provide the Rule for either a smooth or sharp limit in
/// such cases.
///
template <typename VERTEX, typename MASK>
void ComputeVertexLimitMask(VERTEX const& vertexNeighborhood, MASK& positionMask) const;
void ComputeVertexLimitMask(VERTEX const& vertexNeighborhood, MASK& positionMask,
Crease::Rule vertexRule) const;
template <typename VERTEX, typename MASK>
void ComputeVertexLimitMask(VERTEX const& vertexNeighborhood, MASK& positionMask,
MASK& tangent1Mask,
MASK& tangent2Mask) const;
MASK& tangent1Mask, MASK& tangent2Mask,
Crease::Rule vertexRule) const;
//
// Static methods defining traits/properties of the scheme:
@ -155,22 +156,26 @@ protected:
template <typename VERTEX, typename MASK>
void assignCornerMaskForVertex(VERTEX const& edge, MASK& mask) const;
template <typename VERTEX, typename MASK>
void assignCreaseMaskForVertex(VERTEX const& edge, MASK& mask, float const sharpness[]) const;
void assignCreaseMaskForVertex(VERTEX const& edge, MASK& mask, int const creaseEnds[2]) const;
template <typename VERTEX, typename MASK>
void assignSmoothMaskForVertex(VERTEX const& edge, MASK& mask) const;
//
// Limit masks for position and tangents -- boundary and interior cases for both:
// Limit masks for position and tangents at vertices -- three cases for each:
//
template <typename VERTEX, typename MASK>
void assignBoundaryLimitMask(VERTEX const& vertex, MASK& pos) const;
void assignCornerLimitMask(VERTEX const& vertex, MASK& pos) const;
template <typename VERTEX, typename MASK>
void assignInteriorLimitMask(VERTEX const& vertex, MASK& pos) const;
void assignCreaseLimitMask(VERTEX const& vertex, MASK& pos, int const creaseEnds[2]) const;
template <typename VERTEX, typename MASK>
void assignSmoothLimitMask(VERTEX const& vertex, MASK& pos) const;
template <typename VERTEX, typename MASK>
void assignBoundaryLimitTangentMasks(VERTEX const& vertex, MASK& tan1, MASK& tan2) const;
void assignCornerLimitTangentMasks(VERTEX const& vertex, MASK& tan1, MASK& tan2) const;
template <typename VERTEX, typename MASK>
void assignInteriorLimitTangentMasks(VERTEX const& vertex, MASK& tan1, MASK& tan2) const;
void assignCreaseLimitTangentMasks(VERTEX const& vertex, MASK& tan1, MASK& tan2, int const creaseEnds[2]) const;
template <typename VERTEX, typename MASK>
void assignSmoothLimitTangentMasks(VERTEX const& vertex, MASK& tan1, MASK& tan2) const;
private:
Options _options;
@ -521,15 +526,17 @@ Scheme<SCHEME>::ComputeVertexVertexMask(VERTEX const& vertex,
pEdgeSharpness = vertex.GetSharpnessPerEdge(pEdgeSharpnessBuffer);
if (pRule == Crease::RULE_UNKNOWN) {
Crease crease(_options);
pRule = crease.DetermineVertexVertexRule(pVertexSharpness, valence, pEdgeSharpness);
pRule = Crease(_options).DetermineVertexVertexRule(pVertexSharpness, valence, pEdgeSharpness);
}
}
if ((pRule == Crease::RULE_SMOOTH) || (pRule == Crease::RULE_DART)) {
assignSmoothMaskForVertex(vertex, mask);
return; // As done on entry, we can return immediately if parent is Smooth/Dart
} else if (pRule == Crease::RULE_CREASE) {
assignCreaseMaskForVertex(vertex, mask, pEdgeSharpness);
int creaseEnds[2];
Crease(_options).GetSharpEdgePairOfCrease(pEdgeSharpness, valence, creaseEnds);
assignCreaseMaskForVertex(vertex, mask, creaseEnds);
} else {
assignCornerMaskForVertex(vertex, mask);
}
@ -561,7 +568,10 @@ Scheme<SCHEME>::ComputeVertexVertexMask(VERTEX const& vertex,
if ((cRule == Crease::RULE_SMOOTH) || (cRule == Crease::RULE_DART)) {
assignSmoothMaskForVertex(vertex, cMask);
} else if (cRule == Crease::RULE_CREASE) {
assignCreaseMaskForVertex(vertex, cMask, cEdgeSharpness);
int creaseEnds[2];
Crease(_options).GetSharpEdgePairOfCrease(cEdgeSharpness, valence, creaseEnds);
assignCreaseMaskForVertex(vertex, cMask, creaseEnds);
} else {
assignCornerMaskForVertex(vertex, cMask);
}
@ -580,15 +590,21 @@ template <SchemeType SCHEME>
template <typename VERTEX, typename MASK>
void
Scheme<SCHEME>::ComputeVertexLimitMask(VERTEX const& vertex,
MASK& mask) const {
MASK& mask,
Crease::Rule rule) const {
if (vertex.GetNumFaces() == vertex.GetNumEdges()) {
assignInteriorLimitMask(vertex, mask);
} else if ((vertex.GetNumFaces() == 1) &&
(_options.GetVtxBoundaryInterpolation() == Sdc::Options::VTX_BOUNDARY_EDGE_AND_CORNER)) {
assignCornerMaskForVertex(vertex, mask);
if ((rule == Crease::RULE_SMOOTH) || (rule == Crease::RULE_DART)) {
assignSmoothLimitMask(vertex, mask);
} else if (rule == Crease::RULE_CREASE) {
float * edgeSharpness = (float *)alloca(vertex.GetNumEdges() * sizeof(float));
vertex.GetSharpnessPerEdge(edgeSharpness);
int creaseEnds[2];
Crease(_options).GetSharpEdgePairOfCrease(edgeSharpness, vertex.GetNumEdges(), creaseEnds);
assignCreaseLimitMask(vertex, mask, creaseEnds);
} else {
assignBoundaryLimitMask(vertex, mask);
assignCornerLimitMask(vertex, mask);
}
}
@ -598,14 +614,24 @@ void
Scheme<SCHEME>::ComputeVertexLimitMask(VERTEX const& vertex,
MASK& posMask,
MASK& tan1Mask,
MASK& tan2Mask) const {
MASK& tan2Mask,
Crease::Rule rule) const {
if (vertex.GetNumFaces() == vertex.GetNumEdges()) {
assignInteriorLimitMask(vertex, posMask);
assignInteriorLimitTangentMasks(vertex, tan1Mask, tan2Mask);
if ((rule == Crease::RULE_SMOOTH) || (rule == Crease::RULE_DART)) {
assignSmoothLimitMask(vertex, posMask);
assignSmoothLimitTangentMasks(vertex, tan1Mask, tan2Mask);
} else if (rule == Crease::RULE_CREASE) {
float * edgeSharpness = (float *)alloca(vertex.GetNumEdges() * sizeof(float));
vertex.GetSharpnessPerEdge(edgeSharpness);
int creaseEnds[2];
Crease(_options).GetSharpEdgePairOfCrease(edgeSharpness, vertex.GetNumEdges(), creaseEnds);
assignCreaseLimitMask(vertex, posMask, creaseEnds);
assignCreaseLimitTangentMasks(vertex, tan1Mask, tan2Mask, creaseEnds);
} else {
assignBoundaryLimitMask(vertex, posMask);
assignBoundaryLimitTangentMasks(vertex, tan1Mask, tan2Mask);
assignCornerLimitMask(vertex, posMask);
assignCornerLimitTangentMasks(vertex, tan1Mask, tan2Mask);
}
}