Changed Gregory triangle from cubic/quartic hyrid to full quartic:

- changed Far::PatchDescriptor size for GREGORY_TRIANGLE to 18
    - modified Far::LoopPatchBuilder to construct full quartic Gregory patch
    - fixed Far::LoopPatchBuilder bug related to face points near boundaries
    - updated Far patch basis evaluation for Gregory and Bezier triangles
    - updated Osd patch basis evaluation to match Far
    - updated Osd GLSL patch size for Gregory triangle to 18
    - modified Osd GLSL Gregory triangle eval and tess settings for quartic
This commit is contained in:
barry 2019-01-24 12:56:09 -08:00
parent ee6097b399
commit a0e72d80c3
10 changed files with 623 additions and 505 deletions

View File

@ -313,6 +313,8 @@ namespace {
int GetSize() const { return _size; }
void SetWeight(int i, REAL weight) { _weights[i] = weight; }
void Assign(int rowEntry, Index index, REAL weight) {
_indices[rowEntry] = index;
_weights[rowEntry] = weight;
@ -394,6 +396,31 @@ namespace {
}
}
template <typename REAL>
void
_combineSparsePointsInFullRow(SparseMatrixRow<REAL> & p,
REAL aCoeff, SparseMatrixRow<REAL> const & a,
REAL bCoeff, SparseMatrixRow<REAL> const & b,
int rowSize, REAL * rowBuffer, int * maskBuffer) {
std::memset(maskBuffer, 0, rowSize * sizeof(int));
std::memset(rowBuffer, 0, rowSize * sizeof(REAL));
_addSparsePointToFullRow(rowBuffer, a, aCoeff, maskBuffer);
_addSparsePointToFullRow(rowBuffer, b, bCoeff, maskBuffer);
int nWeights = 0;
for (int i = 0; i < rowSize; ++i) {
if (maskBuffer[i]) {
p.Assign(nWeights++, maskBuffer[i] - 1, rowBuffer[i]);
}
}
assert(nWeights <= p.GetSize());
for (int i = nWeights; i < p.GetSize(); ++i, ++nWeights) {
p.Assign(i, 0, 0.0f);
}
}
template <typename REAL>
void
_addSparseRowToFull(REAL * fullRow,
@ -527,11 +554,19 @@ namespace {
// GregoryTriConverter
//
// The GregoryTriConverter class provides a change-of-basis matrix from source
// vertices in a Loop mesh to the 15 control points of a Gregory triangle.
// vertices in a Loop mesh to the 18 control points of a quartic Gregory triangle.
//
// The quartic triangle is first constructed as a cubic/quartic hybrid -- with
// cubic boundary curves and cross-boundary continuity formulated in terms of
// cubics. The result is then raised to a full quartic once continuity across
// all boundaries is achieved. In most cases 2 of the 3 boundaries will be
// cubic (though now represented as quartic) and only one boundary need be a
// true quartic to meet a regular Box-spline patch.
//
// Control points are labeled using the convention adopted for quads, with
// Ep and Em referring to the "plus" and "minus" edge points and similarly
// for the face points Fp and Fm.
// for the face points Fp and Fm. The additional quartic "mid-edge" points
// associated with each boundary are referred to as M.
//
template <typename REAL>
class GregoryTriConverter {
@ -625,6 +660,13 @@ private:
Point & fNear, REAL signForSideOfEdge /* -1.0 or 1.0 */,
Weight *rowWeights, int *columnMask) const;
void assignRegularMidEdgePoint(int edgeIndex, Matrix & matrix) const;
void computeIrregularMidEdgePoint(int edgeIndex, Matrix & matrix,
Weight *rowWeights, int *columnMask) const;
void promoteCubicEdgePointsToQuartic(Matrix & matrix,
Weight *rowWeights, int *columnMask) const;
private:
int _numSourcePoints;
int _maxValence;
@ -813,6 +855,17 @@ GregoryTriConverter<REAL>::Convert(Matrix & matrix) const {
computeIrregularFacePoints(cIndex, matrix, weightBuffer, indexBuffer);
}
}
for (int eIndex = 0; eIndex < 3; ++eIndex) {
if ((_corners[eIndex].isRegular && _corners[(eIndex+1)%3].isRegular) ||
_corners[eIndex].epOnBoundary) {
assignRegularMidEdgePoint(eIndex, matrix);
} else {
computeIrregularMidEdgePoint(eIndex, matrix, weightBuffer, indexBuffer);
}
}
promoteCubicEdgePointsToQuartic(matrix, weightBuffer, indexBuffer);
if (_hasVal2InteriorCorner) {
_removeValence2Duplicates(matrix);
}
@ -829,7 +882,7 @@ GregoryTriConverter<REAL>::resizeMatrixIsolatedIrregular(
int irregPlus = (cornerIndex + 1) % 3;
int irregMinus = (cornerIndex + 2) % 3;
int rowSizes[15];
int rowSizes[18];
int * rowSizePtr = 0;
rowSizePtr = rowSizes + irregCorner * 5;
@ -853,16 +906,21 @@ GregoryTriConverter<REAL>::resizeMatrixIsolatedIrregular(
*rowSizePtr++ = 3 + irregRingSize;
*rowSizePtr++ = 5;
int numElements = 7*irregRingSize + 64;
// The 3 quartic mid-edge points are not grouped with corners:
rowSizes[15 + irregCorner] = 3 + irregRingSize;
rowSizes[15 + irregPlus] = 4;
rowSizes[15 + irregMinus] = 3 + irregRingSize;
_resizeMatrix(matrix, 15, _numSourcePoints, numElements, rowSizes);
int numElements = 9*irregRingSize + 74;
_resizeMatrix(matrix, 18, _numSourcePoints, numElements, rowSizes);
}
template <typename REAL>
void
GregoryTriConverter<REAL>::resizeMatrixUnisolated(Matrix & matrix) const {
int rowSizes[15];
int rowSizes[18];
int numElements = 0;
@ -879,8 +937,8 @@ GregoryTriConverter<REAL>::resizeMatrixUnisolated(Matrix & matrix) const {
rowSize[2] = 7;
} else {
rowSize[0] = 3;
rowSize[1] = corner.epOnBoundary ? 2 : 4;
rowSize[2] = corner.emOnBoundary ? 2 : 4;
rowSize[1] = corner.epOnBoundary ? 3 : 5;
rowSize[2] = corner.emOnBoundary ? 3 : 5;
}
} else {
if (corner.isSharp) {
@ -895,19 +953,19 @@ GregoryTriConverter<REAL>::resizeMatrixUnisolated(Matrix & matrix) const {
} else if (corner.numFaces > 1) {
int ringSize = 1 + corner.valence;
rowSize[0] = 3;
rowSize[1] = corner.epOnBoundary ? 2 : ringSize;
rowSize[2] = corner.emOnBoundary ? 2 : ringSize;
rowSize[1] = corner.epOnBoundary ? 3 : ringSize;
rowSize[2] = corner.emOnBoundary ? 3 : ringSize;
} else {
rowSize[0] = 3;
rowSize[1] = 2;
rowSize[2] = 2;
rowSize[1] = 3;
rowSize[2] = 3;
}
}
numElements += rowSize[0] + rowSize[1] + rowSize[2];
// Second, the pair of face points:
rowSize[3] = 5 - 2 * corner.isCorner;
rowSize[4] = 5 - 2 * corner.isCorner;
rowSize[3] = 5 - corner.epOnBoundary - corner.emOnBoundary;
rowSize[4] = 5 - corner.epOnBoundary - corner.emOnBoundary;
if (!corner.fpIsRegular || !corner.fmIsRegular) {
int cNext = (cIndex + 1) % 3;
int cPrev = (cIndex + 2) % 3;
@ -921,8 +979,21 @@ GregoryTriConverter<REAL>::resizeMatrixUnisolated(Matrix & matrix) const {
}
}
numElements += rowSize[3] + rowSize[4];
// Third, the quartic mid-edge boundary point (edge following corner):
int & midEdgeSize = rowSizes[15 + cIndex];
if (corner.epOnBoundary) {
midEdgeSize = 2;
} else if (corner.isRegular && _corners[(cIndex+1) % 3].isRegular) {
midEdgeSize = 4;
} else {
// Use face-point size here, which also combines edge-point sizes:
midEdgeSize = rowSize[3];
}
numElements += midEdgeSize;
}
_resizeMatrix(matrix, 15, _numSourcePoints, numElements, rowSizes);
_resizeMatrix(matrix, 18, _numSourcePoints, numElements, rowSizes);
}
template <typename REAL>
@ -991,27 +1062,29 @@ GregoryTriConverter<REAL>::assignRegularEdgePoints(int cIndex, Matrix & matrix)
if (corner.epOnBoundary) {
ep.Assign(0, cIndex, twoThirds);
ep.Assign(1, cRing[0], oneThird);
assert(ep.GetSize() == 2);
ep.Assign(2, cRing[3], 0.0f);
assert(ep.GetSize() == 3);
} else {
ep.Assign(0, cIndex, 0.5f);
ep.Assign(1, cRing[1], oneSixth);
ep.Assign(2, cRing[2], oneSixth);
if (!corner.emOnBoundary) ep.Assign(3, cRing[0], oneSixth);
if ( corner.emOnBoundary) ep.Assign(3, cRing[3], oneSixth);
assert(ep.GetSize() == 4);
ep.Assign(3, cRing[corner.emOnBoundary ? 3 : 0], oneSixth);
ep.Assign(4, cRing[corner.emOnBoundary ? 0 : 3], 0.0f);
assert(ep.GetSize() == 5);
}
if (corner.emOnBoundary) {
em.Assign(0, cIndex, twoThirds);
em.Assign(1, cRing[3], oneThird);
assert(em.GetSize() == 2);
em.Assign(2, cRing[0], 0.0f);
assert(em.GetSize() == 3);
} else {
em.Assign(0, cIndex, 0.5f);
em.Assign(1, cRing[1], oneSixth);
em.Assign(2, cRing[2], oneSixth);
if ( corner.epOnBoundary) em.Assign(3, cRing[0], oneSixth);
if (!corner.epOnBoundary) em.Assign(3, cRing[3], oneSixth);
assert(em.GetSize() == 4);
em.Assign(3, cRing[corner.epOnBoundary ? 0 : 3], oneSixth);
em.Assign(4, cRing[corner.epOnBoundary ? 3 : 0], 0.0f);
assert(em.GetSize() == 5);
}
}
}
@ -1068,11 +1141,13 @@ GregoryTriConverter<REAL>::computeIrregularEdgePoints(int cIndex,
ep.Assign(0, cIndex, (REAL) (2.0 / 3.0));
ep.Assign(1, (cIndex+1) % 3, (REAL) (1.0 / 3.0));
assert(ep.GetSize() == 2);
ep.Assign(2, (cIndex+2) % 3, 0.0f);
assert(ep.GetSize() == 3);
em.Assign(0, cIndex, (REAL) (2.0 / 3.0));
em.Assign(1, (cIndex+2) % 3, (REAL) (1.0 / 3.0));
assert(em.GetSize() == 2);
em.Assign(2, (cIndex+1) % 3, 0.0f);
assert(em.GetSize() == 3);
}
}
@ -1166,7 +1241,8 @@ GregoryTriConverter<REAL>::computeIrregularBoundaryEdgePoints(
ep.Assign(0, p0, epWeights[0]);
if (corner.epOnBoundary) {
ep.Assign(1, p1, epWeights[1]);
assert(ep.GetSize() == 2);
ep.Assign(2, pN, 0.0f);
assert(ep.GetSize() == 3);
} else {
for (int i = 1; i < weightWidth; ++i) {
ep.Assign(i, corner.ringPoints[i-1], epWeights[i]);
@ -1179,7 +1255,8 @@ GregoryTriConverter<REAL>::computeIrregularBoundaryEdgePoints(
em.Assign(0, p0, emWeights[0]);
if (corner.emOnBoundary) {
em.Assign(1, pN, emWeights[N]);
assert(em.GetSize() == 2);
em.Assign(2, p1, 0.0f);
assert(em.GetSize() == 3);
} else {
for (int i = 1; i < weightWidth; ++i) {
em.Assign(i, corner.ringPoints[i-1], emWeights[i]);
@ -1194,18 +1271,14 @@ int
GregoryTriConverter<REAL>::getIrregularFacePointSize(
int cIndexNear, int cIndexFar) const {
CornerTopology const & corner = _corners[cIndexNear];
CornerTopology const & adjCorner = _corners[cIndexFar];
CornerTopology const & nearCorner = _corners[cIndexNear];
CornerTopology const & farCorner = _corners[cIndexFar];
int thisSize = corner.isSharp
? 4
: (1 + corner.ringPoints.GetSize());
int nearSize = nearCorner.ringPoints.GetSize() - 3;
int farSize = farCorner.ringPoints.GetSize() - 3;
int adjSize = (adjCorner.isSharp || (adjCorner.numFaces <= 3))
? 0
: (1 + adjCorner.ringPoints.GetSize() - 4);
return thisSize + adjSize;
return 4 + (((nearSize > 0) && !nearCorner.isSharp) ? nearSize : 0)
+ (((farSize > 0) && !farCorner.isSharp) ? farSize : 0);
}
template <typename REAL>
@ -1284,58 +1357,49 @@ GregoryTriConverter<REAL>::assignRegularFacePoints(int cIndex, Matrix & matrix)
int cNext = (cIndex+1) % 3;
int cPrev = (cIndex+2) % 3;
int eNext = corner.isBoundary ? 0 : ((corner.faceInRing + 5) % 6);
int ePrev = corner.isBoundary ? 3 : ((corner.faceInRing + 2) % 6);
int const * cRing = corner.ringPoints;
// Assign regular Fp and/or Fm:
if (corner.fpIsRegular) {
Point fp(matrix, 5*cIndex + 3);
//
// Regular face-points are computed the same for both face-points of a
// a corner (fp and fm), so iterate through both and make appropriate
// assignments when tagged as regular:
//
for (int fIsFm = 0; fIsFm < 2; ++fIsFm) {
bool fIsRegular = fIsFm ? corner.fmIsRegular : corner.fpIsRegular;
if (!fIsRegular) continue;
Point f(matrix, 5*cIndex + 3 + fIsFm);
if (corner.isCorner) {
fp.Assign(0, cIndex, 0.5f);
fp.Assign(1, cNext, 0.25f);
fp.Assign(2, cPrev, 0.25f);
assert(fp.GetSize() == 3);
f.Assign(0, cIndex, 0.5f);
f.Assign(1, cNext, 0.25f);
f.Assign(2, cPrev, 0.25f);
assert(f.GetSize() == 3);
} else if (corner.epOnBoundary) {
fp.Assign(0, cIndex, (REAL) (11.0 / 24.0));
fp.Assign(1, cRing[0], (REAL) ( 7.0 / 24.0));
fp.Assign(2, cRing[1], (REAL) ( 5.0 / 24.0));
fp.Assign(3, cRing[2], (REAL) ( 1.0 / 24.0));
fp.Assign(4, cRing[3], (REAL) ( 0.0 / 24.0));
assert(fp.GetSize() == 5);
} else {
fp.Assign(0, cIndex, (REAL) (10.0 / 24.0));
fp.Assign(1, cPrev, 0.25f);
fp.Assign(2, cNext, 0.25f);
fp.Assign(3, cRing[ePrev], (REAL) ( 1.0 / 24.0));
fp.Assign(4, cRing[eNext], (REAL) ( 1.0 / 24.0));
assert(fp.GetSize() == 5);
}
}
if (corner.fmIsRegular) {
Point fm(matrix, 5*cIndex + 4);
if (corner.isCorner) {
fm.Assign(0, cIndex, 0.5f);
fm.Assign(1, cNext, 0.25f);
fm.Assign(2, cPrev, 0.25f);
assert(fm.GetSize() == 3);
// Face is the first/leading face of the boundary ring:
f.Assign(0, cIndex, (REAL) (11.0 / 24.0));
f.Assign(1, cRing[0], (REAL) ( 7.0 / 24.0));
f.Assign(2, cRing[1], (REAL) ( 5.0 / 24.0));
f.Assign(3, cRing[2], (REAL) ( 1.0 / 24.0));
assert(f.GetSize() == 4);
} else if (corner.emOnBoundary) {
fm.Assign(0, cIndex, (REAL) (11.0 / 24.0));
fm.Assign(1, cRing[0], (REAL) ( 0.0 / 24.0));
fm.Assign(2, cRing[1], (REAL) ( 1.0 / 24.0));
fm.Assign(3, cRing[2], (REAL) ( 5.0 / 24.0));
fm.Assign(4, cRing[3], (REAL) ( 7.0 / 24.0));
assert(fm.GetSize() == 5);
// Face is the last/trailing face of the boundary ring:
f.Assign(0, cIndex, (REAL) (11.0 / 24.0));
f.Assign(1, cRing[3], (REAL) ( 7.0 / 24.0));
f.Assign(2, cRing[2], (REAL) ( 5.0 / 24.0));
f.Assign(3, cRing[1], (REAL) ( 1.0 / 24.0));
assert(f.GetSize() == 4);
} else {
fm.Assign(0, cIndex, (REAL) (10.0 / 24.0));
fm.Assign(1, cNext, 0.25f);
fm.Assign(2, cPrev, 0.25f);
fm.Assign(3, cRing[ePrev], (REAL) ( 1.0 / 24.0));
fm.Assign(4, cRing[eNext], (REAL) ( 1.0 / 24.0));
assert(fm.GetSize() == 5);
// Face is interior or the middle face of the boundary:
int eNext = corner.isBoundary ? 0 : ((corner.faceInRing + 5) % 6);
int ePrev = corner.isBoundary ? 3 : ((corner.faceInRing + 2) % 6);
f.Assign(0, cIndex, (REAL) (10.0 / 24.0));
f.Assign(1, cPrev, 0.25f);
f.Assign(2, cNext, 0.25f);
f.Assign(3, cRing[ePrev], (REAL) ( 1.0 / 24.0));
f.Assign(4, cRing[eNext], (REAL) ( 1.0 / 24.0));
assert(f.GetSize() == 5);
}
}
}
@ -1391,6 +1455,107 @@ GregoryTriConverter<REAL>::computeIrregularFacePoints(int cIndex,
if (!corner.fmIsRegular) assert(matrix.GetRowSize(5*cIndex + 4) == fm.GetSize());
}
template <typename REAL>
void
GregoryTriConverter<REAL>::assignRegularMidEdgePoint(int edgeIndex,
Matrix & matrix) const {
Point M(matrix, 15 + edgeIndex);
CornerTopology const & corner = _corners[edgeIndex];
if (corner.epOnBoundary) {
// Trivial boundary edge case -- midway between two corners
M.Assign(0, edgeIndex, 0.5f);
M.Assign(1, (edgeIndex + 1) % 3, 0.5f);
assert(M.GetSize() == 2);
} else {
// Regular case -- two corners and two vertices opposite the edge
int oppositeInRing = corner.isBoundary ?
(corner.faceInRing - 1) : ((corner.faceInRing + 5) % 6);
int oppositeVertex = corner.ringPoints[oppositeInRing];
M.Assign(0, edgeIndex, (REAL) (1.0 / 3.0));
M.Assign(1, (edgeIndex + 1) % 3, (REAL) (1.0 / 3.0));
M.Assign(2, (edgeIndex + 2) % 3, (REAL) (1.0 / 6.0));
M.Assign(3, oppositeVertex, (REAL) (1.0 / 6.0));
assert(M.GetSize() == 4);
}
}
template <typename REAL>
void
GregoryTriConverter<REAL>::computeIrregularMidEdgePoint(int edgeIndex, Matrix & matrix,
Weight * rowWeights, int * columnMask) const {
//
// General case -- interpolate midway between cubic edge points E0 and E1:
//
int cIndex0 = edgeIndex;
int cIndex1 = (edgeIndex + 1) % 3;
Point E0p(matrix, 5 * (cIndex0) + 1);
Point E1m(matrix, 5 * (cIndex1) + 2);
Point M(matrix, 15 + edgeIndex);
_combineSparsePointsInFullRow(M, (REAL)0.5f, E0p, (REAL)0.5f, E1m,
_numSourcePoints, rowWeights, columnMask);
}
template <typename REAL>
void
GregoryTriConverter<REAL>::promoteCubicEdgePointsToQuartic(Matrix & matrix,
Weight * rowWeights, int * columnMask) const {
//
// Re-assign all regular edge-point weights with quartic coefficients,
// so only perform general combinations for the irregular case.
//
REAL const onBoundaryWeights[3] = { 16, 7, 1 };
REAL const regBoundaryWeights[5] = { 13, 3, 3, 4, 1 };
REAL const regInteriorWeights[7] = { 12, 4, 3, 1, 0, 1, 3 };
REAL const oneOver24 = (REAL) (1.0 / 24.0);
for (int cIndex = 0; cIndex < 3; ++cIndex) {
CornerTopology const & corner = _corners[cIndex];
//
// Ordering of weight values for symmetric ep and em is the same, so
// we can re-assign in a loop of 2 for {ep, em}
//
Point P(matrix, 5 * cIndex);
for (int ePair = 0; ePair < 2; ++ePair) {
Point E(matrix, 5 * cIndex + 1 + ePair);
REAL const * weightsToReassign = 0;
bool eOnBoundary = ePair ? corner.emOnBoundary : corner.epOnBoundary;
if (eOnBoundary && !corner.isSharp) {
assert(E.GetSize() == 3);
weightsToReassign = onBoundaryWeights;
} else if (corner.isRegular) {
if (corner.isBoundary) {
assert(E.GetSize() == 5);
weightsToReassign = regBoundaryWeights;
} else {
assert(E.GetSize() == 7);
weightsToReassign = regInteriorWeights;
}
}
if (weightsToReassign) {
for (int i = 0; i < E.GetSize(); ++i) {
E.SetWeight(i, weightsToReassign[i] * oneOver24);
}
} else {
_combineSparsePointsInFullRow(E, (REAL)0.25f, P, (REAL)0.75f, E,
_numSourcePoints, rowWeights, columnMask);
}
}
}
}
namespace {
template <typename REAL>
void
@ -1563,46 +1728,68 @@ convertToLoop(SourcePatch const & sourcePatch, SparseMatrix<REAL> & matrix) {
//
// Unlike quads, there are not enough degrees of freedom in the regular patch
// to enforce interpolation of the limit point and tangent at the corner while
// preserving the two adjoining boundary curves. That requires constraining
// 7 control points but only 6 can be moved without affecting the opposite
// boundary curve. At minimum, we need to constrain 9 of the 12 patch points,
// which reduces the advantages of a local solution compared to the 7 of 16
// control points for the BSplines of Catmark.
// preserving the two adjoining boundary curves. Since we end up destroying
// neighboring conintuity in doing so, we use a fully constructed Gregory
// patch here for the isolated corner case as well as the general case.
//
// So we might as well compute the Gregory solution and convert back for this
// more common case -- in addition to the more general case where it is needed.
// Tangent continuity is compromised regardless of what is done here. If a
// specific solution for the isolated corner case has advantages in terms of
// either surface quality or performance, we can revisit it.
// Unfortunately, the regular patch here -- a quartic Box-spline triangle --
// is not as flexible as the BSpline patches for Catmark. Unlike BSplines
// and Bezier patches, the Box-splines do not span the full space of possible
// shapes (only 12 control points in a space spanned by 15 polynomials for
// the quartic case). So it is possible to construct shapes with a Gregory
// or Bezier triangle that cannot be represented by the Box-spline.
//
// Use a full 12x12 conversion matrix from a cubic-quartic hybrid Bezier back
// to a Box spline patch. Matrix rows and columns are ordered according to
// control point orientations used elsewhere. Correllation of points between
// the hybrid Bezier and hybrid Gregory points is as follows:
// The solution fit a Box-spline patch to the constructed Gregory patch with
// 12 constraints: position, first derivatives and mixed partial at each of
// the 3 corners. We used the mixed partial (the twist vector) to constrain
// the interior of the patch somehow, otherwise results produce a lot of
// interior bulging in an effort to satisfy boundary constraints.
//
// B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11
// G0 G1 G7 G5 G2 G3,4 G8,9 G6 G11 G13,14 G12 G10
// Given both position and tangent continuity here are compromised for the
// general case -- and at considerable expense (in terms of computation and
// the full set of local control points that result) -- its worth exploring
// simpler solutions for the case of the isolated corner.
//
// For the full 12x15 conversion matrix from 15-point quartic Bezier patch
// back to a Box spline patch, the matrix rows and columns are ordered
// according to control point orientations used elsewhere. Correllation of
// points between the Bezier and Gregory points is as follows:
//
// Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 Q10 Q11 Q12 Q13 Q14
// G0 G1 G15 G7 G5 G2 G3,4 G8,9 G6 G17 G13,14 G16 G11 G12 G10
//
// As with conversion from Gregory to BSpline for Catmark, one of the face
// points (the positive) is chosen as a Bezier point in the conversion rather
// than combining the pair.
// points is chosen as a Bezier point in the conversion rather than combining
// the pair (which would avoid slight asymmetric artefacts of the choice).
//
REAL const gregoryToLoopMatrix[12][12] = {
{ 6.0f, 1.0f,-3.5f, 0.0f,-10.0f, 4.0f, 6.0f, -0.5f, 5.0f, -8.0f, 1.0f, 0.0f },
{ 0.0f, 3.5f, 5.0f, 0.0f, -0.5f, -2.0f, -8.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f },
{ 0.0f, 3.0f,-1.5f, 6.0f, 6.0f,-20.0f, 14.0f,-12.5f,-3.0f, 8.0f, 1.0f, 0.0f },
{ 6.0f,-10.0f, 5.0f, 0.0f, 1.0f, 4.0f, -8.0f, 1.0f,-3.5f, 6.0f, -0.5f, 0.0f },
{ 0.0f, 1.5f,-1.5f, 0.0f, 1.5f, -2.0f, 2.0f, -0.5f,-1.5f, 2.0f, -0.5f, 0.0f },
{ 0.0f, -2.0f, 1.0f, 0.0f, -1.0f, 4.0f, 0.0f, 1.0f, 0.5f, -2.0f, -0.5f, 0.0f },
{ 0.0f, -2.5f,-5.5f, 6.0f, -6.5f, 22.0f,-14.0f, 5.5f, 2.5f, -6.0f, -0.5f, 0.0f },
{ 0.0f, -0.5f, 1.0f, 0.0f, 3.5f, -2.0f, 0.0f, 1.0f, 5.0f, -8.0f, 1.0f, 0.0f },
{ 0.0f, -1.0f, 0.5f, 0.0f, -2.0f, 4.0f, -2.0f, -0.5f, 1.0f, 0.0f, 1.0f, 0.0f },
{ 0.0f, 7.5f,-3.0f, 0.0f, 7.5f,-26.0f, 8.0f, 1.0f,-3.0f, 8.0f, 1.0f, 0.0f },
{ 0.0f, 6.0f,-3.0f, 0.0f, 3.0f,-20.0f, 8.0f, 1.0f,-1.5f, 14.0f,-12.5f, 6.0f },
{ 0.0f, -6.5f, 2.5f, 0.0f, -2.5f, 22.0f, -6.0f, -0.5f,-5.5f,-14.0f, 5.5f, 6.0f }
REAL const gregoryToLoopMatrix[12][15] = {
{ 9.33333f, 5.33333f, -6.f, -0.66666f, 0.33333f,-18.66667f, 4.f, 6.f,
-0.66666f, 8.f, -8.f, 0.f, 1.33333f, 1.33333f, -0.666667f},
{ 0.33333f, -0.66666f, 8.f, 1.33333f, -0.66666f, -0.66666f, -2.f, -8.f,
1.33333f, 0.f, 0.f, 0.f, 1.33333f, 1.33333f, -0.666667f},
{-6.66667f, 13.33333f,-14.f, 7.33333f, 8.33333f, 13.33333f,-20.f, 14.f,
-16.66667f, -8.f, 8.f, 0.f, 1.33333f, 1.33333f, -0.666667f},
{ 9.33333f,-18.66667f, 8.f, 1.33333f, -0.66666f, 5.33333f, 4.f, -8.f,
1.33333f, -6.f, 6.f, 0.f, -0.66666f, -0.66666f, 0.333333f},
{-1.66667f, 3.33333f, -2.f, -0.66666f, 0.33333f, 3.33333f, -2.f, 2.f,
-0.66666f, -2.f, 2.f, 0.f, -0.66666f, -0.66666f, 0.333333f},
{ 1.33333f, -2.66667f, 0.f, 1.33333f, -0.66666f, -2.66667f, 4.f, 0.f,
1.33333f, 2.f, -2.f, 0.f, -0.66666f, -0.66666f, 0.333333f},
{ 6.33333f,-12.66667f, 14.f,-16.66667f, 8.33333f,-12.66667f, 22.f,-14.f,
7.33333f, 6.f, -6.f, 0.f, -0.66666f, -0.66666f, 0.333333f},
{ 0.33333f, -0.66666f, 0.f, 1.33333f, -0.66666f, -0.66666f, -2.f, 0.f,
1.33333f, 8.f, -8.f, 0.f, 1.33333f, 1.33333f, -0.666667f},
{ 1.33333f, -2.66667f, 2.f, -0.66666f, 0.33333f, -2.66667f, 4.f, -2.f,
-0.66666f, 0.f, 0.f, 0.f, 1.33333f, 1.33333f, -0.666667f},
{-7.66667f, 15.33333f, -8.f, 1.33333f, -0.66666f, 15.33333f,-26.f, 8.f,
1.33333f, -8.f, 8.f, 0.f, 1.33333f, 1.33333f, -0.666667f},
{-6.66667f, 13.33333f, -8.f, 1.33333f, -0.66666f, 13.33333f,-20.f, 8.f,
1.33333f,-14.f, 14.f, 0.f, 7.33333f,-16.66667f, 8.333333f},
{ 6.33333f,-12.66667f, 6.f, -0.66666f, 0.33333f,-12.66667f, 22.f, -6.f,
-0.66666f, 14.f,-14.f, 0.f,-16.66667f, 7.33333f, 8.333333f}
};
int const gRowIndices[12] = { 0, 1, 7, 5, 2, 3, 8, 6, 11, 13, 12, 10 };
int const gRowIndices[15] = { 0,1,15,7,5, 2,4,8,6, 17,14,16, 11,12, 10 };
SparseMatrix<REAL> G;
convertToGregory<REAL>(sourcePatch, G);
@ -1611,7 +1798,7 @@ convertToLoop(SourcePatch const & sourcePatch, SparseMatrix<REAL> & matrix) {
for (int i = 0; i < 12; ++i) {
REAL const * gRowWeights = gregoryToLoopMatrix[i];
_combineSparseMatrixRowsInFull(matrix, i, G, 12, gRowIndices, gRowWeights);
_combineSparseMatrixRowsInFull(matrix, i, G, 15, gRowIndices, gRowWeights);
}
}

View File

@ -949,61 +949,26 @@ int EvalBasisBoxSplineTri(REAL s, REAL t,
//
// Hybrid (cubic-quartic) Bezier triangle:
// Quartic Bezier triangle:
//
// The regular patch for Loop subdivision is a quartic triangular Box spline
// with cubic boundaries. So we need a quartic Bezier patch to represent it
// faithfully, but we use a cubic-quartic hybrid to keep the representation
// of boundaries as cubic -- useful for a number of purposes, in addition to
// reducing the number points required from 15 to 12.
// faithfully.
//
// Ultimately this patch is quartic and its basis functions are of maximum
// quartic degree. The formulae for the 15 true quartic basis functions are:
// The formulae for the 15 quartic basis functions are:
//
// 4! i j k
// B (u,v,w) = ------- * (u * v * w )
// ijk i!j!k!
//
// for each i + j + k = 4, and the quartic points and corresponding p<i,j,k>
// are oriented as follows:
// for each i + j + k = 4. The control points (P) and correspondingly
// labeled p<i,j,k> are oriented as follows:
//
// Q14 p040
// Q12 Q13 p031 p130
// Q9 Q10 Q11 p022 p121 p220
// Q5 Q6 Q7 Q8 p013 p112 p211 p310
// Q0 Q1 Q2 Q3 Q4 p004 p103 p202 p301 p400
//
// The points for the corresponding hybrid patch are oriented and numbered:
//
// H11
// H8 H10
// H9
// H4 H5 H6 H7
// H0 H1 H2 H3
//
// Their corresponding basis functions h(u,v,w) are derived by combining the
// quartic basis functions according to degree elevation of their boundary
// curves. This leads to the 12 basis functions:
//
// h[0] = w^3
// h[3] = u^3
// h[11] = v^3
//
// h[1] = 3 * u * w^2 * (u + w)
// h[2] = 3 * u^2 * w * (u + w)
//
// h[7] = 3 * u^2 * v * (u + v)
// h[10] = 3 * u * v^2 * (u + v)
//
// h[8] = 3 * v^2 * w * (w + v)
// h[10] = 3 * v * w^2 * (v + w)
//
// h[5] = 12 * u * v * w^2;
// h[6] = 12 * u^2 * v * w;
// h[9] = 12 * u * v^2 * w;
//
// These remain compact with at most two trivariate terms, and so relatively
// easy to differentiate in this form while keeping the number of terms low.
// P14 p040
// P12 P13 p031 p130
// P9 P10 P11 p022 p121 p220
// P5 P6 P7 P8 p013 p112 p211 p310
// P0 P1 P2 P3 P4 p004 p103 p202 p301 p400
//
namespace {
template <typename REAL>
@ -1014,9 +979,9 @@ namespace {
REAL v = t;
REAL w = 1 - u - v;
REAL u2 = u * u;
REAL v2 = v * v;
REAL w2 = w * w;
REAL uu = u * u;
REAL vv = v * v;
REAL ww = w * w;
REAL uv = u * v;
REAL vw = v * w;
@ -1024,110 +989,104 @@ namespace {
int totalOrder = ds + dt;
if (totalOrder == 0) {
wB[0] = w*w2;
wB[3] = u*u2;
wB[11] = v*v2;
wB[1] = 3 * uw * (uw + w2);
wB[2] = 3 * uw * (uw + u2);
wB[7] = 3 * uv * (uv + u2);
wB[10] = 3 * uv * (uv + v2);
wB[8] = 3 * vw * (vw + v2);
wB[4] = 3 * vw * (vw + w2);
wB[5] = 12 * w2 * uv;
wB[6] = 12 * u2 * vw;
wB[9] = 12 * v2 * uw;
wB[0] = ww * ww;
wB[1] = 4 * uw * ww;
wB[2] = 6 * uw * uw;
wB[3] = 4 * uw * uu;
wB[4] = uu * uu;
wB[5] = 4 * vw * ww;
wB[6] = 12 * ww * uv;
wB[7] = 12 * uu * vw;
wB[8] = 4 * uv * uu;
wB[9] = 6 * vw * vw;
wB[10] = 12 * vv * uw;
wB[11] = 6 * uv * uv;
wB[12] = 4 * vw * vv;
wB[13] = 4 * uv * vv;
wB[14] = vv * vv;
} else if (totalOrder == 1) {
if (ds) {
wB[0] = -3 * w2;
wB[3] = 3 * u2;
wB[11] = 0;
wB[1] = 3 * w * (w2 - uw - 2*u2);
wB[2] = -3 * u * (u2 - uw - 2*w2);
wB[7] = 9 * u2*v + 6 * u*v2;
wB[10] = 3 * v*v2 + 6 * u*v2;
wB[8] = -3 * v*v2 - 6 * v2*w;
wB[4] = -9 * v*w2 - 6 * v2*w;
wB[5] = 12 * vw * (w - 2*u);
wB[6] = 12 * uv * (2*w - u);
wB[9] = 12 * v2 * (w - u);
if (ds == 1) {
wB[0] = -4 * ww * w;
wB[1] = 4 * ww * (w - 3 * u);
wB[2] = 12 * uw * (w - u);
wB[3] = 4 * uu * (3 * w - u);
wB[4] = 4 * uu * u;
wB[5] = -12 * vw * w;
wB[6] = 12 * vw * (w - 2 * u);
wB[7] = 12 * uv * (2 * w - u);
wB[8] = 12 * uv * u;
wB[9] = -12 * vv * w;
wB[10] = 12 * vv * (w - u);
wB[11] = 12 * vv * u;
wB[12] = -4 * vv * v;
wB[13] = 4 * vv * v;
wB[14] = 0;
} else {
wB[0] = -3 * w2;
wB[3] = 0;
wB[11] = 3 * v2;
wB[1] = -9 * u*w2 - 6 * u2*w;
wB[2] = -3 * u*u2 - 6 * u2*w;
wB[7] = 3 * u*u2 + 6 * u2*v;
wB[10] = 9 * u*v2 + 6 * u2*v;
wB[8] = -3 * v * (v2 - vw - 2*w2);
wB[4] = 3 * w * (w2 - vw - 2*v2);
wB[5] = 12 * uw * (w - 2*v);
wB[6] = 12 * u2 * (w - v);
wB[9] = 12 * uv * (2*w - v);
wB[0] = -4 * ww * w;
wB[1] = -12 * ww * u;
wB[2] = -12 * uu * w;
wB[3] = -4 * uu * u;
wB[4] = 0;
wB[5] = 4 * ww * (w - 3 * v);
wB[6] = 12 * uw * (w - 2 * v);
wB[7] = 12 * uu * (w - v);
wB[8] = 4 * uu * u;
wB[9] = 12 * vw * (w - v);
wB[10] = 12 * uv * (2 * w - v);
wB[11] = 12 * uv * u;;
wB[12] = 4 * vv * (3 * w - v);
wB[13] = 12 * vv * u;
wB[14] = 4 * vv * v;
}
} else if (totalOrder == 2) {
if (ds == 2) {
wB[0] = 6 * w;
wB[3] = 6 * u;
wB[11] = 0;
wB[1] = 6 * (u2 - uw - 2*w2);
wB[2] = 6 * (w2 - uw - 2*u2);
wB[7] = 6 * v2 + 18 * uv;
wB[10] = 6 * v2;
wB[8] = 6 * v2;
wB[4] = 6 * v2 + 18 * vw;
wB[5] = 24 * (uv - 2*vw);
wB[6] = 24 * (vw - 2*uv);
wB[9] = -24 * v2;
wB[0] = 12 * ww;
wB[1] = 24 * (uw - ww);
wB[2] = 12 * (uu - 4 * uw + ww);
wB[3] = 24 * (uw - uu);
wB[4] = 12 * uu;
wB[5] = 24 * vw;
wB[6] = 24 * (uv - 2 * vw);
wB[7] = 24 * (vw - 2 * uv);
wB[8] = 24 * uv;
wB[9] = 12 * vv;
wB[10] = -24 * vv;
wB[11] = 12 * vv;
wB[12] = 0;
wB[13] = 0;
wB[14] = 0;
} else if (dt == 2) {
wB[0] = 6 * w;
wB[3] = 0;
wB[11] = 6 * v;
wB[1] = 6 * u2 + 18 * uw;
wB[2] = 6 * u2;
wB[7] = 6 * u2;
wB[10] = 6 * u2 + 18 * uv;
wB[8] = 6 * (w2 - vw - 2*v2);
wB[4] = 6 * (v2 - vw - 2*w2);
wB[5] = 24 * (uv - 2*uw);
wB[6] = -24 * u2;
wB[9] = 24 * (uw - 2*uv);
wB[0] = 12 * ww;
wB[1] = 24 * uw;
wB[2] = 12 * uu;
wB[3] = 0;
wB[4] = 0;
wB[5] = 24 * (vw - ww);
wB[6] = 24 * (uv - 2 * uw);
wB[7] = -24 * uu;
wB[8] = 0;
wB[9] = 12 * (vv - 4 * vw + ww);
wB[10] = 24 * (uw - 2 * uv);
wB[11] = 12 * uu;
wB[12] = 24 * (vw - vv);
wB[13] = 24 * uv;
wB[14] = 12 * vv;
} else {
wB[0] = 6 * w;
wB[3] = 0;
wB[11] = 0;
wB[1] = 6 * (u2 + uw - 1.5f*w2);
wB[2] = -3 * (u2 + 4*uw);
wB[7] = 9 * u2 + 12 * uv;
wB[10] = 9 * v2 + 12 * uv;
wB[8] = -3 * (v2 + 4*vw);
wB[4] = 6 * (v2 + vw - 1.5f*w2);
wB[5] = 24 * (uv - vw - uw + 0.5f*w2);
wB[6] = -24 * (uv - uw + 0.5f*u2);
wB[9] = -24 * (uv - vw + 0.5f*v2);
wB[0] = 12 * ww;
wB[3] = -12 * uu;
wB[13] = 12 * vv;
wB[11] = 24 * uv;
wB[1] = 24 * uw - wB[0];
wB[2] = -24 * uw - wB[3];
wB[5] = 24 * vw - wB[0];
wB[6] = -24 * vw + wB[11] - wB[1];
wB[8] = - wB[3];
wB[7] = -(wB[11] + wB[2]);
wB[9] = wB[13] - wB[5] - wB[0];
wB[10] = -(wB[9] + wB[11]);
wB[12] = - wB[13];
wB[4] = 0;
wB[14] = 0;
}
} else {
assert(totalOrder <= 2);
@ -1138,8 +1097,8 @@ namespace {
template <typename REAL>
int
EvalBasisBezierTri(REAL s, REAL t,
REAL wP[12], REAL wDs[12], REAL wDt[12],
REAL wDss[12], REAL wDst[12], REAL wDtt[12]) {
REAL wP[15], REAL wDs[15], REAL wDt[15],
REAL wDss[15], REAL wDst[15], REAL wDtt[15]) {
if (wP) {
evalBezierTriDerivWeights<REAL>(s, t, 0, 0, wP);
@ -1154,61 +1113,73 @@ EvalBasisBezierTri(REAL s, REAL t,
evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
}
}
return 12;
return 15;
}
//
// Hybrid (cubic-quartic) Gregory triangle:
// Quartic Gregory triangle:
//
// As with the Bezier triangle, and consistent with Loop, Schaefer at al (in
// ("Approximating Subdivision Surfaces with Gregory Patches for Hardware
// Tessellation") we use a cubic-quartic hybrid Gregory patch. Like the
// quad Gregory patch, this patch uses Bezier basis functions (from the
// cubic-quartic hybrid above) and rational multipliers to blend pairs of
// interior points (face points).
// The 18-point quaritic Gregory patch is an extension of the 15-point
// quartic Bezier triangle with the 3 interior points of the Bezier patch
// replaced with pairs of points (face points -- fi+ and fi-) that are
// rationally combined.
//
// The point ordering of Gregory patches deviates considerably from the
// BSpline and Bezier patches by grouping the 5 points at each corner and
// ordering the groups by corner index. This is consistent with the cubic
// Gregory quad patch.
//
// The 3 additional quartic boundary points are currently appended to these
// 3 groups of 5 control points. In contrast to the 5 points associated
// with each corner, these 3 points are more associated with the edge
// between the corner vertices and are equally weighted between the two.
//
namespace {
//
// Expanding a set of 12 Bezier basis functions for the 6 (3 pairs) of
// rational weights for the 15 Gregory basis functions:
// Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
// rational weights for the 18 Gregory basis functions:
//
template <typename REAL>
void
convertBezierWeightsToGregory(REAL const wB[12], REAL const rG[6], REAL wG[15]) {
convertBezierWeightsToGregory(REAL const wB[15], REAL const rG[6], REAL wG[18]) {
wG[0] = wB[0];
wG[1] = wB[1];
wG[2] = wB[4];
wG[3] = wB[5] * rG[0];
wG[4] = wB[5] * rG[1];
wG[2] = wB[5];
wG[3] = wB[6] * rG[0];
wG[4] = wB[6] * rG[1];
wG[5] = wB[3];
wG[6] = wB[7];
wG[7] = wB[2];
wG[8] = wB[6] * rG[2];
wG[9] = wB[6] * rG[3];
wG[5] = wB[4];
wG[6] = wB[8];
wG[7] = wB[3];
wG[8] = wB[7] * rG[2];
wG[9] = wB[7] * rG[3];
wG[10] = wB[11];
wG[11] = wB[8];
wG[12] = wB[10];
wG[13] = wB[9] * rG[4];
wG[14] = wB[9] * rG[5];
wG[10] = wB[14];
wG[11] = wB[12];
wG[12] = wB[13];
wG[13] = wB[10] * rG[4];
wG[14] = wB[10] * rG[5];
wG[15] = wB[2];
wG[16] = wB[11];
wG[17] = wB[9];
}
} // end namespace
template <typename REAL>
int
EvalBasisGregoryTri(REAL s, REAL t,
REAL wP[15], REAL wDs[15], REAL wDt[15],
REAL wDss[15], REAL wDst[15], REAL wDtt[15]) {
REAL wP[18], REAL wDs[18], REAL wDt[18],
REAL wDss[18], REAL wDst[18], REAL wDtt[18]) {
//
// Bezier basis functions are denoted with B while the rational multipliers for the
// interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
// switch to barycentric (u,v,w) briefly to compute G)
//
REAL BP[12], BDs[12], BDt[12], BDss[12], BDst[12], BDtt[12];
REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
REAL G[6] = { 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f };
REAL u = s;
@ -1254,7 +1225,7 @@ EvalBasisGregoryTri(REAL s, REAL t,
convertBezierWeightsToGregory(BDtt, G, wDtt);
}
}
return 15;
return 18;
}
//

View File

@ -76,7 +76,7 @@ int EvalBasisLinearTri(REAL s, REAL t,
template <typename REAL>
int EvalBasisBezierTri(REAL s, REAL t,
REAL wP[12], REAL wDs[12] = 0, REAL wDt[12] = 0, REAL wDss[12] = 0, REAL wDst[12] = 0, REAL wDtt[12] = 0);
REAL wP[15], REAL wDs[15] = 0, REAL wDt[15] = 0, REAL wDss[15] = 0, REAL wDst[15] = 0, REAL wDtt[15] = 0);
template <typename REAL>
int EvalBasisBoxSplineTri(REAL s, REAL t,
@ -84,7 +84,7 @@ int EvalBasisBoxSplineTri(REAL s, REAL t,
template <typename REAL>
int EvalBasisGregoryTri(REAL s, REAL t,
REAL wP[15], REAL wDs[15] = 0, REAL wDt[15] = 0, REAL wDss[15] = 0, REAL wDst[15] = 0, REAL wDtt[15] = 0);
REAL wP[18], REAL wDs[18] = 0, REAL wDt[18] = 0, REAL wDss[18] = 0, REAL wDst[18] = 0, REAL wDtt[18] = 0);
//

View File

@ -149,7 +149,7 @@ PatchDescriptor::GetNumControlVertices( Type type ) {
case GREGORY :
case GREGORY_BOUNDARY : return GetGregoryPatchSize();
case GREGORY_BASIS : return GetGregoryBasisPatchSize();
case GREGORY_TRIANGLE : return 15;
case GREGORY_TRIANGLE : return 18;
case TRIANGLES : return 3;
case LINES : return 2;
case POINTS : return 1;

View File

@ -1644,11 +1644,13 @@ namespace {
{ 0, 0, 1, 0, 0, 1, 1, 2, 2, 1, 2, 2 };
static int const gregoryIndices[] =
{ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3 };
static int const gregoryTriIndices[] =
{ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 0, 1, 2 };
if (type == PatchDescriptor::GREGORY_BASIS) {
return gregoryIndices;
} else if (type == PatchDescriptor::GREGORY_TRIANGLE) {
return gregoryIndices;
return gregoryTriIndices;
} else if (type == PatchDescriptor::REGULAR) {
return bsplineIndices;
} else if (type == PatchDescriptor::LOOP) {

View File

@ -1148,7 +1148,7 @@ OsdEvalPatchBezierTriangle(ivec3 patchParam, vec2 UV,
}
void
OsdEvalPatchGregoryTriangle(ivec3 patchParam, vec2 UV, vec3 cv[15],
OsdEvalPatchGregoryTriangle(ivec3 patchParam, vec2 UV, vec3 cv[18],
out vec3 P, out vec3 dPu, out vec3 dPv,
out vec3 N, out vec3 dNu, out vec3 dNv)
{
@ -1167,16 +1167,16 @@ OsdEvalPatchGregoryTriangle(ivec3 patchParam, vec2 UV, vec3 cv[15],
bezcv[10].P = (dwu == 0.0) ? cv[13] : ((w*cv[13] + u*cv[14]) / dwu);
bezcv[ 0].P = cv[ 0];
bezcv[ 1].P = 0.25*cv[ 0] + 0.75*cv[ 1];
bezcv[ 2].P = 0.5 *cv[ 1] + 0.5 *cv[ 7];
bezcv[ 3].P = 0.25*cv[ 5] + 0.75*cv[ 7];
bezcv[ 1].P = cv[ 1];
bezcv[ 2].P = cv[15];
bezcv[ 3].P = cv[ 7];
bezcv[ 4].P = cv[ 5];
bezcv[ 5].P = 0.25*cv[ 0] + 0.75*cv[ 2];
bezcv[ 8].P = 0.25*cv[ 5] + 0.75*cv[ 6];
bezcv[ 9].P = 0.5 *cv[ 2] + 0.5 *cv[11];
bezcv[11].P = 0.5 *cv[ 6] + 0.5 *cv[12];
bezcv[12].P = 0.25*cv[10] + 0.75*cv[11];
bezcv[13].P = 0.25*cv[10] + 0.75*cv[12];
bezcv[ 5].P = cv[ 2];
bezcv[ 8].P = cv[ 6];
bezcv[ 9].P = cv[17];
bezcv[11].P = cv[16];
bezcv[12].P = cv[11];
bezcv[13].P = cv[12];
bezcv[14].P = cv[10];
OsdEvalPatchBezierTriangle(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);

View File

@ -368,46 +368,6 @@ OsdGetTessLevelsLimitPointsTriangle(vec3 cv[15],
}
}
void
OsdGetTessLevelsLimitPointsGregoryTriangle(vec3 cv[15],
ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
{
// Each edge of a transition patch is adjacent to one or two patches
// at the next refined level of subdivision. When the patch control
// points have been converted to the Bezier basis, the control points
// at the corners are on the limit surface (since a Bezier patch
// interpolates its corner control points). We can compute an adaptive
// tessellation level for transition edges on the limit surface by
// evaluating a limit position at the mid point of each transition edge.
tessOuterLo = vec4(0);
tessOuterHi = vec4(0);
int transitionMask = OsdGetPatchTransitionMask(patchParam);
if ((transitionMask & 4) != 0) {
vec3 P = Osd_EvalBezierCurveMidPoint(cv[0], cv[2], cv[11], cv[10]);
tessOuterLo[0] = OsdComputeTessLevel(cv[0], P);
tessOuterHi[0] = OsdComputeTessLevel(cv[10], P);
} else {
tessOuterLo[0] = OsdComputeTessLevel(cv[0], cv[10]);
}
if ((transitionMask & 1) != 0) {
vec3 P = Osd_EvalBezierCurveMidPoint(cv[0], cv[1], cv[7], cv[5]);
tessOuterLo[1] = OsdComputeTessLevel(cv[0], P);
tessOuterHi[1] = OsdComputeTessLevel(cv[5], P);
} else {
tessOuterLo[1] = OsdComputeTessLevel(cv[0], cv[5]);
}
if ((transitionMask & 2) != 0) {
vec3 P = Osd_EvalBezierCurveMidPoint(cv[5], cv[6], cv[12], cv[10]);
tessOuterLo[2] = OsdComputeTessLevel(cv[10], P);
tessOuterHi[2] = OsdComputeTessLevel(cv[5], P);
} else {
tessOuterLo[2] = OsdComputeTessLevel(cv[5], cv[10]);
}
}
// Round up to the nearest even integer
float OsdRoundUpEven(float x) {
return 2*ceil(x/2);
@ -574,19 +534,6 @@ OsdGetTessLevelsAdaptiveLimitPointsTriangle(vec3 cv[15],
tessLevelOuter, tessLevelInner);
}
void
OsdGetTessLevelsAdaptiveLimitPointsGregoryTriangle(vec3 cv[15],
ivec3 patchParam,
out vec4 tessLevelOuter, out vec2 tessLevelInner,
out vec4 tessOuterLo, out vec4 tessOuterHi)
{
OsdGetTessLevelsLimitPointsGregoryTriangle(cv, patchParam,
tessOuterLo, tessOuterHi);
OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
void
OsdGetTessLevels(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3,
ivec3 patchParam,

View File

@ -59,9 +59,9 @@ in block {
out block {
OsdPerPatchVertexBezier v;
OSD_USER_VARYING_DECLARE
} outpt[15];
} outpt[18];
layout(vertices = 15) out;
layout(vertices = 18) out;
void main()
{
@ -82,7 +82,7 @@ void main()
vec4 tessLevelOuter = vec4(0);
vec2 tessLevelInner = vec2(0);
OSD_PATCH_CULL(15);
OSD_PATCH_CULL(18);
OsdGetTessLevelsUniformTriangle(patchParam,
tessLevelOuter, tessLevelInner,
@ -91,10 +91,23 @@ void main()
#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
// Gather bezier control points to compute limit surface tess levels
vec3 cv[15];
for (int i=0; i<15; ++i) {
cv[i] = outpt[i].v.P;
}
OsdGetTessLevelsAdaptiveLimitPointsGregoryTriangle(
cv[ 0] = outpt[ 0].v.P;
cv[ 1] = outpt[ 1].v.P;
cv[ 2] = outpt[15].v.P;
cv[ 3] = outpt[ 7].v.P;
cv[ 4] = outpt[ 5].v.P;
cv[ 5] = outpt[ 2].v.P;
cv[ 6] = outpt[ 3].v.P;
cv[ 7] = outpt[ 8].v.P;
cv[ 8] = outpt[ 6].v.P;
cv[ 9] = outpt[17].v.P;
cv[10] = outpt[13].v.P;
cv[11] = outpt[16].v.P;
cv[12] = outpt[11].v.P;
cv[13] = outpt[12].v.P;
cv[14] = outpt[10].v.P;
OsdGetTessLevelsAdaptiveLimitPointsTriangle(
cv, patchParam,
tessLevelOuter, tessLevelInner,
tessOuterLo, tessOuterHi);
@ -144,8 +157,8 @@ void main()
vec3 P = vec3(0), dPu = vec3(0), dPv = vec3(0);
vec3 N = vec3(0), dNu = vec3(0), dNv = vec3(0);
vec3 cv[15];
for (int i = 0; i < 15; ++i) {
vec3 cv[18];
for (int i = 0; i < 18; ++i) {
cv[i] = inpt[i].v.P;
}

View File

@ -838,15 +838,15 @@ Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
void
Osd_evalBezierTriDerivWeights(
OSD_REAL s, OSD_REAL t, int ds, int dt,
OSD_OUT_ARRAY(OSD_REAL, wB, 12)) {
OSD_OUT_ARRAY(OSD_REAL, wB, 15)) {
OSD_REAL u = s;
OSD_REAL v = t;
OSD_REAL w = 1 - u - v;
OSD_REAL u2 = u * u;
OSD_REAL v2 = v * v;
OSD_REAL w2 = w * w;
OSD_REAL uu = u * u;
OSD_REAL vv = v * v;
OSD_REAL ww = w * w;
OSD_REAL uv = u * v;
OSD_REAL vw = v * w;
@ -854,110 +854,104 @@ Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
int totalOrder = ds + dt;
if (totalOrder == 0) {
wB[0] = w*w2;
wB[3] = u*u2;
wB[11] = v*v2;
wB[1] = 3 * uw * (uw + w2);
wB[2] = 3 * uw * (uw + u2);
wB[7] = 3 * uv * (uv + u2);
wB[10] = 3 * uv * (uv + v2);
wB[8] = 3 * vw * (vw + v2);
wB[4] = 3 * vw * (vw + w2);
wB[5] = 12 * w2 * uv;
wB[6] = 12 * u2 * vw;
wB[9] = 12 * v2 * uw;
wB[0] = ww * ww;
wB[1] = 4 * uw * ww;
wB[2] = 6 * uw * uw;
wB[3] = 4 * uw * uu;
wB[4] = uu * uu;
wB[5] = 4 * vw * ww;
wB[6] = 12 * ww * uv;
wB[7] = 12 * uu * vw;
wB[8] = 4 * uv * uu;
wB[9] = 6 * vw * vw;
wB[10] = 12 * vv * uw;
wB[11] = 6 * uv * uv;
wB[12] = 4 * vw * vv;
wB[13] = 4 * uv * vv;
wB[14] = vv * vv;
} else if (totalOrder == 1) {
if (ds != 0) {
wB[0] = -3 * w2;
wB[3] = 3 * u2;
wB[11] = 0;
wB[1] = 3 * w * (w2 - uw - 2*u2);
wB[2] = -3 * u * (u2 - uw - 2*w2);
wB[7] = 9 * u2*v + 6 * u*v2;
wB[10] = 3 * v*v2 + 6 * u*v2;
wB[8] = -3 * v*v2 - 6 * v2*w;
wB[4] = -9 * v*w2 - 6 * v2*w;
wB[5] = 12 * vw * (w - 2*u);
wB[6] = 12 * uv * (2*w - u);
wB[9] = 12 * v2 * (w - u);
if (ds == 1) {
wB[0] = -4 * ww * w;
wB[1] = 4 * ww * (w - 3 * u);
wB[2] = 12 * uw * (w - u);
wB[3] = 4 * uu * (3 * w - u);
wB[4] = 4 * uu * u;
wB[5] = -12 * vw * w;
wB[6] = 12 * vw * (w - 2 * u);
wB[7] = 12 * uv * (2 * w - u);
wB[8] = 12 * uv * u;
wB[9] = -12 * vv * w;
wB[10] = 12 * vv * (w - u);
wB[11] = 12 * vv * u;
wB[12] = -4 * vv * v;
wB[13] = 4 * vv * v;
wB[14] = 0;
} else {
wB[0] = -3 * w2;
wB[3] = 0;
wB[11] = 3 * v2;
wB[1] = -9 * u*w2 - 6 * u2*w;
wB[2] = -3 * u*u2 - 6 * u2*w;
wB[7] = 3 * u*u2 + 6 * u2*v;
wB[10] = 9 * u*v2 + 6 * u2*v;
wB[8] = -3 * v * (v2 - vw - 2*w2);
wB[4] = 3 * w * (w2 - vw - 2*v2);
wB[5] = 12 * uw * (w - 2*v);
wB[6] = 12 * u2 * (w - v);
wB[9] = 12 * uv * (2*w - v);
wB[0] = -4 * ww * w;
wB[1] = -12 * ww * u;
wB[2] = -12 * uu * w;
wB[3] = -4 * uu * u;
wB[4] = 0;
wB[5] = 4 * ww * (w - 3 * v);
wB[6] = 12 * uw * (w - 2 * v);
wB[7] = 12 * uu * (w - v);
wB[8] = 4 * uu * u;
wB[9] = 12 * vw * (w - v);
wB[10] = 12 * uv * (2 * w - v);
wB[11] = 12 * uv * u;;
wB[12] = 4 * vv * (3 * w - v);
wB[13] = 12 * vv * u;
wB[14] = 4 * vv * v;
}
} else if (totalOrder == 2) {
if (ds == 2) {
wB[0] = 6 * w;
wB[3] = 6 * u;
wB[11] = 0;
wB[1] = 6 * (u2 - uw - 2*w2);
wB[2] = 6 * (w2 - uw - 2*u2);
wB[7] = 6 * v2 + 18 * uv;
wB[10] = 6 * v2;
wB[8] = 6 * v2;
wB[4] = 6 * v2 + 18 * vw;
wB[5] = 24 * (uv - 2*vw);
wB[6] = 24 * (vw - 2*uv);
wB[9] = -24 * v2;
wB[0] = 12 * ww;
wB[1] = 24 * (uw - ww);
wB[2] = 12 * (uu - 4 * uw + ww);
wB[3] = 24 * (uw - uu);
wB[4] = 12 * uu;
wB[5] = 24 * vw;
wB[6] = 24 * (uv - 2 * vw);
wB[7] = 24 * (vw - 2 * uv);
wB[8] = 24 * uv;
wB[9] = 12 * vv;
wB[10] = -24 * vv;
wB[11] = 12 * vv;
wB[12] = 0;
wB[13] = 0;
wB[14] = 0;
} else if (dt == 2) {
wB[0] = 6 * w;
wB[3] = 0;
wB[11] = 6 * v;
wB[1] = 6 * u2 + 18 * uw;
wB[2] = 6 * u2;
wB[7] = 6 * u2;
wB[10] = 6 * u2 + 18 * uv;
wB[8] = 6 * (w2 - vw - 2*v2);
wB[4] = 6 * (v2 - vw - 2*w2);
wB[5] = 24 * (uv - 2*uw);
wB[6] = -24 * u2;
wB[9] = 24 * (uw - 2*uv);
wB[0] = 12 * ww;
wB[1] = 24 * uw;
wB[2] = 12 * uu;
wB[3] = 0;
wB[4] = 0;
wB[5] = 24 * (vw - ww);
wB[6] = 24 * (uv - 2 * uw);
wB[7] = -24 * uu;
wB[8] = 0;
wB[9] = 12 * (vv - 4 * vw + ww);
wB[10] = 24 * (uw - 2 * uv);
wB[11] = 12 * uu;
wB[12] = 24 * (vw - vv);
wB[13] = 24 * uv;
wB[14] = 12 * vv;
} else {
wB[0] = 6 * w;
wB[3] = 0;
wB[11] = 0;
wB[1] = 6 * (u2 + uw - 1.5f*w2);
wB[2] = -3 * (u2 + 4*uw);
wB[7] = 9 * u2 + 12 * uv;
wB[10] = 9 * v2 + 12 * uv;
wB[8] = -3 * (v2 + 4*vw);
wB[4] = 6 * (v2 + vw - 1.5f*w2);
wB[5] = 24 * (uv - vw - uw + 0.5f*w2);
wB[6] = -24 * (uv - uw + 0.5f*u2);
wB[9] = -24 * (uv - vw + 0.5f*v2);
wB[0] = 12 * ww;
wB[3] = -12 * uu;
wB[13] = 12 * vv;
wB[11] = 24 * uv;
wB[1] = 24 * uw - wB[0];
wB[2] = -24 * uw - wB[3];
wB[5] = 24 * vw - wB[0];
wB[6] = -24 * vw + wB[11] - wB[1];
wB[8] = - wB[3];
wB[7] = -(wB[11] + wB[2]);
wB[9] = wB[13] - wB[5] - wB[0];
wB[10] = -(wB[9] + wB[11]);
wB[12] = - wB[13];
wB[4] = 0;
wB[14] = 0;
}
} else {
// assert(totalOrder <= 2);
@ -969,12 +963,12 @@ OSD_FUNCTION_STORAGE_CLASS
// template <typename REAL>
int
Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
OSD_OUT_ARRAY(OSD_REAL, wP, 12),
OSD_OUT_ARRAY(OSD_REAL, wDs, 12),
OSD_OUT_ARRAY(OSD_REAL, wDt, 12),
OSD_OUT_ARRAY(OSD_REAL, wDss, 12),
OSD_OUT_ARRAY(OSD_REAL, wDst, 12),
OSD_OUT_ARRAY(OSD_REAL, wDtt, 12)) {
OSD_OUT_ARRAY(OSD_REAL, wP, 15),
OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
if (OSD_OPTIONAL(wP)) {
Osd_evalBezierTriDerivWeights(s, t, 0, 0, wP);
@ -989,40 +983,44 @@ Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
Osd_evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
}
}
return 12;
return 15;
}
// namespace {
//
// Expanding a set of 12 Bezier basis functions for the 6 (3 pairs) of
// rational weights for the 15 Gregory basis functions:
// Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
// rational weights for the 18 Gregory basis functions:
//
OSD_FUNCTION_STORAGE_CLASS
// template <typename REAL>
void
Osd_convertBezierWeightsToGregory(
OSD_INOUT_ARRAY(OSD_REAL, wB, 12),
OSD_INOUT_ARRAY(OSD_REAL, wB, 15),
OSD_INOUT_ARRAY(OSD_REAL, rG, 6),
OSD_OUT_ARRAY(OSD_REAL, wG, 15)) {
OSD_OUT_ARRAY(OSD_REAL, wG, 18)) {
wG[0] = wB[0];
wG[1] = wB[1];
wG[2] = wB[4];
wG[3] = wB[5] * rG[0];
wG[4] = wB[5] * rG[1];
wG[2] = wB[5];
wG[3] = wB[6] * rG[0];
wG[4] = wB[6] * rG[1];
wG[5] = wB[3];
wG[6] = wB[7];
wG[7] = wB[2];
wG[8] = wB[6] * rG[2];
wG[9] = wB[6] * rG[3];
wG[5] = wB[4];
wG[6] = wB[8];
wG[7] = wB[3];
wG[8] = wB[7] * rG[2];
wG[9] = wB[7] * rG[3];
wG[10] = wB[11];
wG[11] = wB[8];
wG[12] = wB[10];
wG[13] = wB[9] * rG[4];
wG[14] = wB[9] * rG[5];
wG[10] = wB[14];
wG[11] = wB[12];
wG[12] = wB[13];
wG[13] = wB[10] * rG[4];
wG[14] = wB[10] * rG[5];
wG[15] = wB[2];
wG[16] = wB[11];
wG[17] = wB[9];
}
// } // end namespace
@ -1030,19 +1028,19 @@ OSD_FUNCTION_STORAGE_CLASS
// template <typename REAL>
int
Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
OSD_OUT_ARRAY(OSD_REAL, wP, 15),
OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
OSD_OUT_ARRAY(OSD_REAL, wP, 18),
OSD_OUT_ARRAY(OSD_REAL, wDs, 18),
OSD_OUT_ARRAY(OSD_REAL, wDt, 18),
OSD_OUT_ARRAY(OSD_REAL, wDss, 18),
OSD_OUT_ARRAY(OSD_REAL, wDst, 18),
OSD_OUT_ARRAY(OSD_REAL, wDtt, 18)) {
//
// Bezier basis functions are denoted with B while the rational multipliers for the
// interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
// switch to barycentric (u,v,w) briefly to compute G)
//
OSD_REAL BP[12], BDs[12], BDt[12], BDss[12], BDst[12], BDtt[12];
OSD_REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
OSD_REAL G[6] = OSD_ARRAY_6(OSD_REAL, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f );
OSD_REAL u = s;
@ -1088,7 +1086,7 @@ Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
Osd_convertBezierWeightsToGregory(BDtt, G, wDtt);
}
}
return 15;
return 18;
}
#define OSD_PATCH_BASIS_COMPATIBILITY

View File

@ -92,14 +92,14 @@ OsdEvaluatePatchBasisNormalized(
#if OSD_ARRAY_ARG_BOUND_OPTIONAL
nPoints = Osd_EvalBasisGregoryTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
#else
OSD_REAL wP15[15], wDs15[15], wDt15[15],
wDss15[15], wDst15[15], wDtt15[15];
OSD_REAL wP18[18], wDs18[18], wDt18[18],
wDss18[18], wDst18[18], wDtt18[18];
nPoints = Osd_EvalBasisGregoryTri(
s, t, wP15, wDs15, wDt15, wDss15, wDst15, wDtt15);
s, t, wP18, wDs18, wDt18, wDss18, wDst18, wDtt18);
for (int i=0; i<nPoints; ++i) {
wP[i] = wP15[i];
wDs[i] = wDs15[i]; wDt[i] = wDt15[i];
wDss[i] = wDss15[i]; wDst[i] = wDst15[i]; wDtt[i] = wDtt15[i];
wP[i] = wP18[i];
wDs[i] = wDs18[i]; wDt[i] = wDt18[i];
wDss[i] = wDss18[i]; wDst[i] = wDst18[i]; wDtt[i] = wDtt18[i];
}
#endif
} else if (patchType == OSD_PATCH_DESCRIPTOR_QUADS) {