OpenSubdiv
catmarkScheme.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_CATMARK_SCHEME_H
25 #define OPENSUBDIV3_SDC_CATMARK_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 
37 namespace Sdc {
38 
39 //
40 // Specializations for Scheme<SCHEME_CATMARK>:
41 //
42 
43 //
44 // Catmark traits:
45 //
46 template <>
48 
49 template <>
51 
52 template <>
54 
55 template <>
57 
58 
59 //
60 // Masks for edge-vertices: the hard Crease mask does not need to be specialized
61 // (simply the midpoint), so all that is left is the Smooth case:
62 //
63 // The Smooth mask is complicated by the need to support the "triangle subdivision"
64 // option, which applies different weighting in the presence of triangles. It is
65 // up for debate as to whether this is useful or not -- we may be able to deprecate
66 // this option.
67 //
68 template <>
69 template <typename EDGE, typename MASK>
70 inline void
71 Scheme<SCHEME_CATMARK>::assignSmoothMaskForEdge(EDGE const& edge, MASK& mask) const {
72 
73  typedef typename MASK::Weight Weight;
74 
75  int faceCount = edge.GetNumFaces();
76 
77  mask.SetNumVertexWeights(2);
78  mask.SetNumEdgeWeights(0);
79  mask.SetNumFaceWeights(faceCount);
80  mask.SetFaceWeightsForFaceCenters(true);
81 
82  //
83  // Determine if we need to inspect incident faces and apply alternate weighting for
84  // triangles -- and if so, determine which of the two are triangles.
85  //
86  bool face0IsTri = false;
87  bool face1IsTri = false;
88  bool useTriangleOption = (_options.GetTriangleSubdivision() == Options::TRI_SUB_SMOOTH);
89  if (useTriangleOption) {
90  if (faceCount == 2) {
91  //
92  // Ideally we want to avoid this inspection when we have already subdivided at
93  // least once -- need something in the Edge interface to help avoid this, e.g.
94  // an IsRegular() query, the subdivision level...
95  //
96  int vertsPerFace[2];
97  edge.GetNumVerticesPerFace(vertsPerFace);
98 
99  face0IsTri = (vertsPerFace[0] == 3);
100  face1IsTri = (vertsPerFace[1] == 3);
101  useTriangleOption = face0IsTri || face1IsTri;
102  } else {
103  useTriangleOption = false;
104  }
105  }
106 
107  if (! useTriangleOption) {
108  mask.VertexWeight(0) = 0.25f;
109  mask.VertexWeight(1) = 0.25f;
110 
111  if (faceCount == 2) {
112  mask.FaceWeight(0) = 0.25f;
113  mask.FaceWeight(1) = 0.25f;
114  } else {
115  Weight fWeight = 0.5f / (Weight)faceCount;
116  for (int i = 0; i < faceCount; ++i) {
117  mask.FaceWeight(i) = fWeight;
118  }
119  }
120  } else {
121  //
122  // This mimics the implementation in Hbr in terms of order of operations.
123  //
124  const Weight CATMARK_SMOOTH_TRI_EDGE_WEIGHT = (Weight) 0.470;
125 
126  Weight f0Weight = face0IsTri ? CATMARK_SMOOTH_TRI_EDGE_WEIGHT : 0.25f;
127  Weight f1Weight = face1IsTri ? CATMARK_SMOOTH_TRI_EDGE_WEIGHT : 0.25f;
128 
129  Weight fWeight = 0.5f * (f0Weight + f1Weight);
130  Weight vWeight = 0.5f * (1.0f - 2.0f * fWeight);
131 
132  mask.VertexWeight(0) = vWeight;
133  mask.VertexWeight(1) = vWeight;
134 
135  mask.FaceWeight(0) = fWeight;
136  mask.FaceWeight(1) = fWeight;
137  }
138 }
139 
140 
141 //
142 // Masks for vertex-vertices: the hard Corner mask does not need to be specialized
143 // (simply the vertex itself), leaving the Crease and Smooth cases (Dart is smooth):
144 //
145 template <>
146 template <typename VERTEX, typename MASK>
147 inline void
148 Scheme<SCHEME_CATMARK>::assignCreaseMaskForVertex(VERTEX const& vertex, MASK& mask,
149  int const creaseEnds[2]) const {
150  typedef typename MASK::Weight Weight;
151 
152  int valence = vertex.GetNumEdges();
153 
154  mask.SetNumVertexWeights(1);
155  mask.SetNumEdgeWeights(valence);
156  mask.SetNumFaceWeights(0);
157  mask.SetFaceWeightsForFaceCenters(false);
158 
159  Weight vWeight = 0.75f;
160  Weight eWeight = 0.125f;
161 
162  mask.VertexWeight(0) = vWeight;
163  for (int i = 0; i < valence; ++i) {
164  mask.EdgeWeight(i) = 0.0f;
165  }
166  mask.EdgeWeight(creaseEnds[0]) = eWeight;
167  mask.EdgeWeight(creaseEnds[1]) = eWeight;
168 }
169 
170 template <>
171 template <typename VERTEX, typename MASK>
172 inline void
173 Scheme<SCHEME_CATMARK>::assignSmoothMaskForVertex(VERTEX const& vertex, MASK& mask) const {
174 
175  typedef typename MASK::Weight Weight;
176 
177  //
178  // A Smooth vertex must be manifold and interior -- manifold boundary vertices will be
179  // Creases and non-manifold vertices of any kind will be Corners or Creases. If smooth
180  // rules for non-manifold vertices are ever defined, this will need adjusting:
181  //
182  assert(vertex.GetNumFaces() == vertex.GetNumEdges());
183 
184  int valence = vertex.GetNumFaces();
185 
186  mask.SetNumVertexWeights(1);
187  mask.SetNumEdgeWeights(valence);
188  mask.SetNumFaceWeights(valence);
189  mask.SetFaceWeightsForFaceCenters(true);
190 
191  Weight vWeight = (Weight)(valence - 2) / (Weight)valence;
192  Weight fWeight = 1.0f / (Weight)(valence * valence);
193  Weight eWeight = fWeight;
194 
195  mask.VertexWeight(0) = vWeight;
196  for (int i = 0; i < valence; ++i) {
197  mask.EdgeWeight(i) = eWeight;
198  mask.FaceWeight(i) = fWeight;
199  }
200 }
201 
202 //
203 // Limit masks for position:
204 //
205 template <>
206 template <typename VERTEX, typename MASK>
207 inline void
208 Scheme<SCHEME_CATMARK>::assignCornerLimitMask(VERTEX const& /* vertex */, MASK& posMask) const {
209 
210  posMask.SetNumVertexWeights(1);
211  posMask.SetNumEdgeWeights(0);
212  posMask.SetNumFaceWeights(0);
213  posMask.SetFaceWeightsForFaceCenters(false);
214 
215  posMask.VertexWeight(0) = 1.0f;
216 }
217 
218 template <>
219 template <typename VERTEX, typename MASK>
220 inline void
221 Scheme<SCHEME_CATMARK>::assignCreaseLimitMask(VERTEX const& vertex, MASK& posMask,
222  int const creaseEnds[2]) const {
223 
224  typedef typename MASK::Weight Weight;
225 
226  int valence = vertex.GetNumEdges();
227 
228  posMask.SetNumVertexWeights(1);
229  posMask.SetNumEdgeWeights(valence);
230  posMask.SetNumFaceWeights(0);
231  posMask.SetFaceWeightsForFaceCenters(false);
232 
233  Weight vWeight = (Weight)(2.0 / 3.0);
234  Weight eWeight = (Weight)(1.0 / 6.0);
235 
236  posMask.VertexWeight(0) = vWeight;
237  for (int i = 0; i < valence; ++i) {
238  posMask.EdgeWeight(i) = 0.0f;
239  }
240  posMask.EdgeWeight(creaseEnds[0]) = eWeight;
241  posMask.EdgeWeight(creaseEnds[1]) = eWeight;
242 }
243 
244 template <>
245 template <typename VERTEX, typename MASK>
246 inline void
247 Scheme<SCHEME_CATMARK>::assignSmoothLimitMask(VERTEX const& vertex, MASK& posMask) const {
248 
249  typedef typename MASK::Weight Weight;
250 
251  int valence = vertex.GetNumFaces();
252  if (valence == 2) {
253  assignCornerLimitMask(vertex, posMask);
254  return;
255  }
256 
257  posMask.SetNumVertexWeights(1);
258  posMask.SetNumEdgeWeights(valence);
259  posMask.SetNumFaceWeights(valence);
260  posMask.SetFaceWeightsForFaceCenters(false);
261 
262  // Specialize for the regular case:
263  if (valence == 4) {
264  Weight fWeight = (Weight)(1.0 / 36.0);
265  Weight eWeight = (Weight)(1.0 / 9.0);
266  Weight vWeight = (Weight)(4.0 / 9.0);
267 
268  posMask.VertexWeight(0) = vWeight;
269 
270  posMask.EdgeWeight(0) = eWeight;
271  posMask.EdgeWeight(1) = eWeight;
272  posMask.EdgeWeight(2) = eWeight;
273  posMask.EdgeWeight(3) = eWeight;
274 
275  posMask.FaceWeight(0) = fWeight;
276  posMask.FaceWeight(1) = fWeight;
277  posMask.FaceWeight(2) = fWeight;
278  posMask.FaceWeight(3) = fWeight;
279  } else {
280  Weight Valence = (Weight) valence;
281 
282  Weight fWeight = 1.0f / (Valence * (Valence + 5.0f));
283  Weight eWeight = 4.0f * fWeight;
284  Weight vWeight = 1.0f - Valence * (eWeight + fWeight);
285 
286  posMask.VertexWeight(0) = vWeight;
287  for (int i = 0; i < valence; ++i) {
288  posMask.EdgeWeight(i) = eWeight;
289  posMask.FaceWeight(i) = fWeight;
290  }
291  }
292 }
293 
294 //
295 // Limit masks for tangents -- these are stubs for now, or have a temporary
296 // implementation
297 //
298 template <>
299 template <typename VERTEX, typename MASK>
300 inline void
302  MASK& tan1Mask, MASK& tan2Mask) const {
303 
304  int valence = vertex.GetNumEdges();
305 
306  tan1Mask.SetNumVertexWeights(1);
307  tan1Mask.SetNumEdgeWeights(valence);
308  tan1Mask.SetNumFaceWeights(0);
309  tan1Mask.SetFaceWeightsForFaceCenters(false);
310 
311  tan2Mask.SetNumVertexWeights(1);
312  tan2Mask.SetNumEdgeWeights(valence);
313  tan2Mask.SetNumFaceWeights(0);
314  tan2Mask.SetFaceWeightsForFaceCenters(false);
315 
316  // Should be at least 2 edges -- be sure to clear weights for any more:
317  tan1Mask.VertexWeight(0) = -1.0f;
318  tan1Mask.EdgeWeight(0) = 1.0f;
319  tan1Mask.EdgeWeight(1) = 0.0f;
320 
321  tan2Mask.VertexWeight(0) = -1.0f;
322  tan2Mask.EdgeWeight(0) = 0.0f;
323  tan2Mask.EdgeWeight(1) = 1.0f;
324 
325  for (int i = 2; i < valence; ++i) {
326  tan1Mask.EdgeWeight(i) = 0.0f;
327  tan2Mask.EdgeWeight(i) = 0.0f;
328  }
329 }
330 
331 template <>
332 template <typename VERTEX, typename MASK>
333 inline void
335  MASK& tan1Mask, MASK& tan2Mask, int const creaseEnds[2]) const {
336 
337  typedef typename MASK::Weight Weight;
338 
339  //
340  // First, the tangent along the crease:
341  // The first crease edge is considered the "leading" edge of the span
342  // of surface for which we are evaluating tangents and the second edge the
343  // "trailing edge". By convention, the tangent along the crease is oriented
344  // in the direction of the leading edge.
345  //
346  int numEdges = vertex.GetNumEdges();
347  int numFaces = vertex.GetNumFaces();
348 
349  tan1Mask.SetNumVertexWeights(1);
350  tan1Mask.SetNumEdgeWeights(numEdges);
351  tan1Mask.SetNumFaceWeights(numFaces);
352  tan1Mask.SetFaceWeightsForFaceCenters(false);
353 
354  tan1Mask.VertexWeight(0) = 0.0f;
355  for (int i = 0; i < numEdges; ++i) {
356  tan1Mask.EdgeWeight(i) = 0.0f;
357  }
358  for (int i = 0; i < numFaces; ++i) {
359  tan1Mask.FaceWeight(i) = 0.0f;
360  }
361 
362  tan1Mask.EdgeWeight(creaseEnds[0]) = 0.5f;
363  tan1Mask.EdgeWeight(creaseEnds[1]) = -0.5f;
364 
365  //
366  // Second, the tangent across the interior faces:
367  // Note this is ambiguous for an interior vertex. We currently return
368  // the tangent for the surface in the counter-clockwise span between the
369  // leading and trailing edges that form the crease. Given the expected
370  // computation of a surface normal as Tan1 X Tan2, this tangent should be
371  // oriented "inward" from the crease/boundary -- across the surface rather
372  // than outward and away from it.
373  //
374  tan2Mask.SetNumVertexWeights(1);
375  tan2Mask.SetNumEdgeWeights(numEdges);
376  tan2Mask.SetNumFaceWeights(numFaces);
377  tan2Mask.SetFaceWeightsForFaceCenters(false);
378 
379  // Prepend weights of 0 preceding the crease:
380  for (int i = 0; i < creaseEnds[0]; ++i) {
381  tan2Mask.EdgeWeight(i) = 0.0f;
382  tan2Mask.FaceWeight(i) = 0.0f;
383  }
384 
385  // Assign weights to crease edge and interior points:
386  int interiorEdgeCount = creaseEnds[1] - creaseEnds[0] - 1;
387  if (interiorEdgeCount == 1) {
388  // The regular case -- uniform B-spline cross-tangent:
389 
390  tan2Mask.VertexWeight(0) = (Weight)(-4.0 / 6.0);
391 
392  tan2Mask.EdgeWeight(creaseEnds[0]) = (Weight)(-1.0 / 6.0);
393  tan2Mask.EdgeWeight(creaseEnds[0] + 1) = (Weight)( 4.0 / 6.0);
394  tan2Mask.EdgeWeight(creaseEnds[1]) = (Weight)(-1.0 / 6.0);
395 
396  tan2Mask.FaceWeight(creaseEnds[0]) = (Weight)(1.0 / 6.0);
397  tan2Mask.FaceWeight(creaseEnds[0] + 1) = (Weight)(1.0 / 6.0);
398  } else if (interiorEdgeCount > 1) {
399  // The irregular case -- formulae from Biermann et al:
400 
401  double k = (double) (interiorEdgeCount + 1);
402  double theta = M_PI / k;
403 
404  double cosTheta = std::cos(theta);
405  double sinTheta = std::sin(theta);
406 
407  // Loop/Schaefer use a different divisor here (3*k + cos(theta)):
408  double commonDenom = 1.0f / (k * (3.0f + cosTheta));
409  double R = (cosTheta + 1.0f) / sinTheta;
410 
411  double vertexWeight = 4.0f * R * (cosTheta - 1.0f);
412  double creaseWeight = -R * (1.0f + 2.0f * cosTheta);
413 
414  tan2Mask.VertexWeight(0) = (Weight) (vertexWeight * commonDenom);
415 
416  tan2Mask.EdgeWeight(creaseEnds[0]) = (Weight) (creaseWeight * commonDenom);
417  tan2Mask.EdgeWeight(creaseEnds[1]) = (Weight) (creaseWeight * commonDenom);
418 
419  tan2Mask.FaceWeight(creaseEnds[0]) = (Weight) (sinTheta * commonDenom);
420 
421  double sinThetaI = 0.0f;
422  double sinThetaIplus1 = sinTheta;
423  for (int i = 1; i < k; ++i) {
424  sinThetaI = sinThetaIplus1;
425  sinThetaIplus1 = std::sin((i+1)*theta);
426 
427  tan2Mask.EdgeWeight(creaseEnds[0] + i) = (Weight) ((4.0f * sinThetaI) * commonDenom);
428  tan2Mask.FaceWeight(creaseEnds[0] + i) = (Weight) ((sinThetaI + sinThetaIplus1) * commonDenom);
429  }
430  } else {
431  // Special case for a single face -- simple average of boundary edges:
432 
433  tan2Mask.VertexWeight(0) = -6.0f;
434 
435  tan2Mask.EdgeWeight(creaseEnds[0]) = 3.0f;
436  tan2Mask.EdgeWeight(creaseEnds[1]) = 3.0f;
437 
438  tan2Mask.FaceWeight(creaseEnds[0]) = 0.0f;
439  }
440 
441  // Append weights of 0 following the crease:
442  for (int i = creaseEnds[1]; i < numFaces; ++i) {
443  tan2Mask.FaceWeight(i) = 0.0f;
444  }
445  for (int i = creaseEnds[1] + 1; i < numEdges; ++i) {
446  tan2Mask.EdgeWeight(i) = 0.0f;
447  }
448 }
449 
450 template <>
451 template <typename VERTEX, typename MASK>
452 inline void
454  MASK& tan1Mask, MASK& tan2Mask) const {
455 
456  typedef typename MASK::Weight Weight;
457 
458  int valence = vertex.GetNumFaces();
459  if (valence == 2) {
460  assignCornerLimitTangentMasks(vertex, tan1Mask, tan2Mask);
461  return;
462  }
463 
464  // Compute tan1 initially -- tan2 is simply a rotation:
465  tan1Mask.SetNumVertexWeights(1);
466  tan1Mask.SetNumEdgeWeights(valence);
467  tan1Mask.SetNumFaceWeights(valence);
468  tan1Mask.SetFaceWeightsForFaceCenters(false);
469 
470  tan1Mask.VertexWeight(0) = 0.0f;
471 
472  if (valence == 4) {
473  tan1Mask.EdgeWeight(0) = 4.0f;
474  tan1Mask.EdgeWeight(1) = 0.0f;
475  tan1Mask.EdgeWeight(2) = -4.0f;
476  tan1Mask.EdgeWeight(3) = 0.0f;
477 
478  tan1Mask.FaceWeight(0) = 1.0f;
479  tan1Mask.FaceWeight(1) = -1.0f;
480  tan1Mask.FaceWeight(2) = -1.0f;
481  tan1Mask.FaceWeight(3) = 1.0f;
482  } else {
483  double theta = 2.0f * M_PI / (double)valence;
484 
485  double cosTheta = std::cos(theta);
486  double cosHalfTheta = std::cos(theta * 0.5f);
487 
488  double lambda = (5.0 / 16.0) + (1.0 / 16.0) *
489  (cosTheta + cosHalfTheta * std::sqrt(2.0f * (9.0f + cosTheta)));
490 
491  double edgeWeightScale = 4.0f;
492  double faceWeightScale = 1.0f / (4.0f * lambda - 1.0f);
493 
494  for (int i = 0; i < valence; ++i) {
495  double cosThetaI = std::cos( i * theta);
496  double cosThetaIplus1 = std::cos((i+1)* theta);
497 
498  tan1Mask.EdgeWeight(i) = (Weight) (edgeWeightScale * cosThetaI);
499  tan1Mask.FaceWeight(i) = (Weight) (faceWeightScale * (cosThetaI + cosThetaIplus1));
500  }
501  }
502 
503  // Now rotate/copy tan1 weights to tan2:
504  tan2Mask.SetNumVertexWeights(1);
505  tan2Mask.SetNumEdgeWeights(valence);
506  tan2Mask.SetNumFaceWeights(valence);
507  tan2Mask.SetFaceWeightsForFaceCenters(false);
508 
509  tan2Mask.VertexWeight(0) = 0.0f;
510  if (valence == 4) {
511  tan2Mask.EdgeWeight(0) = 0.0f;
512  tan2Mask.EdgeWeight(1) = 4.0f;
513  tan2Mask.EdgeWeight(2) = 0.0f;
514  tan2Mask.EdgeWeight(3) = -4.0f;
515 
516  tan2Mask.FaceWeight(0) = 1.0f;
517  tan2Mask.FaceWeight(1) = 1.0f;
518  tan2Mask.FaceWeight(2) = -1.0f;
519  tan2Mask.FaceWeight(3) = -1.0f;
520  } else {
521  tan2Mask.EdgeWeight(0) = tan1Mask.EdgeWeight(valence-1);
522  tan2Mask.FaceWeight(0) = tan1Mask.FaceWeight(valence-1);
523  for (int i = 1; i < valence; ++i) {
524  tan2Mask.EdgeWeight(i) = tan1Mask.EdgeWeight(i-1);
525  tan2Mask.FaceWeight(i) = tan1Mask.FaceWeight(i-1);
526  }
527  }
528 }
529 
530 } // end namespace sdc
531 
532 } // end namespace OPENSUBDIV_VERSION
533 using namespace OPENSUBDIV_VERSION;
534 } // end namespace OpenSubdiv
535 
536 #endif /* OPENSUBDIV3_SDC_CATMARK_SCHEME_H */
"smooth triangle" weights (Catmark scheme only)
Definition: options.h:74
void assignSmoothMaskForVertex(VERTEX const &edge, MASK &mask) const
void assignCreaseMaskForVertex(VERTEX const &edge, MASK &mask, int const creaseEnds[2]) const
void assignSmoothLimitTangentMasks(VERTEX const &vertex, MASK &tan1, MASK &tan2) const
void assignCreaseLimitTangentMasks(VERTEX const &vertex, MASK &tan1, MASK &tan2, int const creaseEnds[2]) const
void assignCornerLimitTangentMasks(VERTEX const &vertex, MASK &tan1, MASK &tan2) const
void assignCornerLimitMask(VERTEX const &vertex, MASK &pos) const
void assignCreaseLimitMask(VERTEX const &vertex, MASK &pos, int const creaseEnds[2]) const
Split
Enumerated type for all face splitting schemes.
Definition: types.h:47
Used by Catmark and Bilinear.
Definition: types.h:48
void assignSmoothMaskForEdge(EDGE const &edge, MASK &mask) const
void assignSmoothLimitMask(VERTEX const &vertex, MASK &pos) const