mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-11-28 14:21:07 +00:00
392e5e8bed
While this may be worth revisiting, we should first quantify the benefits and identify the compilers that support it. Ultimately, we may never use pragma once in favor of strictly using standard C++.
661 lines
28 KiB
C++
661 lines
28 KiB
C++
//
|
|
// Copyright 2014 DreamWorks Animation LLC.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "Apache License")
|
|
// with the following modification; you may not use this file except in
|
|
// compliance with the Apache License and the following modification to it:
|
|
// Section 6. Trademarks. is deleted and replaced with:
|
|
//
|
|
// 6. Trademarks. This License does not grant permission to use the trade
|
|
// names, trademarks, service marks, or product names of the Licensor
|
|
// and its affiliates, except as required to comply with Section 4(c) of
|
|
// the License and to reproduce the content of the NOTICE file.
|
|
//
|
|
// You may obtain a copy of the Apache License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the Apache License with the above modification is
|
|
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the Apache License for the specific
|
|
// language governing permissions and limitations under the Apache License.
|
|
//
|
|
#ifndef OPENSUBDIV3_SDC_SCHEME_H
|
|
#define OPENSUBDIV3_SDC_SCHEME_H
|
|
|
|
#include "../version.h"
|
|
|
|
#include "../sdc/types.h"
|
|
#include "../sdc/options.h"
|
|
#include "../sdc/crease.h"
|
|
|
|
#include <cassert>
|
|
#include <cstdlib>
|
|
#include <vector>
|
|
|
|
namespace OpenSubdiv {
|
|
namespace OPENSUBDIV_VERSION {
|
|
|
|
namespace Sdc {
|
|
|
|
///
|
|
/// \brief Scheme is a class template which provides all implementation for the
|
|
/// subdivision schemes supported by OpenSubdiv through specializations of the
|
|
/// methods of each. An instance of Scheme<SCHEME_TYPE> includes a set of Options
|
|
/// that will dictate the variable aspects of its behavior.
|
|
///
|
|
/// The primary purpose of Scheme is to provide the mask weights for vertices
|
|
/// generated by subdivision. Methods to determine the masks are given topological
|
|
/// neighborhoods from which to compute the appropriate weights for neighboring
|
|
/// components. While these neighborhoods may require sharpness values for
|
|
/// creasing, the computation of subdivided crease values is independent of the
|
|
/// scheme type and is also made available through the Crease class.
|
|
///
|
|
/// Mask queries are assisted by two utility classes -- a Neighborhood class
|
|
/// defining the set of relevant data in the topological neighborhood of the vertex
|
|
/// being subdivided, and a Mask class into which the associated mask weights will
|
|
/// be stored. Depending on where and how these queries are used, more or less
|
|
/// information may be available. See the details of the Neighborhood classes as
|
|
/// appropriate initialization of them is critical. It is generally best to
|
|
/// initialize them with what data is known and accessible for immediate and
|
|
/// efficient retrieval, but subclasses can be created to gather it lazily if
|
|
/// desired.
|
|
///
|
|
template <SchemeType SCHEME_TYPE>
|
|
class Scheme {
|
|
|
|
public:
|
|
|
|
Scheme() : _options() { }
|
|
|
|
Scheme(Options const& options) : _options(options) { }
|
|
|
|
Options GetOptions() const { return _options; }
|
|
void SetOptions(const Options& newOptions) { _options = newOptions; }
|
|
|
|
///
|
|
/// \brief Face-vertex masks - trivial for all current schemes
|
|
///
|
|
template <typename FACE, typename MASK>
|
|
void ComputeFaceVertexMask(FACE const& faceNeighborhood, MASK& faceVertexMask) const;
|
|
|
|
///
|
|
/// \brief Edge-vertex masks
|
|
/// If known, the Rule for the edge and/or the derived vertex can be specified to
|
|
/// accelerate the computation (though the Rule for the parent is trivially determined).
|
|
/// In particular, knowing the child rule can avoid the need to subdivide the sharpness
|
|
/// of the edge to see if it is a transitional crease that warrants fractional blending.
|
|
///
|
|
/// Whether to use the "Rules" in this interface is really debatable -- the parent Rule
|
|
/// is really based on the edge and its sharpness, while the child Rule is technically
|
|
/// based on the neighborhood of the child vertex, but it can be deduced from the two
|
|
/// child edges' sharpness. So the Crease methods used to compute these rules differ
|
|
/// from those for the vertex-vertex mask. Perhaps a simple pair of new methods for
|
|
/// Crease should be added specific to the edge-vertex case, i.e. one that takes a
|
|
/// single sharpness (for the parent rule) and one that takes a pair (for the child).
|
|
///
|
|
template <typename EDGE, typename MASK>
|
|
void ComputeEdgeVertexMask(EDGE const& edgeNeighborhood, MASK& edgeVertexMask,
|
|
Crease::Rule parentRule = Crease::RULE_UNKNOWN,
|
|
Crease::Rule childRule = Crease::RULE_UNKNOWN) const;
|
|
|
|
///
|
|
/// \brief Vertex-vertex masks
|
|
/// If known, a single Rule or pair of Rules can be specified (indicating a crease
|
|
/// transition) to accelerate the computation. Either no Rules, the first, or both should
|
|
/// be specified. Specification of only the first Rule implies it to be true for both
|
|
/// (wish the compiler would allow such default value specification), i.e. no transition.
|
|
/// The case of knowing the parent Rule but deferring determination of the child Rule to
|
|
/// this method is not supported.
|
|
///
|
|
template <typename VERTEX, typename MASK>
|
|
void ComputeVertexVertexMask(VERTEX const& vertexNeighborhood, MASK& vertexVertexMask,
|
|
Crease::Rule parentRule = Crease::RULE_UNKNOWN,
|
|
Crease::Rule childRule = Crease::RULE_UNKNOWN) const;
|
|
|
|
///
|
|
/// \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 neither
|
|
/// sharp nor smooth -- in such cases the Rule specified by the caller determines the
|
|
/// result.
|
|
///
|
|
/// For tangent masks, the direction of the first tangent (T1) is oriented towards the
|
|
/// leading edge of the vertex, i.e. the first incident edge of the vertex (beginning
|
|
/// the set of incident edges in counter-clockwise order). The second tangent (T2) lies
|
|
/// within the tangent plane such that its normal can be computed as T1 x T2. So for a
|
|
/// boundary vertex, T1 will point along the boundary in the direction of the leading
|
|
/// edge while T2 points inward across the limit surface.
|
|
///
|
|
/// As for magnitude, no assumptions should be made of the magnitudes of the resulting
|
|
/// tanget vectors. Common formulae often factor out scale factors that contribute to
|
|
/// magnitude. While some attempt has been made to make magnitudes more consistent
|
|
/// between regular corners, boundaries and the interior, the same has not been done at
|
|
/// irregular vertices -- at least not yet. This may be addressed in future, as having
|
|
/// consistent magnitudes returned here can aid in the construction of patches from
|
|
/// limit positions and tangents.
|
|
///
|
|
template <typename VERTEX, typename MASK>
|
|
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,
|
|
Crease::Rule vertexRule) const;
|
|
|
|
//
|
|
// Static methods defining traits/properties of the scheme:
|
|
//
|
|
static Split GetTopologicalSplitType();
|
|
static int GetRegularFaceSize();
|
|
static int GetRegularVertexValence();
|
|
static int GetLocalNeighborhoodSize();
|
|
|
|
protected:
|
|
|
|
//
|
|
// Supporting internal methods -- optionally implemented, depending on specialization:
|
|
//
|
|
|
|
// Subdivision/refinement masks -- two for edge-vertices and three for vertex-vertices:
|
|
//
|
|
template <typename EDGE, typename MASK>
|
|
void assignCreaseMaskForEdge(EDGE const& edge, MASK& mask) const;
|
|
template <typename EDGE, typename MASK>
|
|
void assignSmoothMaskForEdge(EDGE const& edge, MASK& mask) const;
|
|
|
|
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, int const creaseEnds[2]) const;
|
|
template <typename VERTEX, typename MASK>
|
|
void assignSmoothMaskForVertex(VERTEX const& edge, MASK& mask) const;
|
|
|
|
//
|
|
// Limit masks for position and tangents at vertices -- three cases for each:
|
|
//
|
|
template <typename VERTEX, typename MASK>
|
|
void assignCornerLimitMask(VERTEX const& vertex, MASK& pos) const;
|
|
template <typename VERTEX, typename MASK>
|
|
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 assignCornerLimitTangentMasks(VERTEX const& vertex, MASK& tan1, MASK& tan2) const;
|
|
template <typename VERTEX, typename MASK>
|
|
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;
|
|
|
|
protected:
|
|
|
|
//
|
|
// Internal implementation support:
|
|
//
|
|
// We need a local "mask" class to be declared locally within the vertex-vertex mask query
|
|
// to hold one of the two possible mask required and to combine the local mask with the mask
|
|
// the caller provides. It has been parameterized by <WEIGHT> so that a version compatible
|
|
// with the callers mask class is created.
|
|
//
|
|
template <typename WEIGHT>
|
|
class LocalMask {
|
|
|
|
public:
|
|
typedef WEIGHT Weight;
|
|
|
|
public:
|
|
LocalMask(Weight* v, Weight* e, Weight* f) : _vWeights(v), _eWeights(e), _fWeights(f) { }
|
|
~LocalMask() { }
|
|
|
|
public:
|
|
|
|
//
|
|
// Methods required for general mask assignments and queries:
|
|
//
|
|
int GetNumVertexWeights() const { return _vCount; }
|
|
int GetNumEdgeWeights() const { return _eCount; }
|
|
int GetNumFaceWeights() const { return _fCount; }
|
|
|
|
void SetNumVertexWeights(int count) { _vCount = count; }
|
|
void SetNumEdgeWeights( int count) { _eCount = count; }
|
|
void SetNumFaceWeights( int count) { _fCount = count; }
|
|
|
|
Weight const& VertexWeight(int index) const { return _vWeights[index]; }
|
|
Weight const& EdgeWeight( int index) const { return _eWeights[index]; }
|
|
Weight const& FaceWeight( int index) const { return _fWeights[index]; }
|
|
|
|
Weight& VertexWeight(int index) { return _vWeights[index]; }
|
|
Weight& EdgeWeight( int index) { return _eWeights[index]; }
|
|
Weight& FaceWeight( int index) { return _fWeights[index]; }
|
|
|
|
bool AreFaceWeightsForFaceCenters() const { return _fWeightsForCenters; }
|
|
void SetFaceWeightsForFaceCenters(bool on) { _fWeightsForCenters = on; }
|
|
|
|
public:
|
|
|
|
//
|
|
// Additional methods -- mainly the blending method for vertex-vertex masks:
|
|
//
|
|
template <typename USER_MASK>
|
|
inline void
|
|
CombineVertexVertexMasks(Weight thisCoeff, Weight dstCoeff, USER_MASK& dst) const {
|
|
|
|
//
|
|
// This implementation is convoluted by the potential sparsity of each mask. Since
|
|
// it is specific to a vertex-vertex mask, we are guaranteed to have exactly one
|
|
// vertex-weight for both masks, but the edge- and face-weights are optional. The
|
|
// child mask (the "source") should have a superset of the weights of the parent
|
|
// (the "destination") given its reduced sharpness, so we fortunately don't need to
|
|
// test all permutations.
|
|
//
|
|
dst.VertexWeight(0) = dstCoeff * dst.VertexWeight(0) + thisCoeff * this->VertexWeight(0);
|
|
|
|
int edgeWeightCount = this->GetNumEdgeWeights();
|
|
if (edgeWeightCount) {
|
|
if (dst.GetNumEdgeWeights() == 0) {
|
|
dst.SetNumEdgeWeights(edgeWeightCount);
|
|
for (int i = 0; i < edgeWeightCount; ++i) {
|
|
dst.EdgeWeight(i) = thisCoeff * this->EdgeWeight(i);
|
|
}
|
|
} else {
|
|
for (int i = 0; i < edgeWeightCount; ++i) {
|
|
dst.EdgeWeight(i) = dstCoeff * dst.EdgeWeight(i) + thisCoeff * this->EdgeWeight(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
int faceWeightCount = this->GetNumFaceWeights();
|
|
if (faceWeightCount) {
|
|
//
|
|
// If combining face weights, be sure their interpretation (i.e. face-centers
|
|
// or opposite vertices) is properly set in the destination mask:
|
|
//
|
|
if (dst.GetNumFaceWeights() == 0) {
|
|
dst.SetNumFaceWeights(faceWeightCount);
|
|
dst.SetFaceWeightsForFaceCenters(this->AreFaceWeightsForFaceCenters());
|
|
|
|
for (int i = 0; i < faceWeightCount; ++i) {
|
|
dst.FaceWeight(i) = thisCoeff * this->FaceWeight(i);
|
|
}
|
|
} else {
|
|
assert(this->AreFaceWeightsForFaceCenters() == dst.AreFaceWeightsForFaceCenters());
|
|
|
|
for (int i = 0; i < faceWeightCount; ++i) {
|
|
dst.FaceWeight(i) = dstCoeff * dst.FaceWeight(i) + thisCoeff * this->FaceWeight(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
Weight* _vWeights;
|
|
Weight* _eWeights;
|
|
Weight* _fWeights;
|
|
int _vCount;
|
|
int _eCount;
|
|
int _fCount;
|
|
bool _fWeightsForCenters;
|
|
};
|
|
};
|
|
|
|
|
|
//
|
|
// Crease and corner masks are common to most schemes -- the rest need to be provided
|
|
// for each Scheme specialization.
|
|
//
|
|
template <SchemeType SCHEME>
|
|
template <typename EDGE, typename MASK>
|
|
inline void
|
|
Scheme<SCHEME>::assignCreaseMaskForEdge(EDGE const&, MASK& mask) const {
|
|
|
|
mask.SetNumVertexWeights(2);
|
|
mask.SetNumEdgeWeights(0);
|
|
mask.SetNumFaceWeights(0);
|
|
mask.SetFaceWeightsForFaceCenters(false);
|
|
|
|
mask.VertexWeight(0) = 0.5f;
|
|
mask.VertexWeight(1) = 0.5f;
|
|
}
|
|
|
|
template <SchemeType SCHEME>
|
|
template <typename VERTEX, typename MASK>
|
|
inline void
|
|
Scheme<SCHEME>::assignCornerMaskForVertex(VERTEX const&, MASK& mask) const {
|
|
|
|
mask.SetNumVertexWeights(1);
|
|
mask.SetNumEdgeWeights(0);
|
|
mask.SetNumFaceWeights(0);
|
|
mask.SetFaceWeightsForFaceCenters(false);
|
|
|
|
mask.VertexWeight(0) = 1.0f;
|
|
}
|
|
|
|
|
|
//
|
|
// The computation of a face-vertex mask is trivial and consistent for all schemes:
|
|
//
|
|
template <SchemeType SCHEME>
|
|
template <typename FACE, typename MASK>
|
|
void
|
|
Scheme<SCHEME>::ComputeFaceVertexMask(FACE const& face, MASK& mask) const {
|
|
|
|
int vertCount = face.GetNumVertices();
|
|
|
|
mask.SetNumVertexWeights(vertCount);
|
|
mask.SetNumEdgeWeights(0);
|
|
mask.SetNumFaceWeights(0);
|
|
mask.SetFaceWeightsForFaceCenters(false);
|
|
|
|
typename MASK::Weight vWeight = 1.0f / (typename MASK::Weight) vertCount;
|
|
for (int i = 0; i < vertCount; ++i) {
|
|
mask.VertexWeight(i) = vWeight;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// The computation of an edge-vertex mask requires inspection of sharpness values to
|
|
// determine if smooth or a crease, and also to detect and apply a transition from a
|
|
// crease to smooth. Using the protected methods to assign the specific masks (only
|
|
// two -- smooth or crease) this implementation should serve all non-linear schemes
|
|
// (currently Catmark and Loop) and only need to be specialized it for Bilinear to
|
|
// trivialize it to the crease case.
|
|
//
|
|
// The implementation here is slightly complicated by combining two scenarios into a
|
|
// single implementation -- either the caller knows the parent and child rules and
|
|
// provides them, or they don't and the Rules have to be determined from sharpness
|
|
// values. Both cases include quick return once the parent is determined to be
|
|
// smooth or the child a crease, leaving the transitional case remaining.
|
|
//
|
|
// The overall process is as follows:
|
|
//
|
|
// - quickly detect the most common specified or detected Smooth case and return
|
|
// - quickly detect a full Crease by child Rule assignment and return
|
|
// - determine from sharpness if unspecified child is a crease -- return if so
|
|
// - compute smooth mask for child and combine with crease from parent
|
|
//
|
|
// Usage of the parent Rule here allows some misuse in that only three of five possible
|
|
// assignments are legitimate for the parent and four for the child (Dart being only
|
|
// valid for the child and Corner for neither). Results are undefined in these cases.
|
|
//
|
|
template <SchemeType SCHEME>
|
|
template <typename EDGE, typename MASK>
|
|
void
|
|
Scheme<SCHEME>::ComputeEdgeVertexMask(EDGE const& edge,
|
|
MASK& mask,
|
|
Crease::Rule parentRule,
|
|
Crease::Rule childRule) const {
|
|
|
|
//
|
|
// If the parent was specified or determined to be Smooth, we can quickly return
|
|
// with a Smooth mask. Otherwise the parent is a crease -- if the child was
|
|
// also specified to be a crease, we can quickly return with a Crease mask.
|
|
//
|
|
if ((parentRule == Crease::RULE_SMOOTH) ||
|
|
((parentRule == Crease::RULE_UNKNOWN) && (edge.GetSharpness() <= 0.0f))) {
|
|
assignSmoothMaskForEdge(edge, mask);
|
|
return;
|
|
}
|
|
if (childRule == Crease::RULE_CREASE) {
|
|
assignCreaseMaskForEdge(edge, mask);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We have a Crease on the parent and the child was either specified as Smooth
|
|
// or was not specified at all -- deal with the unspecified case first (again
|
|
// returning a Crease mask if the child is also determined to be a Crease) and
|
|
// continue if we have a transition to Smooth.
|
|
//
|
|
// Note when qualifying the child that if the parent sharpness > 1.0, regardless
|
|
// of the creasing method, whether the child sharpness values decay to zero is
|
|
// irrelevant -- the fractional weight for such a case (the value of the parent
|
|
// sharpness) is > 1.0, and when clamped to 1 effectively yields a full crease.
|
|
//
|
|
if (childRule == Crease::RULE_UNKNOWN) {
|
|
Crease crease(_options);
|
|
|
|
bool childIsCrease = false;
|
|
if (parentRule == Crease::RULE_CREASE) {
|
|
// Child unknown as default value but parent Rule specified as Crease
|
|
childIsCrease = true;
|
|
} else if (edge.GetSharpness() >= 1.0f) {
|
|
// Sharpness >= 1.0 always a crease -- see note above
|
|
childIsCrease = true;
|
|
} else if (crease.IsUniform()) {
|
|
// Sharpness < 1.0 is guaranteed to decay to 0.0 for Uniform child edges
|
|
childIsCrease = false;
|
|
} else {
|
|
// Sharpness <= 1.0 does not necessarily decay to 0.0 for both child edges...
|
|
float cEdgeSharpness[2];
|
|
edge.GetChildSharpnesses(crease, cEdgeSharpness);
|
|
childIsCrease = (cEdgeSharpness[0] > 0.0f) && (cEdgeSharpness[1] > 0.0f);
|
|
}
|
|
if (childIsCrease) {
|
|
assignCreaseMaskForEdge(edge, mask);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We are now left with have the Crease-to-Smooth case -- compute the Smooth mask
|
|
// for the child and augment it with the transitional Crease of the parent.
|
|
//
|
|
// A general combination of separately assigned masks here (as done in the vertex-
|
|
// vertex case) is overkill -- trivially combine the 0.5f vertex coefficient for
|
|
// the Crease of the parent with the vertex weights and attenuate the face weights
|
|
// accordingly.
|
|
//
|
|
assignSmoothMaskForEdge(edge, mask);
|
|
|
|
typedef typename MASK::Weight Weight;
|
|
|
|
Weight pWeight = edge.GetSharpness();
|
|
Weight cWeight = 1.0f - pWeight;
|
|
|
|
mask.VertexWeight(0) = pWeight * 0.5f + cWeight * mask.VertexWeight(0);
|
|
mask.VertexWeight(1) = pWeight * 0.5f + cWeight * mask.VertexWeight(1);
|
|
|
|
int faceCount = mask.GetNumFaceWeights();
|
|
for (int i = 0; i < faceCount; ++i) {
|
|
mask.FaceWeight(i) *= cWeight;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The computation of a vertex-vertex mask requires inspection of creasing sharpness values
|
|
// to determine what subdivision Rules apply to the parent and its child vertex, and also to
|
|
// detect and apply a transition between two differing Rules. Using the protected methods to
|
|
// assign specific masks, this implementation should serve all non-linear schemes (currently
|
|
// Catmark and Loop) and only need to be specialized for Bilinear to remove all unnecessary
|
|
// complexity relating to creasing, Rules, etc.
|
|
//
|
|
// The implementation here is slightly complicated by combining two scenarios into one --
|
|
// either the caller knows the parent and child rules and provides them, or they don't and
|
|
// the Rules have to be determined from sharpness values. Even when the Rules are known and
|
|
// provided though, there are cases where the parent and child sharpness values need to be
|
|
// identified, so accounting for the unknown Rules too is not much of an added complication.
|
|
//
|
|
// The benefit of supporting specified Rules is that they can often often be trivially
|
|
// determined from context (e.g. a vertex derived from a face at a previous level will always
|
|
// be smooth) rather than more generally, and at greater cost, inspecting neighboring and
|
|
// they are often the same for parent and child.
|
|
//
|
|
// The overall process is as follows:
|
|
//
|
|
// - quickly detect the most common Smooth case when specified and return
|
|
// - determine if sharpness for parent is required and gather if so
|
|
// - if unspecified, determine the parent rule
|
|
// - assign mask for the parent rule -- returning if Smooth/Dart
|
|
// - return if child rule matches parent
|
|
// - gather sharpness for child to determine or combine child rule
|
|
// - if unspecified, determine the child rule, returning if it matches parent
|
|
// - assign local mask for child rule
|
|
// - combine local child mask with the parent mask
|
|
//
|
|
// Remember -- if the parent rule is specified but the child is not, this implies only one
|
|
// of the two optional rules was specified and is meant to indicate there is no transition,
|
|
// so the child rule should be assigned to be the same (wish the compiler would allow this
|
|
// in default value assignment).
|
|
//
|
|
template <SchemeType SCHEME>
|
|
template <typename VERTEX, typename MASK>
|
|
void
|
|
Scheme<SCHEME>::ComputeVertexVertexMask(VERTEX const& vertex,
|
|
MASK& mask,
|
|
Crease::Rule pRule,
|
|
Crease::Rule cRule) const {
|
|
|
|
// Quick assignment and return for the most common case:
|
|
if ((pRule == Crease::RULE_SMOOTH) || (pRule == Crease::RULE_DART)) {
|
|
assignSmoothMaskForVertex(vertex, mask);
|
|
return;
|
|
}
|
|
// If unspecified, assign the child rule to match the parent rule if specified:
|
|
if ((cRule == Crease::RULE_UNKNOWN) && (pRule != Crease::RULE_UNKNOWN)) {
|
|
cRule = pRule;
|
|
}
|
|
int valence = vertex.GetNumEdges();
|
|
|
|
//
|
|
// Determine if we need the parent edge sharpness values -- identify/gather if so
|
|
// and use it to compute the parent rule if unspecified:
|
|
//
|
|
float * pEdgeSharpnessBuffer = (float *)alloca(valence*sizeof(float)),
|
|
* pEdgeSharpness = 0,
|
|
pVertexSharpness = 0.0f;
|
|
|
|
bool requireParentSharpness = (pRule == Crease::RULE_UNKNOWN) ||
|
|
(pRule == Crease::RULE_CREASE) ||
|
|
(pRule != cRule);
|
|
if (requireParentSharpness) {
|
|
pVertexSharpness = vertex.GetSharpness();
|
|
pEdgeSharpness = vertex.GetSharpnessPerEdge(pEdgeSharpnessBuffer);
|
|
|
|
if (pRule == Crease::RULE_UNKNOWN) {
|
|
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) {
|
|
int creaseEnds[2];
|
|
Crease(_options).GetSharpEdgePairOfCrease(pEdgeSharpness, valence, creaseEnds);
|
|
|
|
assignCreaseMaskForVertex(vertex, mask, creaseEnds);
|
|
} else {
|
|
assignCornerMaskForVertex(vertex, mask);
|
|
}
|
|
if (cRule == pRule) return;
|
|
|
|
//
|
|
// Identify/gather child sharpness to combine masks for the two differing Rules:
|
|
//
|
|
Crease crease(_options);
|
|
|
|
float * cEdgeSharpnessBuffer = (float *)alloca(valence*sizeof(float)),
|
|
* cEdgeSharpness = vertex.GetChildSharpnessPerEdge(crease, cEdgeSharpnessBuffer),
|
|
cVertexSharpness = vertex.GetChildSharpness(crease);
|
|
|
|
if (cRule == Crease::RULE_UNKNOWN) {
|
|
cRule = crease.DetermineVertexVertexRule(cVertexSharpness, valence, cEdgeSharpness);
|
|
if (cRule == pRule) return;
|
|
}
|
|
|
|
//
|
|
// Intialize a local child mask, compute the fractional weight from parent and child
|
|
// sharpness values and combine the two masks:
|
|
//
|
|
typedef typename MASK::Weight Weight;
|
|
|
|
Weight * cMaskWeights = (Weight *)alloca((1 + 2 * valence)*sizeof(Weight));
|
|
LocalMask<Weight> cMask(cMaskWeights, cMaskWeights + 1, cMaskWeights + 1 + valence);
|
|
|
|
if ((cRule == Crease::RULE_SMOOTH) || (cRule == Crease::RULE_DART)) {
|
|
assignSmoothMaskForVertex(vertex, cMask);
|
|
} else if (cRule == Crease::RULE_CREASE) {
|
|
int creaseEnds[2];
|
|
Crease(_options).GetSharpEdgePairOfCrease(cEdgeSharpness, valence, creaseEnds);
|
|
|
|
assignCreaseMaskForVertex(vertex, cMask, creaseEnds);
|
|
} else {
|
|
assignCornerMaskForVertex(vertex, cMask);
|
|
}
|
|
|
|
Weight pWeight = crease.ComputeFractionalWeightAtVertex(pVertexSharpness, cVertexSharpness,
|
|
valence, pEdgeSharpness, cEdgeSharpness);
|
|
Weight cWeight = 1.0f - pWeight;
|
|
|
|
cMask.CombineVertexVertexMasks(cWeight, pWeight, mask);
|
|
}
|
|
|
|
//
|
|
// The computation of limit masks for vertices:
|
|
//
|
|
template <SchemeType SCHEME>
|
|
template <typename VERTEX, typename MASK>
|
|
void
|
|
Scheme<SCHEME>::ComputeVertexLimitMask(VERTEX const& vertex,
|
|
MASK& mask,
|
|
Crease::Rule rule) const {
|
|
|
|
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 {
|
|
assignCornerLimitMask(vertex, mask);
|
|
}
|
|
}
|
|
|
|
template <SchemeType SCHEME>
|
|
template <typename VERTEX, typename MASK>
|
|
void
|
|
Scheme<SCHEME>::ComputeVertexLimitMask(VERTEX const& vertex,
|
|
MASK& posMask,
|
|
MASK& tan1Mask,
|
|
MASK& tan2Mask,
|
|
Crease::Rule rule) const {
|
|
|
|
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 {
|
|
assignCornerLimitMask(vertex, posMask);
|
|
assignCornerLimitTangentMasks(vertex, tan1Mask, tan2Mask);
|
|
}
|
|
}
|
|
|
|
|
|
} // end namespace sdc
|
|
|
|
} // end namespace OPENSUBDIV_VERSION
|
|
using namespace OPENSUBDIV_VERSION;
|
|
} // end namespace OpenSubdiv
|
|
|
|
#endif /* OPENSUBDIV3_SDC_SCHEME_H */
|