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