// // 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_LOOP_SCHEME_H #define OPENSUBDIV3_SDC_LOOP_SCHEME_H #include "../version.h" #include "../sdc/scheme.h" #include #include namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { namespace Sdc { // // Specializations for Sdc::Scheme: // // // // Loop traits: // template <> inline Split Scheme::GetTopologicalSplitType() { return SPLIT_TO_TRIS; } template <> inline int Scheme::GetRegularFaceSize() { return 3; } template <> inline int Scheme::GetRegularVertexValence() { return 6; } template <> inline int Scheme::GetLocalNeighborhoodSize() { return 1; } // // Protected methods to assign the two types of masks for an edge-vertex -- // Crease and Smooth. // // The Crease case does not really need to be specialized, though it may be // preferable to define all explicitly here. // template <> template inline void 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 <> template inline void Scheme::assignSmoothMaskForEdge(EDGE const& edge, MASK& mask) const { int faceCount = edge.GetNumFaces(); mask.SetNumVertexWeights(2); mask.SetNumEdgeWeights(0); mask.SetNumFaceWeights(faceCount); mask.SetFaceWeightsForFaceCenters(false); // // This is where we run into the issue of "face weights" -- we want to weight the // face-centers for Catmark, but face-centers are not generated for Loop. So do // we make assumptions on how the mask is used, assign some property to the mask // to indicate how they were assigned, or take input from the mask itself? // // Regardless, we have two choices: // - face-weights are for the vertices opposite the edge (as in Hbr): // vertex weights = 0.375f; // face weights = 0.125f; // // - face-weights are for the face centers: // vertex weights = 0.125f; // face weights = 0.375f; // // Coincidentally the coefficients are the same but reversed. // typedef typename MASK::Weight Weight; Weight vWeight = mask.AreFaceWeightsForFaceCenters() ? 0.125f : 0.375f; Weight fWeight = mask.AreFaceWeightsForFaceCenters() ? 0.375f : 0.125f; mask.VertexWeight(0) = vWeight; mask.VertexWeight(1) = vWeight; if (faceCount == 2) { mask.FaceWeight(0) = fWeight; mask.FaceWeight(1) = fWeight; } else { // The non-manifold case is not clearly defined -- we adjust the above // face-weight to preserve the ratio of edge-center and face-centers: fWeight *= 2.0f / (Weight) faceCount; for (int i = 0; i < faceCount; ++i) { mask.FaceWeight(i) = fWeight; } } } // // Protected methods to assign the three types of masks for a vertex-vertex -- // Corner, Crease and Smooth (Dart is the same as Smooth). // // Corner and Crease do not really need to be specialized, though it may be // preferable to define all explicitly here. // template <> template inline void Scheme::assignCornerMaskForVertex(VERTEX const&, MASK& mask) const { mask.SetNumVertexWeights(1); mask.SetNumEdgeWeights(0); mask.SetNumFaceWeights(0); mask.SetFaceWeightsForFaceCenters(false); mask.VertexWeight(0) = 1.0f; } template <> template inline void Scheme::assignCreaseMaskForVertex(VERTEX const& vertex, MASK& mask, int const creaseEnds[2]) const { typedef typename MASK::Weight Weight; int valence = vertex.GetNumEdges(); mask.SetNumVertexWeights(1); mask.SetNumEdgeWeights(valence); mask.SetNumFaceWeights(0); mask.SetFaceWeightsForFaceCenters(false); Weight vWeight = 0.75f; Weight eWeight = 0.125f; mask.VertexWeight(0) = vWeight; for (int i = 0; i < valence; ++i) { mask.EdgeWeight(i) = 0.0f; } mask.EdgeWeight(creaseEnds[0]) = eWeight; mask.EdgeWeight(creaseEnds[1]) = eWeight; } template <> template inline void Scheme::assignSmoothMaskForVertex(VERTEX const& vertex, MASK& mask) const { typedef typename MASK::Weight Weight; int valence = vertex.GetNumFaces(); mask.SetNumVertexWeights(1); mask.SetNumEdgeWeights(valence); mask.SetNumFaceWeights(0); mask.SetFaceWeightsForFaceCenters(false); // Specialize for the regular case: 1/16 per edge-vert, 5/8 for the vert itself: Weight eWeight = (Weight) 0.0625f; Weight vWeight = (Weight) 0.625f; if (valence != 6) { // From HbrLoopSubdivision::Subdivide(mesh, vertex): // - could use some lookup tables here for common irregular valence (5, 7, 8) // or all of these cosine calls will be adding up... double dValence = (double) valence; double invValence = 1.0f / dValence; double cosTheta = std::cos(M_PI * 2.0f * invValence); double beta = 0.25f * cosTheta + 0.375f; eWeight = (Weight) ((0.625f - (beta * beta)) * invValence); vWeight = (Weight) (1.0f - (eWeight * dValence)); } mask.VertexWeight(0) = vWeight; for (int i = 0; i < valence; ++i) { mask.EdgeWeight(i) = eWeight; } } // // Limit masks for position: // template <> template inline void Scheme::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 inline void Scheme::assignCreaseLimitMask(VERTEX const& vertex, MASK& posMask, int const creaseEnds[2]) const { typedef typename MASK::Weight Weight; int valence = vertex.GetNumEdges(); posMask.SetNumVertexWeights(1); posMask.SetNumEdgeWeights(valence); 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 = (Weight) (4.0 / 6.0); Weight eWeight = (Weight) (1.0 / 6.0); posMask.VertexWeight(0) = vWeight; for (int i = 0; i < valence; ++i) { posMask.EdgeWeight(i) = 0.0f; } posMask.EdgeWeight(creaseEnds[0]) = eWeight; posMask.EdgeWeight(creaseEnds[1]) = eWeight; } template <> template inline void Scheme::assignSmoothLimitMask(VERTEX const& vertex, MASK& posMask) const { typedef typename MASK::Weight Weight; int valence = vertex.GetNumFaces(); posMask.SetNumVertexWeights(1); posMask.SetNumEdgeWeights(valence); posMask.SetNumFaceWeights(0); posMask.SetFaceWeightsForFaceCenters(false); // Specialize for the regular case: 1/12 per edge-vert, 1/2 for the vert itself: if (valence == 6) { Weight eWeight = (Weight) (1.0 / 12.0); Weight vWeight = 0.5f; posMask.VertexWeight(0) = vWeight; posMask.EdgeWeight(0) = eWeight; posMask.EdgeWeight(1) = eWeight; posMask.EdgeWeight(2) = eWeight; posMask.EdgeWeight(3) = eWeight; posMask.EdgeWeight(4) = eWeight; posMask.EdgeWeight(5) = eWeight; } else { double dValence = (double) valence; double invValence = 1.0f / dValence; double cosTheta = std::cos(M_PI * 2.0f * invValence); double beta = 0.25f * cosTheta + 0.375f; double gamma = (0.625f - (beta * beta)) * invValence; Weight eWeight = (Weight) (1.0f / (dValence + 3.0f / (8.0f * gamma))); Weight vWeight = (Weight) (1.0f - (eWeight * dValence)); posMask.VertexWeight(0) = vWeight; for (int i = 0; i < valence; ++i) { posMask.EdgeWeight(i) = eWeight; } } } /* // Limit masks for tangents: // // A note on tangent magnitudes: // // Several formulae exist for limit tangents at a vertex to accommodate 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 derivatives 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 give us coefficients that are multiples 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 ensure 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 inline void Scheme::assignCornerLimitTangentMasks(VERTEX const& vertex, MASK& tan1Mask, MASK& tan2Mask) 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); // 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 inline void Scheme::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 ambiguous 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.73205080756887729352; 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); tan2Mask.VertexWeight(0) = 0.0f; Weight cWeight = (Weight) (-3.0f * std::sin(theta)); tan2Mask.EdgeWeight(creaseEnds[0]) = cWeight; tan2Mask.EdgeWeight(creaseEnds[1]) = cWeight; double eCoeff = -3.0f * 2.0f * (std::cos(theta) - 1.0f); for (int i = 1; i <= interiorEdgeCount; ++i) { tan2Mask.EdgeWeight(creaseEnds[0] + i) = (Weight) (eCoeff * 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 inline void Scheme::assignSmoothLimitTangentMasks(VERTEX const& vertex, MASK& tan1Mask, MASK& tan2Mask) const { typedef typename MASK::Weight Weight; int valence = vertex.GetNumFaces(); tan1Mask.SetNumVertexWeights(1); tan1Mask.SetNumEdgeWeights(valence); tan1Mask.SetNumFaceWeights(0); tan1Mask.SetFaceWeightsForFaceCenters(false); tan2Mask.SetNumVertexWeights(1); tan2Mask.SetNumEdgeWeights(valence); tan2Mask.SetNumFaceWeights(0); tan2Mask.SetFaceWeightsForFaceCenters(false); tan1Mask.VertexWeight(0) = 0.0f; tan2Mask.VertexWeight(0) = 0.0f; if (valence == 6) { static Weight const Root3by2 = (Weight)(0.5 * 1.73205080756887729352); 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 { double alpha = 2.0f * M_PI / valence; for (int i = 0; i < valence; ++i) { double alphaI = alpha * i; tan1Mask.EdgeWeight(i) = (Weight) std::cos(alphaI); tan2Mask.EdgeWeight(i) = (Weight) std::sin(alphaI); } } } } // end namespace Sdc } // end namespace OPENSUBDIV_VERSION using namespace OPENSUBDIV_VERSION; } // end namespace OpenSubdiv #endif /* OPENSUBDIV3_SDC_LOOP_SCHEME_H */