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 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
34namespace OpenSubdiv {
35namespace OPENSUBDIV_VERSION {
36namespace Sdc {
37
38
39//
40// Specializations for Sdc::Scheme<SCHEME_LOOP>:
41//
42//
43
44//
45// Loop traits:
46//
47template <>
49
50template <>
52
53template <>
55
56template <>
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//
67template <>
68template <typename EDGE, typename MASK>
69inline void
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
81template <>
82template <typename EDGE, typename MASK>
83inline void
84Scheme<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//
139template <>
140template <typename VERTEX, typename MASK>
141inline void
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
152template <>
153template <typename VERTEX, typename MASK>
154inline void
155Scheme<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
177template <>
178template <typename VERTEX, typename MASK>
179inline void
180Scheme<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//
220template <>
221template <typename VERTEX, typename MASK>
222inline void
223Scheme<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
233template <>
234template <typename VERTEX, typename MASK>
235inline void
236Scheme<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
270template <>
271template <typename VERTEX, typename MASK>
272inline void
273Scheme<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*/
383template <>
384template <typename VERTEX, typename MASK>
385inline void
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
417template <>
418template <typename VERTEX, typename MASK>
419inline void
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
529template <>
530template <typename VERTEX, typename MASK>
531inline void
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
580using namespace OPENSUBDIV_VERSION;
581} // end namespace OpenSubdiv
582
583#endif /* OPENSUBDIV3_SDC_LOOP_SCHEME_H */
Split
Enumerated type for all face splitting schemes.
Definition: types.h:47
@ SPLIT_TO_TRIS
Used by Loop.
Definition: types.h:49
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:317
void assignSmoothLimitMask(VERTEX const &vertex, MASK &pos) const
void assignCornerMaskForVertex(VERTEX const &edge, MASK &mask) const
Definition: scheme.h:331
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