1 //
2 //   Copyright 2014 DreamWorks Animation LLC.
3 //
4 //   Licensed under the Apache License, Version 2.0 (the "Apache License")
5 //   with the following modification; you may not use this file except in
6 //   compliance with the Apache License and the following modification to it:
7 //   Section 6. Trademarks. is deleted and replaced with:
8 //
9 //   6. Trademarks. This License does not grant permission to use the trade
10 //      names, trademarks, service marks, or product names of the Licensor
11 //      and its affiliates, except as required to comply with Section 4(c) of
12 //      the License and to reproduce the content of the NOTICE file.
13 //
14 //   You may obtain a copy of the Apache License at
15 //
16 //       http://www.apache.org/licenses/LICENSE-2.0
17 //
18 //   Unless required by applicable law or agreed to in writing, software
19 //   distributed under the Apache License with the above modification is
20 //   distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 //   KIND, either express or implied. See the Apache License for the specific
22 //   language governing permissions and limitations under the Apache License.
23 //
24 #ifndef OPENSUBDIV3_SDC_LOOP_SCHEME_H
25 #define OPENSUBDIV3_SDC_LOOP_SCHEME_H
26 
27 #include "../version.h"
28 
29 #include "../sdc/scheme.h"
30 
31 #include <cassert>
32 #include <cmath>
33 
34 namespace OpenSubdiv {
35 namespace OPENSUBDIV_VERSION {
36 namespace Sdc {
37 
38 
39 //
40 //  Specializations for Sdc::Scheme<SCHEME_LOOP>:
41 //
42 //
43 
44 //
45 //  Loop traits:
46 //
47 template <>
GetTopologicalSplitType()48 inline Split Scheme<SCHEME_LOOP>::GetTopologicalSplitType() { return SPLIT_TO_TRIS; }
49 
50 template <>
GetRegularFaceSize()51 inline int Scheme<SCHEME_LOOP>::GetRegularFaceSize() { return 3; }
52 
53 template <>
GetRegularVertexValence()54 inline int Scheme<SCHEME_LOOP>::GetRegularVertexValence() { return 6; }
55 
56 template <>
GetLocalNeighborhoodSize()57 inline int Scheme<SCHEME_LOOP>::GetLocalNeighborhoodSize() { return 1; }
58 
59 
60 //
61 //  Protected methods to assign the two types of masks for an edge-vertex --
62 //  Crease and Smooth.
63 //
64 //  The Crease case does not really need to be specialized, though it may be
65 //  preferable to define all explicitly here.
66 //
67 template <>
68 template <typename EDGE, typename MASK>
69 inline void
assignCreaseMaskForEdge(EDGE const &,MASK & mask)70 Scheme<SCHEME_LOOP>::assignCreaseMaskForEdge(EDGE const&, MASK& mask) const
71 {
72     mask.SetNumVertexWeights(2);
73     mask.SetNumEdgeWeights(0);
74     mask.SetNumFaceWeights(0);
75     mask.SetFaceWeightsForFaceCenters(false);
76 
77     mask.VertexWeight(0) = 0.5f;
78     mask.VertexWeight(1) = 0.5f;
79 }
80 
81 template <>
82 template <typename EDGE, typename MASK>
83 inline void
assignSmoothMaskForEdge(EDGE const & edge,MASK & mask)84 Scheme<SCHEME_LOOP>::assignSmoothMaskForEdge(EDGE const& edge, MASK& mask) const
85 {
86     int faceCount = edge.GetNumFaces();
87 
88     mask.SetNumVertexWeights(2);
89     mask.SetNumEdgeWeights(0);
90     mask.SetNumFaceWeights(faceCount);
91     mask.SetFaceWeightsForFaceCenters(false);
92 
93     //
94     //  This is where we run into the issue of "face weights" -- we want to weight the
95     //  face-centers for Catmark, but face-centers are not generated for Loop.  So do
96     //  we make assumptions on how the mask is used, assign some property to the mask
97     //  to indicate how they were assigned, or take input from the mask itself?
98     //
99     //  Regardless, we have two choices:
100     //      - face-weights are for the vertices opposite the edge (as in Hbr):
101     //          vertex weights = 0.375f;
102     //          face weights   = 0.125f;
103     //
104     //      - face-weights are for the face centers:
105     //          vertex weights = 0.125f;
106     //          face weights   = 0.375f;
107     //
108     //  Coincidentally the coefficients are the same but reversed.
109     //
110     typedef typename MASK::Weight Weight;
111 
112     Weight vWeight = mask.AreFaceWeightsForFaceCenters() ? 0.125f : 0.375f;
113     Weight fWeight = mask.AreFaceWeightsForFaceCenters() ? 0.375f : 0.125f;
114 
115     mask.VertexWeight(0) = vWeight;
116     mask.VertexWeight(1) = vWeight;
117 
118     if (faceCount == 2) {
119         mask.FaceWeight(0) = fWeight;
120         mask.FaceWeight(1) = fWeight;
121     } else {
122         //  The non-manifold case is not clearly defined -- we adjust the above
123         //  face-weight to preserve the ratio of edge-center and face-centers:
124         fWeight *= 2.0f / (Weight) faceCount;
125         for (int i = 0; i < faceCount; ++i) {
126             mask.FaceWeight(i) = fWeight;
127         }
128     }
129 }
130 
131 
132 //
133 //  Protected methods to assign the three types of masks for a vertex-vertex --
134 //  Corner, Crease and Smooth (Dart is the same as Smooth).
135 //
136 //  Corner and Crease do not really need to be specialized, though it may be
137 //  preferable to define all explicitly here.
138 //
139 template <>
140 template <typename VERTEX, typename MASK>
141 inline void
assignCornerMaskForVertex(VERTEX const &,MASK & mask)142 Scheme<SCHEME_LOOP>::assignCornerMaskForVertex(VERTEX const&, MASK& mask) const
143 {
144     mask.SetNumVertexWeights(1);
145     mask.SetNumEdgeWeights(0);
146     mask.SetNumFaceWeights(0);
147     mask.SetFaceWeightsForFaceCenters(false);
148 
149     mask.VertexWeight(0) = 1.0f;
150 }
151 
152 template <>
153 template <typename VERTEX, typename MASK>
154 inline void
assignCreaseMaskForVertex(VERTEX const & vertex,MASK & mask,int const creaseEnds[2])155 Scheme<SCHEME_LOOP>::assignCreaseMaskForVertex(VERTEX const& vertex, MASK& mask,
156                                                int const creaseEnds[2]) const {
157     typedef typename MASK::Weight Weight;
158 
159     int valence = vertex.GetNumEdges();
160 
161     mask.SetNumVertexWeights(1);
162     mask.SetNumEdgeWeights(valence);
163     mask.SetNumFaceWeights(0);
164     mask.SetFaceWeightsForFaceCenters(false);
165 
166     Weight vWeight = 0.75f;
167     Weight eWeight = 0.125f;
168 
169     mask.VertexWeight(0) = vWeight;
170     for (int i = 0; i < valence; ++i) {
171         mask.EdgeWeight(i) = 0.0f;
172     }
173     mask.EdgeWeight(creaseEnds[0]) = eWeight;
174     mask.EdgeWeight(creaseEnds[1]) = eWeight;
175 }
176 
177 template <>
178 template <typename VERTEX, typename MASK>
179 inline void
assignSmoothMaskForVertex(VERTEX const & vertex,MASK & mask)180 Scheme<SCHEME_LOOP>::assignSmoothMaskForVertex(VERTEX const& vertex, MASK& mask) const
181 {
182     typedef typename MASK::Weight Weight;
183 
184     int valence = vertex.GetNumFaces();
185 
186     mask.SetNumVertexWeights(1);
187     mask.SetNumEdgeWeights(valence);
188     mask.SetNumFaceWeights(0);
189     mask.SetFaceWeightsForFaceCenters(false);
190 
191     //  Specialize for the regular case:  1/16 per edge-vert, 5/8 for the vert itself:
192     Weight eWeight = (Weight) 0.0625f;
193     Weight vWeight = (Weight) 0.625f;
194 
195     if (valence != 6) {
196         //  From HbrLoopSubdivision<T>::Subdivide(mesh, vertex):
197         //     - could use some lookup tables here for common irregular valence (5, 7, 8)
198         //       or all of these cosine calls will be adding up...
199 
200         double dValence   = (double) valence;
201         double invValence = 1.0f / dValence;
202         double cosTheta   = std::cos(M_PI * 2.0f * invValence);
203 
204         double beta = 0.25f * cosTheta + 0.375f;
205 
206         eWeight = (Weight) ((0.625f - (beta * beta)) * invValence);
207         vWeight = (Weight) (1.0f - (eWeight * dValence));
208     }
209 
210     mask.VertexWeight(0) = vWeight;
211     for (int i = 0; i < valence; ++i) {
212         mask.EdgeWeight(i) = eWeight;
213     }
214 }
215 
216 
217 //
218 //  Limit masks for position:
219 //
220 template <>
221 template <typename VERTEX, typename MASK>
222 inline void
assignCornerLimitMask(VERTEX const &,MASK & posMask)223 Scheme<SCHEME_LOOP>::assignCornerLimitMask(VERTEX const& /* vertex */, MASK& posMask) const {
224 
225     posMask.SetNumVertexWeights(1);
226     posMask.SetNumEdgeWeights(0);
227     posMask.SetNumFaceWeights(0);
228     posMask.SetFaceWeightsForFaceCenters(false);
229 
230     posMask.VertexWeight(0) = 1.0f;
231 }
232 
233 template <>
234 template <typename VERTEX, typename MASK>
235 inline void
assignCreaseLimitMask(VERTEX const & vertex,MASK & posMask,int const creaseEnds[2])236 Scheme<SCHEME_LOOP>::assignCreaseLimitMask(VERTEX const& vertex, MASK& posMask,
237                                            int const creaseEnds[2]) const {
238 
239     typedef typename MASK::Weight Weight;
240 
241     int valence = vertex.GetNumEdges();
242 
243     posMask.SetNumVertexWeights(1);
244     posMask.SetNumEdgeWeights(valence);
245     posMask.SetNumFaceWeights(0);
246     posMask.SetFaceWeightsForFaceCenters(false);
247 
248     //
249     //  The refinement mask for a crease vertex is (1/8, 3/4, 1/8) and for a crease
250     //  edge is (1/2, 1/2) -- producing a uniform B-spline curve along the crease
251     //  (boundary) whether the vertex or its crease is regular or not.  The limit
252     //  mask is therefore (1/6, 2/3, 1/6) for ALL cases.
253     //
254     //  An alternative limit mask (1/5, 3/5, 1/5) is often published for use either
255     //  for irregular crease vertices or for all crease/boundary vertices, but this
256     //  is based on an alternate refinement mask for the edge -- (3/8, 5/8) versus
257     //  the usual (1/2, 1/2) -- and will not produce the B-spline curve desired.
258     //
259     Weight vWeight = (Weight) (4.0 / 6.0);
260     Weight eWeight = (Weight) (1.0 / 6.0);
261 
262     posMask.VertexWeight(0) = vWeight;
263     for (int i = 0; i < valence; ++i) {
264         posMask.EdgeWeight(i) = 0.0f;
265     }
266     posMask.EdgeWeight(creaseEnds[0]) = eWeight;
267     posMask.EdgeWeight(creaseEnds[1]) = eWeight;
268 }
269 
270 template <>
271 template <typename VERTEX, typename MASK>
272 inline void
assignSmoothLimitMask(VERTEX const & vertex,MASK & posMask)273 Scheme<SCHEME_LOOP>::assignSmoothLimitMask(VERTEX const& vertex, MASK& posMask) const {
274 
275     typedef typename MASK::Weight Weight;
276 
277     int valence = vertex.GetNumFaces();
278 
279     posMask.SetNumVertexWeights(1);
280     posMask.SetNumEdgeWeights(valence);
281     posMask.SetNumFaceWeights(0);
282     posMask.SetFaceWeightsForFaceCenters(false);
283 
284     //  Specialize for the regular case:  1/12 per edge-vert, 1/2 for the vert itself:
285     if (valence == 6) {
286         Weight eWeight = (Weight) (1.0 / 12.0);
287         Weight vWeight = 0.5f;
288 
289         posMask.VertexWeight(0) = vWeight;
290 
291         posMask.EdgeWeight(0) = eWeight;
292         posMask.EdgeWeight(1) = eWeight;
293         posMask.EdgeWeight(2) = eWeight;
294         posMask.EdgeWeight(3) = eWeight;
295         posMask.EdgeWeight(4) = eWeight;
296         posMask.EdgeWeight(5) = eWeight;
297 
298     } else {
299         double dValence   = (double) valence;
300         double invValence = 1.0f / dValence;
301         double cosTheta   = std::cos(M_PI * 2.0f * invValence);
302 
303         double beta  = 0.25f * cosTheta + 0.375f;
304         double gamma = (0.625f - (beta * beta)) * invValence;
305 
306         Weight eWeight = (Weight) (1.0f / (dValence + 3.0f / (8.0f * gamma)));
307         Weight vWeight = (Weight) (1.0f - (eWeight * dValence));
308 
309         posMask.VertexWeight(0) = vWeight;
310         for (int i = 0; i < valence; ++i) {
311             posMask.EdgeWeight(i) = eWeight;
312         }
313     }
314 }
315 
316 /*
317 //  Limit masks for tangents:
318 //
319 //  A note on tangent magnitudes:
320 //
321 //  Several formulae exist for limit tangents at a vertex to accommodate the
322 //  different topological configurations around the vertex.  While these produce
323 //  the desired direction, there is inconsistency in the resulting magnitudes.
324 //  Ideally a regular mesh of uniformly shaped triangles with similar edge lengths
325 //  should produce tangents of similar magnitudes throughout -- including corners
326 //  and boundaries.  So some of the common formulae for these are adjusted with
327 //  scale factors.
328 //
329 //  For uses where magnitude does not matter, this scaling should be irrelevant.
330 //  But just as with patches, where the magnitudes of partial derivatives are
331 //  consistent between similar patches, the magnitudes of limit tangents should
332 //  also be similar.
333 //
334 //  The reference tangents, in terms of magnitudes, are those produced by the
335 //  limit tangent mask for smooth interior vertices, for which well established
336 //  sin/cos formulae apply -- these remain unscaled.  Formulae for the other
337 //  crease/boundary, corner tangents and irregular cases are scaled to be more
338 //  consistent with these.
339 //
340 //  The crease/boundary tangents for the regular case can be viewed as derived
341 //  from the smooth interior masks with two "phantom" points extrapolated across
342 //  the regular boundary:
343 //
344 //            v3           v2
345 //             X - - - - - X
346 //           /   \       /   \
347 //         /       \   /       \
348 //   v4  X - - - - - X - - - - - X  v1
349 //         .       . 0 .       .
350 //           .   .       .   .
351 //             .   .   .   .
352 //           (v5)         (v6)
353 //
354 //  where v5 = v0 + (v4 - v3) and v6 = v0 + v1 - v2.
355 //
356 //  When the standard limit tangent mask is applied, the cosines of increments
357 //  of pi/3 give us coefficients that are multiples of 1/2, leading to the first
358 //  tangent T1 = 3/2 * (v1 - v4), rather than the widely used T1 = v1 - v4.  So
359 //  this scale factor of 3/2 is applied to ensure tangents along the boundaries
360 //  are of similar magnitude as tangents in the immediate interior (which may be
361 //  parallel).
362 //
363 //  Tangents at corners are essentially a form of boundary tangent, and so its
364 //  simple difference formula is scaled to be consistent with adjoining boundary
365 //  tangents -- not just with the 3/2 factor from above, but with an additional
366 //  2.0 to compensate for the fact that the difference of only side of the vertex
367 //  is considered here.  The resulting scale factor of 3.0 for the regular corner
368 //  is what similarly arises by extrapolating an interior region around the
369 //  vertex and using the interior mask for the first tangent.
370 //
371 //  The cross-tangent formula for the regular crease/boundary is similarly found
372 //  from the above construction of the boundary, but the commonly used weights of
373 //  +/- 1 and 2 result from omitting the common factor of sqrt(3)/2 (arising from
374 //  the sines of increments of pi/3).  With that scale factor close to one, it has
375 //  less impact than the irregular cases, which are analogous to corner tangents
376 //  in that differences on only one side of the vertex are considered.  While a
377 //  scaling of 3.0 is similarly understandable for the valence 2 and 3 cases, it is
378 //  less obvious in the irregular formula for valence > 4, but similarly effective.
379 //
380 //  The end result of these adjustments should be a set of limit tangents that are
381 //  of similar magnitude over a regular mesh including boundaries and corners.
382 */
383 template <>
384 template <typename VERTEX, typename MASK>
385 inline void
assignCornerLimitTangentMasks(VERTEX const & vertex,MASK & tan1Mask,MASK & tan2Mask)386 Scheme<SCHEME_LOOP>::assignCornerLimitTangentMasks(VERTEX const& vertex,
387         MASK& tan1Mask, MASK& tan2Mask) const {
388 
389     int valence = vertex.GetNumEdges();
390 
391     tan1Mask.SetNumVertexWeights(1);
392     tan1Mask.SetNumEdgeWeights(valence);
393     tan1Mask.SetNumFaceWeights(0);
394     tan1Mask.SetFaceWeightsForFaceCenters(false);
395 
396     tan2Mask.SetNumVertexWeights(1);
397     tan2Mask.SetNumEdgeWeights(valence);
398     tan2Mask.SetNumFaceWeights(0);
399     tan2Mask.SetFaceWeightsForFaceCenters(false);
400 
401     //  See note above regarding scale factor of 3.0:
402     tan1Mask.VertexWeight(0) = -3.0f;
403     tan1Mask.EdgeWeight(0)   =  3.0f;
404     tan1Mask.EdgeWeight(1)   =  0.0f;
405 
406     tan2Mask.VertexWeight(0) = -3.0f;
407     tan2Mask.EdgeWeight(0)   =  0.0f;
408     tan2Mask.EdgeWeight(1)   =  3.0f;
409 
410     //  Should be at least 2 edges -- be sure to clear weights for any more:
411     for (int i = 2; i < valence; ++i) {
412         tan1Mask.EdgeWeight(i) = 0.0f;
413         tan2Mask.EdgeWeight(i) = 0.0f;
414     }
415 }
416 
417 template <>
418 template <typename VERTEX, typename MASK>
419 inline void
assignCreaseLimitTangentMasks(VERTEX const & vertex,MASK & tan1Mask,MASK & tan2Mask,int const creaseEnds[2])420 Scheme<SCHEME_LOOP>::assignCreaseLimitTangentMasks(VERTEX const& vertex,
421         MASK& tan1Mask, MASK& tan2Mask, int const creaseEnds[2]) const {
422 
423     typedef typename MASK::Weight Weight;
424 
425     //
426     //  First, the tangent along the crease:
427     //      The first crease edge is considered the "leading" edge of the span
428     //  of surface for which we are evaluating tangents and the second edge the
429     //  "trailing edge".  By convention, the tangent along the crease is oriented
430     //  in the direction of the leading edge.
431     //
432     int valence = vertex.GetNumEdges();
433 
434     tan1Mask.SetNumVertexWeights(1);
435     tan1Mask.SetNumEdgeWeights(valence);
436     tan1Mask.SetNumFaceWeights(0);
437     tan1Mask.SetFaceWeightsForFaceCenters(false);
438 
439     tan1Mask.VertexWeight(0) = 0.0f;
440     for (int i = 0; i < valence; ++i) {
441         tan1Mask.EdgeWeight(i) = 0.0f;
442     }
443 
444     //  See the note above regarding scale factor of 1.5:
445     tan1Mask.EdgeWeight(creaseEnds[0]) =  1.5f;
446     tan1Mask.EdgeWeight(creaseEnds[1]) = -1.5f;
447 
448     //
449     //  Second, the tangent across the interior faces:
450     //      Note this is ambiguous for an interior vertex.  We currently return
451     //  the tangent for the surface in the counter-clockwise span between the
452     //  leading and trailing edges that form the crease.  Given the expected
453     //  computation of a surface normal as Tan1 X Tan2, this tangent should be
454     //  oriented "inward" from the crease/boundary -- across the surface rather
455     //  than outward and away from it.
456     //
457     //  There is inconsistency in the orientation of this tangent in commonly
458     //  published results:  the general formula provided for arbitrary valence
459     //  has the tangent pointing across the crease and "outward" from the surface,
460     //  while the special cases for regular valence and lower have the tangent
461     //  pointing across the surface and "inward" from the crease.  So if we are
462     //  to consistently orient the first tangent along the crease, regardless of
463     //  the interior topology, we have to correct this.  With the first tangent
464     //  following the direction of the leading crease edge, we want the second
465     //  tangent pointing inward/across the surface -- so we flip the result of
466     //  the general formula.
467     //
468     tan2Mask.SetNumVertexWeights(1);
469     tan2Mask.SetNumEdgeWeights(valence);
470     tan2Mask.SetNumFaceWeights(0);
471     tan2Mask.SetFaceWeightsForFaceCenters(false);
472 
473     for (int i = 0; i < creaseEnds[0]; ++i) {
474         tan2Mask.EdgeWeight(i) = 0.0f;
475     }
476     int interiorEdgeCount = creaseEnds[1] - creaseEnds[0] - 1;
477     if (interiorEdgeCount == 2) {
478         //  See note above regarding scale factor of (sin(60 degs) == sqrt(3)/2:
479 
480         static Weight const Root3    = (Weight) 1.73205080756887729352;
481         static Weight const Root3by2 = (Weight) (Root3 * 0.5);
482 
483         tan2Mask.VertexWeight(0) = -Root3;
484 
485         tan2Mask.EdgeWeight(creaseEnds[0]) = -Root3by2;
486         tan2Mask.EdgeWeight(creaseEnds[1]) = -Root3by2;
487 
488         tan2Mask.EdgeWeight(creaseEnds[0] + 1) = Root3;
489         tan2Mask.EdgeWeight(creaseEnds[0] + 2) = Root3;
490     } else if (interiorEdgeCount > 2) {
491         //  See notes above regarding scale factor of -3.0 (-1 for orientation,
492         //  2.0 for considering the region as a half-disk, and 1.5 in keeping
493         //  with the crease tangent):
494 
495         double theta = M_PI / (interiorEdgeCount + 1);
496 
497         tan2Mask.VertexWeight(0) = 0.0f;
498 
499         Weight cWeight = (Weight) (-3.0f * std::sin(theta));
500         tan2Mask.EdgeWeight(creaseEnds[0]) = cWeight;
501         tan2Mask.EdgeWeight(creaseEnds[1]) = cWeight;
502 
503         double eCoeff  = -3.0f * 2.0f * (std::cos(theta) - 1.0f);
504         for (int i = 1; i <= interiorEdgeCount; ++i) {
505             tan2Mask.EdgeWeight(creaseEnds[0] + i) = (Weight) (eCoeff * std::sin(i * theta));
506         }
507     } else if (interiorEdgeCount == 1) {
508         //  See notes above regarding scale factor of 3.0:
509 
510         tan2Mask.VertexWeight(0) = -3.0f;
511 
512         tan2Mask.EdgeWeight(creaseEnds[0]) = 0.0f;
513         tan2Mask.EdgeWeight(creaseEnds[1]) = 0.0f;
514 
515         tan2Mask.EdgeWeight(creaseEnds[0] + 1) = 3.0f;
516     } else {
517         //  See notes above regarding scale factor of 3.0:
518 
519         tan2Mask.VertexWeight(0) = -6.0f;
520 
521         tan2Mask.EdgeWeight(creaseEnds[0]) = 3.0f;
522         tan2Mask.EdgeWeight(creaseEnds[1]) = 3.0f;
523     }
524     for (int i = creaseEnds[1] + 1; i < valence; ++i) {
525         tan2Mask.EdgeWeight(i) = 0.0f;
526     }
527 }
528 
529 template <>
530 template <typename VERTEX, typename MASK>
531 inline void
assignSmoothLimitTangentMasks(VERTEX const & vertex,MASK & tan1Mask,MASK & tan2Mask)532 Scheme<SCHEME_LOOP>::assignSmoothLimitTangentMasks(VERTEX const& vertex,
533         MASK& tan1Mask, MASK& tan2Mask) const {
534 
535     typedef typename MASK::Weight Weight;
536 
537     int valence = vertex.GetNumFaces();
538 
539     tan1Mask.SetNumVertexWeights(1);
540     tan1Mask.SetNumEdgeWeights(valence);
541     tan1Mask.SetNumFaceWeights(0);
542     tan1Mask.SetFaceWeightsForFaceCenters(false);
543 
544     tan2Mask.SetNumVertexWeights(1);
545     tan2Mask.SetNumEdgeWeights(valence);
546     tan2Mask.SetNumFaceWeights(0);
547     tan2Mask.SetFaceWeightsForFaceCenters(false);
548 
549     tan1Mask.VertexWeight(0) = 0.0f;
550     tan2Mask.VertexWeight(0) = 0.0f;
551 
552     if (valence == 6) {
553         static Weight const Root3by2 = (Weight)(0.5 * 1.73205080756887729352);
554 
555         tan1Mask.EdgeWeight(0) =  1.0f;
556         tan1Mask.EdgeWeight(1) =  0.5f;
557         tan1Mask.EdgeWeight(2) = -0.5f;
558         tan1Mask.EdgeWeight(3) = -1.0f;
559         tan1Mask.EdgeWeight(4) = -0.5f;
560         tan1Mask.EdgeWeight(5) =  0.5f;
561 
562         tan2Mask.EdgeWeight(0) =  0.0f;
563         tan2Mask.EdgeWeight(1) =  Root3by2;
564         tan2Mask.EdgeWeight(2) =  Root3by2;
565         tan2Mask.EdgeWeight(3) =  0.0f;
566         tan2Mask.EdgeWeight(4) = -Root3by2;
567         tan2Mask.EdgeWeight(5) = -Root3by2;
568     } else {
569         double alpha = 2.0f * M_PI / valence;
570         for (int i = 0; i < valence; ++i) {
571             double alphaI = alpha * i;
572             tan1Mask.EdgeWeight(i) = (Weight) std::cos(alphaI);
573             tan2Mask.EdgeWeight(i) = (Weight) std::sin(alphaI);
574         }
575     }
576 }
577 
578 } // end namespace Sdc
579 } // end namespace OPENSUBDIV_VERSION
580 using namespace OPENSUBDIV_VERSION;
581 } // end namespace OpenSubdiv
582 
583 #endif /* OPENSUBDIV3_SDC_LOOP_SCHEME_H */
584