Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
#
# Copyright 2013 Pixar
#
# Licensed under the terms set forth in the LICENSE.txt file available at
# https://opensubdiv.org/license.
#
#-------------------------------------------------------------------------------
set(PUBLIC_HEADER_FILES
bilinearScheme.h
catmarkScheme.h
crease.h
loopScheme.h
options.h
scheme.h
types.h
)
set(PRIVATE_HEADER_FILES )
#-------------------------------------------------------------------------------
# source & headers
set(SOURCE_FILES
crease.cpp
typeTraits.cpp
)
set(DOXY_HEADER_FILES ${PUBLIC_HEADER_FILES})
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
if (NOT NO_LIB)
add_library(sdc_obj
OBJECT
${SOURCE_FILES}
${PRIVATE_HEADER_FILES}
${PUBLIC_HEADER_FILES}
)
set_target_properties(sdc_obj
PROPERTIES
FOLDER "opensubdiv"
)
endif()
#-------------------------------------------------------------------------------
osd_add_doxy_headers( "${DOXY_HEADER_FILES}" )
install(
FILES
${PUBLIC_HEADER_FILES}
DESTINATION
"${CMAKE_INCDIR_BASE}/sdc"
PERMISSIONS
OWNER_READ
GROUP_READ
WORLD_READ )

View File

@@ -0,0 +1,147 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_SDC_BILINEAR_SCHEME_H
#define OPENSUBDIV3_SDC_BILINEAR_SCHEME_H
#include "../version.h"
#include "../sdc/scheme.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Sdc {
//
// Specializations for Scheme<SCHEME_BILINEAR>:
//
//
// Bilinear traits:
//
template <>
inline Split Scheme<SCHEME_BILINEAR>::GetTopologicalSplitType() { return SPLIT_TO_QUADS; }
template <>
inline int Scheme<SCHEME_BILINEAR>::GetRegularFaceSize() { return 4; }
template <>
inline int Scheme<SCHEME_BILINEAR>::GetRegularVertexValence() { return 4; }
template <>
inline int Scheme<SCHEME_BILINEAR>::GetLocalNeighborhoodSize() { return 0; }
//
// Refinement masks:
//
template <>
template <typename EDGE, typename MASK>
void
Scheme<SCHEME_BILINEAR>::ComputeEdgeVertexMask(EDGE const& edge, MASK& mask,
Crease::Rule, Crease::Rule) const {
// This should be inline, otherwise trivially replicate it:
assignCreaseMaskForEdge(edge, mask);
}
template <>
template <typename VERTEX, typename MASK>
void
Scheme<SCHEME_BILINEAR>::ComputeVertexVertexMask(VERTEX const& vertex, MASK& mask,
Crease::Rule, Crease::Rule) const {
// This should be inline, otherwise trivially replicate it:
assignCornerMaskForVertex(vertex, mask);
}
//
// Limit masks for position -- the limit position of all vertices is the refined vertex.
//
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_BILINEAR>::assignCornerLimitMask(VERTEX const& /* vertex */, MASK& posMask) const {
posMask.SetNumVertexWeights(1);
posMask.SetNumEdgeWeights(0);
posMask.SetNumFaceWeights(0);
posMask.SetFaceWeightsForFaceCenters(false);
posMask.VertexWeight(0) = 1.0f;
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_BILINEAR>::assignCreaseLimitMask(VERTEX const& vertex, MASK& posMask,
int const /* creaseEnds */[2]) const {
assignCornerLimitMask(vertex, posMask);
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_BILINEAR>::assignSmoothLimitMask(VERTEX const& vertex, MASK& posMask) const {
assignCornerLimitMask(vertex, posMask);
}
//
// Limit masks for tangents -- these are ambiguous around all vertices. Provide
// the tangents based on the incident edges of the first face.
//
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_BILINEAR>::assignCornerLimitTangentMasks(VERTEX const& /* vertex */,
MASK& tan1Mask, MASK& tan2Mask) const {
tan1Mask.SetNumVertexWeights(1);
tan1Mask.SetNumEdgeWeights(2);
tan1Mask.SetNumFaceWeights(0);
tan1Mask.SetFaceWeightsForFaceCenters(false);
tan2Mask.SetNumVertexWeights(1);
tan2Mask.SetNumEdgeWeights(2);
tan2Mask.SetNumFaceWeights(0);
tan2Mask.SetFaceWeightsForFaceCenters(false);
tan1Mask.VertexWeight(0) = -1.0f;
tan1Mask.EdgeWeight(0) = 1.0f;
tan1Mask.EdgeWeight(1) = 0.0f;
tan2Mask.VertexWeight(0) = -1.0f;
tan2Mask.EdgeWeight(0) = 0.0f;
tan2Mask.EdgeWeight(1) = 1.0f;
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_BILINEAR>::assignCreaseLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask, int const /* creaseEnds */[2]) const {
assignCornerLimitTangentMasks(vertex, tan1Mask, tan2Mask);
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_BILINEAR>::assignSmoothLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask) const {
assignCornerLimitTangentMasks(vertex, tan1Mask, tan2Mask);
}
} // end namespace sdc
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_SDC_BILINEAR_SCHEME_H */

View File

@@ -0,0 +1,519 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_SDC_CATMARK_SCHEME_H
#define OPENSUBDIV3_SDC_CATMARK_SCHEME_H
#include "../version.h"
#include "../sdc/scheme.h"
#include <cassert>
#include <cmath>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Sdc {
//
// Specializations for Scheme<SCHEME_CATMARK>:
//
//
// Catmark traits:
//
template <>
inline Split Scheme<SCHEME_CATMARK>::GetTopologicalSplitType() { return SPLIT_TO_QUADS; }
template <>
inline int Scheme<SCHEME_CATMARK>::GetRegularFaceSize() { return 4; }
template <>
inline int Scheme<SCHEME_CATMARK>::GetRegularVertexValence() { return 4; }
template <>
inline int Scheme<SCHEME_CATMARK>::GetLocalNeighborhoodSize() { return 1; }
//
// Masks for edge-vertices: the hard Crease mask does not need to be specialized
// (simply the midpoint), so all that is left is the Smooth case:
//
// The Smooth mask is complicated by the need to support the "triangle subdivision"
// option, which applies different weighting in the presence of triangles. It is
// up for debate as to whether this is useful or not -- we may be able to deprecate
// this option.
//
template <>
template <typename EDGE, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignSmoothMaskForEdge(EDGE const& edge, MASK& mask) const {
typedef typename MASK::Weight Weight;
int faceCount = edge.GetNumFaces();
mask.SetNumVertexWeights(2);
mask.SetNumEdgeWeights(0);
mask.SetNumFaceWeights(faceCount);
mask.SetFaceWeightsForFaceCenters(true);
//
// Determine if we need to inspect incident faces and apply alternate weighting for
// triangles -- and if so, determine which of the two are triangles.
//
bool face0IsTri = false;
bool face1IsTri = false;
bool useTriangleOption = (_options.GetTriangleSubdivision() == Options::TRI_SUB_SMOOTH);
if (useTriangleOption) {
if (faceCount == 2) {
//
// Ideally we want to avoid this inspection when we have already subdivided at
// least once -- need something in the Edge interface to help avoid this, e.g.
// an IsRegular() query, the subdivision level...
//
int vertsPerFace[2];
edge.GetNumVerticesPerFace(vertsPerFace);
face0IsTri = (vertsPerFace[0] == 3);
face1IsTri = (vertsPerFace[1] == 3);
useTriangleOption = face0IsTri || face1IsTri;
} else {
useTriangleOption = false;
}
}
if (! useTriangleOption) {
mask.VertexWeight(0) = 0.25f;
mask.VertexWeight(1) = 0.25f;
if (faceCount == 2) {
mask.FaceWeight(0) = 0.25f;
mask.FaceWeight(1) = 0.25f;
} else {
Weight fWeight = 0.5f / (Weight)faceCount;
for (int i = 0; i < faceCount; ++i) {
mask.FaceWeight(i) = fWeight;
}
}
} else {
//
// This mimics the implementation in Hbr in terms of order of operations.
//
const Weight CATMARK_SMOOTH_TRI_EDGE_WEIGHT = (Weight) 0.470;
Weight f0Weight = face0IsTri ? CATMARK_SMOOTH_TRI_EDGE_WEIGHT : 0.25f;
Weight f1Weight = face1IsTri ? CATMARK_SMOOTH_TRI_EDGE_WEIGHT : 0.25f;
Weight fWeight = 0.5f * (f0Weight + f1Weight);
Weight vWeight = 0.5f * (1.0f - 2.0f * fWeight);
mask.VertexWeight(0) = vWeight;
mask.VertexWeight(1) = vWeight;
mask.FaceWeight(0) = fWeight;
mask.FaceWeight(1) = fWeight;
}
}
//
// Masks for vertex-vertices: the hard Corner mask does not need to be specialized
// (simply the vertex itself), leaving the Crease and Smooth cases (Dart is smooth):
//
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignCreaseMaskForVertex(VERTEX const& vertex, MASK& mask,
int const creaseEnds[2]) const {
typedef typename MASK::Weight Weight;
int valence = vertex.GetNumEdges();
mask.SetNumVertexWeights(1);
mask.SetNumEdgeWeights(valence);
mask.SetNumFaceWeights(0);
mask.SetFaceWeightsForFaceCenters(false);
Weight vWeight = 0.75f;
Weight eWeight = 0.125f;
mask.VertexWeight(0) = vWeight;
for (int i = 0; i < valence; ++i) {
mask.EdgeWeight(i) = 0.0f;
}
mask.EdgeWeight(creaseEnds[0]) = eWeight;
mask.EdgeWeight(creaseEnds[1]) = eWeight;
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignSmoothMaskForVertex(VERTEX const& vertex, MASK& mask) const {
typedef typename MASK::Weight Weight;
//
// A Smooth vertex must be manifold and interior -- manifold boundary vertices will be
// Creases and non-manifold vertices of any kind will be Corners or Creases. If smooth
// rules for non-manifold vertices are ever defined, this will need adjusting:
//
assert(vertex.GetNumFaces() == vertex.GetNumEdges());
int valence = vertex.GetNumFaces();
mask.SetNumVertexWeights(1);
mask.SetNumEdgeWeights(valence);
mask.SetNumFaceWeights(valence);
mask.SetFaceWeightsForFaceCenters(true);
Weight vWeight = (Weight)(valence - 2) / (Weight)valence;
Weight fWeight = 1.0f / (Weight)(valence * valence);
Weight eWeight = fWeight;
mask.VertexWeight(0) = vWeight;
for (int i = 0; i < valence; ++i) {
mask.EdgeWeight(i) = eWeight;
mask.FaceWeight(i) = fWeight;
}
}
//
// Limit masks for position:
//
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignCornerLimitMask(VERTEX const& /* vertex */, MASK& posMask) const {
posMask.SetNumVertexWeights(1);
posMask.SetNumEdgeWeights(0);
posMask.SetNumFaceWeights(0);
posMask.SetFaceWeightsForFaceCenters(false);
posMask.VertexWeight(0) = 1.0f;
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignCreaseLimitMask(VERTEX const& vertex, MASK& posMask,
int const creaseEnds[2]) const {
typedef typename MASK::Weight Weight;
int valence = vertex.GetNumEdges();
posMask.SetNumVertexWeights(1);
posMask.SetNumEdgeWeights(valence);
posMask.SetNumFaceWeights(0);
posMask.SetFaceWeightsForFaceCenters(false);
Weight vWeight = (Weight)(2.0 / 3.0);
Weight eWeight = (Weight)(1.0 / 6.0);
posMask.VertexWeight(0) = vWeight;
for (int i = 0; i < valence; ++i) {
posMask.EdgeWeight(i) = 0.0f;
}
posMask.EdgeWeight(creaseEnds[0]) = eWeight;
posMask.EdgeWeight(creaseEnds[1]) = eWeight;
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignSmoothLimitMask(VERTEX const& vertex, MASK& posMask) const {
typedef typename MASK::Weight Weight;
int valence = vertex.GetNumFaces();
if (valence == 2) {
assignCornerLimitMask(vertex, posMask);
return;
}
posMask.SetNumVertexWeights(1);
posMask.SetNumEdgeWeights(valence);
posMask.SetNumFaceWeights(valence);
posMask.SetFaceWeightsForFaceCenters(false);
// Specialize for the regular case:
if (valence == 4) {
Weight fWeight = (Weight)(1.0 / 36.0);
Weight eWeight = (Weight)(1.0 / 9.0);
Weight vWeight = (Weight)(4.0 / 9.0);
posMask.VertexWeight(0) = vWeight;
posMask.EdgeWeight(0) = eWeight;
posMask.EdgeWeight(1) = eWeight;
posMask.EdgeWeight(2) = eWeight;
posMask.EdgeWeight(3) = eWeight;
posMask.FaceWeight(0) = fWeight;
posMask.FaceWeight(1) = fWeight;
posMask.FaceWeight(2) = fWeight;
posMask.FaceWeight(3) = fWeight;
} else {
Weight Valence = (Weight) valence;
Weight fWeight = 1.0f / (Valence * (Valence + 5.0f));
Weight eWeight = 4.0f * fWeight;
Weight vWeight = 1.0f - Valence * (eWeight + fWeight);
posMask.VertexWeight(0) = vWeight;
for (int i = 0; i < valence; ++i) {
posMask.EdgeWeight(i) = eWeight;
posMask.FaceWeight(i) = fWeight;
}
}
}
//
// Limit masks for tangents -- these are stubs for now, or have a temporary
// implementation
//
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignCornerLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask) const {
int valence = vertex.GetNumEdges();
tan1Mask.SetNumVertexWeights(1);
tan1Mask.SetNumEdgeWeights(valence);
tan1Mask.SetNumFaceWeights(0);
tan1Mask.SetFaceWeightsForFaceCenters(false);
tan2Mask.SetNumVertexWeights(1);
tan2Mask.SetNumEdgeWeights(valence);
tan2Mask.SetNumFaceWeights(0);
tan2Mask.SetFaceWeightsForFaceCenters(false);
// Should be at least 2 edges -- be sure to clear weights for any more:
tan1Mask.VertexWeight(0) = -1.0f;
tan1Mask.EdgeWeight(0) = 1.0f;
tan1Mask.EdgeWeight(1) = 0.0f;
tan2Mask.VertexWeight(0) = -1.0f;
tan2Mask.EdgeWeight(0) = 0.0f;
tan2Mask.EdgeWeight(1) = 1.0f;
for (int i = 2; i < valence; ++i) {
tan1Mask.EdgeWeight(i) = 0.0f;
tan2Mask.EdgeWeight(i) = 0.0f;
}
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignCreaseLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask, int const creaseEnds[2]) const {
typedef typename MASK::Weight Weight;
//
// First, the tangent along the crease:
// The first crease edge is considered the "leading" edge of the span
// of surface for which we are evaluating tangents and the second edge the
// "trailing edge". By convention, the tangent along the crease is oriented
// in the direction of the leading edge.
//
int numEdges = vertex.GetNumEdges();
int numFaces = vertex.GetNumFaces();
tan1Mask.SetNumVertexWeights(1);
tan1Mask.SetNumEdgeWeights(numEdges);
tan1Mask.SetNumFaceWeights(numFaces);
tan1Mask.SetFaceWeightsForFaceCenters(false);
tan1Mask.VertexWeight(0) = 0.0f;
for (int i = 0; i < numEdges; ++i) {
tan1Mask.EdgeWeight(i) = 0.0f;
}
for (int i = 0; i < numFaces; ++i) {
tan1Mask.FaceWeight(i) = 0.0f;
}
tan1Mask.EdgeWeight(creaseEnds[0]) = 0.5f;
tan1Mask.EdgeWeight(creaseEnds[1]) = -0.5f;
//
// Second, the tangent across the interior faces:
// Note this is ambiguous for an interior vertex. We currently return
// the tangent for the surface in the counter-clockwise span between the
// leading and trailing edges that form the crease. Given the expected
// computation of a surface normal as Tan1 X Tan2, this tangent should be
// oriented "inward" from the crease/boundary -- across the surface rather
// than outward and away from it.
//
tan2Mask.SetNumVertexWeights(1);
tan2Mask.SetNumEdgeWeights(numEdges);
tan2Mask.SetNumFaceWeights(numFaces);
tan2Mask.SetFaceWeightsForFaceCenters(false);
// Prepend weights of 0 preceding the crease:
for (int i = 0; i < creaseEnds[0]; ++i) {
tan2Mask.EdgeWeight(i) = 0.0f;
tan2Mask.FaceWeight(i) = 0.0f;
}
// Assign weights to crease edge and interior points:
int interiorEdgeCount = creaseEnds[1] - creaseEnds[0] - 1;
if (interiorEdgeCount == 1) {
// The regular case -- uniform B-spline cross-tangent:
tan2Mask.VertexWeight(0) = (Weight)(-4.0 / 6.0);
tan2Mask.EdgeWeight(creaseEnds[0]) = (Weight)(-1.0 / 6.0);
tan2Mask.EdgeWeight(creaseEnds[0] + 1) = (Weight)( 4.0 / 6.0);
tan2Mask.EdgeWeight(creaseEnds[1]) = (Weight)(-1.0 / 6.0);
tan2Mask.FaceWeight(creaseEnds[0]) = (Weight)(1.0 / 6.0);
tan2Mask.FaceWeight(creaseEnds[0] + 1) = (Weight)(1.0 / 6.0);
} else if (interiorEdgeCount > 1) {
// The irregular case -- formulae from Biermann et al:
double k = (double) (interiorEdgeCount + 1);
double theta = M_PI / k;
double cosTheta = std::cos(theta);
double sinTheta = std::sin(theta);
// Loop/Schaefer use a different divisor here (3*k + cos(theta)):
double commonDenom = 1.0f / (k * (3.0f + cosTheta));
double R = (cosTheta + 1.0f) / sinTheta;
double vertexWeight = 4.0f * R * (cosTheta - 1.0f);
double creaseWeight = -R * (1.0f + 2.0f * cosTheta);
tan2Mask.VertexWeight(0) = (Weight) (vertexWeight * commonDenom);
tan2Mask.EdgeWeight(creaseEnds[0]) = (Weight) (creaseWeight * commonDenom);
tan2Mask.EdgeWeight(creaseEnds[1]) = (Weight) (creaseWeight * commonDenom);
tan2Mask.FaceWeight(creaseEnds[0]) = (Weight) (sinTheta * commonDenom);
double sinThetaI = 0.0f;
double sinThetaIplus1 = sinTheta;
for (int i = 1; i < k; ++i) {
sinThetaI = sinThetaIplus1;
sinThetaIplus1 = std::sin((i+1)*theta);
tan2Mask.EdgeWeight(creaseEnds[0] + i) = (Weight) ((4.0f * sinThetaI) * commonDenom);
tan2Mask.FaceWeight(creaseEnds[0] + i) = (Weight) ((sinThetaI + sinThetaIplus1) * commonDenom);
}
} else {
// Special case for a single face -- simple average of boundary edges:
tan2Mask.VertexWeight(0) = -6.0f;
tan2Mask.EdgeWeight(creaseEnds[0]) = 3.0f;
tan2Mask.EdgeWeight(creaseEnds[1]) = 3.0f;
tan2Mask.FaceWeight(creaseEnds[0]) = 0.0f;
}
// Append weights of 0 following the crease:
for (int i = creaseEnds[1]; i < numFaces; ++i) {
tan2Mask.FaceWeight(i) = 0.0f;
}
for (int i = creaseEnds[1] + 1; i < numEdges; ++i) {
tan2Mask.EdgeWeight(i) = 0.0f;
}
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_CATMARK>::assignSmoothLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask) const {
typedef typename MASK::Weight Weight;
int valence = vertex.GetNumFaces();
if (valence == 2) {
assignCornerLimitTangentMasks(vertex, tan1Mask, tan2Mask);
return;
}
// Compute tan1 initially -- tan2 is simply a rotation:
tan1Mask.SetNumVertexWeights(1);
tan1Mask.SetNumEdgeWeights(valence);
tan1Mask.SetNumFaceWeights(valence);
tan1Mask.SetFaceWeightsForFaceCenters(false);
tan1Mask.VertexWeight(0) = 0.0f;
if (valence == 4) {
tan1Mask.EdgeWeight(0) = 4.0f;
tan1Mask.EdgeWeight(1) = 0.0f;
tan1Mask.EdgeWeight(2) = -4.0f;
tan1Mask.EdgeWeight(3) = 0.0f;
tan1Mask.FaceWeight(0) = 1.0f;
tan1Mask.FaceWeight(1) = -1.0f;
tan1Mask.FaceWeight(2) = -1.0f;
tan1Mask.FaceWeight(3) = 1.0f;
} else {
double theta = 2.0f * M_PI / (double)valence;
double cosTheta = std::cos(theta);
double cosHalfTheta = std::cos(theta * 0.5f);
double lambda = (5.0 / 16.0) + (1.0 / 16.0) *
(cosTheta + cosHalfTheta * std::sqrt(2.0f * (9.0f + cosTheta)));
double edgeWeightScale = 4.0f;
double faceWeightScale = 1.0f / (4.0f * lambda - 1.0f);
for (int i = 0; i < valence; ++i) {
double cosThetaI = std::cos( i * theta);
double cosThetaIplus1 = std::cos((i+1)* theta);
tan1Mask.EdgeWeight(i) = (Weight) (edgeWeightScale * cosThetaI);
tan1Mask.FaceWeight(i) = (Weight) (faceWeightScale * (cosThetaI + cosThetaIplus1));
}
}
// Now rotate/copy tan1 weights to tan2:
tan2Mask.SetNumVertexWeights(1);
tan2Mask.SetNumEdgeWeights(valence);
tan2Mask.SetNumFaceWeights(valence);
tan2Mask.SetFaceWeightsForFaceCenters(false);
tan2Mask.VertexWeight(0) = 0.0f;
if (valence == 4) {
tan2Mask.EdgeWeight(0) = 0.0f;
tan2Mask.EdgeWeight(1) = 4.0f;
tan2Mask.EdgeWeight(2) = 0.0f;
tan2Mask.EdgeWeight(3) = -4.0f;
tan2Mask.FaceWeight(0) = 1.0f;
tan2Mask.FaceWeight(1) = 1.0f;
tan2Mask.FaceWeight(2) = -1.0f;
tan2Mask.FaceWeight(3) = -1.0f;
} else {
tan2Mask.EdgeWeight(0) = tan1Mask.EdgeWeight(valence-1);
tan2Mask.FaceWeight(0) = tan1Mask.FaceWeight(valence-1);
for (int i = 1; i < valence; ++i) {
tan2Mask.EdgeWeight(i) = tan1Mask.EdgeWeight(i-1);
tan2Mask.FaceWeight(i) = tan1Mask.FaceWeight(i-1);
}
}
}
} // end namespace sdc
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_SDC_CATMARK_SCHEME_H */

View File

@@ -0,0 +1,183 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../sdc/crease.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Sdc {
//
// Declarations of creasing constants and non-inline methods:
//
float const Crease::SHARPNESS_SMOOTH = 0.0f;
float const Crease::SHARPNESS_INFINITE = 10.0f;
//
// Creasing queries dependent on sharpness values:
//
Crease::Rule
Crease::DetermineVertexVertexRule(float vertexSharpness, int sharpEdgeCount) const {
if (IsSharp(vertexSharpness)) return Crease::RULE_CORNER;
return (sharpEdgeCount > 2) ? Crease::RULE_CORNER : (Crease::Rule)(1 << sharpEdgeCount);
}
Crease::Rule
Crease::DetermineVertexVertexRule(float vertexSharpness,
int incidentEdgeCount,
float const* incidentEdgeSharpness) const {
if (IsSharp(vertexSharpness)) return Crease::RULE_CORNER;
int sharpEdgeCount = 0;
for (int i = 0; i < incidentEdgeCount; ++i) {
sharpEdgeCount += IsSharp(incidentEdgeSharpness[i]);
}
return (sharpEdgeCount > 2) ? Crease::RULE_CORNER : (Crease::Rule)(1 << sharpEdgeCount);
}
float
Crease::ComputeFractionalWeightAtVertex(float parentVertexSharpness,
float childVertexSharpness,
int incidentEdgeCount,
float const* parentSharpness,
float const* childSharpness) const {
int transitionCount = 0;
float transitionSum = 0.0f;
if (IsSharp(parentVertexSharpness) && IsSmooth(childVertexSharpness)) {
transitionCount = 1;
transitionSum = parentVertexSharpness;
}
//
// We need the child-edge sharpness values for non-simple methods to ensure
// that the sharpness went from a non-zero value (potentially greater than
// 1.0) to zero...
//
if (IsUniform() || (childSharpness == 0)) {
for (int i = 0; i < incidentEdgeCount; ++i) {
if (IsSharp(parentSharpness[i]) && (parentSharpness[i] <= 1.0f)) {
transitionSum += parentSharpness[i];
transitionCount ++;
}
}
} else {
for (int i = 0; i < incidentEdgeCount; ++i) {
if (IsSharp(parentSharpness[i]) && IsSmooth(childSharpness[i])) {
transitionSum += parentSharpness[i];
transitionCount ++;
}
}
}
if (transitionCount == 0) return 0.0f;
float fractionalWeight = transitionSum / (float)transitionCount;
return (fractionalWeight > 1.0f) ? 1.0f : fractionalWeight;
}
//
// Subdividing edge sharpness values (vertex sharpness is inline):
//
float
Crease::SubdivideEdgeSharpnessAtVertex(float edgeSharpness,
int incEdgeCountAtVertex,
float const * incEdgeSharpness) const {
if (IsUniform() || (incEdgeCountAtVertex < 2)) {
return decrementSharpness(edgeSharpness);
}
if (IsSmooth(edgeSharpness)) return Crease::SHARPNESS_SMOOTH;
if (IsInfinite(edgeSharpness)) return Crease::SHARPNESS_INFINITE;
float sharpSum = 0.0f;
int sharpCount = 0;
for (int i = 0; i < incEdgeCountAtVertex; ++i) {
if (IsSemiSharp(incEdgeSharpness[i])) {
sharpCount ++;
sharpSum += incEdgeSharpness[i];
}
}
if (sharpCount > 1) {
// Chaikin rule is 3/4 original sharpness + 1/4 average of the others
float avgSharpnessAtVertex = (sharpSum - edgeSharpness) / (float)(sharpCount - 1);
edgeSharpness = (0.75f * edgeSharpness) + (0.25f * avgSharpnessAtVertex);
}
edgeSharpness -= 1.0f;
return IsSharp(edgeSharpness) ? edgeSharpness : Crease::SHARPNESS_SMOOTH;
}
void
Crease::SubdivideEdgeSharpnessesAroundVertex(int edgeCount,
float const* parentSharpness,
float * childSharpness) const {
if (IsUniform() || (edgeCount < 2)) {
for (int i = 0; i < edgeCount; ++i) {
childSharpness[i] = decrementSharpness(parentSharpness[i]);
}
return;
}
//
// Chaikin creasing is most efficiently computed for all edges around a vertex at
// once as the subdivided value for each creased edge depends on the average of
// the other edges around the vertex. So we can sum up the sharpness around the
// vertex once and use that for each edge, rather than iterating around the vertex
// for each incident edge.
//
if (_options.GetCreasingMethod() == Options::CREASE_CHAIKIN) {
float sharpSum = 0.0f;
int sharpCount = 0;
for (int i = 0; i < edgeCount; ++i) {
if (IsSemiSharp(parentSharpness[i])) {
sharpCount ++;
sharpSum += parentSharpness[i];
}
}
//
// The smooth case is most common -- specialize for it first:
//
if (sharpCount == 0) {
for (int i = 0; i < edgeCount; ++i) {
childSharpness[i] = parentSharpness[i];
}
} else {
for (int i = 0; i < edgeCount; ++i) {
float const& pSharp = parentSharpness[i];
float& cSharp = childSharpness[i];
if (IsSmooth(pSharp)) {
cSharp = Crease::SHARPNESS_SMOOTH;
} else if (IsInfinite(pSharp)) {
cSharp = Crease::SHARPNESS_INFINITE;
} else if (sharpCount == 1) {
// Need special case here anyway to avoid divide by zero below...
cSharp = decrementSharpness(pSharp);
} else {
float pOtherAverage = (sharpSum - pSharp) / (float)(sharpCount - 1);
// Chaikin rule is 3/4 original sharpness + 1/4 average of the others
cSharp = ((0.75f * pSharp) + (0.25f * pOtherAverage)) - 1.0f;
if (IsSmooth(cSharp)) cSharp = Crease::SHARPNESS_SMOOTH;
}
}
}
}
}
} // end namespace sdc
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,230 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_SDC_CREASE_H
#define OPENSUBDIV3_SDC_CREASE_H
#include "../version.h"
#include "../sdc/options.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Sdc {
///
/// \brief Types, constants and utilities related to semi-sharp creasing -- whose implementation
/// is independent of the subdivision scheme.
///
/// Crease is intended to be a light-weight, trivially constructed class that computes
/// crease-related properties -- typically sharpness values and associated interpolation
/// weights. An instance of Crease is defined with a set of options that include current
/// and future variations that will impact computations involving sharpness values.
///
/// The Crease methods do not use topological neighborhoods as input. The methods here
/// rely more on the sharpness values and less on the topology, so we choose to work directly
/// with the sharpness values. We also follow the trend of using primitive arrays in the
/// interface to encourage local gathering for re-use.
///
/// Note on the need for and use of sharpness values:
/// In general, mask queries rely on the sharpness values. The common case of a smooth
/// vertex, when known, avoids the need to inspect them, but unless the rules are well understood,
/// users will be expected to provided them -- particularly when they expect the mask queries
/// to do all of the work (just determining if a vertex is smooth will require inspection of
/// incident edge sharpness).
/// Mask queries will occasionally require the subdivided sharpness values around the
/// child vertex. So users will be expected to either provide them up front when known, or to be
/// gathered on demand. Any implementation of subdivision with creasing cannot avoid subdividing
/// the sharpness values first, so keeping them available for re-use is a worthwhile consideration.
///
class Crease {
public:
//@{
/// Constants and related queries of sharpness values:
///
static float const SHARPNESS_SMOOTH; // = 0.0f, do we really need this?
static float const SHARPNESS_INFINITE; // = 10.0f;
static bool IsSmooth(float sharpness) { return sharpness <= SHARPNESS_SMOOTH; }
static bool IsSharp(float sharpness) { return sharpness > SHARPNESS_SMOOTH; }
static bool IsInfinite(float sharpness) { return sharpness >= SHARPNESS_INFINITE; }
static bool IsSemiSharp(float sharpness) { return (SHARPNESS_SMOOTH < sharpness) && (sharpness < SHARPNESS_INFINITE); }
//@}
///
/// Enum for the types of subdivision rules applied based on sharpness values (note these
/// correspond to Hbr's vertex "mask"). The values are assigned to bit positions as it is
/// useful to use bitwise operations to inspect collections of vertices (i.e. all of the
/// vertices incident a particular face).
///
enum Rule {
RULE_UNKNOWN = 0,
RULE_SMOOTH = (1 << 0),
RULE_DART = (1 << 1),
RULE_CREASE = (1 << 2),
RULE_CORNER = (1 << 3)
};
public:
Crease() : _options() { }
Crease(Options const& options) : _options(options) { }
~Crease() { }
bool IsUniform() const { return _options.GetCreasingMethod() == Options::CREASE_UNIFORM; }
//@{
/// Optional sharp features:
/// Since options treat certain topological features as infinitely sharp -- boundaries
/// or (in future) non-manifold features -- sharpness values should be adjusted before use.
/// The following methods will adjust (by return) specific values according to the options
/// applied.
///
float SharpenBoundaryEdge(float edgeSharpness) const;
float SharpenBoundaryVertex(float edgeSharpness) const;
// For future consideration
//float SharpenNonManifoldEdge(float edgeSharpness) const;
//float SharpenNonManifoldVertex(float edgeSharpness) const;
//@}
//@{
/// Sharpness subdivision:
/// The computation of a Uniform subdivided sharpness value is as follows:
/// - Smooth edges or verts stay Smooth
/// - Sharp edges or verts stay Sharp
/// - semi-sharp edges or verts are decremented by 1.0
/// but for Chaikin (and potentially future non-uniform schemes that improve upon it) the
/// computation is more involved. In the case of edges in particular, the sharpness of a
/// child edge is determined by the sharpness in the neighborhood of the end vertex
/// corresponding to the child. For this reason, an alternative to subdividing sharpness
/// that computes all child edges around a vertex is given.
///
float SubdivideUniformSharpness(float vertexOrEdgeSharpness) const;
float SubdivideVertexSharpness(float vertexSharpness) const;
float SubdivideEdgeSharpnessAtVertex(float edgeSharpness,
int incidentEdgeCountAtEndVertex,
float const* edgeSharpnessAroundEndVertex) const;
void SubdivideEdgeSharpnessesAroundVertex(int incidentEdgeCountAtVertex,
float const* incidentEdgeSharpnessAroundVertex,
float* childEdgesSharpnessAroundVertex) const;
//@}
//@{
/// Rule determination:
/// Mask queries do not require the Rule to be known, it can be determined from
/// the information provided, but it is generally more efficient when the Rule is known
/// and provided. In particular, the Smooth case dominates and is known to be applicable
/// based on the origin of the vertex without inspection of sharpness.
///
Rule DetermineVertexVertexRule(float vertexSharpness,
int incidentEdgeCount,
float const* incidentEdgeSharpness) const;
Rule DetermineVertexVertexRule(float vertexSharpness,
int sharpEdgeCount) const;
//@}
/// \brief Transitional weighting:
/// When the rules applicable to a parent vertex and its child differ, one or more
/// sharpness values has "decayed" to zero. Both rules are then applicable and blended
/// by a weight between 0 and 1 that reflects the transition. Most often this will be
/// a single sharpness value that decays from within the interval [0,1] to zero -- and
/// the weight to apply is exactly that sharpness value -- but more than one may decay,
/// and values > 1 may also decay to 0 in a single step while others within [0,1] may
/// remain > 0.
/// So to properly determine a transitional weight, sharpness values for both the
/// parent and child must be inspected, combined and clamped accordingly.
///
float ComputeFractionalWeightAtVertex(float vertexSharpness,
float childVertexSharpness,
int incidentEdgeCount,
float const* incidentEdgeSharpness,
float const* childEdgesSharpness) const;
void GetSharpEdgePairOfCrease(float const * incidentEdgeSharpness,
int incidentEdgeCount,
int sharpEdgePair[2]) const;
// Would these really help? Maybe only need Rules for the vertex-vertex case...
//
// Rule DetermineEdgeVertexRule(float parentEdgeSharpness) const;
// Rule DetermineEdgeVertexRule(float childEdge1Sharpness, float childEdge2Sharpness) const;
protected:
float decrementSharpness(float sharpness) const;
private:
Options _options;
};
//
// Inline declarations:
//
inline float
Crease::SharpenBoundaryEdge(float /* edgeSharpness */) const {
//
// Despite the presence of the BOUNDARY_NONE option, boundary edges are always sharpened.
// Much of the code relies on sharpness to indicate boundaries to avoid the more complex
// topological inspection
//
return SHARPNESS_INFINITE;
}
inline float
Crease::SharpenBoundaryVertex(float vertexSharpness) const {
return (_options.GetVtxBoundaryInterpolation() == Options::VTX_BOUNDARY_EDGE_AND_CORNER) ?
SHARPNESS_INFINITE : vertexSharpness;
}
inline float
Crease::decrementSharpness(float sharpness) const {
if (IsSmooth(sharpness)) return Crease::SHARPNESS_SMOOTH; // redundant but most common
if (IsInfinite(sharpness)) return Crease::SHARPNESS_INFINITE;
if (sharpness > 1.0f) return (sharpness - 1.0f);
return Crease::SHARPNESS_SMOOTH;
}
inline float
Crease::SubdivideUniformSharpness(float vertexOrEdgeSharpness) const {
return decrementSharpness(vertexOrEdgeSharpness);
}
inline float
Crease::SubdivideVertexSharpness(float vertexSharpness) const {
return decrementSharpness(vertexSharpness);
}
inline void
Crease::GetSharpEdgePairOfCrease(float const * incidentEdgeSharpness, int incidentEdgeCount,
int sharpEdgePair[2]) const {
// Only to be called when a crease is present at a vertex -- exactly two sharp
// edges are expected here:
//
sharpEdgePair[0] = 0;
while (IsSmooth(incidentEdgeSharpness[sharpEdgePair[0]])) ++ sharpEdgePair[0];
sharpEdgePair[1] = incidentEdgeCount - 1;
while (IsSmooth(incidentEdgeSharpness[sharpEdgePair[1]])) -- sharpEdgePair[1];
}
} // end namespace sdc
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_SDC_CREASE_H */

View File

@@ -0,0 +1,566 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_SDC_LOOP_SCHEME_H
#define OPENSUBDIV3_SDC_LOOP_SCHEME_H
#include "../version.h"
#include "../sdc/scheme.h"
#include <cassert>
#include <cmath>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Sdc {
//
// Specializations for Sdc::Scheme<SCHEME_LOOP>:
//
//
//
// Loop traits:
//
template <>
inline Split Scheme<SCHEME_LOOP>::GetTopologicalSplitType() { return SPLIT_TO_TRIS; }
template <>
inline int Scheme<SCHEME_LOOP>::GetRegularFaceSize() { return 3; }
template <>
inline int Scheme<SCHEME_LOOP>::GetRegularVertexValence() { return 6; }
template <>
inline int Scheme<SCHEME_LOOP>::GetLocalNeighborhoodSize() { return 1; }
//
// Protected methods to assign the two types of masks for an edge-vertex --
// Crease and Smooth.
//
// The Crease case does not really need to be specialized, though it may be
// preferable to define all explicitly here.
//
template <>
template <typename EDGE, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignCreaseMaskForEdge(EDGE const&, MASK& mask) const
{
mask.SetNumVertexWeights(2);
mask.SetNumEdgeWeights(0);
mask.SetNumFaceWeights(0);
mask.SetFaceWeightsForFaceCenters(false);
mask.VertexWeight(0) = 0.5f;
mask.VertexWeight(1) = 0.5f;
}
template <>
template <typename EDGE, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignSmoothMaskForEdge(EDGE const& edge, MASK& mask) const
{
int faceCount = edge.GetNumFaces();
mask.SetNumVertexWeights(2);
mask.SetNumEdgeWeights(0);
mask.SetNumFaceWeights(faceCount);
mask.SetFaceWeightsForFaceCenters(false);
//
// This is where we run into the issue of "face weights" -- we want to weight the
// face-centers for Catmark, but face-centers are not generated for Loop. So do
// we make assumptions on how the mask is used, assign some property to the mask
// to indicate how they were assigned, or take input from the mask itself?
//
// Regardless, we have two choices:
// - face-weights are for the vertices opposite the edge (as in Hbr):
// vertex weights = 0.375f;
// face weights = 0.125f;
//
// - face-weights are for the face centers:
// vertex weights = 0.125f;
// face weights = 0.375f;
//
// Coincidentally the coefficients are the same but reversed.
//
typedef typename MASK::Weight Weight;
Weight vWeight = mask.AreFaceWeightsForFaceCenters() ? 0.125f : 0.375f;
Weight fWeight = mask.AreFaceWeightsForFaceCenters() ? 0.375f : 0.125f;
mask.VertexWeight(0) = vWeight;
mask.VertexWeight(1) = vWeight;
if (faceCount == 2) {
mask.FaceWeight(0) = fWeight;
mask.FaceWeight(1) = fWeight;
} else {
// The non-manifold case is not clearly defined -- we adjust the above
// face-weight to preserve the ratio of edge-center and face-centers:
fWeight *= 2.0f / (Weight) faceCount;
for (int i = 0; i < faceCount; ++i) {
mask.FaceWeight(i) = fWeight;
}
}
}
//
// Protected methods to assign the three types of masks for a vertex-vertex --
// Corner, Crease and Smooth (Dart is the same as Smooth).
//
// Corner and Crease do not really need to be specialized, though it may be
// preferable to define all explicitly here.
//
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignCornerMaskForVertex(VERTEX const&, MASK& mask) const
{
mask.SetNumVertexWeights(1);
mask.SetNumEdgeWeights(0);
mask.SetNumFaceWeights(0);
mask.SetFaceWeightsForFaceCenters(false);
mask.VertexWeight(0) = 1.0f;
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignCreaseMaskForVertex(VERTEX const& vertex, MASK& mask,
int const creaseEnds[2]) const {
typedef typename MASK::Weight Weight;
int valence = vertex.GetNumEdges();
mask.SetNumVertexWeights(1);
mask.SetNumEdgeWeights(valence);
mask.SetNumFaceWeights(0);
mask.SetFaceWeightsForFaceCenters(false);
Weight vWeight = 0.75f;
Weight eWeight = 0.125f;
mask.VertexWeight(0) = vWeight;
for (int i = 0; i < valence; ++i) {
mask.EdgeWeight(i) = 0.0f;
}
mask.EdgeWeight(creaseEnds[0]) = eWeight;
mask.EdgeWeight(creaseEnds[1]) = eWeight;
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignSmoothMaskForVertex(VERTEX const& vertex, MASK& mask) const
{
typedef typename MASK::Weight Weight;
int valence = vertex.GetNumFaces();
mask.SetNumVertexWeights(1);
mask.SetNumEdgeWeights(valence);
mask.SetNumFaceWeights(0);
mask.SetFaceWeightsForFaceCenters(false);
// Specialize for the regular case: 1/16 per edge-vert, 5/8 for the vert itself:
Weight eWeight = (Weight) 0.0625f;
Weight vWeight = (Weight) 0.625f;
if (valence != 6) {
// From HbrLoopSubdivision<T>::Subdivide(mesh, vertex):
// - could use some lookup tables here for common irregular valence (5, 7, 8)
// or all of these cosine calls will be adding up...
double dValence = (double) valence;
double invValence = 1.0f / dValence;
double cosTheta = std::cos(M_PI * 2.0f * invValence);
double beta = 0.25f * cosTheta + 0.375f;
eWeight = (Weight) ((0.625f - (beta * beta)) * invValence);
vWeight = (Weight) (1.0f - (eWeight * dValence));
}
mask.VertexWeight(0) = vWeight;
for (int i = 0; i < valence; ++i) {
mask.EdgeWeight(i) = eWeight;
}
}
//
// Limit masks for position:
//
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignCornerLimitMask(VERTEX const& /* vertex */, MASK& posMask) const {
posMask.SetNumVertexWeights(1);
posMask.SetNumEdgeWeights(0);
posMask.SetNumFaceWeights(0);
posMask.SetFaceWeightsForFaceCenters(false);
posMask.VertexWeight(0) = 1.0f;
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignCreaseLimitMask(VERTEX const& vertex, MASK& posMask,
int const creaseEnds[2]) const {
typedef typename MASK::Weight Weight;
int valence = vertex.GetNumEdges();
posMask.SetNumVertexWeights(1);
posMask.SetNumEdgeWeights(valence);
posMask.SetNumFaceWeights(0);
posMask.SetFaceWeightsForFaceCenters(false);
//
// The refinement mask for a crease vertex is (1/8, 3/4, 1/8) and for a crease
// edge is (1/2, 1/2) -- producing a uniform B-spline curve along the crease
// (boundary) whether the vertex or its crease is regular or not. The limit
// mask is therefore (1/6, 2/3, 1/6) for ALL cases.
//
// An alternative limit mask (1/5, 3/5, 1/5) is often published for use either
// for irregular crease vertices or for all crease/boundary vertices, but this
// is based on an alternate refinement mask for the edge -- (3/8, 5/8) versus
// the usual (1/2, 1/2) -- and will not produce the B-spline curve desired.
//
Weight vWeight = (Weight) (4.0 / 6.0);
Weight eWeight = (Weight) (1.0 / 6.0);
posMask.VertexWeight(0) = vWeight;
for (int i = 0; i < valence; ++i) {
posMask.EdgeWeight(i) = 0.0f;
}
posMask.EdgeWeight(creaseEnds[0]) = eWeight;
posMask.EdgeWeight(creaseEnds[1]) = eWeight;
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignSmoothLimitMask(VERTEX const& vertex, MASK& posMask) const {
typedef typename MASK::Weight Weight;
int valence = vertex.GetNumFaces();
posMask.SetNumVertexWeights(1);
posMask.SetNumEdgeWeights(valence);
posMask.SetNumFaceWeights(0);
posMask.SetFaceWeightsForFaceCenters(false);
// Specialize for the regular case: 1/12 per edge-vert, 1/2 for the vert itself:
if (valence == 6) {
Weight eWeight = (Weight) (1.0 / 12.0);
Weight vWeight = 0.5f;
posMask.VertexWeight(0) = vWeight;
posMask.EdgeWeight(0) = eWeight;
posMask.EdgeWeight(1) = eWeight;
posMask.EdgeWeight(2) = eWeight;
posMask.EdgeWeight(3) = eWeight;
posMask.EdgeWeight(4) = eWeight;
posMask.EdgeWeight(5) = eWeight;
} else {
double dValence = (double) valence;
double invValence = 1.0f / dValence;
double cosTheta = std::cos(M_PI * 2.0f * invValence);
double beta = 0.25f * cosTheta + 0.375f;
double gamma = (0.625f - (beta * beta)) * invValence;
Weight eWeight = (Weight) (1.0f / (dValence + 3.0f / (8.0f * gamma)));
Weight vWeight = (Weight) (1.0f - (eWeight * dValence));
posMask.VertexWeight(0) = vWeight;
for (int i = 0; i < valence; ++i) {
posMask.EdgeWeight(i) = eWeight;
}
}
}
/*
// Limit masks for tangents:
//
// A note on tangent magnitudes:
//
// Several formulae exist for limit tangents at a vertex to accommodate the
// different topological configurations around the vertex. While these produce
// the desired direction, there is inconsistency in the resulting magnitudes.
// Ideally a regular mesh of uniformly shaped triangles with similar edge lengths
// should produce tangents of similar magnitudes throughout -- including corners
// and boundaries. So some of the common formulae for these are adjusted with
// scale factors.
//
// For uses where magnitude does not matter, this scaling should be irrelevant.
// But just as with patches, where the magnitudes of partial derivatives are
// consistent between similar patches, the magnitudes of limit tangents should
// also be similar.
//
// The reference tangents, in terms of magnitudes, are those produced by the
// limit tangent mask for smooth interior vertices, for which well established
// sin/cos formulae apply -- these remain unscaled. Formulae for the other
// crease/boundary, corner tangents and irregular cases are scaled to be more
// consistent with these.
//
// The crease/boundary tangents for the regular case can be viewed as derived
// from the smooth interior masks with two "phantom" points extrapolated across
// the regular boundary:
//
// v3 v2
// X - - - - - X
// / \ / \
// / \ / \
// v4 X - - - - - X - - - - - X v1
// . . 0 . .
// . . . .
// . . . .
// (v5) (v6)
//
// where v5 = v0 + (v4 - v3) and v6 = v0 + v1 - v2.
//
// When the standard limit tangent mask is applied, the cosines of increments
// of pi/3 give us coefficients that are multiples of 1/2, leading to the first
// tangent T1 = 3/2 * (v1 - v4), rather than the widely used T1 = v1 - v4. So
// this scale factor of 3/2 is applied to ensure tangents along the boundaries
// are of similar magnitude as tangents in the immediate interior (which may be
// parallel).
//
// Tangents at corners are essentially a form of boundary tangent, and so its
// simple difference formula is scaled to be consistent with adjoining boundary
// tangents -- not just with the 3/2 factor from above, but with an additional
// 2.0 to compensate for the fact that the difference of only side of the vertex
// is considered here. The resulting scale factor of 3.0 for the regular corner
// is what similarly arises by extrapolating an interior region around the
// vertex and using the interior mask for the first tangent.
//
// The cross-tangent formula for the regular crease/boundary is similarly found
// from the above construction of the boundary, but the commonly used weights of
// +/- 1 and 2 result from omitting the common factor of sqrt(3)/2 (arising from
// the sines of increments of pi/3). With that scale factor close to one, it has
// less impact than the irregular cases, which are analogous to corner tangents
// in that differences on only one side of the vertex are considered. While a
// scaling of 3.0 is similarly understandable for the valence 2 and 3 cases, it is
// less obvious in the irregular formula for valence > 4, but similarly effective.
//
// The end result of these adjustments should be a set of limit tangents that are
// of similar magnitude over a regular mesh including boundaries and corners.
*/
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignCornerLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask) const {
int valence = vertex.GetNumEdges();
tan1Mask.SetNumVertexWeights(1);
tan1Mask.SetNumEdgeWeights(valence);
tan1Mask.SetNumFaceWeights(0);
tan1Mask.SetFaceWeightsForFaceCenters(false);
tan2Mask.SetNumVertexWeights(1);
tan2Mask.SetNumEdgeWeights(valence);
tan2Mask.SetNumFaceWeights(0);
tan2Mask.SetFaceWeightsForFaceCenters(false);
// See note above regarding scale factor of 3.0:
tan1Mask.VertexWeight(0) = -3.0f;
tan1Mask.EdgeWeight(0) = 3.0f;
tan1Mask.EdgeWeight(1) = 0.0f;
tan2Mask.VertexWeight(0) = -3.0f;
tan2Mask.EdgeWeight(0) = 0.0f;
tan2Mask.EdgeWeight(1) = 3.0f;
// Should be at least 2 edges -- be sure to clear weights for any more:
for (int i = 2; i < valence; ++i) {
tan1Mask.EdgeWeight(i) = 0.0f;
tan2Mask.EdgeWeight(i) = 0.0f;
}
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignCreaseLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask, int const creaseEnds[2]) const {
typedef typename MASK::Weight Weight;
//
// First, the tangent along the crease:
// The first crease edge is considered the "leading" edge of the span
// of surface for which we are evaluating tangents and the second edge the
// "trailing edge". By convention, the tangent along the crease is oriented
// in the direction of the leading edge.
//
int valence = vertex.GetNumEdges();
tan1Mask.SetNumVertexWeights(1);
tan1Mask.SetNumEdgeWeights(valence);
tan1Mask.SetNumFaceWeights(0);
tan1Mask.SetFaceWeightsForFaceCenters(false);
tan1Mask.VertexWeight(0) = 0.0f;
for (int i = 0; i < valence; ++i) {
tan1Mask.EdgeWeight(i) = 0.0f;
}
// See the note above regarding scale factor of 1.5:
tan1Mask.EdgeWeight(creaseEnds[0]) = 1.5f;
tan1Mask.EdgeWeight(creaseEnds[1]) = -1.5f;
//
// Second, the tangent across the interior faces:
// Note this is ambiguous for an interior vertex. We currently return
// the tangent for the surface in the counter-clockwise span between the
// leading and trailing edges that form the crease. Given the expected
// computation of a surface normal as Tan1 X Tan2, this tangent should be
// oriented "inward" from the crease/boundary -- across the surface rather
// than outward and away from it.
//
// There is inconsistency in the orientation of this tangent in commonly
// published results: the general formula provided for arbitrary valence
// has the tangent pointing across the crease and "outward" from the surface,
// while the special cases for regular valence and lower have the tangent
// pointing across the surface and "inward" from the crease. So if we are
// to consistently orient the first tangent along the crease, regardless of
// the interior topology, we have to correct this. With the first tangent
// following the direction of the leading crease edge, we want the second
// tangent pointing inward/across the surface -- so we flip the result of
// the general formula.
//
tan2Mask.SetNumVertexWeights(1);
tan2Mask.SetNumEdgeWeights(valence);
tan2Mask.SetNumFaceWeights(0);
tan2Mask.SetFaceWeightsForFaceCenters(false);
for (int i = 0; i < creaseEnds[0]; ++i) {
tan2Mask.EdgeWeight(i) = 0.0f;
}
int interiorEdgeCount = creaseEnds[1] - creaseEnds[0] - 1;
if (interiorEdgeCount == 2) {
// See note above regarding scale factor of (sin(60 degs) == sqrt(3)/2:
static Weight const Root3 = (Weight) 1.73205080756887729352;
static Weight const Root3by2 = (Weight) (Root3 * 0.5);
tan2Mask.VertexWeight(0) = -Root3;
tan2Mask.EdgeWeight(creaseEnds[0]) = -Root3by2;
tan2Mask.EdgeWeight(creaseEnds[1]) = -Root3by2;
tan2Mask.EdgeWeight(creaseEnds[0] + 1) = Root3;
tan2Mask.EdgeWeight(creaseEnds[0] + 2) = Root3;
} else if (interiorEdgeCount > 2) {
// See notes above regarding scale factor of -3.0 (-1 for orientation,
// 2.0 for considering the region as a half-disk, and 1.5 in keeping
// with the crease tangent):
double theta = M_PI / (interiorEdgeCount + 1);
tan2Mask.VertexWeight(0) = 0.0f;
Weight cWeight = (Weight) (-3.0f * std::sin(theta));
tan2Mask.EdgeWeight(creaseEnds[0]) = cWeight;
tan2Mask.EdgeWeight(creaseEnds[1]) = cWeight;
double eCoeff = -3.0f * 2.0f * (std::cos(theta) - 1.0f);
for (int i = 1; i <= interiorEdgeCount; ++i) {
tan2Mask.EdgeWeight(creaseEnds[0] + i) = (Weight) (eCoeff * std::sin(i * theta));
}
} else if (interiorEdgeCount == 1) {
// See notes above regarding scale factor of 3.0:
tan2Mask.VertexWeight(0) = -3.0f;
tan2Mask.EdgeWeight(creaseEnds[0]) = 0.0f;
tan2Mask.EdgeWeight(creaseEnds[1]) = 0.0f;
tan2Mask.EdgeWeight(creaseEnds[0] + 1) = 3.0f;
} else {
// See notes above regarding scale factor of 3.0:
tan2Mask.VertexWeight(0) = -6.0f;
tan2Mask.EdgeWeight(creaseEnds[0]) = 3.0f;
tan2Mask.EdgeWeight(creaseEnds[1]) = 3.0f;
}
for (int i = creaseEnds[1] + 1; i < valence; ++i) {
tan2Mask.EdgeWeight(i) = 0.0f;
}
}
template <>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME_LOOP>::assignSmoothLimitTangentMasks(VERTEX const& vertex,
MASK& tan1Mask, MASK& tan2Mask) const {
typedef typename MASK::Weight Weight;
int valence = vertex.GetNumFaces();
tan1Mask.SetNumVertexWeights(1);
tan1Mask.SetNumEdgeWeights(valence);
tan1Mask.SetNumFaceWeights(0);
tan1Mask.SetFaceWeightsForFaceCenters(false);
tan2Mask.SetNumVertexWeights(1);
tan2Mask.SetNumEdgeWeights(valence);
tan2Mask.SetNumFaceWeights(0);
tan2Mask.SetFaceWeightsForFaceCenters(false);
tan1Mask.VertexWeight(0) = 0.0f;
tan2Mask.VertexWeight(0) = 0.0f;
if (valence == 6) {
static Weight const Root3by2 = (Weight)(0.5 * 1.73205080756887729352);
tan1Mask.EdgeWeight(0) = 1.0f;
tan1Mask.EdgeWeight(1) = 0.5f;
tan1Mask.EdgeWeight(2) = -0.5f;
tan1Mask.EdgeWeight(3) = -1.0f;
tan1Mask.EdgeWeight(4) = -0.5f;
tan1Mask.EdgeWeight(5) = 0.5f;
tan2Mask.EdgeWeight(0) = 0.0f;
tan2Mask.EdgeWeight(1) = Root3by2;
tan2Mask.EdgeWeight(2) = Root3by2;
tan2Mask.EdgeWeight(3) = 0.0f;
tan2Mask.EdgeWeight(4) = -Root3by2;
tan2Mask.EdgeWeight(5) = -Root3by2;
} else {
double alpha = 2.0f * M_PI / valence;
for (int i = 0; i < valence; ++i) {
double alphaI = alpha * i;
tan1Mask.EdgeWeight(i) = (Weight) std::cos(alphaI);
tan2Mask.EdgeWeight(i) = (Weight) std::sin(alphaI);
}
}
}
} // end namespace Sdc
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_SDC_LOOP_SCHEME_H */

View File

@@ -0,0 +1,111 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_SDC_OPTIONS_H
#define OPENSUBDIV3_SDC_OPTIONS_H
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Sdc {
///
/// \brief All supported options applying to subdivision scheme.
///
/// The Options class contains all supported options that can be applied to a
/// subdivision scheme to affect the shape of the limit surface. These differ from
/// approximations that may be applied at a higher level, i.e. options to limit the
/// level of feature adaptive subdivision, options to ignore fractional creasing,
/// or creasing entirely, etc. These options define the shape of a particular
/// limit surface, including the "shape" of primitive variable data associated with
/// it.
///
/// The intent is that these sets of options be defined at a high level and
/// propagated into the lowest-level computation in support of each subdivision
/// scheme. Ideally it remains a set of bit-fields (essentially an int) and so
/// remains light weight and easily passed around by value.
///
class Options {
public:
enum VtxBoundaryInterpolation {
VTX_BOUNDARY_NONE = 0, ///< no boundary interpolation, except where
///< boundary edges were explicitly sharpened
VTX_BOUNDARY_EDGE_ONLY, ///< all boundary edges sharpened and interpolated
VTX_BOUNDARY_EDGE_AND_CORNER ///< all boundary edges and corner vertices
///< sharpened and interpolated
};
enum FVarLinearInterpolation {
FVAR_LINEAR_NONE = 0, ///< smooth everywhere ("edge only")
FVAR_LINEAR_CORNERS_ONLY, ///< sharpen corners only
FVAR_LINEAR_CORNERS_PLUS1, ///< ("edge corner")
FVAR_LINEAR_CORNERS_PLUS2, ///< ("edge and corner + propagate corner")
FVAR_LINEAR_BOUNDARIES, ///< sharpen all boundaries ("always sharp")
FVAR_LINEAR_ALL ///< bilinear interpolation ("bilinear")
};
enum CreasingMethod {
CREASE_UNIFORM = 0, ///< Catmark rule
CREASE_CHAIKIN ///< Chaikin rule
};
enum TriangleSubdivision {
TRI_SUB_CATMARK = 0, ///< Catmark weights (Catmark scheme only)
TRI_SUB_SMOOTH ///< "smooth triangle" weights (Catmark scheme only)
};
public:
Options() : _vtxBoundInterp(VTX_BOUNDARY_NONE),
_fvarLinInterp(FVAR_LINEAR_ALL),
_creasingMethod(CREASE_UNIFORM),
_triangleSub(TRI_SUB_CATMARK) { }
//
// Trivial get/set methods:
//
/// \brief Get vertex boundary interpolation rule
VtxBoundaryInterpolation GetVtxBoundaryInterpolation() const { return (VtxBoundaryInterpolation) _vtxBoundInterp; }
/// \brief Set vertex boundary interpolation rule
void SetVtxBoundaryInterpolation(VtxBoundaryInterpolation b) { _vtxBoundInterp = (EnumIntType) b; }
/// \brief Get face-varying interpolation rule
FVarLinearInterpolation GetFVarLinearInterpolation() const { return (FVarLinearInterpolation) _fvarLinInterp; }
/// \brief Set face-varying interpolation rule
void SetFVarLinearInterpolation(FVarLinearInterpolation b) { _fvarLinInterp = (EnumIntType) b; }
/// \brief Get edge crease rule
CreasingMethod GetCreasingMethod() const { return (CreasingMethod) _creasingMethod; }
/// \brief Set edge crease rule
void SetCreasingMethod(CreasingMethod c) { _creasingMethod = (EnumIntType) c; }
/// \brief Get triangle subdivision weights rule (Catmark scheme only !)
TriangleSubdivision GetTriangleSubdivision() const { return (TriangleSubdivision) _triangleSub; }
/// \brief Set triangle subdivision weights rule (Catmark scheme only !)
void SetTriangleSubdivision(TriangleSubdivision t) { _triangleSub = (EnumIntType) t; }
private:
// Use a small integer type to pack these rather than bitfields:
typedef unsigned char EnumIntType;
EnumIntType _vtxBoundInterp;
EnumIntType _fvarLinInterp;
EnumIntType _creasingMethod;
EnumIntType _triangleSub;
};
} // end namespace sdc
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_SDC_OPTIONS_H */

View File

@@ -0,0 +1,643 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_SDC_SCHEME_H
#define OPENSUBDIV3_SDC_SCHEME_H
#include "../version.h"
#include "../sdc/types.h"
#include "../sdc/options.h"
#include "../sdc/crease.h"
#include <cassert>
#include <cstdlib>
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Sdc {
///
/// \brief Scheme is a class template which provides all implementation for the
/// subdivision schemes supported by OpenSubdiv through specializations of the
/// methods of each. An instance of Scheme<SCHEME_TYPE> includes a set of Options
/// that will dictate the variable aspects of its behavior.
///
/// The primary purpose of Scheme is to provide the mask weights for vertices
/// generated by subdivision. Methods to determine the masks are given topological
/// neighborhoods from which to compute the appropriate weights for neighboring
/// components. While these neighborhoods may require sharpness values for
/// creasing, the computation of subdivided crease values is independent of the
/// scheme type and is also made available through the Crease class.
///
/// Mask queries are assisted by two utility classes -- a Neighborhood class
/// defining the set of relevant data in the topological neighborhood of the vertex
/// being subdivided, and a Mask class into which the associated mask weights will
/// be stored. Depending on where and how these queries are used, more or less
/// information may be available. See the details of the Neighborhood classes as
/// appropriate initialization of them is critical. It is generally best to
/// initialize them with what data is known and accessible for immediate and
/// efficient retrieval, but subclasses can be created to gather it lazily if
/// desired.
///
template <SchemeType SCHEME_TYPE>
class Scheme {
public:
Scheme() : _options() { }
Scheme(Options const& options) : _options(options) { }
Options GetOptions() const { return _options; }
void SetOptions(const Options& newOptions) { _options = newOptions; }
///
/// \brief Face-vertex masks - trivial for all current schemes
///
template <typename FACE, typename MASK>
void ComputeFaceVertexMask(FACE const& faceNeighborhood, MASK& faceVertexMask) const;
///
/// \brief Edge-vertex masks
/// If known, the Rule for the edge and/or the derived vertex can be specified to
/// accelerate the computation (though the Rule for the parent is trivially determined).
/// In particular, knowing the child rule can avoid the need to subdivide the sharpness
/// of the edge to see if it is a transitional crease that warrants fractional blending.
///
/// Whether to use the "Rules" in this interface is really debatable -- the parent Rule
/// is really based on the edge and its sharpness, while the child Rule is technically
/// based on the neighborhood of the child vertex, but it can be deduced from the two
/// child edges' sharpness. So the Crease methods used to compute these rules differ
/// from those for the vertex-vertex mask. Perhaps a simple pair of new methods for
/// Crease should be added specific to the edge-vertex case, i.e. one that takes a
/// single sharpness (for the parent rule) and one that takes a pair (for the child).
///
template <typename EDGE, typename MASK>
void ComputeEdgeVertexMask(EDGE const& edgeNeighborhood, MASK& edgeVertexMask,
Crease::Rule parentRule = Crease::RULE_UNKNOWN,
Crease::Rule childRule = Crease::RULE_UNKNOWN) const;
///
/// \brief Vertex-vertex masks
/// If known, a single Rule or pair of Rules can be specified (indicating a crease
/// transition) to accelerate the computation. Either no Rules, the first, or both should
/// be specified. Specification of only the first Rule implies it to be true for both
/// (wish the compiler would allow such default value specification), i.e. no transition.
/// The case of knowing the parent Rule but deferring determination of the child Rule to
/// this method is not supported.
///
template <typename VERTEX, typename MASK>
void ComputeVertexVertexMask(VERTEX const& vertexNeighborhood, MASK& vertexVertexMask,
Crease::Rule parentRule = Crease::RULE_UNKNOWN,
Crease::Rule childRule = Crease::RULE_UNKNOWN) const;
///
/// \brief Limit masks for vertices -- position and tangents
/// These presume that a vertex is suitably isolated for its limit to be well-defined
/// and, unlike the refinement masks, the subdivision Rule for the vertex (presumably at
/// its last level of refinement) is required rather than being optional. In the
/// presence of semi-sharp creasing that has not decayed to zero, the limit is neither
/// sharp nor smooth -- in such cases the Rule specified by the caller determines the
/// result.
///
/// For tangent masks, the direction of the first tangent (T1) is oriented towards the
/// leading edge of the vertex, i.e. the first incident edge of the vertex (beginning
/// the set of incident edges in counter-clockwise order). The second tangent (T2) lies
/// within the tangent plane such that its normal can be computed as T1 x T2. So for a
/// boundary vertex, T1 will point along the boundary in the direction of the leading
/// edge while T2 points inward across the limit surface.
///
/// As for magnitude, no assumptions should be made of the magnitudes of the resulting
/// tangent vectors. Common formulae often factor out scale factors that contribute to
/// magnitude. While some attempt has been made to make magnitudes more consistent
/// between regular corners, boundaries and the interior, the same has not been done at
/// irregular vertices -- at least not yet. This may be addressed in future, as having
/// consistent magnitudes returned here can aid in the construction of patches from
/// limit positions and tangents.
///
template <typename VERTEX, typename MASK>
void ComputeVertexLimitMask(VERTEX const& vertexNeighborhood, MASK& positionMask,
Crease::Rule vertexRule) const;
template <typename VERTEX, typename MASK>
void ComputeVertexLimitMask(VERTEX const& vertexNeighborhood, MASK& positionMask,
MASK& tangent1Mask, MASK& tangent2Mask,
Crease::Rule vertexRule) const;
//
// Static methods defining traits/properties of the scheme:
//
static Split GetTopologicalSplitType();
static int GetRegularFaceSize();
static int GetRegularVertexValence();
static int GetLocalNeighborhoodSize();
protected:
//
// Supporting internal methods -- optionally implemented, depending on specialization:
//
// Subdivision/refinement masks -- two for edge-vertices and three for vertex-vertices:
//
template <typename EDGE, typename MASK>
void assignCreaseMaskForEdge(EDGE const& edge, MASK& mask) const;
template <typename EDGE, typename MASK>
void assignSmoothMaskForEdge(EDGE const& edge, MASK& mask) const;
template <typename VERTEX, typename MASK>
void assignCornerMaskForVertex(VERTEX const& edge, MASK& mask) const;
template <typename VERTEX, typename MASK>
void assignCreaseMaskForVertex(VERTEX const& edge, MASK& mask, int const creaseEnds[2]) const;
template <typename VERTEX, typename MASK>
void assignSmoothMaskForVertex(VERTEX const& edge, MASK& mask) const;
//
// Limit masks for position and tangents at vertices -- three cases for each:
//
template <typename VERTEX, typename MASK>
void assignCornerLimitMask(VERTEX const& vertex, MASK& pos) const;
template <typename VERTEX, typename MASK>
void assignCreaseLimitMask(VERTEX const& vertex, MASK& pos, int const creaseEnds[2]) const;
template <typename VERTEX, typename MASK>
void assignSmoothLimitMask(VERTEX const& vertex, MASK& pos) const;
template <typename VERTEX, typename MASK>
void assignCornerLimitTangentMasks(VERTEX const& vertex, MASK& tan1, MASK& tan2) const;
template <typename VERTEX, typename MASK>
void assignCreaseLimitTangentMasks(VERTEX const& vertex, MASK& tan1, MASK& tan2, int const creaseEnds[2]) const;
template <typename VERTEX, typename MASK>
void assignSmoothLimitTangentMasks(VERTEX const& vertex, MASK& tan1, MASK& tan2) const;
private:
Options _options;
protected:
//
// Internal implementation support:
//
// We need a local "mask" class to be declared locally within the vertex-vertex mask query
// to hold one of the two possible masks required and to combine the local mask with the mask
// the caller provides. It has been parameterized by <WEIGHT> so that a version compatible
// with the caller's mask class is created.
//
template <typename WEIGHT>
class LocalMask {
public:
typedef WEIGHT Weight;
public:
LocalMask(Weight* v, Weight* e, Weight* f) : _vWeights(v), _eWeights(e), _fWeights(f) { }
~LocalMask() { }
public:
//
// Methods required for general mask assignments and queries:
//
int GetNumVertexWeights() const { return _vCount; }
int GetNumEdgeWeights() const { return _eCount; }
int GetNumFaceWeights() const { return _fCount; }
void SetNumVertexWeights(int count) { _vCount = count; }
void SetNumEdgeWeights( int count) { _eCount = count; }
void SetNumFaceWeights( int count) { _fCount = count; }
Weight const& VertexWeight(int index) const { return _vWeights[index]; }
Weight const& EdgeWeight( int index) const { return _eWeights[index]; }
Weight const& FaceWeight( int index) const { return _fWeights[index]; }
Weight& VertexWeight(int index) { return _vWeights[index]; }
Weight& EdgeWeight( int index) { return _eWeights[index]; }
Weight& FaceWeight( int index) { return _fWeights[index]; }
bool AreFaceWeightsForFaceCenters() const { return _fWeightsForCenters; }
void SetFaceWeightsForFaceCenters(bool on) { _fWeightsForCenters = on; }
public:
//
// Additional methods -- mainly the blending method for vertex-vertex masks:
//
template <typename USER_MASK>
inline void
CombineVertexVertexMasks(Weight thisCoeff, Weight dstCoeff, USER_MASK& dst) const {
//
// This implementation is convoluted by the potential sparsity of each mask. Since
// it is specific to a vertex-vertex mask, we are guaranteed to have exactly one
// vertex-weight for both masks, but the edge- and face-weights are optional. The
// child mask (the "source") should have a superset of the weights of the parent
// (the "destination") given its reduced sharpness, so we fortunately don't need to
// test all permutations.
//
dst.VertexWeight(0) = dstCoeff * dst.VertexWeight(0) + thisCoeff * this->VertexWeight(0);
int edgeWeightCount = this->GetNumEdgeWeights();
if (edgeWeightCount) {
if (dst.GetNumEdgeWeights() == 0) {
dst.SetNumEdgeWeights(edgeWeightCount);
for (int i = 0; i < edgeWeightCount; ++i) {
dst.EdgeWeight(i) = thisCoeff * this->EdgeWeight(i);
}
} else {
for (int i = 0; i < edgeWeightCount; ++i) {
dst.EdgeWeight(i) = dstCoeff * dst.EdgeWeight(i) + thisCoeff * this->EdgeWeight(i);
}
}
}
int faceWeightCount = this->GetNumFaceWeights();
if (faceWeightCount) {
//
// If combining face weights, be sure their interpretation (i.e. face-centers
// or opposite vertices) is properly set in the destination mask:
//
if (dst.GetNumFaceWeights() == 0) {
dst.SetNumFaceWeights(faceWeightCount);
dst.SetFaceWeightsForFaceCenters(this->AreFaceWeightsForFaceCenters());
for (int i = 0; i < faceWeightCount; ++i) {
dst.FaceWeight(i) = thisCoeff * this->FaceWeight(i);
}
} else {
assert(this->AreFaceWeightsForFaceCenters() == dst.AreFaceWeightsForFaceCenters());
for (int i = 0; i < faceWeightCount; ++i) {
dst.FaceWeight(i) = dstCoeff * dst.FaceWeight(i) + thisCoeff * this->FaceWeight(i);
}
}
}
}
private:
Weight* _vWeights;
Weight* _eWeights;
Weight* _fWeights;
int _vCount;
int _eCount;
int _fCount;
bool _fWeightsForCenters;
};
};
//
// Crease and corner masks are common to most schemes -- the rest need to be provided
// for each Scheme specialization.
//
template <SchemeType SCHEME>
template <typename EDGE, typename MASK>
inline void
Scheme<SCHEME>::assignCreaseMaskForEdge(EDGE const&, MASK& mask) const {
mask.SetNumVertexWeights(2);
mask.SetNumEdgeWeights(0);
mask.SetNumFaceWeights(0);
mask.SetFaceWeightsForFaceCenters(false);
mask.VertexWeight(0) = 0.5f;
mask.VertexWeight(1) = 0.5f;
}
template <SchemeType SCHEME>
template <typename VERTEX, typename MASK>
inline void
Scheme<SCHEME>::assignCornerMaskForVertex(VERTEX const&, MASK& mask) const {
mask.SetNumVertexWeights(1);
mask.SetNumEdgeWeights(0);
mask.SetNumFaceWeights(0);
mask.SetFaceWeightsForFaceCenters(false);
mask.VertexWeight(0) = 1.0f;
}
//
// The computation of a face-vertex mask is trivial and consistent for all schemes:
//
template <SchemeType SCHEME>
template <typename FACE, typename MASK>
void
Scheme<SCHEME>::ComputeFaceVertexMask(FACE const& face, MASK& mask) const {
int vertCount = face.GetNumVertices();
mask.SetNumVertexWeights(vertCount);
mask.SetNumEdgeWeights(0);
mask.SetNumFaceWeights(0);
mask.SetFaceWeightsForFaceCenters(false);
typename MASK::Weight vWeight = 1.0f / (typename MASK::Weight) vertCount;
for (int i = 0; i < vertCount; ++i) {
mask.VertexWeight(i) = vWeight;
}
}
//
// The computation of an edge-vertex mask requires inspection of sharpness values to
// determine if smooth or a crease, and also to detect and apply a transition from a
// crease to smooth. Using the protected methods to assign the specific masks (only
// two -- smooth or crease) this implementation should serve all non-linear schemes
// (currently Catmark and Loop) and only needs to be specialized for Bilinear to
// trivialize it to the crease case.
//
// The implementation here is slightly complicated by combining two scenarios into a
// single implementation -- either the caller knows the parent and child rules and
// provides them, or they don't and the Rules have to be determined from sharpness
// values. Both cases include quick return once the parent is determined to be
// smooth or the child a crease, leaving the transitional case remaining.
//
// The overall process is as follows:
//
// - quickly detect the most common specified or detected Smooth case and return
// - quickly detect a full Crease by child Rule assignment and return
// - determine from sharpness if unspecified child is a crease -- return if so
// - compute smooth mask for child and combine with crease from parent
//
// Usage of the parent Rule here allows some misuse in that only three of five possible
// assignments are legitimate for the parent and four for the child (Dart being only
// valid for the child and Corner for neither). Results are undefined in these cases.
//
template <SchemeType SCHEME>
template <typename EDGE, typename MASK>
void
Scheme<SCHEME>::ComputeEdgeVertexMask(EDGE const& edge,
MASK& mask,
Crease::Rule parentRule,
Crease::Rule childRule) const {
//
// If the parent was specified or determined to be Smooth, we can quickly return
// with a Smooth mask. Otherwise the parent is a crease -- if the child was
// also specified to be a crease, we can quickly return with a Crease mask.
//
if ((parentRule == Crease::RULE_SMOOTH) ||
((parentRule == Crease::RULE_UNKNOWN) && (edge.GetSharpness() <= 0.0f))) {
assignSmoothMaskForEdge(edge, mask);
return;
}
if (childRule == Crease::RULE_CREASE) {
assignCreaseMaskForEdge(edge, mask);
return;
}
//
// We have a Crease on the parent and the child was either specified as Smooth
// or was not specified at all -- deal with the unspecified case first (again
// returning a Crease mask if the child is also determined to be a Crease) and
// continue if we have a transition to Smooth.
//
// Note when qualifying the child that if the parent sharpness > 1.0, regardless
// of the creasing method, whether the child sharpness values decay to zero is
// irrelevant -- the fractional weight for such a case (the value of the parent
// sharpness) is > 1.0, and when clamped to 1 effectively yields a full crease.
//
if (childRule == Crease::RULE_UNKNOWN) {
Crease crease(_options);
bool childIsCrease = false;
if (parentRule == Crease::RULE_CREASE) {
// Child unknown as default value but parent Rule specified as Crease
childIsCrease = true;
} else if (edge.GetSharpness() >= 1.0f) {
// Sharpness >= 1.0 always a crease -- see note above
childIsCrease = true;
} else if (crease.IsUniform()) {
// Sharpness < 1.0 is guaranteed to decay to 0.0 for Uniform child edges
childIsCrease = false;
} else {
// Sharpness <= 1.0 does not necessarily decay to 0.0 for both child edges...
float cEdgeSharpness[2];
edge.GetChildSharpnesses(crease, cEdgeSharpness);
childIsCrease = (cEdgeSharpness[0] > 0.0f) && (cEdgeSharpness[1] > 0.0f);
}
if (childIsCrease) {
assignCreaseMaskForEdge(edge, mask);
return;
}
}
//
// We are now left with the Crease-to-Smooth case -- compute the Smooth mask
// for the child and augment it with the transitional Crease of the parent.
//
// A general combination of separately assigned masks here (as done in the vertex-
// vertex case) is overkill -- trivially combine the 0.5f vertex coefficient for
// the Crease of the parent with the vertex weights and attenuate the face weights
// accordingly.
//
assignSmoothMaskForEdge(edge, mask);
typedef typename MASK::Weight Weight;
Weight pWeight = edge.GetSharpness();
Weight cWeight = 1.0f - pWeight;
mask.VertexWeight(0) = pWeight * 0.5f + cWeight * mask.VertexWeight(0);
mask.VertexWeight(1) = pWeight * 0.5f + cWeight * mask.VertexWeight(1);
int faceCount = mask.GetNumFaceWeights();
for (int i = 0; i < faceCount; ++i) {
mask.FaceWeight(i) *= cWeight;
}
}
//
// The computation of a vertex-vertex mask requires inspection of creasing sharpness values
// to determine what subdivision Rules apply to the parent and its child vertex, and also to
// detect and apply a transition between two differing Rules. Using the protected methods to
// assign specific masks, this implementation should serve all non-linear schemes (currently
// Catmark and Loop) and only needs to be specialized for Bilinear to remove all unnecessary
// complexity relating to creasing, Rules, etc.
//
// The implementation here is slightly complicated by combining two scenarios into one --
// either the caller knows the parent and child rules and provides them, or they don't and
// the Rules have to be determined from sharpness values. Even when the Rules are known and
// provided though, there are cases where the parent and child sharpness values need to be
// identified, so accounting for the unknown Rules too is not much of an added complication.
//
// The benefit of supporting specified Rules is that they can often be trivially
// determined from context (e.g. a vertex derived from a face at a previous level will always
// be smooth) rather than more generally, and at greater cost, inspecting neighboring and
// they are often the same for parent and child.
//
// The overall process is as follows:
//
// - quickly detect the most common Smooth case when specified and return
// - determine if sharpness for parent is required and gather if so
// - if unspecified, determine the parent rule
// - assign mask for the parent rule -- returning if Smooth/Dart
// - return if child rule matches parent
// - gather sharpness for child to determine or combine child rule
// - if unspecified, determine the child rule, returning if it matches parent
// - assign local mask for child rule
// - combine local child mask with the parent mask
//
// Remember -- if the parent rule is specified but the child is not, this implies only one
// of the two optional rules was specified and is meant to indicate there is no transition,
// so the child rule should be assigned to be the same (wish the compiler would allow this
// in default value assignment).
//
template <SchemeType SCHEME>
template <typename VERTEX, typename MASK>
void
Scheme<SCHEME>::ComputeVertexVertexMask(VERTEX const& vertex,
MASK& mask,
Crease::Rule pRule,
Crease::Rule cRule) const {
// Quick assignment and return for the most common case:
if ((pRule == Crease::RULE_SMOOTH) || (pRule == Crease::RULE_DART)) {
assignSmoothMaskForVertex(vertex, mask);
return;
}
// If unspecified, assign the child rule to match the parent rule if specified:
if ((cRule == Crease::RULE_UNKNOWN) && (pRule != Crease::RULE_UNKNOWN)) {
cRule = pRule;
}
int valence = vertex.GetNumEdges();
//
// Determine if we need the parent edge sharpness values -- identify/gather if so
// and use it to compute the parent rule if unspecified:
//
float * pEdgeSharpnessBuffer = (float *)alloca(valence*sizeof(float)),
* pEdgeSharpness = 0,
pVertexSharpness = 0.0f;
bool requireParentSharpness = (pRule == Crease::RULE_UNKNOWN) ||
(pRule == Crease::RULE_CREASE) ||
(pRule != cRule);
if (requireParentSharpness) {
pVertexSharpness = vertex.GetSharpness();
pEdgeSharpness = vertex.GetSharpnessPerEdge(pEdgeSharpnessBuffer);
if (pRule == Crease::RULE_UNKNOWN) {
pRule = Crease(_options).DetermineVertexVertexRule(pVertexSharpness, valence, pEdgeSharpness);
}
}
if ((pRule == Crease::RULE_SMOOTH) || (pRule == Crease::RULE_DART)) {
assignSmoothMaskForVertex(vertex, mask);
return; // As done on entry, we can return immediately if parent is Smooth/Dart
} else if (pRule == Crease::RULE_CREASE) {
int creaseEnds[2];
Crease(_options).GetSharpEdgePairOfCrease(pEdgeSharpness, valence, creaseEnds);
assignCreaseMaskForVertex(vertex, mask, creaseEnds);
} else {
assignCornerMaskForVertex(vertex, mask);
}
if (cRule == pRule) return;
//
// Identify/gather child sharpness to combine masks for the two differing Rules:
//
Crease crease(_options);
float * cEdgeSharpnessBuffer = (float *)alloca(valence*sizeof(float)),
* cEdgeSharpness = vertex.GetChildSharpnessPerEdge(crease, cEdgeSharpnessBuffer),
cVertexSharpness = vertex.GetChildSharpness(crease);
if (cRule == Crease::RULE_UNKNOWN) {
cRule = crease.DetermineVertexVertexRule(cVertexSharpness, valence, cEdgeSharpness);
if (cRule == pRule) return;
}
//
// Initialize a local child mask, compute the fractional weight from parent and child
// sharpness values and combine the two masks:
//
typedef typename MASK::Weight Weight;
Weight * cMaskWeights = (Weight *)alloca((1 + 2 * valence)*sizeof(Weight));
LocalMask<Weight> cMask(cMaskWeights, cMaskWeights + 1, cMaskWeights + 1 + valence);
if ((cRule == Crease::RULE_SMOOTH) || (cRule == Crease::RULE_DART)) {
assignSmoothMaskForVertex(vertex, cMask);
} else if (cRule == Crease::RULE_CREASE) {
int creaseEnds[2];
Crease(_options).GetSharpEdgePairOfCrease(cEdgeSharpness, valence, creaseEnds);
assignCreaseMaskForVertex(vertex, cMask, creaseEnds);
} else {
assignCornerMaskForVertex(vertex, cMask);
}
Weight pWeight = crease.ComputeFractionalWeightAtVertex(pVertexSharpness, cVertexSharpness,
valence, pEdgeSharpness, cEdgeSharpness);
Weight cWeight = 1.0f - pWeight;
cMask.CombineVertexVertexMasks(cWeight, pWeight, mask);
}
//
// The computation of limit masks for vertices:
//
template <SchemeType SCHEME>
template <typename VERTEX, typename MASK>
void
Scheme<SCHEME>::ComputeVertexLimitMask(VERTEX const& vertex,
MASK& mask,
Crease::Rule rule) const {
if ((rule == Crease::RULE_SMOOTH) || (rule == Crease::RULE_DART)) {
assignSmoothLimitMask(vertex, mask);
} else if (rule == Crease::RULE_CREASE) {
float * edgeSharpness = (float *)alloca(vertex.GetNumEdges() * sizeof(float));
vertex.GetSharpnessPerEdge(edgeSharpness);
int creaseEnds[2];
Crease(_options).GetSharpEdgePairOfCrease(edgeSharpness, vertex.GetNumEdges(), creaseEnds);
assignCreaseLimitMask(vertex, mask, creaseEnds);
} else {
assignCornerLimitMask(vertex, mask);
}
}
template <SchemeType SCHEME>
template <typename VERTEX, typename MASK>
void
Scheme<SCHEME>::ComputeVertexLimitMask(VERTEX const& vertex,
MASK& posMask,
MASK& tan1Mask,
MASK& tan2Mask,
Crease::Rule rule) const {
if ((rule == Crease::RULE_SMOOTH) || (rule == Crease::RULE_DART)) {
assignSmoothLimitMask(vertex, posMask);
assignSmoothLimitTangentMasks(vertex, tan1Mask, tan2Mask);
} else if (rule == Crease::RULE_CREASE) {
float * edgeSharpness = (float *)alloca(vertex.GetNumEdges() * sizeof(float));
vertex.GetSharpnessPerEdge(edgeSharpness);
int creaseEnds[2];
Crease(_options).GetSharpEdgePairOfCrease(edgeSharpness, vertex.GetNumEdges(), creaseEnds);
assignCreaseLimitMask(vertex, posMask, creaseEnds);
assignCreaseLimitTangentMasks(vertex, tan1Mask, tan2Mask, creaseEnds);
} else {
assignCornerLimitMask(vertex, posMask);
assignCornerLimitTangentMasks(vertex, tan1Mask, tan2Mask);
}
}
} // end namespace sdc
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_SDC_SCHEME_H */

View File

@@ -0,0 +1,78 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../sdc/types.h"
#include "../sdc/bilinearScheme.h"
#include "../sdc/catmarkScheme.h"
#include "../sdc/loopScheme.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Sdc {
struct TraitsEntry {
char const * _name;
Split _splitType;
int _regularFaceSize;
int _regularVertexValence;
int _localNeighborhood;
};
static const TraitsEntry staticTraitsTable[3] = {
{ "bilinear", Scheme<SCHEME_BILINEAR>::GetTopologicalSplitType(),
Scheme<SCHEME_BILINEAR>::GetRegularFaceSize(),
Scheme<SCHEME_BILINEAR>::GetRegularVertexValence(),
Scheme<SCHEME_BILINEAR>::GetLocalNeighborhoodSize() },
{ "catmark", Scheme<SCHEME_CATMARK>::GetTopologicalSplitType(),
Scheme<SCHEME_CATMARK>::GetRegularFaceSize(),
Scheme<SCHEME_CATMARK>::GetRegularVertexValence(),
Scheme<SCHEME_CATMARK>::GetLocalNeighborhoodSize() },
{ "loop", Scheme<SCHEME_LOOP>::GetTopologicalSplitType(),
Scheme<SCHEME_LOOP>::GetRegularFaceSize(),
Scheme<SCHEME_LOOP>::GetRegularVertexValence(),
Scheme<SCHEME_LOOP>::GetLocalNeighborhoodSize() }
};
//
// Static methods for SchemeTypeTraits:
//
char const*
SchemeTypeTraits::GetName(SchemeType schemeType) {
return staticTraitsTable[schemeType]._name;
}
Split
SchemeTypeTraits::GetTopologicalSplitType(SchemeType schemeType) {
return staticTraitsTable[schemeType]._splitType;
}
int
SchemeTypeTraits::GetRegularFaceSize(SchemeType schemeType) {
return staticTraitsTable[schemeType]._regularFaceSize;
}
int
SchemeTypeTraits::GetRegularVertexValence(SchemeType schemeType) {
return staticTraitsTable[schemeType]._regularVertexValence;
}
int
SchemeTypeTraits::GetLocalNeighborhoodSize(SchemeType schemeType) {
return staticTraitsTable[schemeType]._localNeighborhood;
}
} // end namespace sdc
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,59 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_SDC_TYPES_H
#define OPENSUBDIV3_SDC_TYPES_H
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Sdc {
///
/// \brief Enumerated type for all subdivision schemes supported by OpenSubdiv
///
enum SchemeType {
SCHEME_BILINEAR,
SCHEME_CATMARK,
SCHEME_LOOP
};
///
/// \brief Enumerated type for all face splitting schemes
///
enum Split {
SPLIT_TO_QUADS, ///< Used by Catmark and Bilinear
SPLIT_TO_TRIS, ///< Used by Loop
SPLIT_HYBRID ///< Not currently used (potential future extension)
};
///
/// \brief Traits associated with the types of all subdivision schemes -- parameterized by
/// the scheme type. All traits are also defined in the scheme itself.
///
struct SchemeTypeTraits {
static SchemeType GetType(SchemeType schemeType) { return schemeType; }
static Split GetTopologicalSplitType(SchemeType schemeType);
static int GetRegularFaceSize(SchemeType schemeType);
static int GetRegularVertexValence(SchemeType schemeType);
static int GetLocalNeighborhoodSize(SchemeType schemeType);
static char const* GetName(SchemeType schemeType);
};
} // end namespace sdc
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_SDC_TYPES_H */