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