mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2025-01-18 12:10:06 +00:00
51eb6a041c
- fixed IrregularPatchBuilder to correctly assign boundary sharpness - modified test shape creating creased val-2 interior face on boundary
921 lines
33 KiB
C++
921 lines
33 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/irregularPatchBuilder.h"
|
|
#include "../bfr/patchTreeBuilder.h"
|
|
#include "../bfr/patchTree.h"
|
|
#include "../far/topologyDescriptor.h"
|
|
#include "../far/topologyRefiner.h"
|
|
|
|
#include <cstring>
|
|
|
|
namespace OpenSubdiv {
|
|
namespace OPENSUBDIV_VERSION {
|
|
|
|
namespace Bfr {
|
|
|
|
//
|
|
// Trivial constructor -- initializes members related to the control hull:
|
|
//
|
|
IrregularPatchBuilder::IrregularPatchBuilder(
|
|
FaceSurface const & surfaceDescription, Options const & options) :
|
|
_surface(surfaceDescription),
|
|
_options(options) {
|
|
|
|
initializeControlHullInventory();
|
|
}
|
|
|
|
//
|
|
// Inline private methods for accessing indices associated with the
|
|
// face-vertex topology and indices stored in map or vector members:
|
|
//
|
|
inline IrregularPatchBuilder::Index const *
|
|
IrregularPatchBuilder::getSurfaceIndices() const {
|
|
|
|
return _surface.GetIndices();
|
|
}
|
|
|
|
inline IrregularPatchBuilder::Index const *
|
|
IrregularPatchBuilder::getCornerIndices(int corner) const {
|
|
|
|
return getSurfaceIndices() +
|
|
_cornerHullInfo[corner].surfaceIndicesOffset;
|
|
}
|
|
|
|
inline IrregularPatchBuilder::Index const *
|
|
IrregularPatchBuilder::getBaseFaceIndices() const {
|
|
|
|
FaceVertex const & corner0 = _surface.GetCornerTopology(0);
|
|
return getSurfaceIndices() +
|
|
corner0.GetFaceIndexOffset(corner0.GetFace());
|
|
}
|
|
|
|
inline IrregularPatchBuilder::Index const *
|
|
IrregularPatchBuilder::getCornerFaceIndices(int corner, int face) const {
|
|
|
|
return getCornerIndices(corner) +
|
|
_surface.GetCornerTopology(corner).GetFaceIndexOffset(face);
|
|
}
|
|
|
|
inline int
|
|
IrregularPatchBuilder::getLocalControlVertex(Index meshVertIndex) const {
|
|
|
|
return _controlVertMap.find(meshVertIndex)->second;
|
|
}
|
|
|
|
inline IrregularPatchBuilder::Index
|
|
IrregularPatchBuilder::getMeshControlVertex(int localVertIndex) const {
|
|
|
|
return _controlVerts[localVertIndex];
|
|
}
|
|
|
|
//
|
|
// The IrregularPatchBuilder assembles a control hull for the base face
|
|
// from the topology information given for each corner of the face. It
|
|
// first initializes the number of control vertices and faces required,
|
|
// along with the contributions of each from the corners of the face.
|
|
//
|
|
// Once initialized, iteration over the corners of the base face is
|
|
// expected to follow a similar pattern when inspecting the incident
|
|
// faces of a corner:
|
|
//
|
|
// - deal with faces after the base face (skipping the first)
|
|
// - deal with boundary vertex between faces after and before
|
|
// - deal with faces before the base face (all)
|
|
//
|
|
// Tags and other inventory assigned here help to expedite and simplify
|
|
// those iterations.
|
|
//
|
|
void
|
|
IrregularPatchBuilder::initializeControlHullInventory() {
|
|
|
|
//
|
|
// Iterate through the corners to identify the vertices, faces and
|
|
// face-vertices that contribute to the collective control hull --
|
|
// keeping track of a few situations that cause complications:
|
|
//
|
|
int numVal2IntCorners = 0;
|
|
int numVal3IntAdjTris = 0;
|
|
int numSrcFaceIndices = 0;
|
|
|
|
int faceSize = _surface.GetFaceSize();
|
|
|
|
_cornerHullInfo.SetSize(faceSize);
|
|
|
|
_numControlFaces = 1;
|
|
_numControlVerts = faceSize;
|
|
_numControlFaceVerts = faceSize;
|
|
|
|
for (int corner = 0; corner < faceSize; ++corner) {
|
|
FaceVertex const & cTop = _surface.GetCornerTopology(corner);
|
|
FaceVertexSubset const & cSub = _surface.GetCornerSubset(corner);
|
|
|
|
//
|
|
// Inspect faces after the corner face first -- dealing with a few
|
|
// special cases for interior vertices of low valence -- followed
|
|
// by those faces before the corner face:
|
|
//
|
|
CornerHull & cHull = _cornerHullInfo[corner];
|
|
cHull.Clear();
|
|
|
|
int numCornerFaceVerts = 0;
|
|
|
|
if (cSub._numFacesAfter) {
|
|
int nextFace = cTop.GetFaceNext(cTop.GetFace());
|
|
|
|
if (cSub.IsBoundary()) {
|
|
// Boundary -- no special cases:
|
|
for (int i = 1; i < cSub._numFacesAfter; ++i) {
|
|
nextFace = cTop.GetFaceNext(nextFace);
|
|
int S = cTop.GetFaceSize(nextFace);
|
|
|
|
cHull.numControlVerts += S - 2;
|
|
numCornerFaceVerts += S;
|
|
}
|
|
cHull.numControlFaces = cSub._numFacesAfter - 1;
|
|
// Include unshared vertex of trailing edge
|
|
cHull.numControlVerts ++;
|
|
} else if ((cSub._numFacesTotal == 3) &&
|
|
(cTop.GetFaceSize(cTop.GetFaceAfter(2)) == 3)) {
|
|
// Interior, valence-3, adjacent triangle -- special case:
|
|
if (++numVal3IntAdjTris == faceSize) {
|
|
cHull.singleSharedVert = true;
|
|
cHull.numControlVerts = 1;
|
|
}
|
|
cHull.numControlFaces = 1;
|
|
numCornerFaceVerts = 3;
|
|
} else if (cSub._numFacesTotal > 2) {
|
|
// Interior -- general case:
|
|
for (int i = 2; i < cSub._numFacesTotal; ++i) {
|
|
nextFace = cTop.GetFaceNext(nextFace);
|
|
int S = cTop.GetFaceSize(nextFace);
|
|
|
|
cHull.numControlVerts += S - 2;
|
|
numCornerFaceVerts += S;
|
|
}
|
|
cHull.numControlFaces = cSub._numFacesTotal - 2;
|
|
// Exclude vertex shared with/contributed by next corner
|
|
cHull.numControlVerts --;
|
|
} else {
|
|
// Interior, valence-2 -- special case:
|
|
if (++numVal2IntCorners == faceSize) {
|
|
cHull.singleSharedFace = true;
|
|
cHull.numControlFaces = 1;
|
|
numCornerFaceVerts = faceSize;
|
|
}
|
|
}
|
|
}
|
|
if (cSub._numFacesBefore) {
|
|
assert(cSub.IsBoundary());
|
|
int nextFace = cTop.GetFaceFirst(cSub);
|
|
|
|
for (int i = 0; i < cSub._numFacesBefore; ++i) {
|
|
int S = cTop.GetFaceSize(nextFace);
|
|
nextFace = cTop.GetFaceNext(nextFace);
|
|
|
|
cHull.numControlVerts += S - 2;
|
|
numCornerFaceVerts += S;
|
|
}
|
|
cHull.numControlFaces += cSub._numFacesBefore;
|
|
// Exclude vertex shared with/contributed by next corner
|
|
cHull.numControlVerts --;
|
|
}
|
|
|
|
// Assign the contributions for this corner:
|
|
cHull.nextControlVert = _numControlVerts;
|
|
cHull.surfaceIndicesOffset = numSrcFaceIndices;
|
|
|
|
_numControlFaces += cHull.numControlFaces;
|
|
_numControlVerts += cHull.numControlVerts;
|
|
_numControlFaceVerts += numCornerFaceVerts;
|
|
|
|
numSrcFaceIndices += cTop.GetNumFaceVertices();
|
|
}
|
|
|
|
//
|
|
// Use/build a map for the control vertex indices when incident
|
|
// faces overlap to an extent that makes traversal ill-defined:
|
|
//
|
|
_controlFacesOverlap = (numVal2IntCorners > 0);
|
|
|
|
_useControlVertMap = _controlFacesOverlap;
|
|
if (_useControlVertMap) {
|
|
initializeControlVertexMap();
|
|
}
|
|
}
|
|
|
|
void
|
|
IrregularPatchBuilder::addMeshControlVertex(Index meshVertIndex) {
|
|
|
|
if (_controlVertMap.find(meshVertIndex) == _controlVertMap.end()) {
|
|
int newLocalVertIndex = (int) _controlVerts.size();
|
|
_controlVertMap[meshVertIndex] = newLocalVertIndex;
|
|
_controlVerts.push_back(meshVertIndex);
|
|
}
|
|
}
|
|
|
|
void
|
|
IrregularPatchBuilder::addMeshControlVertices(Index const fVerts[], int fSize) {
|
|
|
|
// Ignore the first index of the face, which corresponds to a corner
|
|
for (int i = 1; i < fSize; ++i) {
|
|
addMeshControlVertex(fVerts[i]);
|
|
}
|
|
}
|
|
|
|
void
|
|
IrregularPatchBuilder::initializeControlVertexMap() {
|
|
|
|
//
|
|
// Add CV indices from the base face first -- be careful to ensure
|
|
// that a vector entry is made for each base face vertex in cases
|
|
// when repeated indices may occur:
|
|
Index const * baseVerts = getBaseFaceIndices();
|
|
|
|
int faceSize = _surface.GetFaceSize();
|
|
for (int i = 0; i < faceSize; ++i) {
|
|
addMeshControlVertex(baseVerts[i]);
|
|
if ((int)_controlVerts.size() == i) {
|
|
_controlVerts.push_back(baseVerts[i]);
|
|
}
|
|
}
|
|
|
|
//
|
|
// For each corner, add face-vertices to the map only for those
|
|
// incident faces that contribute to the control hull:
|
|
//
|
|
for (int corner = 0; corner < faceSize; ++corner) {
|
|
CornerHull & cHull = _cornerHullInfo[corner];
|
|
if (cHull.numControlFaces == 0) continue;
|
|
|
|
FaceVertex const & cTop = _surface.GetCornerTopology(corner);
|
|
FaceVertexSubset const & cSub = _surface.GetCornerSubset(corner);
|
|
|
|
// Special case of a single shared back-to-back face first:
|
|
if (cHull.singleSharedFace) {
|
|
int nextFace = cTop.GetFaceAfter(1);
|
|
addMeshControlVertices(getCornerFaceIndices(corner, nextFace),
|
|
cTop.GetFaceSize(nextFace));
|
|
continue;
|
|
}
|
|
|
|
// Follow the common pattern: faces after, boundary, faces before
|
|
// (no need to deal with isolated boundary vertex in this case)
|
|
if (cSub._numFacesAfter > 1) {
|
|
int nextFace = cTop.GetFaceAfter(1);
|
|
for (int j = 1; j < cSub._numFacesAfter; ++j) {
|
|
nextFace = cTop.GetFaceNext(nextFace);
|
|
|
|
addMeshControlVertices(getCornerFaceIndices(corner, nextFace),
|
|
cTop.GetFaceSize(nextFace));
|
|
}
|
|
}
|
|
if (cSub._numFacesBefore) {
|
|
int nextFace = cTop.GetFaceFirst(cSub);
|
|
for (int i = 0; i < cSub._numFacesBefore; ++i) {
|
|
addMeshControlVertices(getCornerFaceIndices(corner, nextFace),
|
|
cTop.GetFaceSize(nextFace));
|
|
|
|
nextFace = cTop.GetFaceNext(nextFace);
|
|
}
|
|
}
|
|
}
|
|
_numControlVerts = (int) _controlVerts.size();
|
|
}
|
|
|
|
//
|
|
// Methods for gathering control vertices, faces, sharpness, etc. -- the
|
|
// method to gather control vertex indices is for external use, while the
|
|
// rest are internal:
|
|
//
|
|
int
|
|
IrregularPatchBuilder::GatherControlVertexIndices(Index cvIndices[]) const {
|
|
|
|
//
|
|
// If a map was built, simply copy the associated vector of indices:
|
|
//
|
|
if (_useControlVertMap) {
|
|
std::memcpy(cvIndices, &_controlVerts[0], _numControlVerts*sizeof(int));
|
|
return _numControlVerts;
|
|
}
|
|
|
|
//
|
|
// Assign CV indices from the base face first:
|
|
//
|
|
int faceSize = _surface.GetFaceSize();
|
|
int numIndices = faceSize;
|
|
|
|
std::memcpy(cvIndices, getBaseFaceIndices(), faceSize * sizeof(Index));
|
|
|
|
//
|
|
// Assign vertex indices identified as contributed by each corner:
|
|
//
|
|
for (int corner = 0; corner < faceSize; ++corner) {
|
|
CornerHull const & cHull = _cornerHullInfo[corner];
|
|
if (cHull.numControlVerts == 0) continue;
|
|
|
|
FaceVertex const & cTop = _surface.GetCornerTopology(corner);
|
|
FaceVertexSubset const & cSub = _surface.GetCornerSubset(corner);
|
|
|
|
// Special case with all val-3 interior triangles:
|
|
if (cHull.singleSharedVert) {
|
|
assert(!cSub.IsBoundary() && (cSub._numFacesTotal == 3) &&
|
|
(cTop.GetFaceSize(cTop.GetFaceAfter(2)) == 3));
|
|
|
|
cvIndices[numIndices++] =
|
|
getCornerFaceIndices(corner, cTop.GetFaceAfter(2))[1];
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Follow the common pattern: faces after, boundary, faces before
|
|
//
|
|
if (cSub._numFacesAfter > 1) {
|
|
int nextFace = cTop.GetFaceAfter(1);
|
|
int N = cSub._numFacesAfter - 1;
|
|
for (int j = 0; j < N; ++j) {
|
|
nextFace = cTop.GetFaceNext(nextFace);
|
|
|
|
Index const * faceVerts = getCornerFaceIndices(corner,nextFace);
|
|
|
|
int S = cTop.GetFaceSize(nextFace);
|
|
int L = ((j < (N-1)) || cSub.IsBoundary()) ? 0 : 1;
|
|
int M = (S - 2) - L;
|
|
for (int k = 1; k <= M; ++k) {
|
|
cvIndices[numIndices++] = faceVerts[k];
|
|
}
|
|
}
|
|
}
|
|
if (cSub._numFacesAfter && cSub.IsBoundary()) {
|
|
// Include trailing edge for boundary before crossing the gap:
|
|
int nextFace = cTop.GetFaceAfter(cSub._numFacesAfter);
|
|
cvIndices[numIndices++] = cTop.GetFaceIndexTrailing(nextFace,
|
|
getCornerIndices(corner));
|
|
}
|
|
if (cSub._numFacesBefore) {
|
|
int nextFace = cTop.GetFaceFirst(cSub);
|
|
int N = cSub._numFacesBefore;
|
|
for (int j = 0; j < N; ++j) {
|
|
Index const * faceVerts = getCornerFaceIndices(corner,nextFace);
|
|
|
|
int S = cTop.GetFaceSize(nextFace);
|
|
int L = (j < (N-1)) ? 0 : 1;
|
|
int M = (S - 2) - L;
|
|
for (int k = 1; k <= M; ++k) {
|
|
cvIndices[numIndices++] = faceVerts[k];
|
|
}
|
|
nextFace = cTop.GetFaceNext(nextFace);
|
|
}
|
|
}
|
|
}
|
|
assert(numIndices == _numControlVerts);
|
|
return numIndices;
|
|
}
|
|
|
|
int
|
|
IrregularPatchBuilder::gatherControlFaces(int faceSizes[],
|
|
int faceVertices[]) const {
|
|
|
|
//
|
|
// Assign face-vertices for the first/base face:
|
|
//
|
|
int * faceVerts = faceVertices;
|
|
|
|
int faceSize = _surface.GetFaceSize();
|
|
for (int i = 0; i < faceSize; ++i) {
|
|
*faceVerts++ = i;
|
|
}
|
|
*faceSizes++ = faceSize;
|
|
|
|
//
|
|
// Assign face-vertex indices for faces "local to" each corner:
|
|
//
|
|
for (int corner = 0; corner < faceSize; ++corner) {
|
|
CornerHull const & cHull = _cornerHullInfo[corner];
|
|
if (cHull.numControlFaces == 0) continue;
|
|
|
|
FaceVertex const & cTop = _surface.GetCornerTopology(corner);
|
|
FaceVertexSubset const & cSub = _surface.GetCornerSubset(corner);
|
|
|
|
// Special case of a single shared opposing face first:
|
|
if (cHull.singleSharedFace) {
|
|
assert(_useControlVertMap);
|
|
getControlFaceVertices(faceVerts, faceSize, corner,
|
|
getCornerFaceIndices(corner, cTop.GetFaceAfter(1)));
|
|
*faceSizes++ = faceSize;
|
|
faceVerts += faceSize;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Follow the common pattern: faces after, boundary, faces before
|
|
//
|
|
int nextVert = cHull.nextControlVert;
|
|
|
|
if (cSub._numFacesAfter > 1) {
|
|
int nextFace = cTop.GetFaceAfter(2);
|
|
int N = cSub._numFacesAfter - 1;
|
|
for (int j = 0; j < N; ++j) {
|
|
int S = cTop.GetFaceSize(nextFace);
|
|
|
|
if (_useControlVertMap) {
|
|
getControlFaceVertices(faceVerts, S, corner,
|
|
getCornerFaceIndices(corner, nextFace));
|
|
} else if (cSub.IsBoundary()) {
|
|
getControlFaceVertices(faceVerts, S, corner, nextVert);
|
|
} else {
|
|
getControlFaceVertices(faceVerts, S, corner, nextVert,
|
|
(j == (N - 1)));
|
|
}
|
|
*faceSizes++ = S;
|
|
faceVerts += S;
|
|
|
|
nextVert += S - 2;
|
|
nextFace = cTop.GetFaceNext(nextFace);
|
|
}
|
|
}
|
|
if (cSub._numFacesAfter && cSub.IsBoundary()) {
|
|
nextVert ++;
|
|
}
|
|
if (cSub._numFacesBefore) {
|
|
int nextFace = cTop.GetFaceFirst(cSub);
|
|
int N = cSub._numFacesBefore;
|
|
for (int j = 0; j < N; ++j) {
|
|
int S = cTop.GetFaceSize(nextFace);
|
|
|
|
if (_useControlVertMap) {
|
|
getControlFaceVertices(faceVerts, S, corner,
|
|
getCornerFaceIndices(corner, nextFace));
|
|
} else {
|
|
getControlFaceVertices(faceVerts, S, corner, nextVert,
|
|
(j == (N - 1)));
|
|
}
|
|
*faceSizes++ = S;
|
|
faceVerts += S;
|
|
|
|
nextVert += S - 2;
|
|
nextFace = cTop.GetFaceNext(nextFace);
|
|
}
|
|
}
|
|
}
|
|
assert((faceVerts - faceVertices) == _numControlFaceVerts);
|
|
return _numControlFaceVerts;
|
|
}
|
|
|
|
int
|
|
IrregularPatchBuilder::gatherControlVertexSharpness(
|
|
int vertIndices[], float vertSharpness[]) const {
|
|
|
|
int nSharpVerts = 0;
|
|
for (int i = 0; i < _surface.GetFaceSize(); ++i) {
|
|
FaceVertexSubset const & cSub = _surface.GetCornerSubset(i);
|
|
|
|
if (cSub._tag.IsInfSharp()) {
|
|
vertSharpness[nSharpVerts] = Sdc::Crease::SHARPNESS_INFINITE;
|
|
vertIndices[nSharpVerts++] = i;
|
|
} else if (cSub._tag.IsSemiSharp()) {
|
|
vertSharpness[nSharpVerts] = (cSub._localSharpness > 0.0f) ?
|
|
cSub._localSharpness :
|
|
_surface.GetCornerTopology(i).GetVertexSharpness();
|
|
vertIndices[nSharpVerts++] = i;
|
|
}
|
|
}
|
|
return nSharpVerts;
|
|
}
|
|
|
|
int
|
|
IrregularPatchBuilder::gatherControlEdgeSharpness(
|
|
int edgeVertPairs[], float edgeSharpness[]) const {
|
|
|
|
//
|
|
// First test the forward edge of each corner of the face (avoid
|
|
// including redundant inf-sharp boundary edges):
|
|
//
|
|
int nSharpEdges = 0;
|
|
|
|
int faceSize = _surface.GetFaceSize();
|
|
|
|
for (int corner = 0; corner < faceSize; ++corner) {
|
|
FaceVertexSubset const & cSub = _surface.GetCornerSubset(corner);
|
|
if (!cSub._tag.HasSharpEdges()) continue;
|
|
|
|
if (!cSub.IsBoundary() || cSub._numFacesBefore) {
|
|
FaceVertex const & cTop = _surface.GetCornerTopology(corner);
|
|
|
|
int cornerFace = cTop.GetFace();
|
|
float sharpness = cTop.GetFaceEdgeSharpness(cornerFace, 0);
|
|
if (Sdc::Crease::IsSharp(sharpness)) {
|
|
*edgeSharpness++ = sharpness;
|
|
*edgeVertPairs++ = corner;
|
|
*edgeVertPairs++ = (corner + 1) % faceSize;
|
|
nSharpEdges++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// For each corner, test any interior edges connected to vertices
|
|
// on the perimeter:
|
|
//
|
|
for (int corner = 0; corner < faceSize; ++corner) {
|
|
FaceVertexSubset const & cSub = _surface.GetCornerSubset(corner);
|
|
if (!cSub._tag.HasSharpEdges()) continue;
|
|
|
|
CornerHull const & cHull = _cornerHullInfo[corner];
|
|
if (cHull.numControlFaces == 0) continue;
|
|
|
|
FaceVertex const & cTop = _surface.GetCornerTopology(corner);
|
|
|
|
//
|
|
// Inspect interior edges around the subset -- testing sharpness
|
|
// of the trailing edge of the faces after/before the corner face.
|
|
//
|
|
// Follow the common pattern: faces after, boundary, faces before
|
|
//
|
|
// Track perimeter index to identify verts at end of sharp edges:
|
|
int maxVert = _numControlVerts;
|
|
int nextVert = cHull.nextControlVert;
|
|
|
|
Index const * cVerts = getCornerIndices(corner);
|
|
|
|
if (cSub._numFacesAfter > 1) {
|
|
int nextFace = cTop.GetFaceAfter(1);
|
|
for (int i = 1; i < cSub._numFacesAfter; ++i) {
|
|
float sharpness = cTop.GetFaceEdgeSharpness(nextFace, 1);
|
|
if (Sdc::Crease::IsSharp(sharpness)) {
|
|
int edgeVert = (nextVert < maxVert) ? nextVert : faceSize;
|
|
if (_useControlVertMap) {
|
|
edgeVert = getLocalControlVertex(
|
|
cTop.GetFaceIndexTrailing(nextFace, cVerts));
|
|
}
|
|
|
|
*edgeSharpness++ = sharpness;
|
|
*edgeVertPairs++ = corner;
|
|
*edgeVertPairs++ = edgeVert;
|
|
nSharpEdges++;
|
|
}
|
|
nextFace = cTop.GetFaceNext(nextFace);
|
|
nextVert += cTop.GetFaceSize(nextFace) - 2;
|
|
}
|
|
}
|
|
if (cSub._numFacesAfter && cSub.IsBoundary()) {
|
|
nextVert += cSub.IsBoundary();
|
|
}
|
|
if (cSub._numFacesBefore) {
|
|
int nextFace = cTop.GetFaceFirst(cSub);
|
|
for (int i = 1; i < cSub._numFacesBefore; ++i) {
|
|
nextVert += cTop.GetFaceSize(nextFace) - 2;
|
|
float sharpness = cTop.GetFaceEdgeSharpness(nextFace, 1);
|
|
if (Sdc::Crease::IsSharp(sharpness)) {
|
|
int edgeVert = (nextVert < maxVert) ? nextVert : faceSize;
|
|
if (_useControlVertMap) {
|
|
edgeVert = getLocalControlVertex(
|
|
cTop.GetFaceIndexTrailing(nextFace, cVerts));
|
|
}
|
|
|
|
*edgeSharpness++ = sharpness;
|
|
*edgeVertPairs++ = corner;
|
|
*edgeVertPairs++ = edgeVert;
|
|
nSharpEdges++;
|
|
}
|
|
nextFace = cTop.GetFaceNext(nextFace);
|
|
}
|
|
}
|
|
}
|
|
return nSharpEdges;
|
|
}
|
|
|
|
|
|
//
|
|
// Methods to gather the local face-vertices for a particular incident
|
|
// face of a corner. The first is the trivial case -- where all vertices
|
|
// other than the initial corner vertex lie on the perimeter and do not
|
|
// wrap around. The second deals handles more of the complications and
|
|
// is used in all cases where the trivial method cannot be applied.
|
|
//
|
|
void
|
|
IrregularPatchBuilder::getControlFaceVertices(int fVerts[], int numFVerts,
|
|
int corner, Index const srcVerts[]) const {
|
|
assert(_useControlVertMap);
|
|
|
|
*fVerts++ = corner;
|
|
for (int i = 1; i < numFVerts; ++i) {
|
|
*fVerts++ = getLocalControlVertex(srcVerts[i]);
|
|
}
|
|
}
|
|
|
|
void
|
|
IrregularPatchBuilder::getControlFaceVertices(int fVerts[], int numFVerts,
|
|
int corner, int nextPerimeterVert) const {
|
|
|
|
*fVerts++ = corner;
|
|
for (int i = 1; i < numFVerts; ++i) {
|
|
*fVerts++ = nextPerimeterVert + i - 1;
|
|
}
|
|
}
|
|
|
|
void
|
|
IrregularPatchBuilder::getControlFaceVertices(int fVerts[], int numFVerts,
|
|
int corner, int nextPerimeterVert, bool lastFace) const {
|
|
|
|
int S = numFVerts;
|
|
int N = _surface.GetFaceSize();
|
|
|
|
//
|
|
// The typical case: the corner vertex first, followed by vertices
|
|
// on the perimeter -- potentially wrapping around the perimeter to
|
|
// the first corner or the base face:
|
|
//
|
|
// - for the last corner only, the face-vert preceding the last
|
|
// will wrap around the perimeter
|
|
// - for all corners, the last face-vert of the last face wraps
|
|
// around the face to the next corner
|
|
//
|
|
*fVerts++ = corner;
|
|
|
|
for (int i = 1; i < S - 2; ++i) {
|
|
*fVerts++ = nextPerimeterVert + i - 1;
|
|
}
|
|
|
|
int nextToLastPerimOfFace = nextPerimeterVert + S - 3;
|
|
if (nextToLastPerimOfFace == _numControlVerts) {
|
|
nextToLastPerimOfFace = N;
|
|
}
|
|
*fVerts++ = nextToLastPerimOfFace;
|
|
|
|
int lastPerimOfFace = nextPerimeterVert + S - 2;
|
|
if (lastPerimOfFace == _numControlVerts) {
|
|
lastPerimOfFace = N;
|
|
}
|
|
*fVerts++ = lastFace ? ((corner + 1) % N) : lastPerimOfFace;
|
|
}
|
|
|
|
//
|
|
// Detection and removal of duplicate control faces -- which can only
|
|
// occur when incident faces overlap with the base face:
|
|
//
|
|
namespace {
|
|
//
|
|
// Internal helper functions to detect duplicate faces:
|
|
//
|
|
bool
|
|
doFacesMatch(int size, int const a[], int const b[], int bStart) {
|
|
for (int i = 0, j = bStart; i < size; ++i, ++j) {
|
|
j = (j == size) ? 0 : j;
|
|
if (a[i] != b[j]) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
doFacesMatch(int size, int const a[], int const b[]) {
|
|
// Find a matching vertex to correlate possible rotation:
|
|
for (int i = 0; i < size; ++i) {
|
|
if (b[i] == a[0]) {
|
|
return doFacesMatch(size, a, b, i);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void
|
|
IrregularPatchBuilder::removeDuplicateControlFaces(
|
|
int faceSizes[], int faceVerts[],
|
|
int * numFaces, int * numFaceVerts) const {
|
|
|
|
//
|
|
// Work backwards from the last face -- detecting if it matches a
|
|
// face earlier in the arrays and removing it if so:
|
|
//
|
|
int numSizesAfter = 0;
|
|
int numVertsAfter = 0;
|
|
|
|
int * sizesAfter = faceSizes + *numFaces;
|
|
int * vertsAfter = faceVerts + *numFaceVerts;
|
|
|
|
for (int i = *numFaces - 1; i > 1; --i) {
|
|
int iSize = faceSizes[i];
|
|
int * iVerts = vertsAfter - iSize;
|
|
|
|
// Inspect the faces preceding this face for a duplicate:
|
|
bool isDuplicate = false;
|
|
|
|
int * jVerts = iVerts;
|
|
for (int j = i - 1; !isDuplicate && (j > 0); --j) {
|
|
jVerts = jVerts - faceSizes[j];
|
|
if (iSize == faceSizes[j]) {
|
|
isDuplicate = doFacesMatch(iSize, iVerts, jVerts);
|
|
}
|
|
}
|
|
|
|
// If this face was duplicated by one preceding it, remove it:
|
|
if (isDuplicate) {
|
|
if (numSizesAfter) {
|
|
std::memmove(sizesAfter - 1, sizesAfter,
|
|
numSizesAfter * sizeof(int));
|
|
std::memmove(vertsAfter - iSize, vertsAfter,
|
|
numVertsAfter * sizeof(int));
|
|
}
|
|
(*numFaces) --;
|
|
(*numFaceVerts) -= iSize;
|
|
} else {
|
|
numSizesAfter ++;
|
|
numVertsAfter += iSize;
|
|
}
|
|
sizesAfter --;
|
|
vertsAfter -= iSize;
|
|
}
|
|
}
|
|
|
|
void
|
|
IrregularPatchBuilder::sharpenBoundaryControlEdges(
|
|
int edgeVertPairs[], float edgeSharpness[], int * numSharpEdges) const {
|
|
|
|
//
|
|
// When extracting a manifold subset from a greater non-manifold
|
|
// region, the boundary edges for the subset sometimes occur on
|
|
// non-manifold edges. When extracting the subset topology in
|
|
// cases where faces overlap, those boundary edges can sometimes
|
|
// be misinterpreted as manifold interior edges -- as the extra
|
|
// faces connected to them that made the edge non-manifold are
|
|
// not included in the subset.
|
|
//
|
|
// So boundary edges are sharpened here -- which generally has no
|
|
// effect (as boundary edges are implicitly sharpened) but ensures
|
|
// that if the edge is misinterpreted as interior, it will remain
|
|
// sharp. And only boundary edges of the base face are sharpened
|
|
// here -- it is not necessary to deal with others.
|
|
//
|
|
// Append boundary edge sharpness to existing sharp edges:
|
|
edgeVertPairs += *numSharpEdges * 2;
|
|
edgeSharpness += *numSharpEdges;
|
|
|
|
int faceSize = _surface.GetFaceSize();
|
|
|
|
for (int corner = 0; corner < faceSize; ++corner) {
|
|
FaceVertexSubset const & cSub = _surface.GetCornerSubset(corner);
|
|
|
|
if (cSub.IsBoundary() && (cSub._numFacesBefore == 0)) {
|
|
*edgeSharpness++ = Sdc::Crease::SHARPNESS_INFINITE;
|
|
*edgeVertPairs++ = corner;
|
|
*edgeVertPairs++ = (corner + 1) % faceSize;
|
|
(*numSharpEdges) ++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// The main build/assembly method to create a PatchTree:
|
|
//
|
|
// Note that the IrregularPatchBuilder was conceived to potentially build
|
|
// different representations of irregular patches (all sharing a virtual
|
|
// interface to hide that from it clients). It was here that topology
|
|
// would be inspected and builders for the different representations would
|
|
// be dispatched.
|
|
//
|
|
// At this point, the PatchTree is used for all topological cases, so
|
|
// those future intentions are not reflected here.
|
|
//
|
|
internal::IrregularPatchSharedPtr
|
|
IrregularPatchBuilder::Build() {
|
|
|
|
//
|
|
// Build a PatchTree -- the sole representation used for all irregular
|
|
// patch topologies.
|
|
//
|
|
// Given the PatchTree's origin in Far, it's builder class requires a
|
|
// Far::TopologyRefiner. Now that PatchTreeBuilder is part of Bfr, it
|
|
// could be adapted to accept the Bfr::FaceSurface directly and deal
|
|
// with any any intermediate TopologyRefiner internally.
|
|
//
|
|
// For now, the quickest way of constructing a TopologyRefiner is via
|
|
// a Far::TopologyDescriptor -- which simply refers to pre-allocated
|
|
// topology arrays. Those arrays will be allocated on the stack here
|
|
// to accommodate typical cases not involving excessively high valence.
|
|
//
|
|
// Use of TopologyDescriptor could be eliminated by defining a factory
|
|
// to create a TopologyRefiner directly from the FaceSurface, but the
|
|
// benefits relative to the cost of creating the PatchTree are not
|
|
// significant. The fact that the current assembly requires removing
|
|
// duplicate faces in some cases further complicates that process.
|
|
//
|
|
int numVerts = _numControlVerts;
|
|
int numFaces = _numControlFaces;
|
|
int numFaceVerts = _numControlFaceVerts;
|
|
int numCorners = _surface.GetFaceSize();
|
|
int numCreases = _numControlVerts;
|
|
|
|
// Allocate and partition stack buffers for the topology arrays:
|
|
int numInts = numFaces + numFaceVerts + numCorners + numCreases*2;
|
|
int numFloats = numCorners + numCreases;
|
|
|
|
Vtr::internal::StackBuffer<int, 256,true> intBuffer(numInts);
|
|
Vtr::internal::StackBuffer<float,64,true> floatBuffer(numFloats);
|
|
|
|
int * faceSizes = intBuffer;
|
|
int * faceVerts = faceSizes + numFaces;
|
|
int * cornerIndices = faceVerts + numFaceVerts;
|
|
int * creaseIndices = cornerIndices + numCorners;
|
|
|
|
float * cornerWeights = floatBuffer;
|
|
float * creaseWeights = cornerWeights + numCorners;
|
|
|
|
// Gather face topology (sizes and vertices) and explicit sharpness:
|
|
gatherControlFaces(faceSizes, faceVerts);
|
|
|
|
numCorners = _surface.GetTag().HasSharpVertices() ?
|
|
gatherControlVertexSharpness(cornerIndices, cornerWeights) : 0;
|
|
|
|
numCreases = _surface.GetTag().HasSharpEdges() ?
|
|
gatherControlEdgeSharpness(creaseIndices, creaseWeights) : 0;
|
|
|
|
// Make some adjustments when control faces may overlap:
|
|
if (controlFacesMayOverlap()) {
|
|
if (numFaces > 2) {
|
|
removeDuplicateControlFaces(faceSizes, faceVerts,
|
|
&numFaces, &numFaceVerts);
|
|
}
|
|
if (_surface.GetTag().HasBoundaryVertices()) {
|
|
sharpenBoundaryControlEdges(creaseIndices, creaseWeights,
|
|
&numCreases);
|
|
}
|
|
}
|
|
|
|
// Declare a TopologyDescriptor to reference the data gathered above:
|
|
Far::TopologyDescriptor topDescriptor;
|
|
|
|
topDescriptor.numVertices = numVerts;
|
|
topDescriptor.numFaces = numFaces;
|
|
|
|
topDescriptor.numVertsPerFace = faceSizes;
|
|
topDescriptor.vertIndicesPerFace = faceVerts;
|
|
|
|
if (numCorners) {
|
|
topDescriptor.numCorners = numCorners;
|
|
topDescriptor.cornerVertexIndices = cornerIndices;
|
|
topDescriptor.cornerWeights = cornerWeights;
|
|
}
|
|
|
|
if (numCreases) {
|
|
topDescriptor.numCreases = numCreases;
|
|
topDescriptor.creaseVertexIndexPairs = creaseIndices;
|
|
topDescriptor.creaseWeights = creaseWeights;
|
|
}
|
|
|
|
// Construct a TopologyRefiner in order to create a PatchTree:
|
|
typedef Far::TopologyDescriptor Descriptor;
|
|
typedef Far::TopologyRefinerFactory<Descriptor> RefinerFactory;
|
|
|
|
RefinerFactory::Options refinerOptions;
|
|
refinerOptions.schemeType = _surface.GetSdcScheme();
|
|
refinerOptions.schemeOptions = _surface.GetSdcOptionsInEffect();
|
|
// WIP - enable for debugging
|
|
//refinerOptions.validateFullTopology = true;
|
|
|
|
Far::TopologyRefiner * refiner =
|
|
RefinerFactory::Create(topDescriptor, refinerOptions);
|
|
|
|
// Create the PatchTree from the TopologyRefiner:
|
|
PatchTreeBuilder::Options patchTreeOptions;
|
|
patchTreeOptions.includeInteriorPatches = false;
|
|
patchTreeOptions.maxPatchDepthSharp = (unsigned char)_options.sharpLevel;
|
|
patchTreeOptions.maxPatchDepthSmooth = (unsigned char)_options.smoothLevel;
|
|
patchTreeOptions.useDoublePrecision = _options.doublePrecision;
|
|
|
|
PatchTreeBuilder patchTreeBuilder(*refiner, patchTreeOptions);
|
|
|
|
PatchTree const * patchTree = patchTreeBuilder.Build();
|
|
|
|
assert(patchTree->GetNumControlPoints() == _numControlVerts);
|
|
|
|
delete refiner;
|
|
return internal::IrregularPatchSharedPtr(patchTree);
|
|
}
|
|
|
|
} // end namespace Bfr
|
|
|
|
} // end namespace OPENSUBDIV_VERSION
|
|
} // end namespace OpenSubdiv
|