OpenSubdiv/opensubdiv/bfr/regularPatchBuilder.cpp
Barry Fowler a1c7be7c8e Addition of Bfr interface (1 of 4): opensubdiv/bfr
This set of commits includes the addition of a new evaluation interface
that treats a subdivision mesh more like a piecewise parametric surface
primitive.  The new interface was placed in namespace "Bfr" for "Base
Face Representation" as all concepts and classes relate to a single face
of the base mesh.
2022-08-02 20:38:17 -07:00

442 lines
15 KiB
C++

//
// Copyright 2021 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../bfr/regularPatchBuilder.h"
#include <cstring>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Bfr {
//
// Internal helper functions:
//
namespace {
inline int
encodeTriBoundaryMask(int eBits, int vBits) {
int upperBits = 0;
int lowerBits = eBits;
if (vBits) {
if (eBits == 0) {
upperBits = 1;
lowerBits = vBits;
} else if ((vBits == 7) &&
((eBits == 1) || (eBits == 2) || (eBits == 4))) {
upperBits = 2;
lowerBits = eBits;
}
}
return (upperBits << 3) | lowerBits;
}
}
//
// Static methods:
//
int
RegularPatchBuilder::GetBoundaryMask(int faceSize, Index const cvs[]) {
bool isQuad = (faceSize == 4);
if (isQuad) {
int eMask = ((cvs[ 1] < 0) << 0) |
((cvs[ 7] < 0) << 1) |
((cvs[14] < 0) << 2) |
((cvs[ 8] < 0) << 3);
return eMask;
} else {
int eMask = ((cvs[ 1] < 0) << 0) |
((cvs[ 9] < 0) << 1) |
((cvs[ 7] < 0) << 2) ;
int vMask = (((cvs[ 0] < 0) | (cvs[ 3] < 0)) << 0) |
(((cvs[ 2] < 0) | (cvs[ 6] < 0)) << 1) |
(((cvs[10] < 0) | (cvs[11] < 0)) << 2) ;
return encodeTriBoundaryMask(eMask, vMask);
}
}
//
// Constructor (empty destructor is inline):
//
RegularPatchBuilder::RegularPatchBuilder(FaceSurface const & surface) :
_surface(surface) {
_isQuad = (_surface.GetTopology()._faceSize == 4);
if (_isQuad) {
assert(_surface.GetTopology()._regFaceSize == 4);
_patchType = Far::PatchDescriptor::REGULAR;
_patchSize = 16;
} else {
assert(_surface.GetTopology()._faceSize == 3);
assert(_surface.GetTopology()._regFaceSize == 3);
_patchType = Far::PatchDescriptor::LOOP;
_patchSize = 12;
}
_isBoundary = _surface.GetTag().HasBoundaryVertices();
if (!_isBoundary) {
_boundaryMask = 0;
} else if (_isQuad) {
// Boundary mask for quad trivial -- bit for each boundary edge:
FaceVertexSubset const * C = _surface.GetSubsets();
int eMask = ((C[0].IsBoundary() & (C[0]._numFacesBefore == 0)) << 0) |
((C[1].IsBoundary() & (C[1]._numFacesBefore == 0)) << 1) |
((C[2].IsBoundary() & (C[2]._numFacesBefore == 0)) << 2) |
((C[3].IsBoundary() & (C[3]._numFacesBefore == 0)) << 3);
_boundaryMask = eMask;
} else {
// Boundary mask for tris not so trivial -- boundary verts can exist
// on tris without boundary edges, so bits for both are combined:
FaceVertexSubset const * C = _surface.GetSubsets();
int eMask = ((C[0].IsBoundary() & (C[0]._numFacesBefore == 0)) << 0) |
((C[1].IsBoundary() & (C[1]._numFacesBefore == 0)) << 1) |
((C[2].IsBoundary() & (C[2]._numFacesBefore == 0)) << 2);
int vMask = (C[0].IsBoundary() << 0) |
(C[1].IsBoundary() << 1) |
(C[2].IsBoundary() << 2);
_boundaryMask = encodeTriBoundaryMask(eMask, vMask);
}
}
//
// Methods for gathering control vertices:
//
void
RegularPatchBuilder::gatherInteriorPatchPoints4(Index P[]) const {
Index const * fvIndices = &_surface.GetIndices()[0];
Index const * fvOpposite = 0;
//
// For each of the 4 corners, identify the opposite face in the ring
// and assign its 4 indices to the corresponding quadrant of the patch:
//
FaceVertex const & cTop0 = _surface.GetCornerTopology(0);
fvOpposite = fvIndices + cTop0.GetFaceIndexOffset(cTop0.GetFaceAfter(2));
P[ 5] = fvOpposite[0];
P[ 4] = fvOpposite[1];
P[ 0] = fvOpposite[2];
P[ 1] = fvOpposite[3];
fvIndices += cTop0.GetNumFaceVertices();
FaceVertex const & cTop1 = _surface.GetCornerTopology(1);
fvOpposite = fvIndices + cTop1.GetFaceIndexOffset(cTop1.GetFaceAfter(2));
P[ 6] = fvOpposite[0];
P[ 2] = fvOpposite[1];
P[ 3] = fvOpposite[2];
P[ 7] = fvOpposite[3];
fvIndices += cTop1.GetNumFaceVertices();
FaceVertex const & cTop2 = _surface.GetCornerTopology(2);
fvOpposite = fvIndices + cTop2.GetFaceIndexOffset(cTop2.GetFaceAfter(2));
P[10] = fvOpposite[0];
P[11] = fvOpposite[1];
P[15] = fvOpposite[2];
P[14] = fvOpposite[3];
fvIndices += cTop2.GetNumFaceVertices();
FaceVertex const & cTop3 = _surface.GetCornerTopology(3);
fvOpposite = fvIndices + cTop3.GetFaceIndexOffset(cTop3.GetFaceAfter(2));
P[ 9] = fvOpposite[0];
P[13] = fvOpposite[1];
P[12] = fvOpposite[2];
P[ 8] = fvOpposite[3];
}
void
RegularPatchBuilder::gatherBoundaryPatchPoints4(Index P[]) const {
Index const * fvIndices = &_surface.GetIndices()[0];
//
// For each of the 4 corners -- whether boundary or interior -- one
// incident face contains all indices that will contribute to the points
// of the corresponding patch. Identify it first and then retrieve and
// assign the indices accordingly:
//
for (int i = 0; i < 4; ++i) {
FaceVertex const & cTop = _surface.GetCornerTopology(i);
FaceVertexSubset const & cSub = _surface.GetCornerSubset(i);
int faceCorner = cTop.GetFace();
int faceOther = faceCorner;
if (!cSub.IsBoundary()) {
faceOther = cTop.GetFaceAfter(2);
} else if (cSub._numFacesAfter) {
faceOther = cTop.GetFaceNext(faceCorner);
} else if (cSub._numFacesBefore) {
faceOther = cTop.GetFacePrevious(faceCorner);
}
Index const * fvOther = fvIndices + cTop.GetFaceIndexOffset(faceOther);
Index fvPhantom = fvOther[0];
switch (i) {
case 0:
P[5] = fvOther[0];
if (!cSub.IsBoundary()) {
P[4] = fvOther[1];
P[0] = fvOther[2];
P[1] = fvOther[3];
} else {
P[4] = cSub._numFacesAfter ? fvOther[3] : fvPhantom;
P[0] = fvPhantom;
P[1] = cSub._numFacesBefore ? fvOther[1] : fvPhantom;
}
break;
case 1:
P[6] = fvOther[0];
if (!cSub.IsBoundary()) {
P[2] = fvOther[1];
P[3] = fvOther[2];
P[7] = fvOther[3];
} else {
P[2] = cSub._numFacesAfter ? fvOther[3] : fvPhantom;
P[3] = fvPhantom;
P[7] = cSub._numFacesBefore ? fvOther[1] : fvPhantom;
}
break;
case 2:
P[10] = fvOther[0];
if (!cSub.IsBoundary()) {
P[11] = fvOther[1];
P[15] = fvOther[2];
P[14] = fvOther[3];
} else {
P[11] = cSub._numFacesAfter ? fvOther[3] : fvPhantom;
P[15] = fvPhantom;
P[14] = cSub._numFacesBefore ? fvOther[1] : fvPhantom;
}
break;
case 3:
P[ 9] = fvOther[0];
if (!cSub.IsBoundary()) {
P[13] = fvOther[1];
P[12] = fvOther[2];
P[ 8] = fvOther[3];
} else {
P[13] = cSub._numFacesAfter ? fvOther[3] : fvPhantom;
P[12] = fvPhantom;
P[ 8] = cSub._numFacesBefore ? fvOther[1] : fvPhantom;
}
break;
}
fvIndices += cTop.GetNumFaceVertices();
}
}
void
RegularPatchBuilder::gatherInteriorPatchPoints3(Index P[]) const {
Index const * fvIndices = &_surface.GetIndices()[0];
//
// For each of the 3 corners, the indices for the four contributing
// points come from the 2nd and 3rd faces following the corner face:
//
Index const * fvNext2 = 0;
Index const * fvNext3 = 0;
FaceVertex const & cTop0 = _surface.GetCornerTopology(0);
fvNext2 = fvIndices + cTop0.GetFaceIndexOffset(cTop0.GetFaceAfter(2));
fvNext3 = fvIndices + cTop0.GetFaceIndexOffset(cTop0.GetFaceAfter(3));
P[ 4] = fvNext2[0];
P[ 7] = fvNext2[1];
P[ 3] = fvNext2[2];
P[ 0] = fvNext3[2];
fvIndices += cTop0.GetNumFaceVertices();
FaceVertex const & cTop1 = _surface.GetCornerTopology(1);
fvNext2 = fvIndices + cTop1.GetFaceIndexOffset(cTop1.GetFaceAfter(2));
fvNext3 = fvIndices + cTop1.GetFaceIndexOffset(cTop1.GetFaceAfter(3));
P[ 5] = fvNext2[0];
P[ 1] = fvNext2[1];
P[ 2] = fvNext2[2];
P[ 6] = fvNext3[2];
fvIndices += cTop1.GetNumFaceVertices();
FaceVertex const & cTop2 = _surface.GetCornerTopology(2);
fvNext2 = fvIndices + cTop2.GetFaceIndexOffset(cTop2.GetFaceAfter(2));
fvNext3 = fvIndices + cTop2.GetFaceIndexOffset(cTop2.GetFaceAfter(3));
P[ 8] = fvNext2[0];
P[ 9] = fvNext2[1];
P[11] = fvNext2[2];
P[10] = fvNext3[2];
}
void
RegularPatchBuilder::gatherBoundaryPatchPoints3(Index P[]) const {
Index const * fvIndices = &_surface.GetIndices()[0];
//
// For each of the 3 corners, one incident face contains all indices
// that will contribute to the points of the corresponding patch, but
// interior vertices require two:
//
for (int i = 0; i < 3; ++i) {
FaceVertex const & cTop = _surface.GetCornerTopology(i);
FaceVertexSubset const & cSub = _surface.GetCornerSubset(i);
int faceCorner = cTop.GetFace();
int faceOther = -1;
if (!cSub.IsBoundary()) {
faceOther = cTop.GetFaceAfter(2);
} else if (cSub._numFacesTotal == 1) {
faceOther = faceCorner;
} else if (cSub._numFacesBefore == 0) {
faceOther = cTop.GetFaceAfter(2);
} else if (cSub._numFacesAfter == 0) {
faceOther = cTop.GetFaceBefore(2);
} else {
faceOther = cTop.GetFaceNext(faceCorner);
}
assert(faceOther >= 0);
Index const * fvOther = fvIndices + cTop.GetFaceIndexOffset(faceOther);
Index fvPhantom = fvOther[0];
switch (i) {
case 0:
P[4] = fvOther[0];
if (!cSub.IsBoundary()) {
P[7] = fvOther[1];
P[3] = fvOther[2];
fvOther = fvIndices +
cTop.GetFaceIndexOffset(cTop.GetFaceNext(faceOther));
P[0] = fvOther[2];
} else {
P[7] = (cSub._numFacesAfter) ? fvOther[3 - cSub._numFacesAfter]
: fvPhantom;
P[3] = (cSub._numFacesAfter == 2) ? fvOther[2] : fvPhantom;
P[0] = (cSub._numFacesBefore == 2) ? fvOther[1] : fvPhantom;
}
break;
case 1:
P[5] = fvOther[0];
if (!cSub.IsBoundary()) {
P[1] = fvOther[1];
P[2] = fvOther[2];
fvOther = fvIndices +
cTop.GetFaceIndexOffset(cTop.GetFaceNext(faceOther));
P[6] = fvOther[2];
} else {
P[1] = (cSub._numFacesAfter) ? fvOther[3 - cSub._numFacesAfter]
: fvPhantom;
P[2] = (cSub._numFacesAfter == 2) ? fvOther[2] : fvPhantom;
P[6] = (cSub._numFacesBefore == 2) ? fvOther[1] : fvPhantom;
}
break;
case 2:
P[8] = fvOther[0];
if (!cSub.IsBoundary()) {
P[ 9] = fvOther[1];
P[11] = fvOther[2];
fvOther = fvIndices +
cTop.GetFaceIndexOffset(cTop.GetFaceNext(faceOther));
P[10] = fvOther[2];
} else {
P[ 9] = (cSub._numFacesAfter) ? fvOther[3 - cSub._numFacesAfter]
: fvPhantom;
P[11] = (cSub._numFacesAfter == 2) ? fvOther[2] : fvPhantom;
P[10] = (cSub._numFacesBefore == 2) ? fvOther[1] : fvPhantom;
}
break;
}
fvIndices += cTop.GetNumFaceVertices();
}
}
int
RegularPatchBuilder::GatherControlVertexIndices(Index cvIndices[]) const {
if (_isQuad) {
if (_isBoundary) {
gatherBoundaryPatchPoints4(cvIndices);
} else {
gatherInteriorPatchPoints4(cvIndices);
}
} else {
if (_isBoundary) {
gatherBoundaryPatchPoints3(cvIndices);
} else {
gatherInteriorPatchPoints3(cvIndices);
}
}
return _patchSize;
}
//
// Methods for debugging...
//
void
RegularPatchBuilder::print(Index const P[]) const {
printf("RegularPatchBuilder:\n");
if (_patchType == Far::PatchDescriptor::REGULAR) {
printf(" patch type = REGULAR (B-Spline, quad)\n");
} else if (_patchType == Far::PatchDescriptor::LOOP) {
printf(" patch type = LOOP (Box-Spline, tri)\n");
} else {
assert("Unknown _patchType for RegularPatchBuilder" == 0);
}
printf(" patch size = %d\n", _patchSize);
printf(" is quad = %d\n", _isQuad);
printf(" is boundary = %d\n", _isBoundary);
if (P) {
const char * label = " patch points:";
const char * indent = " ";
if (_isQuad) {
printf("%s %4d %4d %4d %4d\n", label, P[12], P[13], P[14], P[15]);
printf("%s %4d %4d %4d %4d\n", indent, P[ 8], P[ 9], P[10], P[11]);
printf("%s %4d %4d %4d %4d\n", indent, P[ 4], P[ 5], P[ 6], P[ 7]);
printf("%s %4d %4d %4d %4d\n", indent, P[ 0], P[ 1], P[ 2], P[ 3]);
} else {
printf("%s %4d %4d\n", label, P[10], P[11]);
printf("%s %4d %4d %4d\n", indent, P[7], P[8], P[9]);
printf("%s %4d %4d %4d %4d\n", indent, P[3], P[4], P[5], P[6]);
printf("%s %4d %4d %4d\n", indent, P[0], P[1], P[2]);
}
}
}
} // end namespace Bfr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv